diff --git a/.github/workflows/Sublimation.yml b/.github/workflows/Sublimation.yml index 6a23f93..b422c09 100644 --- a/.github/workflows/Sublimation.yml +++ b/.github/workflows/Sublimation.yml @@ -9,21 +9,13 @@ jobs: build-ubuntu: name: Build on Ubuntu env: - SWIFT_VER: ${{ matrix.swift-version }} - runs-on: ${{ matrix.runs-on }} + SWIFT_VER: 6.0 if: "!contains(github.event.head_commit.message, 'ci skip')" - strategy: - matrix: - runs-on: [ubuntu-20.04, ubuntu-22.04] - swift-version: ["5.10"] + runs-on: ubuntu-latest + container: + image: swift:6.0-noble steps: - uses: actions/checkout@v4 - - name: Set Ubuntu Release DOT - run: echo "RELEASE_DOT=$(lsb_release -sr)" >> $GITHUB_ENV - - name: Set Ubuntu Release NUM - run: echo "RELEASE_NUM=${RELEASE_DOT//[-._]/}" >> $GITHUB_ENV - - name: Set Ubuntu Codename - run: echo "RELEASE_NAME=$(lsb_release -sc)" >> $GITHUB_ENV - name: Cache swift package modules id: cache-spm-linux uses: actions/cache@v4 @@ -31,42 +23,24 @@ jobs: cache-name: cache-spm with: path: .build - key: ${{ runner.os }}-${{ env.RELEASE_DOT }}-${{ env.cache-name }}-${{ matrix.swift-version }}-${{ hashFiles('Package.resolved') }} - restore-keys: | - ${{ runner.os }}-${{ env.RELEASE_DOT }}-${{ env.cache-name }}-${{ matrix.swift-version }}- - ${{ runner.os }}-${{ env.RELEASE_DOT }}-${{ env.cache-name }}- - - name: Cache swift - id: cache-swift-linux - uses: actions/cache@v4 - env: - cache-name: cache-swift - with: - path: swift-${{ env.SWIFT_VER }}-RELEASE-ubuntu${{ env.RELEASE_DOT }} - key: ${{ runner.os }}-${{ env.cache-name }}-${{ matrix.swift-version }}-${{ env.RELEASE_DOT }} + key: ${{ runner.os }}-${{ env.cache-name }}-${{ matrix.swift-version }}-${{ hashFiles('Package.resolved') }} restore-keys: | ${{ runner.os }}-${{ env.cache-name }}-${{ matrix.swift-version }}- - - name: Download Swift - if: steps.cache-swift-linux.outputs.cache-hit != 'true' - run: curl -O https://download.swift.org/swift-${SWIFT_VER}-release/ubuntu${RELEASE_NUM}/swift-${SWIFT_VER}-RELEASE/swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}.tar.gz - - name: Extract Swift - if: steps.cache-swift-linux.outputs.cache-hit != 'true' - run: tar xzf swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}.tar.gz - - name: Add Path - run: echo "$GITHUB_WORKSPACE/swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}/usr/bin" >> $GITHUB_PATH + ${{ runner.os }}-${{ env.cache-name }}- - name: Test run: swift test --enable-code-coverage - uses: sersoft-gmbh/swift-coverage-action@v4 id: coverage-files - with: + with: fail-on-empty-output: true - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 with: fail_ci_if_error: true - flags: swift-${{ matrix.swift-version }},ubuntu-${{ matrix.RELEASE_DOT }} + flags: swift-${{ matrix.swift-version }},ubuntu verbose: true token: ${{ secrets.CODECOV_TOKEN }} - files: ${{ join(fromJSON(steps.coverage-files.outputs.files), ',') }} + files: ${{ join(fromJSON(steps.coverage-files.outputs.files), ',') }} build-macos: name: Build on macOS runs-on: ${{ matrix.os }} @@ -74,18 +48,12 @@ jobs: strategy: matrix: include: - - xcode: "/Applications/Xcode_15.3.app" + - xcode: "/Applications/Xcode_16.1.app" os: macos-14 - iOSVersion: "17.2" - watchOSVersion: "10.2" - watchName: "Apple Watch Ultra (49mm)" - iPhoneName: "iPhone 15 Pro" - - xcode: "/Applications/Xcode_15.4.app" - os: macos-14 - iOSVersion: "17.4" - watchOSVersion: "10.4" - watchName: "Apple Watch Ultra 2 (49mm)" - iPhoneName: "iPhone 15 Pro Max" + iOSVersion: "18.1" + watchOSVersion: "11.0" + watchName: "Apple Watch Series 9 (41mm)" + iPhoneName: "iPhone 15" steps: - uses: actions/checkout@v4 - name: Cache swift package modules @@ -99,7 +67,7 @@ jobs: restore-keys: | ${{ matrix.os }}-build-${{ env.cache-name }}-${{ matrix.xcode }}- - name: Cache mint - if: startsWith(matrix.xcode,'/Applications/Xcode_15.3') + if: startsWith(matrix.xcode,'/Applications/Xcode_16.1') id: cache-mint uses: actions/cache@v4 env: @@ -114,9 +82,9 @@ jobs: - name: Set Xcode Name run: echo "XCODE_NAME=$(basename -- ${{ matrix.xcode }} | sed 's/\.[^.]*$//' | cut -d'_' -f2)" >> $GITHUB_ENV - name: Setup Xcode - run: sudo xcode-select -s ${{ matrix.xcode }}/Contents/Developer + run: sudo xcode-select -s ${{ matrix.xcode }}/Contents/Developer || (sudo ls -1 /Applications | grep "Xcode") - name: Install mint - if: startsWith(matrix.xcode,'/Applications/Xcode_15.3') + if: startsWith(matrix.xcode,'/Applications/Xcode_16.1') run: | brew update brew install mint @@ -126,7 +94,7 @@ jobs: run: swift test --enable-code-coverage - uses: sersoft-gmbh/swift-coverage-action@v4 id: coverage-files-spm - with: + with: fail-on-empty-output: true - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v4 @@ -138,12 +106,12 @@ jobs: run: rm -rf .build - name: Lint run: ./scripts/lint.sh - if: startsWith(matrix.xcode,'/Applications/Xcode_15.3') + if: startsWith(matrix.xcode,'/Applications/Xcode_16.1') - name: Run iOS target tests run: xcodebuild test -scheme ${{ env.PACKAGE_NAME }}-Package -sdk "iphonesimulator" -destination 'platform=iOS Simulator,name=${{ matrix.iPhoneName }},OS=${{ matrix.iOSVersion }}' -enableCodeCoverage YES build test - uses: sersoft-gmbh/swift-coverage-action@v4 id: coverage-files-iOS - with: + with: fail-on-empty-output: true - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 @@ -157,7 +125,7 @@ jobs: run: xcodebuild test -scheme ${{ env.PACKAGE_NAME }}-Package -sdk "watchsimulator" -destination 'platform=watchOS Simulator,name=${{ matrix.watchName }},OS=${{ matrix.watchOSVersion }}' -enableCodeCoverage YES build test - uses: sersoft-gmbh/swift-coverage-action@v4 id: coverage-files-watchOS - with: + with: fail-on-empty-output: true - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 @@ -167,49 +135,3 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} files: ${{ join(fromJSON(steps.coverage-files-watchOS.outputs.files), ',') }} flags: watchOS,watchOS${{ matrix.watchOSVersion }},macOS,${{ env.XCODE_NAME }} - build-self: - name: Build on Self-Hosting macOS - runs-on: [self-hosted, macOS] - if: github.event.repository.owner.login == github.event.organization.login && !contains(github.event.head_commit.message, 'ci skip') - steps: - - uses: actions/checkout@v4 - - name: Cache swift package modules - id: cache-spm-macos - uses: actions/cache@v4 - env: - cache-name: cache-spm - with: - path: .build - key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('Package.resolved') }} - restore-keys: | - ${{ runner.os }}-build-${{ env.cache-name }}- - ${{ runner.os }}-build- - ${{ runner.os }}- - - name: Cache mint - id: cache-mint - uses: actions/cache@v4 - env: - cache-name: cache-mint - with: - path: .mint - key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('Mintfile') }} - restore-keys: | - ${{ runner.os }}-build-${{ env.cache-name }}- - ${{ runner.os }}-build- - ${{ runner.os }}- - - name: Build - run: swift build - - name: Run Swift Package tests - run: swift test --enable-code-coverage - - uses: sersoft-gmbh/swift-coverage-action@v4 - with: - fail-on-empty-output: true - - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v4 - with: - token: ${{ secrets.CODECOV_TOKEN }} - flags: macOS,${{ env.XCODE_NAME }} - - name: Clean up spm build directory - run: rm -rf .build - - name: Lint - run: ./scripts/lint.sh diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index d361ce5..92b69d6 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -23,6 +23,7 @@ on: jobs: analyze: + if: false name: Analyze # Runner size impacts CodeQL analysis time. To learn more, please see: # - https://gh.io/recommended-hardware-resources-for-running-codeql diff --git a/.gitignore b/.gitignore index 2d84c52..0c1965d 100644 --- a/.gitignore +++ b/.gitignore @@ -119,7 +119,7 @@ fastlane/test_output iOSInjectionProject/ ### SwiftPackageManager ### -Packages +# Packages xcuserdata *.xcodeproj @@ -142,3 +142,6 @@ xcuserdata !Demo/SublimationDemoApp.xcodeproj .mint # End of https://www.toptal.com/developers/gitignore/api/swift,swiftpm,swiftpackagemanager,xcode,macos + +test_output.log +.docc-build \ No newline at end of file diff --git a/.hound.yml b/.hound.yml deleted file mode 100644 index 6941f63..0000000 --- a/.hound.yml +++ /dev/null @@ -1,2 +0,0 @@ -swiftlint: - config_file: .swiftlint.yml diff --git a/.periphery.yml b/.periphery.yml index 0a16e43..85b884a 100644 --- a/.periphery.yml +++ b/.periphery.yml @@ -1,3 +1 @@ retain_public: true -index_exclude: - - Sources/NgrokOpenAPIClient/*.swift diff --git a/.spi.yml b/.spi.yml index b47d782..7edb708 100644 --- a/.spi.yml +++ b/.spi.yml @@ -1,4 +1,5 @@ version: 1 builder: configs: - - documentation_targets: [Ngrokit, Sublimation, SublimationVapor] + - documentation_targets: [Sublimation] + swift_version: 6.0 diff --git a/.swift-format b/.swift-format new file mode 100644 index 0000000..4f562bf --- /dev/null +++ b/.swift-format @@ -0,0 +1,70 @@ +{ + "fileScopedDeclarationPrivacy" : { + "accessLevel" : "fileprivate" + }, + "indentation" : { + "spaces" : 2 + }, + "indentConditionalCompilationBlocks" : true, + "indentSwitchCaseLabels" : true, + "lineBreakAroundMultilineExpressionChainComponents" : true, + "lineBreakBeforeControlFlowKeywords" : true, + "lineBreakBeforeEachArgument" : true, + "lineBreakBeforeEachGenericRequirement" : true, + "lineLength" : 100, + "maximumBlankLines" : 1, + "multiElementCollectionTrailingCommas" : true, + "noAssignmentInExpressions" : { + "allowedFunctions" : [ + "XCTAssertNoThrow" + ] + }, + "prioritizeKeepingFunctionOutputTogether" : false, + "respectsExistingLineBreaks" : false, + "rules" : { + "AllPublicDeclarationsHaveDocumentation" : true, + "AlwaysUseLiteralForEmptyCollectionInit" : false, + "AlwaysUseLowerCamelCase" : true, + "AmbiguousTrailingClosureOverload" : true, + "BeginDocumentationCommentWithOneLineSummary" : false, + "DoNotUseSemicolons" : true, + "DontRepeatTypeInStaticProperties" : true, + "FileScopedDeclarationPrivacy" : true, + "FullyIndirectEnum" : true, + "GroupNumericLiterals" : true, + "IdentifiersMustBeASCII" : true, + "NeverForceUnwrap" : true, + "NeverUseForceTry" : true, + "NeverUseImplicitlyUnwrappedOptionals" : true, + "NoAccessLevelOnExtensionDeclaration" : true, + "NoAssignmentInExpressions" : true, + "NoBlockComments" : true, + "NoCasesWithOnlyFallthrough" : true, + "NoEmptyTrailingClosureParentheses" : true, + "NoLabelsInCasePatterns" : true, + "NoLeadingUnderscores" : false, + "NoParensAroundConditions" : true, + "NoPlaygroundLiterals" : true, + "NoVoidReturnOnFunctionSignature" : true, + "OmitExplicitReturns" : false, + "OneCasePerLine" : true, + "OneVariableDeclarationPerLine" : true, + "OnlyOneTrailingClosureArgument" : true, + "OrderedImports" : true, + "ReplaceForEachWithForLoop" : true, + "ReturnVoidInsteadOfEmptyTuple" : true, + "TypeNamesShouldBeCapitalized" : true, + "UseEarlyExits" : false, + "UseExplicitNilCheckInConditions" : true, + "UseLetInEveryBoundCaseVariable" : true, + "UseShorthandTypeNames" : true, + "UseSingleLinePropertyGetter" : true, + "UseSynthesizedInitializer" : true, + "UseTripleSlashForDocumentationComments" : true, + "UseWhereClausesInForLoops" : true, + "ValidateDocumentationComments" : true + }, + "spacesAroundRangeFormationOperators" : false, + "tabWidth" : 2, + "version" : 1 +} diff --git a/.swift-version b/.swift-version deleted file mode 100644 index f9ce5a9..0000000 --- a/.swift-version +++ /dev/null @@ -1 +0,0 @@ -5.10 diff --git a/.swiftformat b/.swiftformat deleted file mode 100644 index 0f010e9..0000000 --- a/.swiftformat +++ /dev/null @@ -1,7 +0,0 @@ ---indent 2 ---header "\n .*?\.swift\n Sublimation\n\n Created by Leo Dion.\n Copyright © {year} BrightDigit.\n\n Permission is hereby granted, free of charge, to any person\n obtaining a copy of this software and associated documentation\n files (the “Software”), to deal in the Software without\n restriction, including without limitation the rights to use,\n copy, modify, merge, publish, distribute, sublicense, and/or\n sell copies of the Software, and to permit persons to whom the\n Software is furnished to do so, subject to the following\n conditions:\n \n The above copyright notice and this permission notice shall be\n included in all copies or substantial portions of the Software.\n\n THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,\n EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\n OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n OTHER DEALINGS IN THE SOFTWARE.\n" ---commas inline ---disable wrapMultilineStatementBraces, redundantInternal,redundantSelf,wrapMultilineStatementBraces,genericExtensions ---extensionacl on-declarations ---decimalgrouping 3,4 ---exclude .build, DerivedData, .swiftpm, Sources/NgrokOpenAPIClient, Demo diff --git a/.swiftlint.yml b/.swiftlint.yml deleted file mode 100644 index 39e2b65..0000000 --- a/.swiftlint.yml +++ /dev/null @@ -1,137 +0,0 @@ -opt_in_rules: - - array_init - - closure_body_length - - closure_end_indentation - - closure_spacing - - collection_alignment - - conditional_returns_on_newline - - contains_over_filter_count - - contains_over_filter_is_empty - - contains_over_first_not_nil - - contains_over_range_nil_comparison - - convenience_type - - discouraged_object_literal - - discouraged_optional_boolean - - empty_collection_literal - - empty_count - - empty_string - - empty_xctest_method - - enum_case_associated_values_count - - expiring_todo - - explicit_acl - - explicit_init - - explicit_top_level_acl - # - fallthrough - - fatal_error_message - - file_name - - file_name_no_space - - file_types_order - - first_where - - flatmap_over_map_reduce - - force_unwrapping -# - function_default_parameter_at_end - - ibinspectable_in_extension - - identical_operands - - implicit_return - - implicitly_unwrapped_optional - - indentation_width - - joined_default_parameter - - last_where - - legacy_multiple - - legacy_random - - literal_expression_end_indentation - - lower_acl_than_parent - - missing_docs - - modifier_order - - multiline_arguments - - multiline_arguments_brackets - - multiline_function_chains - - multiline_literal_brackets - - multiline_parameters - - nimble_operator - - nslocalizedstring_key - - nslocalizedstring_require_bundle - - number_separator - - object_literal - - operator_usage_whitespace - - optional_enum_case_matching - - overridden_super_call - - override_in_extension - - pattern_matching_keywords - - prefer_self_type_over_type_of_self - - prefer_zero_over_explicit_init - - private_action - - private_outlet - - prohibited_interface_builder - - prohibited_super_call - - quick_discouraged_call - - quick_discouraged_focused_test - - quick_discouraged_pending_test - - reduce_into - - redundant_nil_coalescing - - redundant_type_annotation - - required_enum_case - - single_test_class - - sorted_first_last - - sorted_imports - - static_operator - - strong_iboutlet - - toggle_bool -# - trailing_closure - - type_contents_order - - unavailable_function - - unneeded_parentheses_in_closure_argument - - unowned_variable_capture - - untyped_error_in_catch - - vertical_parameter_alignment_on_call - - vertical_whitespace_closing_braces - - vertical_whitespace_opening_braces - - xct_specific_matcher - - yoda_condition -analyzer_rules: - - unused_import - - unused_declaration -cyclomatic_complexity: - - 6 - - 12 -type_body_length: - - 160 - - 250 -file_length: - warning: 215 - error: 300 -function_body_length: - - 25 - - 40 -function_parameter_count: 8 -line_length: - - 108 - - 200 -closure_body_length: - - 50 - - 60 -generic_type_name: - max_length: 40 -identifier_name: - excluded: - - id - - no -excluded: - - DerivedData - - .build - - .swiftpm - - Demo - - Sources/NgrokOpenAPIClient - - Sources/SublimationVapor/Tunnel - - Tests/SublimationVaporTests/NetworkResultTests.swift -indentation_width: - indentation_width: 2 -file_name: - severity: error -fatal_error_message: - severity: error -disabled_rules: - - todo - - nesting - - implicit_getter - - switch_case_alignment diff --git a/Assets/logo.png b/Assets/logo.png deleted file mode 100644 index 3a6dd69..0000000 Binary files a/Assets/logo.png and /dev/null differ diff --git a/Assets/logo.pxd b/Assets/logo.pxd deleted file mode 100644 index 27ccb83..0000000 Binary files a/Assets/logo.pxd and /dev/null differ diff --git a/Assets/logo.svg b/Assets/logo.svg deleted file mode 100644 index a28c199..0000000 --- a/Assets/logo.svg +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Demo/SublimationDemoApp.xcodeproj/xcshareddata/xcschemes/SublimationDemoApp Watch App.xcscheme b/Demo/SublimationDemoApp.xcodeproj/xcshareddata/xcschemes/SublimationDemoApp Watch App.xcscheme new file mode 100644 index 0000000..584947a --- /dev/null +++ b/Demo/SublimationDemoApp.xcodeproj/xcshareddata/xcschemes/SublimationDemoApp Watch App.xcscheme @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Demo/SublimationDemoApp.xcodeproj/xcshareddata/xcschemes/SublimationDemoApp.xcscheme b/Demo/SublimationDemoApp.xcodeproj/xcshareddata/xcschemes/SublimationDemoApp.xcscheme new file mode 100644 index 0000000..f2ba4e9 --- /dev/null +++ b/Demo/SublimationDemoApp.xcodeproj/xcshareddata/xcschemes/SublimationDemoApp.xcscheme @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Demo/SublimationDemoServer/Package.resolved b/Demo/SublimationDemoServer/Package.resolved index 2fe6b27..768170a 100644 --- a/Demo/SublimationDemoServer/Package.resolved +++ b/Demo/SublimationDemoServer/Package.resolved @@ -36,33 +36,6 @@ "version" : "4.5.2" } }, - { - "identity" : "prch", - "kind" : "remoteSourceControl", - "location" : "https://github.com/brightdigit/Prch.git", - "state" : { - "revision" : "600b49cc7a77271ea3d2a9f9e449be081562a087", - "version" : "0.2.1" - } - }, - { - "identity" : "prchnio", - "kind" : "remoteSourceControl", - "location" : "https://github.com/brightdigit/PrchNIO.git", - "state" : { - "revision" : "337d0b2f77b0c4ad5b5b3bad6768a9ecd70b003c", - "version" : "0.2.0-beta.1" - } - }, - { - "identity" : "prchvapor", - "kind" : "remoteSourceControl", - "location" : "https://github.com/brightdigit/PrchVapor.git", - "state" : { - "revision" : "8c19c645f9c2763014885e42a42096ae61da636b", - "version" : "0.2.0-beta.2" - } - }, { "identity" : "routing-kit", "kind" : "remoteSourceControl", diff --git a/Mintfile b/Mintfile index 093d6db..7060932 100644 --- a/Mintfile +++ b/Mintfile @@ -1,3 +1,2 @@ -nicklockwood/SwiftFormat@0.53.10 -realm/SwiftLint@0.55.0 +apple/swift-format@4b62459 peripheryapp/periphery@2.20.0 \ No newline at end of file diff --git a/Package.resolved b/Package.resolved index 43777bb..da9962c 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,113 +1,6 @@ { + "originHash" : "cd08db74df2a8f06d7330086fd1e59256ee470c6728689829d13ec97f6e9f232", "pins" : [ - { - "identity" : "async-http-client", - "kind" : "remoteSourceControl", - "location" : "https://github.com/swift-server/async-http-client.git", - "state" : { - "revision" : "291438696abdd48d2a83b52465c176efbd94512b", - "version" : "1.20.1" - } - }, - { - "identity" : "async-kit", - "kind" : "remoteSourceControl", - "location" : "https://github.com/vapor/async-kit.git", - "state" : { - "revision" : "7ece208cd401687641c88367a00e3ea2b04311f1", - "version" : "1.19.0" - } - }, - { - "identity" : "console-kit", - "kind" : "remoteSourceControl", - "location" : "https://github.com/vapor/console-kit.git", - "state" : { - "revision" : "a31f44ebfbd15a2cc0fda705279676773ac16355", - "version" : "4.14.1" - } - }, - { - "identity" : "multipart-kit", - "kind" : "remoteSourceControl", - "location" : "https://github.com/vapor/multipart-kit.git", - "state" : { - "revision" : "12ee56f25bd3fc4c2d09c2aa16e69de61dc786e8", - "version" : "4.6.0" - } - }, - { - "identity" : "openapikit", - "kind" : "remoteSourceControl", - "location" : "https://github.com/mattpolzin/OpenAPIKit", - "state" : { - "revision" : "283454875cc6e5b2801d184d65835b92252d1784", - "version" : "3.1.2" - } - }, - { - "identity" : "routing-kit", - "kind" : "remoteSourceControl", - "location" : "https://github.com/vapor/routing-kit.git", - "state" : { - "revision" : "2a92a7eac411a82fb3a03731be5e76773ebe1b3e", - "version" : "4.9.0" - } - }, - { - "identity" : "swift-algorithms", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-algorithms.git", - "state" : { - "revision" : "f6919dfc309e7f1b56224378b11e28bab5bccc42", - "version" : "1.2.0" - } - }, - { - "identity" : "swift-argument-parser", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-argument-parser", - "state" : { - "revision" : "c8ed701b513cf5177118a175d85fbbbcd707ab41", - "version" : "1.3.0" - } - }, - { - "identity" : "swift-atomics", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-atomics.git", - "state" : { - "revision" : "cd142fd2f64be2100422d658e7411e39489da985", - "version" : "1.2.0" - } - }, - { - "identity" : "swift-collections", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-collections", - "state" : { - "revision" : "d029d9d39c87bed85b1c50adee7c41795261a192", - "version" : "1.0.6" - } - }, - { - "identity" : "swift-crypto", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-crypto.git", - "state" : { - "revision" : "cc76b894169a3c86b71bac10c78a4db6beb7a9ad", - "version" : "3.2.0" - } - }, - { - "identity" : "swift-http-types", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-http-types", - "state" : { - "revision" : "12358d55a3824bd5fed310b999ea8cf83a9a1a65", - "version" : "1.0.3" - } - }, { "identity" : "swift-log", "kind" : "remoteSourceControl", @@ -116,133 +9,7 @@ "revision" : "e97a6fcb1ab07462881ac165fdbb37f067e205d5", "version" : "1.5.4" } - }, - { - "identity" : "swift-metrics", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-metrics.git", - "state" : { - "revision" : "971ba26378ab69c43737ee7ba967a896cb74c0d1", - "version" : "2.4.1" - } - }, - { - "identity" : "swift-nio", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-nio", - "state" : { - "revision" : "635b2589494c97e48c62514bc8b37ced762e0a62", - "version" : "2.63.0" - } - }, - { - "identity" : "swift-nio-extras", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-nio-extras.git", - "state" : { - "revision" : "363da63c1966405764f380c627409b2f9d9e710b", - "version" : "1.21.0" - } - }, - { - "identity" : "swift-nio-http2", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-nio-http2.git", - "state" : { - "revision" : "0904bf0feb5122b7e5c3f15db7df0eabe623dd87", - "version" : "1.30.0" - } - }, - { - "identity" : "swift-nio-ssl", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-nio-ssl.git", - "state" : { - "revision" : "7c381eb6083542b124a6c18fae742f55001dc2b5", - "version" : "2.26.0" - } - }, - { - "identity" : "swift-nio-transport-services", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-nio-transport-services.git", - "state" : { - "revision" : "6cbe0ed2b394f21ab0d46b9f0c50c6be964968ce", - "version" : "1.20.1" - } - }, - { - "identity" : "swift-numerics", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-numerics.git", - "state" : { - "revision" : "0a5bc04095a675662cf24757cc0640aa2204253b", - "version" : "1.0.2" - } - }, - { - "identity" : "swift-openapi-async-http-client", - "kind" : "remoteSourceControl", - "location" : "https://github.com/swift-server/swift-openapi-async-http-client", - "state" : { - "revision" : "abfe558a66992ef1e896a577010f957915f30591", - "version" : "1.0.0" - } - }, - { - "identity" : "swift-openapi-generator", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-openapi-generator", - "state" : { - "revision" : "b18becec9d33b8a23d6252b47bb6bf1d45304244", - "version" : "1.2.0" - } - }, - { - "identity" : "swift-openapi-runtime", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-openapi-runtime", - "state" : { - "revision" : "76951d77a0609599d2dc233e7e40808a74767c6a", - "version" : "1.3.2" - } - }, - { - "identity" : "swift-system", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-system.git", - "state" : { - "revision" : "025bcb1165deab2e20d4eaba79967ce73013f496", - "version" : "1.2.1" - } - }, - { - "identity" : "vapor", - "kind" : "remoteSourceControl", - "location" : "https://github.com/vapor/vapor.git", - "state" : { - "revision" : "4942d74e8493fc918ed6144c835c8a0e6affd4f4", - "version" : "4.92.1" - } - }, - { - "identity" : "websocket-kit", - "kind" : "remoteSourceControl", - "location" : "https://github.com/vapor/websocket-kit.git", - "state" : { - "revision" : "53fe0639a98903858d0196b699720decb42aee7b", - "version" : "2.14.0" - } - }, - { - "identity" : "yams", - "kind" : "remoteSourceControl", - "location" : "https://github.com/jpsim/Yams", - "state" : { - "revision" : "0d9ee7ea8c4ebd4a489ad7a73d5c6cad55d6fed3", - "version" : "5.0.6" - } } ], - "version" : 2 + "version" : 3 } diff --git a/Package.swift b/Package.swift index bee7896..3bab51c 100644 --- a/Package.swift +++ b/Package.swift @@ -1,15 +1,30 @@ -// swift-tools-version: 5.9 -// swiftlint:disable explicit_acl explicit_top_level_acl +// swift-tools-version: 6.0 + import PackageDescription let swiftSettings: [SwiftSetting] = [ - SwiftSetting.enableUpcomingFeature("BareSlashRegexLiterals"), - SwiftSetting.enableUpcomingFeature("ConciseMagicFile"), - SwiftSetting.enableUpcomingFeature("ExistentialAny"), - SwiftSetting.enableUpcomingFeature("ForwardTrailingClosures"), - SwiftSetting.enableUpcomingFeature("ImplicitOpenExistentials"), - SwiftSetting.enableUpcomingFeature("DisableOutwardActorInference"), - SwiftSetting.enableExperimentalFeature("StrictConcurrency") + SwiftSetting.enableExperimentalFeature("AccessLevelOnImport"), + SwiftSetting.enableExperimentalFeature("BitwiseCopyable"), + SwiftSetting.enableExperimentalFeature("GlobalActorIsolatedTypesUsability"), + SwiftSetting.enableExperimentalFeature("IsolatedAny"), + SwiftSetting.enableExperimentalFeature("MoveOnlyPartialConsumption"), + SwiftSetting.enableExperimentalFeature("NestedProtocols"), + SwiftSetting.enableExperimentalFeature("NoncopyableGenerics"), + SwiftSetting.enableExperimentalFeature("RegionBasedIsolation"), + SwiftSetting.enableExperimentalFeature("TransferringArgsAndResults"), + SwiftSetting.enableExperimentalFeature("VariadicGenerics"), + + SwiftSetting.enableUpcomingFeature("FullTypedThrows"), + SwiftSetting.enableUpcomingFeature("InternalImportsByDefault") + + // SwiftSetting.unsafeFlags([ + // "-Xfrontend", + // "-warn-long-function-bodies=100" + // ]), + // SwiftSetting.unsafeFlags([ + // "-Xfrontend", + // "-warn-long-expression-type-checking=100" + // ]) ] let package = Package( @@ -24,49 +39,16 @@ let package = Package( ], products: [ .library(name: "Sublimation", targets: ["Sublimation"]), - .library(name: "SublimationVapor", targets: ["SublimationVapor"]), - .library(name: "Ngrokit", targets: ["Ngrokit"]) + .library(name: "SublimationCore", targets: ["SublimationCore"]) ], dependencies: [ - .package( - url: "https://github.com/vapor/vapor.git", - from: "4.92.0" - ), - .package( - url: "https://github.com/apple/swift-openapi-generator", - from: "1.0.0" - ), - .package( - url: "https://github.com/apple/swift-openapi-runtime", - from: "1.0.0" - ), - .package( - url: "https://github.com/swift-server/swift-openapi-async-http-client", - from: "1.0.0" - ), .package(url: "https://github.com/apple/swift-log.git", from: "1.0.0") ], - targets: [ - .target( - name: "NgrokOpenAPIClient", - dependencies: [ - .product(name: "OpenAPIRuntime", package: "swift-openapi-runtime") - ] - ), - .target( - name: "Ngrokit", - dependencies: [ - "NgrokOpenAPIClient", - .product(name: "OpenAPIRuntime", package: "swift-openapi-runtime") - ], - swiftSettings: swiftSettings - ), + targets: [ .target( name: "Sublimation", dependencies: [ "SublimationCore", - "SublimationBonjour", - "SublimationNgrok", .product(name: "Logging", package: "swift-log") ], swiftSettings: swiftSettings @@ -78,81 +60,10 @@ let package = Package( ], swiftSettings: swiftSettings ), - .target( - name: "SublimationTunnel", - dependencies: ["SublimationCore"], - swiftSettings: swiftSettings - ), - .target( - name: "SublimationNgrok", - dependencies: ["SublimationTunnel", "Ngrokit"], - swiftSettings: swiftSettings - ), - .target( - name: "SublimationKVdb", - dependencies: ["SublimationTunnel"], - swiftSettings: swiftSettings - ), - .target( - name: "SublimationBonjour", - dependencies: ["SublimationCore"], - swiftSettings: swiftSettings - ), - .target( - name: "SublimationVapor", - dependencies: [ - .product( - name: "OpenAPIAsyncHTTPClient", - package: "swift-openapi-async-http-client" - ), - "Ngrokit", - "Sublimation", - "SublimationKVdb", - .product( - name: "Vapor", - package: "vapor" - ) - ], - swiftSettings: swiftSettings - ), - .target( - name: "NgrokitMocks", - dependencies: ["Ngrokit"], - swiftSettings: swiftSettings - ), - .testTarget( - name: "NgrokitTests", - dependencies: ["Ngrokit", "NgrokitMocks"], - swiftSettings: swiftSettings - ), - .target( - name: "SublimationMocks", - dependencies: ["Sublimation"], - swiftSettings: swiftSettings - ), .testTarget( - name: "SublimationBonjourTests", - dependencies: ["SublimationMocks", "SublimationBonjour"], - swiftSettings: swiftSettings - ), - .testTarget( - name: "SublimationTunnelTests", - dependencies: ["SublimationTunnel", "SublimationMocks"], - swiftSettings: swiftSettings - ), - .testTarget( - name: "SublimationKVdbTests", - dependencies: ["SublimationMocks", "SublimationKVdb"], - swiftSettings: swiftSettings - ), - .testTarget( - name: "SublimationVaporTests", - dependencies: [ - "SublimationVapor", - "NgrokitMocks" - ], - swiftSettings: swiftSettings + name: "SublimationTests", + dependencies: ["Sublimation"] ) ] ) -// swiftlint:enable explicit_acl explicit_top_level_acl + diff --git a/README.md b/README.md index da7aa7c..58debf8 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,13 @@

- Sublimation + Sublimation

Sublimation

-Share your local development server easily with your Apple devices. +Enable **automatic discovery of your local development server** on the fly +Turn your Server-Side Swift app from a _mysterious vapor_ to a **tangible solid server** + +[![](https://img.shields.io/badge/docc-read_documentation-blue)](https://swiftpackageindex.com/brightdigit/Sublimation/documentation) [![SwiftPM](https://img.shields.io/badge/SPM-Linux%20%7C%20iOS%20%7C%20macOS%20%7C%20watchOS%20%7C%20tvOS-success?logo=swift)](https://swift.org) [![Twitter](https://img.shields.io/badge/twitter-@brightdigit-blue.svg?style=flat)](http://twitter.com/brightdigit) ![GitHub](https://img.shields.io/github/license/brightdigit/Sublimation) @@ -14,191 +17,169 @@ Share your local development server easily with your Apple devices. [![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fbrightdigit%2FSublimation%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/brightdigit/Sublimation) [![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fbrightdigit%2FSublimation%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/brightdigit/Sublimation) - - [![CodeFactor Grade](https://img.shields.io/codefactor/grade/github/brightdigit/Sublimation)](https://www.codefactor.io/repository/github/brightdigit/Sublimation) [![codebeat badge](https://codebeat.co/badges/54695d4b-98c8-4f0f-855e-215500163094)](https://codebeat.co/projects/github-com-brightdigit-Sublimation-main) [![Code Climate maintainability](https://img.shields.io/codeclimate/maintainability/brightdigit/Sublimation)](https://codeclimate.com/github/brightdigit/Sublimation) [![Code Climate technical debt](https://img.shields.io/codeclimate/tech-debt/brightdigit/Sublimation?label=debt)](https://codeclimate.com/github/brightdigit/Sublimation) [![Code Climate issues](https://img.shields.io/codeclimate/issues/brightdigit/Sublimation)](https://codeclimate.com/github/brightdigit/Sublimation) -[![Reviewed by Hound](https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg)](https://houndci.com) # Table of Contents * [Introduction](#introduction) - * [Requirements](#requirements) -* [Installation](#installation) - * [Server Installation](#server-installation) - * [Client Installation](#client-installation) -* [Bonjour vs Ngrok](#bonjour-vs-ngrok) - * [Using Bonjour](#using-bonjour) - * [Using Ngrok](#using-ngrok) + * [Requirements](#requirements) +* [Package Ecosystem](#package-ecosystem) +* [Usage](#usage) +* [Documentation](#documentation) * [License](#license) -# Introduction + -When you are developing a full stack Swift application, you want to easily test and debug your application on both the device (iPhone, Apple Watch, iPad, etc...) as well as your development server. If you are using simulator then setting your host server to `localhost` will work but often we need to test on an actual device. You can either be an IT expert on your local network's DNS or you can use Sublimation to easily connect your local server to your device. +# Introduction + +When you are developing a Full Stack Swift application, you want to easily test and debug your application on both the device (iPhone, Apple Watch, iPad, etc...) as well as your development server. If you are using simulator then setting your host server to `localhost` this may work but often you need to test on an actual device. + +For the server and client **we need a way to communicate that information** without the client knowing where the server is initially. + +```mermaid +flowchart TD +%% Nodes for devices with Font Awesome icons + subgraph Devices + iPhone("fa:fa-mobile-alt iPhone") + Watch("fa:fa-square Apple Watch") + iPad("fa:fa-tablet-alt iPad") + VisionPro("fa:fa-vr-cardboard Vision Pro") + end + +%% Node for Sublimation service with Font Awesome package icon + Sublimation("fa:fa-box Sublimation") + +%% Node for API server with Font Awesome icon + Server("fa:fa-server API Server") + +%% Edge connections + Devices <--> Sublimation + Sublimation <--> Server +``` ## Requirements **Apple Platforms** -- Xcode 15.0 or later -- Swift 5.9 or later +- Xcode 16.0 or later +- Swift 6.0 or later - iOS 17 / watchOS 10.0 / tvOS 17 / macOS 14 or later deployment targets **Linux** - Ubuntu 20.04 or later -- Swift 5.9 or later +- Swift 6.0 or later + +For **older operating systems or Swift versions**, check out [the main branch and 1.0.0 releases](https://github.com/brightdigit/Sublimation/releases/tag/1.0.0-alpha.2). + +# Package Ecosystem + +| Repository | Description | +| ---------- | ----------- | +| [**SublimationBonjour**](https://github.com/brightdigit/SublimationBonjour) | `Sublimatory` for using [Bonjour](https://developer.apple.com/bonjour/) for auto-discovery for development server. | +| [**SublimationNgrok**](https://github.com/brightdigit/SublimationNgrok) | `Sublimatory` for using [Ngrok](https://ngrok.com/) and [KVdb](https://kvdb.io) to create public urls and share them. | +| [**SublimationService**](https://github.com/brightdigit/SublimationService) | Use **Sublimation** as a [Lifecycle Service](https://github.com/swift-server/swift-service-lifecycle). | +| [**SublimationVapor**](https://github.com/brightdigit/SublimationVapor) | Use **Sublimation** as a [Vapor Lifecycle Handler](https://docs.vapor.codes/advanced/services/#lifecycle). | + +```mermaid +graph TD + A[Which Sublimation Packages to Use] --> B{Need to publicly share URL?} + B -->|Yes| C[Use **SublimationNgrok**] + B -->|No| D[Use **SublimationBonjour**] + C --> E{Which server framework?} + D --> E + E -->|*Vapor*| F[Use **SublimationVapor**] + E -->|*Hummingbird* or other *Lifecycle Service*| G[Use **SublimationService** ] +``` -# Installation +To use **Sublimation**, you'll need to choose: -Sublimation has two components: Server and Client. You can check out the SublimationDemoApp Xcode project for an example. +* **Sublimatory**, that is the method by which you advertise the development server + * [**Bonjour**](https://developer.apple.com/bonjour/) via [SublimationBonjour](https://github.com/brightdigit/SublimationBonjour) + * [**Ngrok**](https://ngrok.com/) via [SublimationNgrok](https://github.com/brightdigit/SublimationBonjour) _which is only needed if you need to advertise your address publicaly_ +* How it connects to the server + * [Lifecycle Handler for Vapor](https://docs.vapor.codes/advanced/services/#lifecycle) via [SublimationVapor](https://github.com/brightdigit/SublimationBonjour) + * [Lifecycle Service](https://github.com/swift-server/swift-service-lifecycle) via [SublimationService](https://github.com/brightdigit/SublimationBonjour) _for server frameworks such as [Hummingbird](https://docs.hummingbird.codes/2.0/documentation/hummingbird/)_ -## Server Installation +# Usage -To integrate **Sublimation** into your Vapor app using SPM, specify it in your Package.swift file: +For instance if you were using **Bonjour** with **Hummingbird** and an iOS app your package may look something like this: -```swift +```swift let package = Package( ... dependencies: [ - .package(url: "https://github.com/brightdigit/Sublimation.git", from: "2.0.0-alpha.3") + .package(url: "https://github.com/hummingbird-project/hummingbird.git", from: "2.0.0-alpha.1"), + .package(url: "https://github.com/brightdigit/SublimationBonjour.git", from: "1.0.0"), + .package(url: "https://github.com/brightdigit/SublimationService.git", from: "1.0.0") ], targets: [ + .target( - name: "YourVaporServerApp", + name: "YouriOSApp", dependencies: [ - .product(name: "SublimationVapor", package: "Sublimation"), ... + .product(name: "SublimationBonjour", package: "SublimationBonjour"), + ... + ]), + ... + .target( + name: "YourServerApp", + dependencies: [ + .product(name: "Hummingbird", package: "hummingbird"), + .product(name: "SublimationBonjour", package: "SublimationBonjour"), + .product(name: "SublimationService", package: "SublimationService"), + ... ]), ... ] ) ``` -## Client Installation - -In your Xcode project, add the swift package for Sublimation at: - -``` -https://github.com/brightdigit/Sublimation.git -``` - -In your application target, you only need a reference to the `Sublimation` library. - -# Bonjour vs Ngrok - -Unless you need public exposure for your development server, **your best bet is to use _Bonjour_ for letting your devices know about your server.** - -## Using Bonjour - -In order to use Bonjour to notify your network devices of your server, you need to add Sublimation as part of [the lifecycle of your server application](https://docs.vapor.codes/advanced/services/#lifecycle). By default, Sublimation uses Bonjour and all the default parameters should be sufficient. Here's an example for Vapor: +If you were to use **Vapor** and **Ngrok** instead, it'd look more like this: ```swift -#if os(macOS) && DEBUG - app.lifecycle.use( - Sublimation() - ) -#endif -``` - -Notice: -1. You'll only want to **run this in development.** -2. Sublimation **only works on macOS** and not Linux. - -### How it works - -The `BonjourSublimatory` does 2 things: - -1. Gets the address of the server host. -2. Start an [`NWListener`](https://developer.apple.com/documentation/network/nwlistener) to advertise those addresses. - -Once your server is started, it should automatically advertise these on your local network. - -In your client application, you'll need to create a `BonjourDepositor`. The `BonjourDepositor` searches your network for you development server. You can call 'BonjourDepositor.urls' to get an [`AsyncStream`](https://developer.apple.com/documentation/swift/asyncstream) of urls. However in most cases `.first` should be sufficient: - -```swift -let baseURL : URL -#if os(macOS) && DEBUG - let depositor = BonjourDepositor() - // hostURL = http://192.168.0.1 - guard let hostURL = await depositor.first() else { - // handle when no url is returned - } - // hostURL = http://192.168.0.1/api/v1/ - baseURL = hostURL.appendPathComponent("/api/v1/") -#else - // handle instances where the server is running - // outside of your development environment (i.e. staging, production, etc...) -#endif -``` - -## Using Ngrok - -[Ngrok](https://ngrok.com) is a fantastic service for setting up local development server for outside access. Let's say you need to share your local development server because you're testing on an actual device which can't access your machine via your local network. You can run `ngrok` to setup an https address which tunnels to your local development server: - -```bash -> vapor run serve -p 1337 -> ngrok http 1337 -``` -Now you'll get a message saying your vapor app is served through ngrok: - -``` -Forwarding https://c633-2600-1702-4050-7d30-cc59-3ffb-effa-6719.ngrok.io -> http://localhost:1337 -``` - -Sublimation can be used to automate this process and let your client devices automatically know. - -### Using the Cloud for Meta-Server Access - -With Sublimation and Ngrok you save the address (such as `https://c633-2600-1702-4050-7d30-cc59-3ffb-effa-6719.ngrok.io`) to a key-value storage and pull that address from your Apple device during development. - -### Cloud Setup - -If you haven't already setup an account with ngrok and install the command-line tool via homebrew. Next let's setup a key-value storage with kvdb.io which is currently supported. _If you have another service, please create an issue in the repo. Your feedback is helpful._ - -Sign up at [kvdb.io](https://kvdb.io) and get a bucket name you'll use. You'll be using that for your setup. Essentially there are three components you'll need: - -* path to ngrok on your machine - if you installed via homebrew it's `/opt/homebrew/bin/ngrok` but you can find out using: `which ngrok` after installation -* your kvdb.io bucket name -* your kvdb.io key - you just need to pick something unique for your server and client to use - -Now let's setup your Vapor server application... - -### On your server - -`Sublimation` makes it easy to setup `Ngrok` by passing in the path to ngrok and the information from KVdb. Simply add `Sublimation` to your server application. In the case of Vapor add it to your lifecycle: +let package = Package( + ... + dependencies: [ + .package(url: "https://github.com/vapor/vapor.git", from: "4.76.0"), + .package(url: "https://github.com/brightdigit/SublimationNgrok.git", from: "1.0.0"), + .package(url: "https://github.com/brightdigit/SublimationVapor.git", from: "1.0.0") + ], + targets: [ -```swift -let app = Application(env) -... -app.lifecycle.use( - Sublimation( - ngrokPath: "/opt/homebrew/bin/ngrok", - bucketName: "bucket-name", - key: "application key name" - ) + .target( + name: "YouriOSApp", + dependencies: [ + .product(name: "SublimationKVdb", package: "SublimationNgrok"), + ... + ]), + ... + .target( + name: "YourServerApp", + dependencies: [ + .product(name: "Vapor", package: "vapor"), + .product(name: "SublimationNgrok", package: "SublimationNgrok"), + .product(name: "SublimationVapor", package: "SublimationVapor"), + ... + ]), + ... + ] ) ``` -This will run `ngrok` and setup the forwarding address. Once it receives the address it saves it your kvdb bucket with key setup here. +* _[Why KVdb for the app?](https://github.com/brightdigit/SublimationNgrok#client-setup)_ -Remember the ngrok path is the path from your development machine while the bucket name is from kvdb.io. However, the key can be anything you want as long as it's consistent and used by your client. Speaking of your client, let's talk about setting this up in your iOS app. +Please check the respective package documentation from the [Package Ecosystem](#package-ecosystem) section. -### On your device +# Documentation -Now to pull the url saved by your service, all you have to call is: - -```swift -import Sublimation - -let baseURL = try await KVdb.url(withKey: key, atBucket: bucketName) -``` +To learn more, check out the full [documentation](https://swiftpackageindex.com/brightdigit/Sublimation/2.0.0-beta.1/documentation/sublimation). -At the point, you'll have the base url of your Vapor application and can begin using it in your application! # License This code is distributed under the MIT license. See the [LICENSE](https://github.com/brightdigit/Sublimation/LICENSE) file for more info. diff --git a/scripts/gh-md-toc b/Scripts/gh-md-toc similarity index 100% rename from scripts/gh-md-toc rename to Scripts/gh-md-toc diff --git a/Scripts/header.sh b/Scripts/header.sh new file mode 100755 index 0000000..4ed7446 --- /dev/null +++ b/Scripts/header.sh @@ -0,0 +1,98 @@ +#!/bin/bash + +# Function to print usage +usage() { + echo "Usage: $0 -d directory -c creator -o company -p package [-y year]" + echo " -d directory Directory to read from (including subdirectories)" + echo " -c creator Name of the creator" + echo " -o company Name of the company with the copyright" + echo " -p package Package or library name" + echo " -y year Copyright year (optional, defaults to current year)" + exit 1 +} + +# Get the current year if not provided +current_year=$(date +"%Y") + +# Default values +year="$current_year" + +# Parse arguments +while getopts ":d:c:o:p:y:" opt; do + case $opt in + d) directory="$OPTARG" ;; + c) creator="$OPTARG" ;; + o) company="$OPTARG" ;; + p) package="$OPTARG" ;; + y) year="$OPTARG" ;; + *) usage ;; + esac +done + +# Check for mandatory arguments +if [ -z "$directory" ] || [ -z "$creator" ] || [ -z "$company" ] || [ -z "$package" ]; then + usage +fi + +# Define the header template +header_template="// +// %s +// %s +// +// Created by %s. +// Copyright © %s %s. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the “Software”), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +//" + +# Loop through each Swift file in the specified directory and subdirectories +find "$directory" -type f -name "*.swift" | while read -r file; do + # Check if the first line is the swift-format-ignore indicator + first_line=$(head -n 1 "$file") + if [[ "$first_line" == "// swift-format-ignore-file" ]]; then + echo "Skipping $file due to swift-format-ignore directive." + continue + fi + + # Create the header with the current filename + filename=$(basename "$file") + header=$(printf "$header_template" "$filename" "$package" "$creator" "$year" "$company") + + # Remove all consecutive lines at the beginning which start with "// ", contain only whitespace, or only "//" + awk ' + BEGIN { skip = 1 } + { + if (skip && ($0 ~ /^\/\/ / || $0 ~ /^\/\/$/ || $0 ~ /^$/)) { + next + } + skip = 0 + print + }' "$file" > temp_file + + # Add the header to the cleaned file + (echo "$header"; echo; cat temp_file) > "$file" + + # Remove the temporary file + rm temp_file +done + +echo "Headers added or files skipped appropriately across all Swift files in the directory and subdirectories." \ No newline at end of file diff --git a/Scripts/lint.sh b/Scripts/lint.sh new file mode 100755 index 0000000..cdd265e --- /dev/null +++ b/Scripts/lint.sh @@ -0,0 +1,50 @@ +#!/bin/sh + +if [ "$ACTION" == "install" ]; then + if [ -n "$SRCROOT" ]; then + exit + fi +fi + +export MINT_PATH="$PWD/.mint" +MINT_ARGS="-n -m Mintfile --silent" +MINT_RUN="/opt/homebrew/bin/mint run $MINT_ARGS" + +if [ -z "$SRCROOT" ] || [ -n "$CHILD_PACKAGE" ]; then + SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + PACKAGE_DIR="${SCRIPT_DIR}/.." + PERIPHERY_OPTIONS="" +else + PACKAGE_DIR="${SRCROOT}" + PERIPHERY_OPTIONS="" +fi + + +if [ "$LINT_MODE" == "NONE" ]; then + exit +elif [ "$LINT_MODE" == "STRICT" ]; then + SWIFTFORMAT_OPTIONS="--strict" +else + SWIFTFORMAT_OPTIONS="" +fi + +/opt/homebrew/bin/mint bootstrap + +echo "LINT Mode is $LINT_MODE" + +if [ "$LINT_MODE" == "INSTALL" ]; then + exit +fi + +if [ -z "$CI" ]; then + $MINT_RUN swift-format format --recursive --parallel --in-place $PACKAGE_DIR/Sources +else + set -e +fi + +$PACKAGE_DIR/scripts/header.sh -d $PACKAGE_DIR/Sources -c "Leo Dion" -o "BrightDigit" -p "Sublimation" +$MINT_RUN swift-format lint --recursive --parallel $SWIFTFORMAT_OPTIONS $PACKAGE_DIR/Sources + +pushd $PACKAGE_DIR +$MINT_RUN periphery scan $PERIPHERY_OPTIONS --disable-update-check +popd diff --git a/Sources/NgrokOpenAPIClient/Client.swift b/Sources/NgrokOpenAPIClient/Client.swift deleted file mode 100644 index 103e3f2..0000000 --- a/Sources/NgrokOpenAPIClient/Client.swift +++ /dev/null @@ -1,305 +0,0 @@ -// Generated by swift-openapi-generator, do not modify. -@_spi(Generated) import OpenAPIRuntime -#if os(Linux) -@preconcurrency import struct Foundation.URL -@preconcurrency import struct Foundation.Data -@preconcurrency import struct Foundation.Date -#else -import struct Foundation.URL -import struct Foundation.Data -import struct Foundation.Date -#endif -import HTTPTypes -package struct Client: APIProtocol { - /// The underlying HTTP client. - private let client: UniversalClient - /// Creates a new client. - /// - Parameters: - /// - serverURL: The server URL that the client connects to. Any server - /// URLs defined in the OpenAPI document are available as static methods - /// on the ``Servers`` type. - /// - configuration: A set of configuration values for the client. - /// - transport: A transport that performs HTTP operations. - /// - middlewares: A list of middlewares to call before the transport. - package init( - serverURL: Foundation.URL, - configuration: Configuration = .init(), - transport: any ClientTransport, - middlewares: [any ClientMiddleware] = [] - ) { - self.client = .init( - serverURL: serverURL, - configuration: configuration, - transport: transport, - middlewares: middlewares - ) - } - private var converter: Converter { - client.converter - } - /// Access the root API resource of a running ngrok agent - /// - /// - Remark: HTTP `GET /api`. - /// - Remark: Generated from `#/paths//api/get`. - package func get_sol_api(_ input: Operations.get_sol_api.Input) async throws -> Operations.get_sol_api.Output { - try await client.send( - input: input, - forOperation: Operations.get_sol_api.id, - serializer: { input in - let path = try converter.renderedPath( - template: "/api", - parameters: [] - ) - var request: HTTPTypes.HTTPRequest = .init( - soar_path: path, - method: .get - ) - suppressMutabilityWarning(&request) - return (request, nil) - }, - deserializer: { response, responseBody in - switch response.status.code { - case 200: - return .ok(.init()) - default: - return .undocumented( - statusCode: response.status.code, - .init( - headerFields: response.headerFields, - body: responseBody - ) - ) - } - } - ) - } - /// List Tunnels - /// - /// - Remark: HTTP `GET /api/tunnels`. - /// - Remark: Generated from `#/paths//api/tunnels/get(listTunnels)`. - package func listTunnels(_ input: Operations.listTunnels.Input) async throws -> Operations.listTunnels.Output { - try await client.send( - input: input, - forOperation: Operations.listTunnels.id, - serializer: { input in - let path = try converter.renderedPath( - template: "/api/tunnels", - parameters: [] - ) - var request: HTTPTypes.HTTPRequest = .init( - soar_path: path, - method: .get - ) - suppressMutabilityWarning(&request) - converter.setAcceptHeader( - in: &request.headerFields, - contentTypes: input.headers.accept - ) - return (request, nil) - }, - deserializer: { response, responseBody in - switch response.status.code { - case 200: - let contentType = converter.extractContentTypeIfPresent(in: response.headerFields) - let body: Operations.listTunnels.Output.Ok.Body - let chosenContentType = try converter.bestContentType( - received: contentType, - options: [ - "application/json" - ] - ) - switch chosenContentType { - case "application/json": - body = try await converter.getResponseBodyAsJSON( - Components.Schemas.TunnelList.self, - from: responseBody, - transforming: { value in - .json(value) - } - ) - default: - preconditionFailure("bestContentType chose an invalid content type.") - } - return .ok(.init(body: body)) - default: - return .undocumented( - statusCode: response.status.code, - .init( - headerFields: response.headerFields, - body: responseBody - ) - ) - } - } - ) - } - /// Start tunnel - /// - /// - Remark: HTTP `POST /api/tunnels`. - /// - Remark: Generated from `#/paths//api/tunnels/post(startTunnel)`. - package func startTunnel(_ input: Operations.startTunnel.Input) async throws -> Operations.startTunnel.Output { - try await client.send( - input: input, - forOperation: Operations.startTunnel.id, - serializer: { input in - let path = try converter.renderedPath( - template: "/api/tunnels", - parameters: [] - ) - var request: HTTPTypes.HTTPRequest = .init( - soar_path: path, - method: .post - ) - suppressMutabilityWarning(&request) - converter.setAcceptHeader( - in: &request.headerFields, - contentTypes: input.headers.accept - ) - let body: OpenAPIRuntime.HTTPBody? - switch input.body { - case let .json(value): - body = try converter.setRequiredRequestBodyAsJSON( - value, - headerFields: &request.headerFields, - contentType: "application/json; charset=utf-8" - ) - } - return (request, body) - }, - deserializer: { response, responseBody in - switch response.status.code { - case 201: - let contentType = converter.extractContentTypeIfPresent(in: response.headerFields) - let body: Operations.startTunnel.Output.Created.Body - let chosenContentType = try converter.bestContentType( - received: contentType, - options: [ - "application/json" - ] - ) - switch chosenContentType { - case "application/json": - body = try await converter.getResponseBodyAsJSON( - Components.Schemas.TunnelResponse.self, - from: responseBody, - transforming: { value in - .json(value) - } - ) - default: - preconditionFailure("bestContentType chose an invalid content type.") - } - return .created(.init(body: body)) - default: - return .undocumented( - statusCode: response.status.code, - .init( - headerFields: response.headerFields, - body: responseBody - ) - ) - } - } - ) - } - /// Tunnel detail - /// - /// - Remark: HTTP `GET /api/tunnels/{name}`. - /// - Remark: Generated from `#/paths//api/tunnels/{name}/get(getTunnel)`. - package func getTunnel(_ input: Operations.getTunnel.Input) async throws -> Operations.getTunnel.Output { - try await client.send( - input: input, - forOperation: Operations.getTunnel.id, - serializer: { input in - let path = try converter.renderedPath( - template: "/api/tunnels/{}", - parameters: [ - input.path.name - ] - ) - var request: HTTPTypes.HTTPRequest = .init( - soar_path: path, - method: .get - ) - suppressMutabilityWarning(&request) - converter.setAcceptHeader( - in: &request.headerFields, - contentTypes: input.headers.accept - ) - return (request, nil) - }, - deserializer: { response, responseBody in - switch response.status.code { - case 200: - let contentType = converter.extractContentTypeIfPresent(in: response.headerFields) - let body: Operations.getTunnel.Output.Ok.Body - let chosenContentType = try converter.bestContentType( - received: contentType, - options: [ - "application/json" - ] - ) - switch chosenContentType { - case "application/json": - body = try await converter.getResponseBodyAsJSON( - OpenAPIRuntime.OpenAPIValueContainer.self, - from: responseBody, - transforming: { value in - .json(value) - } - ) - default: - preconditionFailure("bestContentType chose an invalid content type.") - } - return .ok(.init(body: body)) - default: - return .undocumented( - statusCode: response.status.code, - .init( - headerFields: response.headerFields, - body: responseBody - ) - ) - } - } - ) - } - /// Stop tunnel - /// - /// - Remark: HTTP `DELETE /api/tunnels/{name}`. - /// - Remark: Generated from `#/paths//api/tunnels/{name}/delete(stopTunnel)`. - package func stopTunnel(_ input: Operations.stopTunnel.Input) async throws -> Operations.stopTunnel.Output { - try await client.send( - input: input, - forOperation: Operations.stopTunnel.id, - serializer: { input in - let path = try converter.renderedPath( - template: "/api/tunnels/{}", - parameters: [ - input.path.name - ] - ) - var request: HTTPTypes.HTTPRequest = .init( - soar_path: path, - method: .delete - ) - suppressMutabilityWarning(&request) - return (request, nil) - }, - deserializer: { response, responseBody in - switch response.status.code { - case 204: - return .noContent(.init()) - default: - return .undocumented( - statusCode: response.status.code, - .init( - headerFields: response.headerFields, - body: responseBody - ) - ) - } - } - ) - } -} diff --git a/Sources/NgrokOpenAPIClient/Types.swift b/Sources/NgrokOpenAPIClient/Types.swift deleted file mode 100644 index 2984cc7..0000000 --- a/Sources/NgrokOpenAPIClient/Types.swift +++ /dev/null @@ -1,846 +0,0 @@ -// Generated by swift-openapi-generator, do not modify. -@_spi(Generated) import OpenAPIRuntime -#if os(Linux) -@preconcurrency import struct Foundation.URL -@preconcurrency import struct Foundation.Data -@preconcurrency import struct Foundation.Date -#else -import struct Foundation.URL -import struct Foundation.Data -import struct Foundation.Date -#endif -/// A type that performs HTTP operations defined by the OpenAPI document. -package protocol APIProtocol: Sendable { - /// Access the root API resource of a running ngrok agent - /// - /// - Remark: HTTP `GET /api`. - /// - Remark: Generated from `#/paths//api/get`. - func get_sol_api(_ input: Operations.get_sol_api.Input) async throws -> Operations.get_sol_api.Output - /// List Tunnels - /// - /// - Remark: HTTP `GET /api/tunnels`. - /// - Remark: Generated from `#/paths//api/tunnels/get(listTunnels)`. - func listTunnels(_ input: Operations.listTunnels.Input) async throws -> Operations.listTunnels.Output - /// Start tunnel - /// - /// - Remark: HTTP `POST /api/tunnels`. - /// - Remark: Generated from `#/paths//api/tunnels/post(startTunnel)`. - func startTunnel(_ input: Operations.startTunnel.Input) async throws -> Operations.startTunnel.Output - /// Tunnel detail - /// - /// - Remark: HTTP `GET /api/tunnels/{name}`. - /// - Remark: Generated from `#/paths//api/tunnels/{name}/get(getTunnel)`. - func getTunnel(_ input: Operations.getTunnel.Input) async throws -> Operations.getTunnel.Output - /// Stop tunnel - /// - /// - Remark: HTTP `DELETE /api/tunnels/{name}`. - /// - Remark: Generated from `#/paths//api/tunnels/{name}/delete(stopTunnel)`. - func stopTunnel(_ input: Operations.stopTunnel.Input) async throws -> Operations.stopTunnel.Output -} - -/// Convenience overloads for operation inputs. -extension APIProtocol { - /// Access the root API resource of a running ngrok agent - /// - /// - Remark: HTTP `GET /api`. - /// - Remark: Generated from `#/paths//api/get`. - package func get_sol_api() async throws -> Operations.get_sol_api.Output { - try await get_sol_api(Operations.get_sol_api.Input()) - } - /// List Tunnels - /// - /// - Remark: HTTP `GET /api/tunnels`. - /// - Remark: Generated from `#/paths//api/tunnels/get(listTunnels)`. - package func listTunnels(headers: Operations.listTunnels.Input.Headers = .init()) async throws -> Operations.listTunnels.Output { - try await listTunnels(Operations.listTunnels.Input(headers: headers)) - } - /// Start tunnel - /// - /// - Remark: HTTP `POST /api/tunnels`. - /// - Remark: Generated from `#/paths//api/tunnels/post(startTunnel)`. - package func startTunnel( - headers: Operations.startTunnel.Input.Headers = .init(), - body: Operations.startTunnel.Input.Body - ) async throws -> Operations.startTunnel.Output { - try await startTunnel(Operations.startTunnel.Input( - headers: headers, - body: body - )) - } - /// Tunnel detail - /// - /// - Remark: HTTP `GET /api/tunnels/{name}`. - /// - Remark: Generated from `#/paths//api/tunnels/{name}/get(getTunnel)`. - package func getTunnel( - path: Operations.getTunnel.Input.Path, - headers: Operations.getTunnel.Input.Headers = .init() - ) async throws -> Operations.getTunnel.Output { - try await getTunnel(Operations.getTunnel.Input( - path: path, - headers: headers - )) - } - /// Stop tunnel - /// - /// - Remark: HTTP `DELETE /api/tunnels/{name}`. - /// - Remark: Generated from `#/paths//api/tunnels/{name}/delete(stopTunnel)`. - package func stopTunnel(path: Operations.stopTunnel.Input.Path) async throws -> Operations.stopTunnel.Output { - try await stopTunnel(Operations.stopTunnel.Input(path: path)) - } -} - -/// Server URLs defined in the OpenAPI document. -package enum Servers { - /// Default Local Server - package static func server1() throws -> Foundation.URL { - try Foundation.URL( - validatingOpenAPIServerURL: "http://127.0.0.1:4040", - variables: [] - ) - } -} - -/// Types generated from the components section of the OpenAPI document. -package enum Components { - /// Types generated from the `#/components/schemas` section of the OpenAPI document. - package enum Schemas { - /// - Remark: Generated from `#/components/schemas/TunnelList`. - package struct TunnelList: Codable, Hashable, Sendable { - /// - Remark: Generated from `#/components/schemas/TunnelList/tunnels`. - package var tunnels: [Components.Schemas.TunnelResponse] - /// Creates a new `TunnelList`. - /// - /// - Parameters: - /// - tunnels: - package init(tunnels: [Components.Schemas.TunnelResponse]) { - self.tunnels = tunnels - } - package enum CodingKeys: String, CodingKey { - case tunnels - } - } - /// - Remark: Generated from `#/components/schemas/TunnelRequest`. - package struct TunnelRequest: Codable, Hashable, Sendable { - /// - Remark: Generated from `#/components/schemas/TunnelRequest/addr`. - package var addr: Swift.String - /// - Remark: Generated from `#/components/schemas/TunnelRequest/proto`. - package var proto: Swift.String - /// - Remark: Generated from `#/components/schemas/TunnelRequest/name`. - package var name: Swift.String - /// Creates a new `TunnelRequest`. - /// - /// - Parameters: - /// - addr: - /// - proto: - /// - name: - package init( - addr: Swift.String, - proto: Swift.String, - name: Swift.String - ) { - self.addr = addr - self.proto = proto - self.name = name - } - package enum CodingKeys: String, CodingKey { - case addr - case proto - case name - } - } - /// - Remark: Generated from `#/components/schemas/TunnelResponse`. - package struct TunnelResponse: Codable, Hashable, Sendable { - /// - Remark: Generated from `#/components/schemas/TunnelResponse/name`. - package var name: Swift.String - /// - Remark: Generated from `#/components/schemas/TunnelResponse/uri`. - package var uri: Swift.String? - /// - Remark: Generated from `#/components/schemas/TunnelResponse/public_url`. - package var public_url: Swift.String - /// - Remark: Generated from `#/components/schemas/TunnelResponse/proto`. - package var proto: Swift.String? - /// - Remark: Generated from `#/components/schemas/TunnelResponse/config`. - package struct configPayload: Codable, Hashable, Sendable { - /// - Remark: Generated from `#/components/schemas/TunnelResponse/config/addr`. - package var addr: Swift.String - /// - Remark: Generated from `#/components/schemas/TunnelResponse/config/inspect`. - package var inspect: Swift.Bool - /// Creates a new `configPayload`. - /// - /// - Parameters: - /// - addr: - /// - inspect: - package init( - addr: Swift.String, - inspect: Swift.Bool - ) { - self.addr = addr - self.inspect = inspect - } - package enum CodingKeys: String, CodingKey { - case addr - case inspect - } - } - /// - Remark: Generated from `#/components/schemas/TunnelResponse/config`. - package var config: Components.Schemas.TunnelResponse.configPayload - /// - Remark: Generated from `#/components/schemas/TunnelResponse/metrics`. - package struct metricsPayload: Codable, Hashable, Sendable { - /// - Remark: Generated from `#/components/schemas/TunnelResponse/metrics/conns`. - package struct connsPayload: Codable, Hashable, Sendable { - /// - Remark: Generated from `#/components/schemas/TunnelResponse/metrics/conns/count`. - package var count: Swift.Int - /// - Remark: Generated from `#/components/schemas/TunnelResponse/metrics/conns/gauge`. - package var gauge: Swift.Int - /// - Remark: Generated from `#/components/schemas/TunnelResponse/metrics/conns/rate1`. - package var rate1: Swift.Int - /// - Remark: Generated from `#/components/schemas/TunnelResponse/metrics/conns/rate5`. - package var rate5: Swift.Int - /// - Remark: Generated from `#/components/schemas/TunnelResponse/metrics/conns/rate15`. - package var rate15: Swift.Int - /// - Remark: Generated from `#/components/schemas/TunnelResponse/metrics/conns/p50`. - package var p50: Swift.Int - /// - Remark: Generated from `#/components/schemas/TunnelResponse/metrics/conns/p90`. - package var p90: Swift.Int - /// - Remark: Generated from `#/components/schemas/TunnelResponse/metrics/conns/p95`. - package var p95: Swift.Int - /// - Remark: Generated from `#/components/schemas/TunnelResponse/metrics/conns/p99`. - package var p99: Swift.Int - /// Creates a new `connsPayload`. - /// - /// - Parameters: - /// - count: - /// - gauge: - /// - rate1: - /// - rate5: - /// - rate15: - /// - p50: - /// - p90: - /// - p95: - /// - p99: - package init( - count: Swift.Int, - gauge: Swift.Int, - rate1: Swift.Int, - rate5: Swift.Int, - rate15: Swift.Int, - p50: Swift.Int, - p90: Swift.Int, - p95: Swift.Int, - p99: Swift.Int - ) { - self.count = count - self.gauge = gauge - self.rate1 = rate1 - self.rate5 = rate5 - self.rate15 = rate15 - self.p50 = p50 - self.p90 = p90 - self.p95 = p95 - self.p99 = p99 - } - package enum CodingKeys: String, CodingKey { - case count - case gauge - case rate1 - case rate5 - case rate15 - case p50 - case p90 - case p95 - case p99 - } - } - /// - Remark: Generated from `#/components/schemas/TunnelResponse/metrics/conns`. - package var conns: Components.Schemas.TunnelResponse.metricsPayload.connsPayload? - /// - Remark: Generated from `#/components/schemas/TunnelResponse/metrics/http`. - package struct httpPayload: Codable, Hashable, Sendable { - /// - Remark: Generated from `#/components/schemas/TunnelResponse/metrics/http/count`. - package var count: Swift.Int - /// - Remark: Generated from `#/components/schemas/TunnelResponse/metrics/http/rate1`. - package var rate1: Swift.Int - /// - Remark: Generated from `#/components/schemas/TunnelResponse/metrics/http/rate5`. - package var rate5: Swift.Int - /// - Remark: Generated from `#/components/schemas/TunnelResponse/metrics/http/rate15`. - package var rate15: Swift.Int - /// - Remark: Generated from `#/components/schemas/TunnelResponse/metrics/http/p50`. - package var p50: Swift.Int - /// - Remark: Generated from `#/components/schemas/TunnelResponse/metrics/http/p90`. - package var p90: Swift.Int - /// - Remark: Generated from `#/components/schemas/TunnelResponse/metrics/http/p95`. - package var p95: Swift.Int - /// - Remark: Generated from `#/components/schemas/TunnelResponse/metrics/http/p99`. - package var p99: Swift.Int - /// Creates a new `httpPayload`. - /// - /// - Parameters: - /// - count: - /// - rate1: - /// - rate5: - /// - rate15: - /// - p50: - /// - p90: - /// - p95: - /// - p99: - package init( - count: Swift.Int, - rate1: Swift.Int, - rate5: Swift.Int, - rate15: Swift.Int, - p50: Swift.Int, - p90: Swift.Int, - p95: Swift.Int, - p99: Swift.Int - ) { - self.count = count - self.rate1 = rate1 - self.rate5 = rate5 - self.rate15 = rate15 - self.p50 = p50 - self.p90 = p90 - self.p95 = p95 - self.p99 = p99 - } - package enum CodingKeys: String, CodingKey { - case count - case rate1 - case rate5 - case rate15 - case p50 - case p90 - case p95 - case p99 - } - } - /// - Remark: Generated from `#/components/schemas/TunnelResponse/metrics/http`. - package var http: Components.Schemas.TunnelResponse.metricsPayload.httpPayload? - /// Creates a new `metricsPayload`. - /// - /// - Parameters: - /// - conns: - /// - http: - package init( - conns: Components.Schemas.TunnelResponse.metricsPayload.connsPayload? = nil, - http: Components.Schemas.TunnelResponse.metricsPayload.httpPayload? = nil - ) { - self.conns = conns - self.http = http - } - package enum CodingKeys: String, CodingKey { - case conns - case http - } - } - /// - Remark: Generated from `#/components/schemas/TunnelResponse/metrics`. - package var metrics: Components.Schemas.TunnelResponse.metricsPayload? - /// Creates a new `TunnelResponse`. - /// - /// - Parameters: - /// - name: - /// - uri: - /// - public_url: - /// - proto: - /// - config: - /// - metrics: - package init( - name: Swift.String, - uri: Swift.String? = nil, - public_url: Swift.String, - proto: Swift.String? = nil, - config: Components.Schemas.TunnelResponse.configPayload, - metrics: Components.Schemas.TunnelResponse.metricsPayload? = nil - ) { - self.name = name - self.uri = uri - self.public_url = public_url - self.proto = proto - self.config = config - self.metrics = metrics - } - package enum CodingKeys: String, CodingKey { - case name - case uri - case public_url - case proto - case config - case metrics - } - } - } - /// Types generated from the `#/components/parameters` section of the OpenAPI document. - package enum Parameters {} - /// Types generated from the `#/components/requestBodies` section of the OpenAPI document. - package enum RequestBodies {} - /// Types generated from the `#/components/responses` section of the OpenAPI document. - package enum Responses {} - /// Types generated from the `#/components/headers` section of the OpenAPI document. - package enum Headers {} -} - -/// API operations, with input and output types, generated from `#/paths` in the OpenAPI document. -package enum Operations { - /// Access the root API resource of a running ngrok agent - /// - /// - Remark: HTTP `GET /api`. - /// - Remark: Generated from `#/paths//api/get`. - package enum get_sol_api { - package static let id: Swift.String = "get/api" - package struct Input: Sendable, Hashable { - /// Creates a new `Input`. - package init() {} - } - @frozen package enum Output: Sendable, Hashable { - package struct Ok: Sendable, Hashable { - /// Creates a new `Ok`. - package init() {} - } - /// Successful response - /// - /// - Remark: Generated from `#/paths//api/get/responses/200`. - /// - /// HTTP response code: `200 ok`. - case ok(Operations.get_sol_api.Output.Ok) - /// The associated value of the enum case if `self` is `.ok`. - /// - /// - Throws: An error if `self` is not `.ok`. - /// - SeeAlso: `.ok`. - package var ok: Operations.get_sol_api.Output.Ok { - get throws { - switch self { - case let .ok(response): - return response - default: - try throwUnexpectedResponseStatus( - expectedStatus: "ok", - response: self - ) - } - } - } - /// Undocumented response. - /// - /// A response with a code that is not documented in the OpenAPI document. - case undocumented(statusCode: Swift.Int, OpenAPIRuntime.UndocumentedPayload) - } - } - /// List Tunnels - /// - /// - Remark: HTTP `GET /api/tunnels`. - /// - Remark: Generated from `#/paths//api/tunnels/get(listTunnels)`. - package enum listTunnels { - package static let id: Swift.String = "listTunnels" - package struct Input: Sendable, Hashable { - /// - Remark: Generated from `#/paths/api/tunnels/GET/header`. - package struct Headers: Sendable, Hashable { - package var accept: [OpenAPIRuntime.AcceptHeaderContentType] - /// Creates a new `Headers`. - /// - /// - Parameters: - /// - accept: - package init(accept: [OpenAPIRuntime.AcceptHeaderContentType] = .defaultValues()) { - self.accept = accept - } - } - package var headers: Operations.listTunnels.Input.Headers - /// Creates a new `Input`. - /// - /// - Parameters: - /// - headers: - package init(headers: Operations.listTunnels.Input.Headers = .init()) { - self.headers = headers - } - } - @frozen package enum Output: Sendable, Hashable { - package struct Ok: Sendable, Hashable { - /// - Remark: Generated from `#/paths/api/tunnels/GET/responses/200/content`. - @frozen package enum Body: Sendable, Hashable { - /// - Remark: Generated from `#/paths/api/tunnels/GET/responses/200/content/application\/json`. - case json(Components.Schemas.TunnelList) - /// The associated value of the enum case if `self` is `.json`. - /// - /// - Throws: An error if `self` is not `.json`. - /// - SeeAlso: `.json`. - package var json: Components.Schemas.TunnelList { - get throws { - switch self { - case let .json(body): - return body - } - } - } - } - /// Received HTTP response body - package var body: Operations.listTunnels.Output.Ok.Body - /// Creates a new `Ok`. - /// - /// - Parameters: - /// - body: Received HTTP response body - package init(body: Operations.listTunnels.Output.Ok.Body) { - self.body = body - } - } - /// Successful response - /// - /// - Remark: Generated from `#/paths//api/tunnels/get(listTunnels)/responses/200`. - /// - /// HTTP response code: `200 ok`. - case ok(Operations.listTunnels.Output.Ok) - /// The associated value of the enum case if `self` is `.ok`. - /// - /// - Throws: An error if `self` is not `.ok`. - /// - SeeAlso: `.ok`. - package var ok: Operations.listTunnels.Output.Ok { - get throws { - switch self { - case let .ok(response): - return response - default: - try throwUnexpectedResponseStatus( - expectedStatus: "ok", - response: self - ) - } - } - } - /// Undocumented response. - /// - /// A response with a code that is not documented in the OpenAPI document. - case undocumented(statusCode: Swift.Int, OpenAPIRuntime.UndocumentedPayload) - } - @frozen package enum AcceptableContentType: AcceptableProtocol { - case json - case other(Swift.String) - package init?(rawValue: Swift.String) { - switch rawValue.lowercased() { - case "application/json": - self = .json - default: - self = .other(rawValue) - } - } - package var rawValue: Swift.String { - switch self { - case let .other(string): - return string - case .json: - return "application/json" - } - } - package static var allCases: [Self] { - [ - .json - ] - } - } - } - /// Start tunnel - /// - /// - Remark: HTTP `POST /api/tunnels`. - /// - Remark: Generated from `#/paths//api/tunnels/post(startTunnel)`. - package enum startTunnel { - package static let id: Swift.String = "startTunnel" - package struct Input: Sendable, Hashable { - /// - Remark: Generated from `#/paths/api/tunnels/POST/header`. - package struct Headers: Sendable, Hashable { - package var accept: [OpenAPIRuntime.AcceptHeaderContentType] - /// Creates a new `Headers`. - /// - /// - Parameters: - /// - accept: - package init(accept: [OpenAPIRuntime.AcceptHeaderContentType] = .defaultValues()) { - self.accept = accept - } - } - package var headers: Operations.startTunnel.Input.Headers - /// - Remark: Generated from `#/paths/api/tunnels/POST/requestBody`. - @frozen package enum Body: Sendable, Hashable { - /// - Remark: Generated from `#/paths/api/tunnels/POST/requestBody/content/application\/json`. - case json(Components.Schemas.TunnelRequest) - } - package var body: Operations.startTunnel.Input.Body - /// Creates a new `Input`. - /// - /// - Parameters: - /// - headers: - /// - body: - package init( - headers: Operations.startTunnel.Input.Headers = .init(), - body: Operations.startTunnel.Input.Body - ) { - self.headers = headers - self.body = body - } - } - @frozen package enum Output: Sendable, Hashable { - package struct Created: Sendable, Hashable { - /// - Remark: Generated from `#/paths/api/tunnels/POST/responses/201/content`. - @frozen package enum Body: Sendable, Hashable { - /// - Remark: Generated from `#/paths/api/tunnels/POST/responses/201/content/application\/json`. - case json(Components.Schemas.TunnelResponse) - /// The associated value of the enum case if `self` is `.json`. - /// - /// - Throws: An error if `self` is not `.json`. - /// - SeeAlso: `.json`. - package var json: Components.Schemas.TunnelResponse { - get throws { - switch self { - case let .json(body): - return body - } - } - } - } - /// Received HTTP response body - package var body: Operations.startTunnel.Output.Created.Body - /// Creates a new `Created`. - /// - /// - Parameters: - /// - body: Received HTTP response body - package init(body: Operations.startTunnel.Output.Created.Body) { - self.body = body - } - } - /// Tunnel started successfully - /// - /// - Remark: Generated from `#/paths//api/tunnels/post(startTunnel)/responses/201`. - /// - /// HTTP response code: `201 created`. - case created(Operations.startTunnel.Output.Created) - /// The associated value of the enum case if `self` is `.created`. - /// - /// - Throws: An error if `self` is not `.created`. - /// - SeeAlso: `.created`. - package var created: Operations.startTunnel.Output.Created { - get throws { - switch self { - case let .created(response): - return response - default: - try throwUnexpectedResponseStatus( - expectedStatus: "created", - response: self - ) - } - } - } - /// Undocumented response. - /// - /// A response with a code that is not documented in the OpenAPI document. - case undocumented(statusCode: Swift.Int, OpenAPIRuntime.UndocumentedPayload) - } - @frozen package enum AcceptableContentType: AcceptableProtocol { - case json - case other(Swift.String) - package init?(rawValue: Swift.String) { - switch rawValue.lowercased() { - case "application/json": - self = .json - default: - self = .other(rawValue) - } - } - package var rawValue: Swift.String { - switch self { - case let .other(string): - return string - case .json: - return "application/json" - } - } - package static var allCases: [Self] { - [ - .json - ] - } - } - } - /// Tunnel detail - /// - /// - Remark: HTTP `GET /api/tunnels/{name}`. - /// - Remark: Generated from `#/paths//api/tunnels/{name}/get(getTunnel)`. - package enum getTunnel { - package static let id: Swift.String = "getTunnel" - package struct Input: Sendable, Hashable { - /// - Remark: Generated from `#/paths/api/tunnels/{name}/GET/path`. - package struct Path: Sendable, Hashable { - /// - Remark: Generated from `#/paths/api/tunnels/{name}/GET/path/name`. - package var name: Swift.String - /// Creates a new `Path`. - /// - /// - Parameters: - /// - name: - package init(name: Swift.String) { - self.name = name - } - } - package var path: Operations.getTunnel.Input.Path - /// - Remark: Generated from `#/paths/api/tunnels/{name}/GET/header`. - package struct Headers: Sendable, Hashable { - package var accept: [OpenAPIRuntime.AcceptHeaderContentType] - /// Creates a new `Headers`. - /// - /// - Parameters: - /// - accept: - package init(accept: [OpenAPIRuntime.AcceptHeaderContentType] = .defaultValues()) { - self.accept = accept - } - } - package var headers: Operations.getTunnel.Input.Headers - /// Creates a new `Input`. - /// - /// - Parameters: - /// - path: - /// - headers: - package init( - path: Operations.getTunnel.Input.Path, - headers: Operations.getTunnel.Input.Headers = .init() - ) { - self.path = path - self.headers = headers - } - } - @frozen package enum Output: Sendable, Hashable { - package struct Ok: Sendable, Hashable { - /// - Remark: Generated from `#/paths/api/tunnels/{name}/GET/responses/200/content`. - @frozen package enum Body: Sendable, Hashable { - /// - Remark: Generated from `#/paths/api/tunnels/{name}/GET/responses/200/content/application\/json`. - case json(OpenAPIRuntime.OpenAPIValueContainer) - /// The associated value of the enum case if `self` is `.json`. - /// - /// - Throws: An error if `self` is not `.json`. - /// - SeeAlso: `.json`. - package var json: OpenAPIRuntime.OpenAPIValueContainer { - get throws { - switch self { - case let .json(body): - return body - } - } - } - } - /// Received HTTP response body - package var body: Operations.getTunnel.Output.Ok.Body - /// Creates a new `Ok`. - /// - /// - Parameters: - /// - body: Received HTTP response body - package init(body: Operations.getTunnel.Output.Ok.Body) { - self.body = body - } - } - /// Successful response - /// - /// - Remark: Generated from `#/paths//api/tunnels/{name}/get(getTunnel)/responses/200`. - /// - /// HTTP response code: `200 ok`. - case ok(Operations.getTunnel.Output.Ok) - /// The associated value of the enum case if `self` is `.ok`. - /// - /// - Throws: An error if `self` is not `.ok`. - /// - SeeAlso: `.ok`. - package var ok: Operations.getTunnel.Output.Ok { - get throws { - switch self { - case let .ok(response): - return response - default: - try throwUnexpectedResponseStatus( - expectedStatus: "ok", - response: self - ) - } - } - } - /// Undocumented response. - /// - /// A response with a code that is not documented in the OpenAPI document. - case undocumented(statusCode: Swift.Int, OpenAPIRuntime.UndocumentedPayload) - } - @frozen package enum AcceptableContentType: AcceptableProtocol { - case json - case other(Swift.String) - package init?(rawValue: Swift.String) { - switch rawValue.lowercased() { - case "application/json": - self = .json - default: - self = .other(rawValue) - } - } - package var rawValue: Swift.String { - switch self { - case let .other(string): - return string - case .json: - return "application/json" - } - } - package static var allCases: [Self] { - [ - .json - ] - } - } - } - /// Stop tunnel - /// - /// - Remark: HTTP `DELETE /api/tunnels/{name}`. - /// - Remark: Generated from `#/paths//api/tunnels/{name}/delete(stopTunnel)`. - package enum stopTunnel { - package static let id: Swift.String = "stopTunnel" - package struct Input: Sendable, Hashable { - /// - Remark: Generated from `#/paths/api/tunnels/{name}/DELETE/path`. - package struct Path: Sendable, Hashable { - /// - Remark: Generated from `#/paths/api/tunnels/{name}/DELETE/path/name`. - package var name: Swift.String - /// Creates a new `Path`. - /// - /// - Parameters: - /// - name: - package init(name: Swift.String) { - self.name = name - } - } - package var path: Operations.stopTunnel.Input.Path - /// Creates a new `Input`. - /// - /// - Parameters: - /// - path: - package init(path: Operations.stopTunnel.Input.Path) { - self.path = path - } - } - @frozen package enum Output: Sendable, Hashable { - package struct NoContent: Sendable, Hashable { - /// Creates a new `NoContent`. - package init() {} - } - /// Tunnel stopped successfully - /// - /// - Remark: Generated from `#/paths//api/tunnels/{name}/delete(stopTunnel)/responses/204`. - /// - /// HTTP response code: `204 noContent`. - case noContent(Operations.stopTunnel.Output.NoContent) - /// The associated value of the enum case if `self` is `.noContent`. - /// - /// - Throws: An error if `self` is not `.noContent`. - /// - SeeAlso: `.noContent`. - package var noContent: Operations.stopTunnel.Output.NoContent { - get throws { - switch self { - case let .noContent(response): - return response - default: - try throwUnexpectedResponseStatus( - expectedStatus: "noContent", - response: self - ) - } - } - } - /// Undocumented response. - /// - /// A response with a code that is not documented in the OpenAPI document. - case undocumented(statusCode: Swift.Int, OpenAPIRuntime.UndocumentedPayload) - } - } -} diff --git a/Sources/Ngrokit/FileHandle.swift b/Sources/Ngrokit/FileHandle.swift deleted file mode 100644 index bfb55f6..0000000 --- a/Sources/Ngrokit/FileHandle.swift +++ /dev/null @@ -1,75 +0,0 @@ -// -// FileHandle.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation - -// swiftlint:disable:next force_try -private let ngrokCLIErrorRegex = try! NSRegularExpression(pattern: "ERR_NGROK_([0-9]+)") - -/// A protocol for handling data. -public protocol DataHandle { - /// Reads data until the end. - /// - /// - Returns: The data read until the end, or `nil` if there is no more data. - /// - Throws: An error if there was a problem reading the data. - func readToEnd() throws -> Data? -} - -extension FileHandle: DataHandle {} - -extension DataHandle { - /// Parses the ngrok error code from the data. - /// - /// - Returns: The parsed ngrok error code. - /// - Throws: An error if there was a problem parsing the error code. - internal func parseNgrokErrorCode() throws -> NgrokError { - guard let data = try readToEnd() else { - throw RuntimeError.unknownError - } - let text = String(decoding: data, as: UTF8.self) - - guard let match = ngrokCLIErrorRegex.firstMatch( - in: text, - range: .init(location: 0, length: text.count) - ), match.numberOfRanges > 0 else { - throw RuntimeError.unknownEarlyTermination(text) - } - - guard let range = Range(match.range(at: 1), in: text) else { - throw RuntimeError.unknownEarlyTermination(text) - } - guard let code = Int(text[range]) else { - throw RuntimeError.unknownEarlyTermination(text) - } - guard let error = NgrokError(rawValue: code) else { - throw RuntimeError.unknownNgrokErrorCode(code) - } - return error - } -} diff --git a/Sources/Ngrokit/NgrokCLIAPI.swift b/Sources/Ngrokit/NgrokCLIAPI.swift deleted file mode 100644 index dde9d32..0000000 --- a/Sources/Ngrokit/NgrokCLIAPI.swift +++ /dev/null @@ -1,54 +0,0 @@ -// -// NgrokCLIAPI.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation - -/// A protocol for interacting with the Ngrok CLI API. -/// -/// This protocol extends the `Sendable` protocol. -/// -/// - Note: The `Sendable` protocol is not defined in this code snippet. -/// -/// - Important: The `NgrokCLIAPI` protocol is not defined in this code snippet. -/// -/// - Requires: The `NgrokProcess` type to be defined. -/// -/// - SeeAlso: `NgrokProcess` -public protocol NgrokCLIAPI: Sendable { - /// Creates a process for the specified HTTP port. - /// - /// - Parameter httpPort: The port number for the HTTP server. - /// - /// - Returns: An instance of `NgrokProcess` for the specified port. - func process(forHTTPPort httpPort: Int) -> any NgrokProcess -} - -/// A type representing a process created by the Ngrok CLI API. -/// -/// - Note: The `NgrokProcess` type is not defined in this code snippet. diff --git a/Sources/Ngrokit/NgrokClient.swift b/Sources/Ngrokit/NgrokClient.swift deleted file mode 100644 index 12d29e2..0000000 --- a/Sources/Ngrokit/NgrokClient.swift +++ /dev/null @@ -1,120 +0,0 @@ -// -// NgrokClient.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation -import NgrokOpenAPIClient -import OpenAPIRuntime - -#if canImport(FoundationNetworking) - import FoundationNetworking -#endif - -/// A client for interacting with the Ngrok API. -/// -/// Use this client to start and stop tunnels, as well as list existing tunnels. -/// -/// To create an instance of `NgrokClient`, -/// you need to provide a transport and an optional server URL. -/// -/// Example usage: -/// -/// ```swift -/// let client = NgrokClient(transport: URLSession.shared) -/// ``` -/// -/// - Note: The default server URL is `try! Servers.server1()`. -/// -/// - SeeAlso: `TunnelRequest` -/// - SeeAlso: `Tunnel` -public struct NgrokClient: Sendable { - // swiftlint:disable force_try - /// The default server URL. - public static let defaultServerURL = try! Servers.server1() - // swiftlint:enable force_try - - private let underlyingClient: any APIProtocol - - /// Initializes a new instance of `NgrokClient`. - /// - /// - Parameters: - /// - transport: The transport to use for making API requests. - /// - serverURL: The server URL to use. If `nil`, - /// the default server URL will be used. - public init(transport: any ClientTransport, serverURL: URL? = nil) { - let underlyingClient = NgrokOpenAPIClient.Client( - serverURL: serverURL ?? Self.defaultServerURL, - transport: transport - ) - self.init(underlyingClient: underlyingClient) - } - - internal init(underlyingClient: any APIProtocol) { - self.underlyingClient = underlyingClient - } - - /// Starts a new tunnel. - /// - /// - Parameter request: The tunnel request. - /// - /// - Returns: The created tunnel. - /// - /// - Throws: An error if the tunnel creation fails. - public func startTunnel(_ request: TunnelRequest) async throws -> NgrokTunnel { - let tunnelRequest: Components.Schemas.TunnelRequest - tunnelRequest = .init(request: request) - let response = try await underlyingClient.startTunnel( - .init( - body: .json(tunnelRequest) - ) - ).created.body.json - let tunnel: NgrokTunnel = try .init(response: response) - return tunnel - } - - /// Stops a tunnel with the specified name. - /// - /// - Parameter name: The name of the tunnel to stop. - /// - /// - Throws: An error if the tunnel cannot be stopped. - public func stopTunnel(withName name: String) async throws { - _ = try await underlyingClient.stopTunnel(path: .init(name: name)).noContent - } - - /// Lists all existing tunnels. - /// - /// - Returns: An array of tunnels. - /// - /// - Throws: An error if the tunnel listing fails. - public func listTunnels() async throws -> [NgrokTunnel] { - try await underlyingClient - .listTunnels() - .ok.body.json.tunnels - .map(NgrokTunnel.init(response:)) - } -} diff --git a/Sources/Ngrokit/NgrokError.swift b/Sources/Ngrokit/NgrokError.swift deleted file mode 100644 index 54ae124..0000000 --- a/Sources/Ngrokit/NgrokError.swift +++ /dev/null @@ -1,108 +0,0 @@ -// -// NgrokError.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation - -// swiftlint:disable line_length - -/// An enumeration representing possible errors that can occur with Ngrok. -/// -/// - invalidMetadataLength: The metadata length is invalid. -/// - accountLimitExceeded: The account limit for simultaneous ngrok agent sessions has been exceeded. -/// - unsupportedAgentVersion: The ngrok agent version is no longer supported. -/// - captchaFailed: The captcha solving failed. -/// - accountViolation: Creating an ngrok account is disallowed due to violation of the terms of service. -/// - gatewayError: Ngrok gateway error. -/// - tunnelNotFound: The tunnel was not found. -/// - accountBanned: The account associated with the hostname has been banned. -/// - passwordTooShort: The password is too short. -/// - accountCreationNotAllowed: Creating a new account is not allowed. -/// - invalidCredentials: The email or password entered is not valid. -/// - userAlreadyExists: A user with the email address already exists. -/// - disallowedEmailProvider: Sign-ups are disallowed for the email provider. -/// - htmlContentSignupRequired: Signing up for an ngrok account and installing the authtoken is required before serving HTML content. -/// - websiteVisitWarning: A warning before visiting a website served by ngrok.com. -/// - tunnelConnectionFailed: The ngrok agent failed to establish a connection to the upstream web service. -/// -public enum NgrokError: Int, LocalizedError { - case invalidMetadataLength = 100 - case accountLimitExceeded = 108 - case unsupportedAgentVersion = 120 - case captchaFailed = 1_205 - case accountViolation = 1_226 - case gatewayError = 3_004 - case tunnelNotFound = 3_200 - case accountBanned = 3_208 - case passwordTooShort = 4_011 - case accountCreationNotAllowed = 4_013 - case invalidCredentials = 4_100 - case userAlreadyExists = 4_101 - case disallowedEmailProvider = 4_108 - case htmlContentSignupRequired = 6_022 - case websiteVisitWarning = 6_024 - case tunnelConnectionFailed = 8_012 - - public var errorDescription: String? { - switch self { - case .invalidMetadataLength: - "Invalid metadata length" - case .accountLimitExceeded: - "You've hit your account limit for simultaneous ngrok agent sessions. Try stopping an existing agent or upgrading your account." - case .unsupportedAgentVersion: - "Your ngrok agent version is no longer supported. Only the most recent version of the ngrok agent is supported without an account. Update to a newer version with ngrok update or by downloading from https://ngrok.com/download. Sign up for an account to avoid forced version upgrades: https://ngrok.com/signup." - case .captchaFailed: - "You failed to solve the captcha, please try again." - case .accountViolation: - "You are disallowed from creating an ngrok account due to violation of the terms of service." - case .gatewayError: - "Ngrok gateway error. The server returned an invalid or incomplete HTTP response. Try starting ngrok with the full upstream service URL (e.g. ngrok http https://localhost:8081)" - case .tunnelNotFound: - "Tunnel not found. This could be because your agent is not online or your tunnel has been flagged by our automated moderation system." - case .accountBanned: - "The account associated with this hostname has been banned. We've determined this account to be in violation of ngrok's terms of service. If you are the account owner and believe this is a mistake, please contact support@ngrok.com." - case .passwordTooShort: - "Your password must be at least 10 characters." - case .accountCreationNotAllowed: - "You may not create a new account because you are already a member of a free account. Upgrade or delete that account first before creating a new account." - case .invalidCredentials: - "The email or password you entered is not valid." - case .userAlreadyExists: - "A user with the email address already exists." - case .disallowedEmailProvider: - "Sign-ups are disallowed for the email provider. Please sign up with a different email provider." - case .htmlContentSignupRequired: - "Before you can serve HTML content, you must sign up for an ngrok account and install your authtoken." - case .websiteVisitWarning: - "You are about to visit HOSTPORT, served by SERVINGIP. This website is served for free through ngrok.com. You should only visit this website if you trust whoever sent the link to you." - case .tunnelConnectionFailed: - "Traffic was successfully tunneled to the ngrok agent, but the agent failed to establish a connection to the upstream web service" - } - } - // swiftlint:enable line_length -} diff --git a/Sources/Ngrokit/NgrokMacProcess.swift b/Sources/Ngrokit/NgrokMacProcess.swift deleted file mode 100644 index 07cae72..0000000 --- a/Sources/Ngrokit/NgrokMacProcess.swift +++ /dev/null @@ -1,115 +0,0 @@ -// -// NgrokMacProcess.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation - -/// A class representing a Ngrok process on macOS. -/// -/// This class conforms to the `NgrokProcess` protocol. -/// -/// - Note: This class is an actor, -/// meaning it can be safely accessed from multiple concurrent tasks. -/// -/// - Author: Leo Dion -/// - Version: 2024 -/// - Copyright: © BrightDigit -/// -/// - SeeAlso: `NgrokProcess` -public actor NgrokMacProcess: NgrokProcess { - private var terminationHandler: (@Sendable (any Error) -> Void)? - internal let process: ProcessType - private let pipe: ProcessType.PipeType - - /// Initializes a new instance of `NgrokMacProcess`. - /// - /// - Parameters: - /// - ngrokPath: The path to the Ngrok executable. - /// - httpPort: The port to use for the HTTP connection. - /// - processType: The type of process to use. - /// - /// - Returns: A new instance of `NgrokMacProcess`. - public init( - ngrokPath: String, - httpPort: Int, - processType _: ProcessType.Type - ) { - self.init( - process: .init( - executableFilePath: ngrokPath, - scheme: "http", - port: httpPort - ) - ) - } - - private init( - process: ProcessType, - pipe: ProcessType.PipeType? = nil, - terminationHandler: (@Sendable (any Error) -> Void)? = nil - ) { - self.terminationHandler = terminationHandler - self.process = process - if let pipe { - self.pipe = pipe - } else { - let newPipe: ProcessType.PipeType = process.createPipe() - self.process.standardError = newPipe - self.pipe = newPipe - } - } - - /// A private method that handles the termination of the process. - /// - /// - Parameters: - /// - forProcess: The process that has terminated. - @Sendable - private nonisolated func terminationHandler(forProcess _: any Processable) { - Task { - let error: any Error - do { - error = try self.pipe.fileHandleForReading.parseNgrokErrorCode() - } catch let runtimeError as RuntimeError { - error = runtimeError - } - await self.terminationHandler?(error) - } - } - - /// Runs the Ngrok process. - /// - /// - Parameters: - /// - onError: A closure that handles any errors that occur during the process. - /// - /// - Throws: An error if the process fails to run. - public func run(onError: @Sendable @escaping (any Error) -> Void) async throws { - process.setTerminationHandler(terminationHandler(forProcess:)) - terminationHandler = onError - try process.run() - } -} diff --git a/Sources/Ngrokit/NgrokProcess.swift b/Sources/Ngrokit/NgrokProcess.swift deleted file mode 100644 index 2e3aee1..0000000 --- a/Sources/Ngrokit/NgrokProcess.swift +++ /dev/null @@ -1,47 +0,0 @@ -// -// NgrokProcess.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -/// A protocol representing a process for running Ngrok. -/// -/// - Note: This protocol is `Sendable`, allowing it to be used in asynchronous contexts. -/// -/// - Important: Implementations of this protocol -/// must provide a `run` method that runs the Ngrok process. -/// -/// - Parameter onError: A closure to handle any errors that occur during the process. -/// -/// - Throws: An error if the process fails to run. -/// -/// - SeeAlso: `NgrokProcessImplementation` -public protocol NgrokProcess: Sendable { - /// Runs the Ngrok process. - /// - /// - Parameter onError: A closure to handle any errors that occur during the process. - func run(onError: @Sendable @escaping (any Error) -> Void) async throws -} diff --git a/Sources/Ngrokit/NgrokProcessCLIAPI.swift b/Sources/Ngrokit/NgrokProcessCLIAPI.swift deleted file mode 100644 index 6d73a14..0000000 --- a/Sources/Ngrokit/NgrokProcessCLIAPI.swift +++ /dev/null @@ -1,67 +0,0 @@ -// -// NgrokProcessCLIAPI.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation - -/// A struct representing the Ngrok CLI API. -/// -/// Use this API to interact with Ngrok and create tunnels. -/// -/// - Note: This API requires a valid Ngrok installation. -/// -/// - Parameters: -/// - ngrokPath: The path to the Ngrok executable. -/// -/// - SeeAlso: `NgrokCLIAPI` -public struct NgrokProcessCLIAPI { - /// The path to the Ngrok executable. - public let ngrokPath: String - - /// Initializes a new instance of `NgrokProcessCLIAPI`. - /// - /// - Parameter ngrokPath: The path to the Ngrok executable. - public init(ngrokPath: String) { - self.ngrokPath = ngrokPath - } -} - -extension NgrokProcessCLIAPI: NgrokCLIAPI { - /// Creates a new `NgrokProcess` for the specified HTTP port. - /// - /// - Parameter httpPort: The port number for the HTTP server. - /// - /// - Returns: An instance of `NgrokProcess` for the specified HTTP port. - public func process(forHTTPPort httpPort: Int) -> any NgrokProcess { - NgrokMacProcess( - ngrokPath: ngrokPath, - httpPort: httpPort, - processType: ProcessType.self - ) - } -} diff --git a/Sources/Ngrokit/NgrokTunnel.swift b/Sources/Ngrokit/NgrokTunnel.swift deleted file mode 100644 index 77c32c9..0000000 --- a/Sources/Ngrokit/NgrokTunnel.swift +++ /dev/null @@ -1,106 +0,0 @@ -// -// NgrokTunnel.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation -import NgrokOpenAPIClient - -/// A struct representing a tunnel. -/// -/// - Note: This struct conforms to the `Sendable` protocol. -/// -/// - Parameters: -/// - name: The name of the tunnel. -/// - publicURL: The public URL of the tunnel. -/// - config: The configuration of the tunnel. -/// -/// - SeeAlso: `NgrokTunnelConfiguration` -/// -/// - Throws: `RuntimeError.invalidURL` if the public URL or the address URL is invalid. -/// -/// - SeeAlso: `RuntimeError` -/// -/// - Note: This struct has an additional initializer -/// that takes a `TunnelResponse` object. -/// -/// - SeeAlso: `Components.Schemas.TunnelResponse` -public struct NgrokTunnel: Sendable { - /// The name of the tunnel. - public let name: String - - /// The public URL of the tunnel. - public let publicURL: URL - - /// The configuration of the tunnel. - public let config: NgrokTunnelConfiguration - - /// Initializes a new `Tunnel` instance. - /// - /// - Parameters: - /// - name: The name of the tunnel. - /// - publicURL: The public URL of the tunnel. - /// - config: The configuration of the tunnel. - public init(name: String, publicURL: URL, config: NgrokTunnelConfiguration) { - self.name = name - self.publicURL = publicURL - self.config = config - } -} - -extension NgrokTunnel { - /// Initializes a new `Tunnel` instance from a `TunnelResponse` object. - /// - /// - Parameters: - /// - response: The `TunnelResponse` object. - /// - /// - Throws: `RuntimeError.invalidURL` - /// if the public URL or the address URL is invalid. - /// - /// - SeeAlso: `RuntimeError` - /// - /// - Note: This initializer is internal and should not be used directly. - /// - /// - SeeAlso: `Components.Schemas.TunnelResponse` - - internal init(response: Components.Schemas.TunnelResponse) throws { - guard let publicURL = URL(string: response.public_url) else { - throw RuntimeError.invalidURL(response.public_url) - } - guard let addr = URL(string: response.config.addr) else { - throw RuntimeError.invalidURL(response.config.addr) - } - self.init( - name: response.name, - publicURL: publicURL, - config: .init( - addr: addr, - inspect: response.config.inspect - ) - ) - } -} diff --git a/Sources/Ngrokit/NgrokTunnelConfiguration.swift b/Sources/Ngrokit/NgrokTunnelConfiguration.swift deleted file mode 100644 index 6e25f7a..0000000 --- a/Sources/Ngrokit/NgrokTunnelConfiguration.swift +++ /dev/null @@ -1,50 +0,0 @@ -// -// NgrokTunnelConfiguration.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -/// Represents the configuration for an Ngrok tunnel. -/// -/// - Note: This struct is `Sendable`. -/// -/// - Parameters: -/// - addr: The URL of the tunnel. -/// - inspect: A boolean value indicating whether to enable inspection of the tunnel. -/// -import Foundation - -#if canImport(FoundationNetworking) - import FoundationNetworking -#endif - -public struct NgrokTunnelConfiguration: Sendable { - /// The URL of the tunnel. - public let addr: URL - - /// A boolean value indicating whether to enable inspection of the tunnel. - public let inspect: Bool -} diff --git a/Sources/Ngrokit/Pipable.swift b/Sources/Ngrokit/Pipable.swift deleted file mode 100644 index c745b58..0000000 --- a/Sources/Ngrokit/Pipable.swift +++ /dev/null @@ -1,46 +0,0 @@ -// -// Pipable.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation - -/// A protocol for types that can be piped. -/// -/// Types conforming to this protocol must also conform to `Sendable`. -/// -/// - Note: The associated type `DataHandleType` must conform to `DataHandle`. -/// -/// - Important: The `fileHandleForReading` property -/// must be implemented to provide a handle for reading data. -public protocol Pipable: Sendable { - /// The associated type representing the data handle. - associatedtype DataHandleType: DataHandle - - /// The file handle used for reading data. - var fileHandleForReading: DataHandleType { get } -} diff --git a/Sources/Ngrokit/Processable.swift b/Sources/Ngrokit/Processable.swift deleted file mode 100644 index 2969dff..0000000 --- a/Sources/Ngrokit/Processable.swift +++ /dev/null @@ -1,103 +0,0 @@ -// -// Processable.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -/// A protocol for objects that can be processed. -/// -/// - Note: This protocol is `Sendable` and `AnyObject`. -/// -/// - Important: The associated type `PipeType` must conform to `Pipable`. -/// -/// - SeeAlso: `Pipable` -/// -/// - SeeAlso: `TerminationReason` -/// -/// - SeeAlso: `PipeType` -/// -/// - SeeAlso: `run()` -/// -/// - SeeAlso: `setTerminationHandler(_:)` -/// -/// - SeeAlso: `createPipe()` -/// -/// - SeeAlso: `standardErrorPipe` -/// -/// - SeeAlso: `terminationReason` -/// -/// - Requires: `executableFilePath`, `scheme`, and `port` parameters to initialize. -/// -/// - Requires: `run()` method to be implemented. -/// -/// - Requires: `standardErrorPipe` property to be gettable and settable. -/// -/// - Requires: `terminationReason` property to be gettable. -/// -/// - Requires: `setTerminationHandler(_:)` method to be implemented. -/// -public protocol Processable: Sendable, AnyObject { - /// The associated type for the pipe used by the process. - associatedtype PipeType: Pipable - - /// The pipe used for standard error output. - var standardError: PipeType? { get set } - - /// The reason for the process termination. - var terminationReason: TerminationReason { get } - - /// Initializes a `Processable` object. - /// - /// - Parameters: - /// - executableFilePath: The file path of the executable. - /// - scheme: The scheme to use. - /// - port: The port to use. - /// - /// - Requires: This initializer must be implemented. - init(executableFilePath: String, scheme: String, port: Int) - - /// Sets a closure to be called when the process terminates. - /// - /// - Parameters: - /// - closure: The closure to be called. - /// - /// - Requires: This method must be implemented. - func setTerminationHandler(_ closure: @escaping @Sendable (Self) -> Void) - - /// Creates a new pipe. - /// - /// - Returns: A new instance of `PipeType`. - /// - /// - Requires: This method must be implemented. - func createPipe() -> PipeType - - /// Runs the process. - /// - /// - Throws: An error if the process fails. - /// - /// - Requires: This method must be implemented. - func run() throws -} diff --git a/Sources/Ngrokit/ProcessableProcess.swift b/Sources/Ngrokit/ProcessableProcess.swift deleted file mode 100644 index aacb4f6..0000000 --- a/Sources/Ngrokit/ProcessableProcess.swift +++ /dev/null @@ -1,107 +0,0 @@ -// -// ProcessableProcess.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation - -#if os(macOS) - - /// A process that can be processed and executed. - /// - /// - Note: This class is only available on macOS. - /// - /// - Important: Make sure to set the `standardErrorPipe` - /// property before executing the process. - /// - /// - SeeAlso: `Processable` - public final class ProcessableProcess: Processable { - /// The type of pipe used for standard error. - public typealias PipeType = Pipe - - private let process: Process - - public var terminationReason: TerminationReason { - process.terminationReason - } - - /// The pipe used for standard error. - public var standardError: Pipe? { - get { - process.standardError as? Pipe - } - set { - process.standardError = newValue - } - } - - private init(process: Process) { - self.process = process - } - - /// Initializes a new `ProcessableProcess` instance. - /// - /// - Parameters: - /// - executableFilePath: The file path of the executable. - /// - scheme: The scheme to use. - /// - port: The port to use. - /// - /// - Important: Make sure to set the `standardErrorPipe` - /// property before executing the process. - public convenience init(executableFilePath: String, scheme: String, port: Int) { - let process = Process() - process.executableURL = .init(filePath: executableFilePath) - process.arguments = [scheme, port.description] - self.init(process: process) - } - - /// Sets the termination handler closure for the process. - /// - /// - Parameter closure: The closure to be called when the process terminates. - public func setTerminationHandler( - _ closure: @escaping @Sendable (ProcessableProcess) -> Void - ) { - process.terminationHandler = { process in - assert(process == self.process) - closure(self) - } - } - - /// Creates a new pipe. - /// - /// - Returns: A new `Pipe` instance. - public func createPipe() -> Pipe { - Pipe() - } - - public func run() throws { - try process.run() - } - } - - extension Pipe: Pipable {} -#endif diff --git a/Sources/Ngrokit/RuntimeError.swift b/Sources/Ngrokit/RuntimeError.swift deleted file mode 100644 index bee17ba..0000000 --- a/Sources/Ngrokit/RuntimeError.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// RuntimeError.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation - -public enum RuntimeError: Error { - case invalidURL(String) - case invalidErrorData(Data) - case unknownEarlyTermination(String) - case unknownError - case unknownNgrokErrorCode(Int) -} diff --git a/Sources/Ngrokit/TerminationReason.swift b/Sources/Ngrokit/TerminationReason.swift deleted file mode 100644 index dff7ba5..0000000 --- a/Sources/Ngrokit/TerminationReason.swift +++ /dev/null @@ -1,47 +0,0 @@ -// -// TerminationReason.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation - -// swiftlint:disable file_types_order -#if os(macOS) - /// Represents the reason for the termination of a process. - public typealias TerminationReason = Process.TerminationReason -#else - - /// Represents the reason for the termination of a process. - /// - /// - exit: The process exited normally. - /// - uncaughtSignal: The process terminated due to an uncaught signal. - public enum TerminationReason: Int, Sendable { - case exit = 1 - case uncaughtSignal = 2 - } -#endif -// swiftlint:enable file_types_order diff --git a/Sources/Ngrokit/TunnelRequest.swift b/Sources/Ngrokit/TunnelRequest.swift deleted file mode 100644 index 1255f11..0000000 --- a/Sources/Ngrokit/TunnelRequest.swift +++ /dev/null @@ -1,74 +0,0 @@ -// -// TunnelRequest.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import NgrokOpenAPIClient - -/// Represents a request to create a tunnel. -public struct TunnelRequest: Sendable { - /// The address of the tunnel. - public let addr: String - - /// The protocol to use for the tunnel. - public let proto: String - - /// The name of the tunnel. - public let name: String - - /// Initializes a new `TunnelRequest` instance. - /// - /// - Parameters: - /// - addr: The address of the tunnel. - /// - proto: The protocol to use for the tunnel. - /// - name: The name of the tunnel. - public init(addr: String, proto: String, name: String) { - self.addr = addr - self.proto = proto - self.name = name - } - - /// Initializes a new `TunnelRequest` instance. - /// - /// - Parameters: - /// - port: The port number of the tunnel. - /// - name: The name of the tunnel. - /// - proto: The protocol to use for the tunnel. Default value is "http". - public init(port: Int, name: String, proto: String = "http") { - self.init(addr: port.description, proto: proto, name: name) - } -} - -extension Components.Schemas.TunnelRequest { - /// Initializes a new `Components.Schemas.TunnelRequest` instance. - /// - /// - Parameters: - /// - request: The `TunnelRequest` instance to initialize from. - internal init(request: TunnelRequest) { - self.init(addr: request.addr, proto: request.proto, name: request.name) - } -} diff --git a/Sources/NgrokitMocks/MockAPI.swift b/Sources/NgrokitMocks/MockAPI.swift deleted file mode 100644 index 4bfc397..0000000 --- a/Sources/NgrokitMocks/MockAPI.swift +++ /dev/null @@ -1,91 +0,0 @@ -// -// MockAPI.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation -import NgrokOpenAPIClient - -#if canImport(FoundationNetworking) - import FoundationNetworking -#endif - -package final actor MockAPI: APIProtocol { - private let actualStopTunnelResult: Result? - package private(set) var stopTunnelPassed: [Operations.stopTunnel.Input] = [] - - private let actualStartTunnelResult: Result? - package private(set) var startTunnelPassed: [Operations.startTunnel.Input] = [] - - private let actualListTunnelResult: Result? - package private(set) var listTunnelPassed: [Operations.listTunnels.Input] = [] - - package init( - actualStopTunnelResult: Result? = nil, - actualStartTunnelResult: Result? = nil, - actualListTunnelResult: Result? = nil - ) { - self.actualStopTunnelResult = actualStopTunnelResult - self.actualStartTunnelResult = actualStartTunnelResult - self.actualListTunnelResult = actualListTunnelResult - } - - // swiftlint:disable unavailable_function force_unwrapping - package func getTunnel( - _: NgrokOpenAPIClient.Operations.getTunnel.Input - ) async throws -> NgrokOpenAPIClient.Operations.getTunnel.Output { - fatalError("not implemented") - } - - package func stopTunnel( - _ input: Operations.stopTunnel.Input - ) async throws -> Operations.stopTunnel.Output { - stopTunnelPassed.append(input) - return try actualStopTunnelResult!.get() - } - - package func startTunnel( - _ input: Operations.startTunnel.Input - ) async throws -> Operations.startTunnel.Output { - startTunnelPassed.append(input) - return try actualStartTunnelResult!.get() - } - - package func listTunnels( - _ input: NgrokOpenAPIClient.Operations.listTunnels.Input - ) async throws -> NgrokOpenAPIClient.Operations.listTunnels.Output { - listTunnelPassed.append(input) - return try actualListTunnelResult!.get() - } - - package func get_sol_api( - _: NgrokOpenAPIClient.Operations.get_sol_api.Input - ) async throws -> NgrokOpenAPIClient.Operations.get_sol_api.Output { - fatalError("not implemented") - } - // swiftlint:enable unavailable_function force_unwrapping -} diff --git a/Sources/NgrokitMocks/MockDataHandle.swift b/Sources/NgrokitMocks/MockDataHandle.swift deleted file mode 100644 index 85a46ee..0000000 --- a/Sources/NgrokitMocks/MockDataHandle.swift +++ /dev/null @@ -1,59 +0,0 @@ -// -// MockDataHandle.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation -import Ngrokit - -package struct MockDataHandle: DataHandle { - package static let code: Data = .init(""" - ERROR: authentication failed: Your account is limited to 1 simultaneous ngrok agent session. - ERROR: You can run multiple tunnels on a single agent session using a configuration file. - ERROR: To learn more, see https://ngrok.com/docs/secure-tunnels/ngrok-agent/reference/config/ - ERROR: - ERROR: Active ngrok agent sessions in region 'us': - ERROR: - ts_2bjiyVxWh6dMoaZUfjXNsHWFNta (2607:fb90:8da8:5b15:900b:13fd:c5e7:f9c6) - ERROR: - ERROR: ERR_NGROK_108 - ERROR: - """.utf8) - - private let actualResult: Result - - package init(_ actualResult: Result) { - self.actualResult = actualResult - } - - package static func withNgrokCode() -> MockDataHandle { - .init(.success(code)) - } - - package func readToEnd() throws -> Data? { - try actualResult.get() - } -} diff --git a/Sources/NgrokitMocks/MockNgrokCLIAPI.swift b/Sources/NgrokitMocks/MockNgrokCLIAPI.swift deleted file mode 100644 index 9080351..0000000 --- a/Sources/NgrokitMocks/MockNgrokCLIAPI.swift +++ /dev/null @@ -1,48 +0,0 @@ -// -// MockNgrokCLIAPI.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation -import Ngrokit - -package final class MockNgrokCLIAPI: NgrokCLIAPI { - package let process: any NgrokProcess - package private(set) var httpPorts = [Int]() - - package convenience init(id: UUID) { - self.init(process: MockNgrokProcess(id: id)) - } - - internal init(process: any NgrokProcess) { - self.process = process - } - - package func process(forHTTPPort _: Int) -> any Ngrokit.NgrokProcess { - process - } -} diff --git a/Sources/NgrokitMocks/MockNgrokProcess.swift b/Sources/NgrokitMocks/MockNgrokProcess.swift deleted file mode 100644 index 5016487..0000000 --- a/Sources/NgrokitMocks/MockNgrokProcess.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// MockNgrokProcess.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation -import Ngrokit - -package final class MockNgrokProcess: NgrokProcess { - package let id: UUID - - package init(id: UUID) { - self.id = id - } - - package func run(onError _: @escaping @Sendable (any Error) -> Void) async throws {} -} diff --git a/Sources/NgrokitMocks/MockPipe.swift b/Sources/NgrokitMocks/MockPipe.swift deleted file mode 100644 index da7186d..0000000 --- a/Sources/NgrokitMocks/MockPipe.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// MockPipe.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation -import Ngrokit - -package final class MockPipe: Pipable { - package typealias DataHandleType = MockDataHandle - - package let fileHandleForReading: MockDataHandle - - internal init(fileHandleForReading: MockDataHandle) { - self.fileHandleForReading = fileHandleForReading - } -} diff --git a/Sources/NgrokitMocks/MockProcess.swift b/Sources/NgrokitMocks/MockProcess.swift deleted file mode 100644 index 8b38601..0000000 --- a/Sources/NgrokitMocks/MockProcess.swift +++ /dev/null @@ -1,92 +0,0 @@ -// -// MockProcess.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation -import Ngrokit - -package final class MockProcess: Processable { - package typealias PipeType = MockPipe - - package let executableFilePath: String - package let scheme: String - package let port: Int - package let pipeDataResult: Result - package let runError: (any Error)? - package let terminationReason: Ngrokit.TerminationReason - package var standardError: MockPipe? - - package private(set) var isTerminationHandlerSet = false - package private(set) var isRunCalled = false - - internal init( - executableFilePath: String, - scheme: String, - port: Int, - terminationReason: TerminationReason, - standardError: MockPipe? = nil, - pipeDataResult: Result = .success(nil), - runError: (any Error)? = nil - ) { - self.executableFilePath = executableFilePath - self.scheme = scheme - self.port = port - self.standardError = standardError - self.terminationReason = terminationReason - self.pipeDataResult = pipeDataResult - self.runError = runError - } - - package convenience init( - executableFilePath: String, - scheme: String, - port: Int - ) { - self.init( - executableFilePath: executableFilePath, - scheme: scheme, - port: port, - terminationReason: .exit - ) - } - - package nonisolated func createPipe() -> MockPipe { - .init(fileHandleForReading: .init(pipeDataResult)) - } - - package func setTerminationHandler(_: @escaping @Sendable (MockProcess) -> Void) { - isTerminationHandlerSet = true - } - - package func run() throws { - isRunCalled = true - if let error = runError { - throw error - } - } -} diff --git a/Sources/NgrokitMocks/URL.swift b/Sources/NgrokitMocks/URL.swift deleted file mode 100644 index 1417f8a..0000000 --- a/Sources/NgrokitMocks/URL.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// URL.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation - -#if canImport(FoundationNetworking) - import FoundationNetworking -#endif - -extension URL { - package static func temporaryDirectory() -> URL { - URL(fileURLWithPath: NSTemporaryDirectory()) - } -} diff --git a/Sources/Sublimation/Documentation.docc/Documentation.md b/Sources/Sublimation/Documentation.docc/Documentation.md new file mode 100644 index 0000000..4486a1c --- /dev/null +++ b/Sources/Sublimation/Documentation.docc/Documentation.md @@ -0,0 +1,107 @@ +# ``Sublimation`` + +Enable automatic discovery of your local development server on the fly by turning your server-side swift app from a mysterious vapor to a tangible solid server to connect to. + +## Overview + +![Sublimation Logo](Sublimation.svg) + +When you are developing a full stack Swift application, you want to easily test and debug your application on both the device (iPhone, Apple Watch, iPad, etc...) as well as your development server. If you are using simulator then setting your host server to `localhost` will work but often we need to test on an actual device. + +For the server and client we need a way to communicate that information without the client knowing where the server is initially. + +![Diagram on Sublimation Communication](Sublimation-Diagram.svg) + +There's two ways to do this - have a consistent location for fetching the address or a way to discover the service on the network. + +### Package Ecosystem + +| Repository | Description | +| ---------- | ----------- | +| [**SublimationBonjour**](https://github.com/brightdigit/SublimationBonjour) | `Sublimatory` for using [Bonjour](https://developer.apple.com/bonjour/) for auto-discovery for development server. | +| [**SublimationNgrok**](https://github.com/brightdigit/SublimationNgrok) | `Sublimatory` for using [Ngrok](https://ngrok.com/) and [KVdb](https://kvdb.io) to create public urls and share them. | +| [**SublimationService**](https://github.com/brightdigit/SublimationService) | Use **Sublimation** as a [Lifecycle Service](https://github.com/swift-server/swift-service-lifecycle). | +| [**SublimationVapor**](https://github.com/brightdigit/SublimationVapor) | Use **Sublimation** as a [Vapor Lifecycle Handler](https://docs.vapor.codes/advanced/services/#lifecycle). | + +![Choosing Sublimation Packages](Sublimation-Choose.svg) + +To use **Sublimation**, you'll need to choose: + +* **Sublimatory**, that is the method by which you advertise the development server + * [**Bonjour**](https://developer.apple.com/bonjour/) via [SublimationBonjour](https://github.com/brightdigit/SublimationBonjour) + * [**Ngrok**](https://ngrok.com/) via [SublimationNgrok](https://github.com/brightdigit/SublimationBonjour) _which is only needed if you need to advertise your address publicaly_ +* How it connects to the server + * [Lifecycle Handler for Vapor](https://docs.vapor.codes/advanced/services/#lifecycle) via [SublimationVapor](https://github.com/brightdigit/SublimationBonjour) + * [Lifecycle Service](https://github.com/swift-server/swift-service-lifecycle) via [SublimationService](https://github.com/brightdigit/SublimationBonjour) _for server frameworks such as [Hummingbird](https://docs.hummingbird.codes/2.0/documentation/hummingbird/)_ + +### Example + +For instance if you were using Bonjour with Hummingbird and an iOS app your package may look something like this: + +```swift +let package = Package( + ... + dependencies: [ + .package(url: "https://github.com/hummingbird-project/hummingbird.git", from: "2.0.0-alpha.1"), + .package(url: "https://github.com/brightdigit/SublimationBonjour.git", from: "1.0.0"), + .package(url: "https://github.com/brightdigit/SublimationService.git", from: "1.0.0") + ], + targets: [ + + .target( + name: "YouriOSApp", + dependencies: [ + .product(name: "SublimationBonjour", package: "SublimationBonjour"), + ... + ]), + ... + .target( + name: "YourServerApp", + dependencies: [ + .product(name: "Hummingbird", package: "hummingbird"), + .product(name: "SublimationBonjour", package: "SublimationBonjour"), + .product(name: "SublimationService", package: "SublimationService"), + ... + ]), + ... + ] +) +``` + +If you were to use Vapor and Ngrok instead, it'd look more like this: + +```swift +let package = Package( + ... + dependencies: [ + .package(url: "https://github.com/vapor/vapor.git", from: "4.76.0"), + .package(url: "https://github.com/brightdigit/SublimationNgrok.git", from: "1.0.0"), + .package(url: "https://github.com/brightdigit/SublimationVapor.git", from: "1.0.0") + ], + targets: [ + + .target( + name: "YouriOSApp", + dependencies: [ + .product(name: "SublimationKVdb", package: "SublimationNgrok"), + ... + ]), + ... + .target( + name: "YourServerApp", + dependencies: [ + .product(name: "Vapor", package: "vapor"), + .product(name: "SublimationNgrok", package: "SublimationNgrok"), + .product(name: "SublimationVapor", package: "SublimationVapor"), + ... + ]), + ... + ] +) +``` + +Please check the respective package documentation from the section. + +## Topics + +- ``Sublimation`` diff --git a/Sources/Sublimation/Documentation.docc/Resources/Sublimation-Choose.svg b/Sources/Sublimation/Documentation.docc/Resources/Sublimation-Choose.svg new file mode 100644 index 0000000..03143eb --- /dev/null +++ b/Sources/Sublimation/Documentation.docc/Resources/Sublimation-Choose.svg @@ -0,0 +1 @@ +

Yes

No

Vapor

Hummingbird or other Lifecycle Service

Which Sublimation Packages to Use

Need to publicly share URL?

Use SublimationNgrok

Use SublimationBonjour

Which server framework?

Use SublimationVapor

Use SublimationService

\ No newline at end of file diff --git a/Sources/Sublimation/Documentation.docc/Resources/Sublimation-Choose~dark.svg b/Sources/Sublimation/Documentation.docc/Resources/Sublimation-Choose~dark.svg new file mode 100644 index 0000000..5115002 --- /dev/null +++ b/Sources/Sublimation/Documentation.docc/Resources/Sublimation-Choose~dark.svg @@ -0,0 +1 @@ +

Yes

No

Vapor

Hummingbird or other Lifecycle Service

Which Sublimation Packages to Use

Need to publicly share URL?

Use SublimationNgrok

Use SublimationBonjour

Which server framework?

Use SublimationVapor

Use SublimationService

diff --git a/Sources/Sublimation/Documentation.docc/Resources/Sublimation-Diagram.svg b/Sources/Sublimation/Documentation.docc/Resources/Sublimation-Diagram.svg new file mode 100644 index 0000000..963a68e --- /dev/null +++ b/Sources/Sublimation/Documentation.docc/Resources/Sublimation-Diagram.svg @@ -0,0 +1 @@ +

Devices

iPhone

Apple Watch

iPad

Vision Pro

Sublimation

API Server

\ No newline at end of file diff --git a/Sources/Sublimation/Documentation.docc/Resources/Sublimation-Diagram~dark.svg b/Sources/Sublimation/Documentation.docc/Resources/Sublimation-Diagram~dark.svg new file mode 100644 index 0000000..3208318 --- /dev/null +++ b/Sources/Sublimation/Documentation.docc/Resources/Sublimation-Diagram~dark.svg @@ -0,0 +1 @@ +

Devices

iPhone

Apple Watch

iPad

Vision Pro

Sublimation

API Server

diff --git a/Sources/Sublimation/Documentation.docc/Resources/Sublimation.png b/Sources/Sublimation/Documentation.docc/Resources/Sublimation.png new file mode 100644 index 0000000..4050eeb Binary files /dev/null and b/Sources/Sublimation/Documentation.docc/Resources/Sublimation.png differ diff --git a/Sources/Sublimation/Documentation.docc/Resources/Sublimation.svg b/Sources/Sublimation/Documentation.docc/Resources/Sublimation.svg new file mode 100644 index 0000000..a9fd8d9 --- /dev/null +++ b/Sources/Sublimation/Documentation.docc/Resources/Sublimation.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sources/Sublimation/Documentation.docc/Resources/Sublimation@0.5x.png b/Sources/Sublimation/Documentation.docc/Resources/Sublimation@0.5x.png new file mode 100644 index 0000000..e43848b Binary files /dev/null and b/Sources/Sublimation/Documentation.docc/Resources/Sublimation@0.5x.png differ diff --git a/Sources/Sublimation/Sublimation+Bonjour.swift b/Sources/Sublimation/Sublimation+Bonjour.swift deleted file mode 100644 index fa60702..0000000 --- a/Sources/Sublimation/Sublimation+Bonjour.swift +++ /dev/null @@ -1,89 +0,0 @@ -// -// Sublimation+Bonjour.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -#if canImport(Network) - import Network - - @_exported import class SublimationBonjour.BonjourDepositor - import class SublimationBonjour.BonjourSublimatory - - extension Sublimation { - /// Initializes a `Sublimation` instance with the provided parameters. - /// - /// - Parameters: - /// - listenerParameters: The network parameters to use for the listener. Default is `.tcp`. - /// - serviceType: The Bonjour service type. Default is `BonjourSublimatory.httpTCPServiceType`. - /// - maximumCount: The maximum number of connections. Default is `nil`. - /// - addresses: A closure that asynchronously returns a list of addresses. - /// - addressFilter: A closure that filters the addresses. Default is `String.isIPv4NotLocalhost(_:)`. - public convenience init( - listenerParameters: NWParameters = .tcp, - serviceType: String = BonjourSublimatory.httpTCPServiceType, - maximumCount: Int? = nil, - addresses: @escaping @Sendable () async -> [String], - addressFilter: @escaping @Sendable (String) -> Bool = String.isIPv4NotLocalhost(_:) - ) { - let sublimatory = BonjourSublimatory( - listenerParameters: listenerParameters, - serviceType: serviceType, - maximumCount: maximumCount, - addresses: addresses, - addressFilter: addressFilter - ) - self.init( - sublimatory: sublimatory - ) - } - - #if os(macOS) - /// Initializes a `Sublimation` instance with the provided parameters on macOS. - /// - /// - Parameters: - /// - listenerParameters: The network parameters to use for the listener. Default is `.tcp`. - /// - serviceType: The Bonjour service type. Default is `BonjourSublimatory.httpTCPServiceType`. - /// - maximumCount: The maximum number of connections. Default is `nil`. - /// - addressFilter: A closure that filters the addresses. - /// Default is `String.isIPv4NotLocalhost(_:)`. - public convenience init( - listenerParameters: NWParameters = .tcp, - serviceType: String = BonjourSublimatory.httpTCPServiceType, - maximumCount: Int? = nil, - addressFilter: @escaping @Sendable (String) -> Bool = String.isIPv4NotLocalhost(_:) - ) { - self.init( - listenerParameters: listenerParameters, - serviceType: serviceType, - maximumCount: maximumCount, - addresses: BonjourSublimatory.addressesFromHost, - addressFilter: addressFilter - ) - } - #endif - } -#endif diff --git a/Sources/Sublimation/Sublimation.swift b/Sources/Sublimation/Sublimation.swift index bfd244d..b2b8b04 100644 --- a/Sources/Sublimation/Sublimation.swift +++ b/Sources/Sublimation/Sublimation.swift @@ -1,6 +1,6 @@ // // Sublimation.swift -// Sublimation +// SublimationBonjour // // Created by Leo Dion. // Copyright © 2024 BrightDigit. @@ -29,31 +29,14 @@ import Foundation import Logging -import OpenAPIRuntime -import SublimationCore +public import SublimationCore +/// Adds the ability to auto-discover development urls to your full stack applicaiton. public final class Sublimation: Sendable { + /// Implementation for publishing and discovering the development server url. public let sublimatory: any Sublimatory - public init(sublimatory: any Sublimatory) { - self.sublimatory = sublimatory - } - - public func willBoot(_ application: @Sendable @escaping () -> any Application) { - Task { - await self.sublimatory.willBoot(from: application) - } - } - - public func didBoot(_ application: @Sendable @escaping () -> any Application) { - Task { - await self.sublimatory.didBoot(from: application) - } - } - - public func shutdown(_ application: @Sendable @escaping () -> any Application) { - Task { - await self.sublimatory.shutdown(from: application) - } - } + /// Creates an object to auto-discover development urls to your full stack applicaiton. + /// - Parameter sublimatory: Method for publishing and discovering the development server url. + public init(sublimatory: any Sublimatory) { self.sublimatory = sublimatory } } diff --git a/Sources/SublimationBonjour/BonjourDepositor.swift b/Sources/SublimationBonjour/BonjourDepositor.swift deleted file mode 100644 index e7c8cc1..0000000 --- a/Sources/SublimationBonjour/BonjourDepositor.swift +++ /dev/null @@ -1,213 +0,0 @@ -// -// BonjourDepositor.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -#if canImport(Network) - import Foundation - #if canImport(os) - import os - #else - import Logging - #endif - import Network - - /// `BonjourDepositor` using Bonjour services, to collect URLs advertised by your `Sublimation` server. - public actor BonjourDepositor { - /// Default configuration values for the `BonjourDepositor`. - public enum Defaults { - /// The default port number used for the service. - public static let port = 80 - /// The default TLS setting for the service. - public static let isTLS = false - /// The default Bonjour service type. - public static let bonjourType = "_http._tcp" - /// The default Bonjour domain. - public static let bonourDomain = "local." - } - - private let browser: NetworkBrowser - private let queue: DispatchQueue = .global() - private let logger: LoggingActor? - private let streams = StreamManager() - - private let defaultPort: Int - private let defaultTLS: Bool - - /// The current state of the NWBrowser. - public var state: NWBrowser.State? { - get async { - await self.browser.currentState - } - } - - /// An asynchronous stream of URLs discovered by the browser. - /// - /// If the stream manager is empty, it starts the browser and begins parsing results. - public var urls: AsyncStream { - get async { - let browser = browser - let streams = streams - if await self.streams.isEmpty { - logger?.log { $0.debug("Starting Browser.") } - - await browser.start(queue: queue, parser: { result in - Task { - await self.parseResult(result) - } - }) - } - return AsyncStream { continuation in - Task { - await streams.append(continuation) { - await browser.stop() - self.logger?.log { $0.debug("Shutting down browser.") } - } - } - } - } - } - - /// Initializes a `BonjourDepositor` with a Bonjour service type and domain. - /// - /// - Parameters: - /// - type: The Bonjour service type. - /// - domain: The Bonjour domain. - /// - parameters: The network parameters. - /// - defaultPort: The default port number. - /// - defaultTLS: The default TLS setting. - /// - logger: An optional logger. - public init( - bonjourWithType type: String = Defaults.bonjourType, - domain: String = Defaults.bonourDomain, - using parameters: NWParameters = .tcp, - defaultPort: Int = Defaults.port, - defaultTLS: Bool = Defaults.isTLS, - logger: (@Sendable () -> Logger)? = nil - ) { - self.init( - for: .bonjourWithTXTRecord(type: type, domain: domain), - using: parameters, - defaultPort: defaultPort, - defaultTLS: defaultTLS, - logger: logger - ) - } - - /// Initializes a `BonjourDepositor` with a browser descriptor. - /// - /// - Parameters: - /// - descriptor: The browser descriptor. - /// - parameters: The network parameters. - /// - defaultPort: The default port number. - /// - defaultTLS: The default TLS setting. - /// - logger: An optional logger. - public init( - for descriptor: NWBrowser.Descriptor, - using parameters: NWParameters, - defaultPort: Int = Defaults.port, - defaultTLS: Bool = Defaults.isTLS, - logger: (@Sendable () -> Logger)? = nil - ) { - self.init( - browser: .init(for: descriptor, using: parameters), - logger: logger, - defaultPort: defaultPort, - defaultTLS: defaultTLS - ) - } - - private init( - browser: NetworkBrowser, - logger: (@Sendable () -> Logger)?, - defaultPort: Int, - defaultTLS: Bool - ) { - self.logger = logger.map(LoggingActor.init(logger:)) - self.browser = browser - self.defaultTLS = defaultTLS - self.defaultPort = defaultPort - } - - /// Parses a browser result and extracts URLs. - /// - /// - Parameters: - /// - result: The browser result. - /// - logger: An optional logger. - /// - defaultPort: The default port number. - /// - defaultTLS: The default TLS setting. - /// - /// - Returns: An array of URLs if extraction is successful, otherwise nil. - private static func urls( - from result: NWBrowser.Result, - logger: LoggingActor?, - defaultPort: Int, - defaultTLS: Bool - ) -> [URL]? { - guard case .service = result.endpoint else { - logger?.log { $0.debug("Not service.") } - return nil - } - guard case let .bonjour(txtRecord) = result.metadata else { - logger?.log { $0.debug("No txt record.") } - return nil - } - return txtRecord.urls(defaultPort: defaultPort, defaultTLS: defaultTLS, logger: logger) - } - - /// Parses the browser result and yields the extracted URLs to the stream manager. - /// - /// - Parameter result: The browser result to be parsed. - private func parseResult(_ result: NWBrowser.Result) { - guard let urls = Self.urls( - from: result, - logger: logger, - defaultPort: defaultPort, - defaultTLS: defaultTLS - ) else { - return - } - let logger = logger - let streams = streams - Task { - await streams.yield(urls, logger: logger) - } - } - } - - extension BonjourDepositor { - /// Retrieves the first URL from the asynchronous stream. - /// - /// - Returns: The first URL if available, otherwise nil. - public func first() async -> URL? { - for await baseURL in await self.urls { - return baseURL - } - return nil - } - } -#endif diff --git a/Sources/SublimationBonjour/BonjourSublimatory.swift b/Sources/SublimationBonjour/BonjourSublimatory.swift deleted file mode 100644 index 0a55800..0000000 --- a/Sources/SublimationBonjour/BonjourSublimatory.swift +++ /dev/null @@ -1,164 +0,0 @@ -// -// BonjourSublimatory.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -#if canImport(Network) - import Foundation - import Logging - import Network - import SublimationCore - - public actor BonjourSublimatory: Sublimatory { - public static let httpTCPServiceType = "_http._tcp" - private let serviceType: String - private let maximumCount: Int? - private let addresses: @Sendable () async -> [String] - // TODO: Create a Filter Builder - private let addressFilter: @Sendable (String) -> Bool - private let listenerParameters: NWParameters - private nonisolated(unsafe) var logger: Logger? - private var listener: NWListener? { - didSet { - guard let listener else { - return - } - - listener.stateUpdateHandler = { newState in - Task { - await self.updateState(newState) - } - } - listener.newConnectionHandler = { connection in - self.logger?.debug("Cancelling connection: \(connection.debugDescription)") - connection.cancel() - } - - listener.serviceRegistrationUpdateHandler = { _ in - // TODO: put this in an AsyncStream - } - } - } - - internal private(set) var state: NWListener.State? - - public init( - listenerParameters: NWParameters = .tcp, - serviceType: String = httpTCPServiceType, - maximumCount: Int? = nil, - addresses: @escaping @Sendable () async -> [String], - addressFilter: @escaping @Sendable (String) -> Bool = String.isIPv4NotLocalhost(_:) - ) { - self.listenerParameters = listenerParameters - self.serviceType = serviceType - self.maximumCount = maximumCount - self.addressFilter = addressFilter - self.addresses = addresses - } - - #if os(macOS) - public init( - listenerParameters: NWParameters = .tcp, - serviceType: String = httpTCPServiceType, - maximumCount: Int? = nil, - addressFilter: @escaping @Sendable (String) -> Bool = String.isIPv4NotLocalhost(_:) - ) { - self.init( - listenerParameters: listenerParameters, - serviceType: serviceType, - maximumCount: maximumCount, - addresses: Self.addressesFromHost, - addressFilter: addressFilter - ) - } - - @Sendable - public static func addressesFromHost() -> [String] { - Host.current().addresses - } - #endif - - internal func stop() { - assert(listener != nil) - listener?.stateUpdateHandler = nil - listener?.cancel() - listener = nil - } - - private func updateState(_ newState: NWListener.State) { - state = newState - logger?.debug("Listener changed state to \(newState.debugDescription).") - } - - public func willBoot(from application: @escaping @Sendable () -> any Application) async { - let application = application() - await self.start( - isTLS: application.httpServerTLS, - port: application.httpServerConfigurationPort, - logger: application.logger - ) - } - - public func shutdown(from _: @escaping @Sendable () -> any Application) async { - self.stop() - } - } - - extension BonjourSublimatory { - internal func start( - isTLS: Bool, - port: Int, - logger: Logger - ) async { - self.logger = logger - let addresses = await self.addresses() - let txtRecord: NWTXTRecord = .init( - isTLS: isTLS, - port: port, - maximumCount: maximumCount, - addresses: addresses, - filter: addressFilter - ) - let listener: NWListener - do { - listener = try NWListener( - using: listenerParameters, - serviceType: self.serviceType, - txtRecord: txtRecord - ) - } catch { - assertionFailure("Unable to create listener: \(error.localizedDescription)") - logger.error("Unable to create listener: \(error.localizedDescription)") - return - } - - self.listener = listener - listener.start(queue: .global(qos: .default)) - } - } - -#endif diff --git a/Sources/SublimationBonjour/EntryResult.swift b/Sources/SublimationBonjour/EntryResult.swift deleted file mode 100644 index 9ebf0c6..0000000 --- a/Sources/SublimationBonjour/EntryResult.swift +++ /dev/null @@ -1,67 +0,0 @@ -// -// EntryResult.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation - -internal enum EntryResult { - case empty - case failure(String) - case success(T) -} - -extension EntryResult { - internal var invalidEntryString: String? { - if case let .failure(string) = self { - return string - } - return nil - } - - internal var value: T? { - if case let .success(value) = self { - return value - } - return nil - } - - internal var isEmpty: Bool { - if case .empty = self { - return true - } - return false - } - - internal init(string: String) { - if let value = T(string) { - self = .success(value) - } else { - self = .failure(string) - } - } -} diff --git a/Sources/SublimationBonjour/LoggingActor.swift b/Sources/SublimationBonjour/LoggingActor.swift deleted file mode 100644 index 7f36904..0000000 --- a/Sources/SublimationBonjour/LoggingActor.swift +++ /dev/null @@ -1,54 +0,0 @@ -// -// LoggingActor.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -#if canImport(os) - import os -#else - import Logging -#endif - -internal actor LoggingActor { - private let logger: Logger - - internal init(logger: @escaping @Sendable () -> Logger) { - self.logger = logger() - } - - private func logWith(_ closure: @Sendable @escaping (Logger) -> Void) { - Task { - closure(self.logger) - } - } - - internal nonisolated func log(_ closure: @Sendable @escaping (Logger) -> Void) { - Task { - await self.logWith(closure) - } - } -} diff --git a/Sources/SublimationBonjour/NWBrowser.Result.Change.swift b/Sources/SublimationBonjour/NWBrowser.Result.Change.swift deleted file mode 100644 index ab2d8a1..0000000 --- a/Sources/SublimationBonjour/NWBrowser.Result.Change.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// NWBrowser.Result.Change.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -#if canImport(Network) - import Network - - extension NWBrowser.Result.Change { - internal var newMetadataChange: NWBrowser.Result? { - if case let .added(result) = self { - result - } else if case let .changed(_, new, .metadataChanged) = self { - new - } else { - nil - } - } - } -#endif diff --git a/Sources/SublimationBonjour/NWListener.swift b/Sources/SublimationBonjour/NWListener.swift deleted file mode 100644 index 75b36ce..0000000 --- a/Sources/SublimationBonjour/NWListener.swift +++ /dev/null @@ -1,63 +0,0 @@ -// -// NWListener.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -#if canImport(Network) - import Foundation - import Network - - extension NWListener { - internal convenience init( - using parameters: NWParameters, - serviceType: String, - txtRecord: NWTXTRecord - ) throws { - try self.init(using: parameters) - self.service = NWListener.Service(type: serviceType, txtRecord: txtRecord.data) - } - } - - extension NWListener.State: CustomDebugStringConvertible { - public var debugDescription: String { - switch self { - case .setup: - "setup" - case let .waiting(error): - "waiting: \(error.debugDescription)" - case .ready: - "ready" - case let .failed(error): - "failed: \(error.debugDescription)" - case .cancelled: - "cancelled" - @unknown default: - "unknown state" - } - } - } -#endif diff --git a/Sources/SublimationBonjour/NWTXTRecord.swift b/Sources/SublimationBonjour/NWTXTRecord.swift deleted file mode 100644 index 467765f..0000000 --- a/Sources/SublimationBonjour/NWTXTRecord.swift +++ /dev/null @@ -1,45 +0,0 @@ -// -// NWTXTRecord.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -#if canImport(Network) - import Network - - extension NWTXTRecord: TXTRecord { - internal func getStringEntry(for key: String) -> String? { - guard case let .string(value) = self.getEntry(for: key) else { - return nil - } - return value - } - - internal func getKeys() -> any Sequence { - self.dictionary.keys - } - } -#endif diff --git a/Sources/SublimationBonjour/NetworkBrowser.swift b/Sources/SublimationBonjour/NetworkBrowser.swift deleted file mode 100644 index 7961ed4..0000000 --- a/Sources/SublimationBonjour/NetworkBrowser.swift +++ /dev/null @@ -1,88 +0,0 @@ -// -// NetworkBrowser.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -#if canImport(Network) - import Network - - internal actor NetworkBrowser { - internal private(set) var currentState: NWBrowser.State? - private let browser: NWBrowser - private var parseResult: ((NWBrowser.Result) -> Void)? - - internal init(for descriptor: NWBrowser.Descriptor, using parameters: NWParameters) { - let browser = NWBrowser(for: descriptor, using: parameters) - self.init(browser: browser) - browser.stateUpdateHandler = { state in - Task { - await self.onUpdateState(state) - } - } - browser.browseResultsChangedHandler = { newResults, changes in - Task { - await self.onResultsChanged(to: newResults, withChanges: changes) - } - } - } - - private init(browser: NWBrowser) { - self.browser = browser - } - - internal func start( - queue: DispatchQueue, - parser: @Sendable @escaping (NWBrowser.Result) -> Void - ) { - browser.start(queue: queue) - parseResult = parser - } - - private func onUpdateState(_ state: NWBrowser.State) { - currentState = state - } - - private func onResultsChanged( - to _: Set, - withChanges changes: Set - ) { - guard let parseResult else { - return - } - for change in changes { - if let result = change.newMetadataChange { - parseResult(result) - } - } - } - - internal func stop() { - browser.stateUpdateHandler = nil - browser.cancel() - } - } -#endif diff --git a/Sources/SublimationBonjour/StreamManager.swift b/Sources/SublimationBonjour/StreamManager.swift deleted file mode 100644 index d52a441..0000000 --- a/Sources/SublimationBonjour/StreamManager.swift +++ /dev/null @@ -1,85 +0,0 @@ -// -// StreamManager.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation - -internal actor StreamManager { - private var streamContinuations = [Key: AsyncStream.Continuation]() - - private var newID: @Sendable () -> Key - - internal var isEmpty: Bool { - streamContinuations.isEmpty - } - - internal init(newID: @escaping @Sendable () -> Key) { - self.newID = newID - } - - internal func yield(_ urls: [Value], logger: LoggingActor?) { - if streamContinuations.isEmpty { - logger?.log { $0.debug("Missing Continuations.") } - } - for streamContinuation in streamContinuations { - for url in urls { - streamContinuation.value.yield(url) - } - } - } - - private func onTerminationOf(_ id: Key) -> Bool { - streamContinuations.removeValue(forKey: id) - return streamContinuations.isEmpty - } - - internal func append( - _ continuation: AsyncStream.Continuation, - onCancel: @Sendable @escaping () async -> Void - ) { - let id = newID() - streamContinuations[id] = continuation - continuation.onTermination = { _ in - Task { - let shouldCancel = - await self.onTerminationOf(id) - if shouldCancel { - await onCancel() - } - } - } - } -} - -extension StreamManager { - internal init() where Key == UUID { - self.init { - UUID() - } - } -} diff --git a/Sources/SublimationBonjour/String.swift b/Sources/SublimationBonjour/String.swift deleted file mode 100644 index 3bdc0ca..0000000 --- a/Sources/SublimationBonjour/String.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// String.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -extension String { - /// Filters strings which are only v4 and not refering the localhost. - /// - Parameter address: The host address string. - /// - Returns: True, if the address passes the filter. - @Sendable - public static func isIPv4NotLocalhost(_ address: String) -> Bool { - guard !(["127.0.0.1", "::1", "localhost"].contains(address)) else { - return false - } - guard !address.contains(":") else { - return false - } - return true - } -} diff --git a/Sources/SublimationBonjour/SublimationKey.swift b/Sources/SublimationBonjour/SublimationKey.swift deleted file mode 100644 index fb523c4..0000000 --- a/Sources/SublimationBonjour/SublimationKey.swift +++ /dev/null @@ -1,97 +0,0 @@ -// -// SublimationKey.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -private enum SublimationKeyValues: String { - case tls = "Sublimation_TLS" - case port = "Sublimation_Port" - case address = "Sublimation_Address" -} - -public enum SublimationKey: Hashable { - case tls - case port - case address(Int) -} - -extension SublimationKey { - internal var stringValue: String { - let value: (any CustomStringConvertible)? = switch self { - case let .address(index): - index - default: - nil - } - let prefix = SublimationKeyValues(key: self).rawValue - guard let value else { - return prefix - } - return [prefix, value.description].joined(separator: "_") - } - - internal static func isValid(_ string: String) -> Bool { - if SublimationKeyValues(rawValue: string) != nil { - return true - } - if string.hasPrefix(SublimationKeyValues.address.rawValue), - string.count > SublimationKeyValues.address.rawValue.count + 1 { - let indexString = string.suffix( - from: string.index( - string.startIndex, - offsetBy: SublimationKeyValues.address.rawValue.count + 1 - ) - ) - - return (Int(indexString) ?? .min) >= 0 - } - return false - } -} - -extension SublimationKeyValues { - fileprivate init(key: SublimationKey) { - switch key { - case .address: - self = .address - case .port: - self = .port - case .tls: - self = .tls - } - } -} - -extension [String: String] { - internal init(sublimationTxt: [SublimationKey: any CustomStringConvertible]) { - let pairs = sublimationTxt - .map { (key: SublimationKey, value: any CustomStringConvertible) in - (key.stringValue, value.description) - } - self.init(uniqueKeysWithValues: pairs) - } -} diff --git a/Sources/SublimationBonjour/SublimationValue.swift b/Sources/SublimationBonjour/SublimationValue.swift deleted file mode 100644 index 2d058b1..0000000 --- a/Sources/SublimationBonjour/SublimationValue.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// SublimationValue.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -public protocol SublimationValue: CustomStringConvertible { - init?(_ string: String) -} - -extension Bool: SublimationValue {} - -extension Int: SublimationValue {} - -extension String: SublimationValue {} diff --git a/Sources/SublimationBonjour/TXTRecord.swift b/Sources/SublimationBonjour/TXTRecord.swift deleted file mode 100644 index 45e67e7..0000000 --- a/Sources/SublimationBonjour/TXTRecord.swift +++ /dev/null @@ -1,155 +0,0 @@ -// -// TXTRecord.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation - -internal protocol TXTRecord { - var count: Int { get } - init(_ dictionary: [String: String]) - func getStringEntry(for key: String) -> String? - func getKeys() -> any Sequence -} - -extension TXTRecord { - private init(_ dictionary: [SublimationKey: any CustomStringConvertible]) { - self.init(.init(sublimationTxt: dictionary)) - } - - internal init( - isTLS: Bool, - port: Int, - maximumCount: Int?, - addresses: @autoclosure () -> [String], - filter: @Sendable @escaping (String) -> Bool - ) { - var dictionary: [SublimationKey: any CustomStringConvertible] = [ - .tls: isTLS, - .port: port - ] - - let allAddresses = addresses() - let addresses: any Sequence = if let maximumCount { - allAddresses.prefix(maximumCount) - } else { - allAddresses - } - for address in addresses { - guard filter(address) else { - continue - } - let index = dictionary.count - 2 - dictionary[.address(index)] = address - } - self.init(dictionary) - } - - internal func getEntry(for key: SublimationKey, of _: T.Type) -> EntryResult { - guard let string = getStringEntry(for: key.stringValue) else { - return .empty - } - return .init(string: string) - } - - internal func getEntry(for key: SublimationKey) -> EntryResult { - self.getEntry(for: key, of: T.self) - } - - internal func urls( - defaultPort: Int, - defaultTLS: Bool, - logger: LoggingActor? - ) -> [URL] { - let configuration = self.urlConfiguration( - logger: logger, - defaultPort: defaultPort, - defaultTLS: defaultTLS - ) - guard let configuration else { - return [] - } - logger?.log { $0.debug("Parsing \(configuration.count) Addresses") } - return (0 ..< configuration.count).compactMap { index -> URL? in - let host: String? = self.getEntry(for: .address(index)).value - assert(host != nil) - guard let host else { - logger?.log { $0.debug("Invalid Address At Index: \(index)") } - return nil - } - return URL(scheme: configuration.scheme, host: host, port: configuration.port) - } - } - - private func urlConfiguration( - at offset: Int, - port: Int, - isTLS: Bool, - logger: LoggingActor? - ) -> URL.Configuration { - let scheme = isTLS ? "https" : "http" - logger?.log { $0.debug("Scheme: \(scheme)") } - let addressCount = self.count - offset - return .init(scheme: scheme, port: port, count: addressCount) - } - - private func urlConfiguration( - logger: LoggingActor?, - defaultPort: Int, - defaultTLS: Bool - ) -> URL.Configuration? { - var offset = 0 - - let portEntry = self.getEntry(for: .port, of: Int.self) - let port = portEntry.value ?? defaultPort - offset += portEntry.isEmpty ? 0 : 1 - - if let invalidPortEntryString = portEntry.invalidEntryString { - assert(portEntry.invalidEntryString == nil, "Port Entry is invalid: \(invalidPortEntryString)") - logger?.log { $0.warning("Port Entry is invalid: \(invalidPortEntryString)") } - } - - let tlsEntry = self.getEntry(for: .tls, of: Bool.self) - let isTLS = tlsEntry.value ?? defaultTLS - offset += tlsEntry.isEmpty ? 0 : 1 - - if let invalidTLSEntryString = tlsEntry.invalidEntryString { - assert(tlsEntry.invalidEntryString == nil, "Port Entry is invalid: \(invalidTLSEntryString)") - logger?.log { $0.warning("Port Entry is invalid: \(invalidTLSEntryString)") } - } - - let isValid = self.getKeys().allSatisfy { key in - SublimationKey.isValid(key) - } - - guard isValid else { - return nil - } - - return self.urlConfiguration(at: offset, port: port, isTLS: isTLS, logger: logger) - } -} diff --git a/Sources/SublimationBonjour/URL.swift b/Sources/SublimationBonjour/URL.swift deleted file mode 100644 index 94492f0..0000000 --- a/Sources/SublimationBonjour/URL.swift +++ /dev/null @@ -1,51 +0,0 @@ -// -// URL.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation - -extension URL { - internal init?(scheme: String, host: String, port: Int) { - var components = URLComponents() - components.scheme = scheme - components.host = host - components.port = port - guard let url = components.url else { - return nil - } - self = url - } -} - -extension URL { - internal struct Configuration { - internal let scheme: String - internal let port: Int - internal let count: Int - } -} diff --git a/Sources/SublimationCore/Application.swift b/Sources/SublimationCore/Application.swift index 1e8dbdc..26b6369 100644 --- a/Sources/SublimationCore/Application.swift +++ b/Sources/SublimationCore/Application.swift @@ -1,6 +1,6 @@ // // Application.swift -// Sublimation +// SublimationBonjour // // Created by Leo Dion. // Copyright © 2024 BrightDigit. @@ -27,11 +27,11 @@ // OTHER DEALINGS IN THE SOFTWARE. // -import Foundation -import Logging +public import Foundation +public import Logging /// Server Application -public protocol Application { +@available(*, deprecated, message: "Only used by SublimationNgrok.") public protocol Application { /// The port number for the HTTP server configuration. var httpServerConfigurationPort: Int { get } @@ -45,10 +45,12 @@ public protocol Application { /// - Parameters: /// - url: The url to post to. /// - body: The optional data. + /// - Throws: If there's an issue with the request. func post(to url: URL, body: Data?) async throws /// Makes a client call to a url. /// - Parameter url: The url to call. - /// - Returns: <#description#> + /// - Returns: The data returned from that request. + /// - Throws: If there's an issue with the request. func get(from url: URL) async throws -> Data? } diff --git a/Sources/SublimationCore/Sublimatory.swift b/Sources/SublimationCore/Sublimatory.swift index 82af98f..5e35fb0 100644 --- a/Sources/SublimationCore/Sublimatory.swift +++ b/Sources/SublimationCore/Sublimatory.swift @@ -1,6 +1,6 @@ // // Sublimatory.swift -// Sublimation +// SublimationBonjour // // Created by Leo Dion. // Copyright © 2024 BrightDigit. @@ -27,18 +27,16 @@ // OTHER DEALINGS IN THE SOFTWARE. // +/// Different methods for Sublimation. public protocol Sublimatory: Sendable { - func willBoot(from application: @escaping @Sendable () -> any Application) async - func didBoot(from application: @escaping @Sendable () -> any Application) async - func shutdown(from application: @escaping @Sendable () -> any Application) async + /// Runs the Sublimatory service. + /// - Note: This method contains long running work, returning from it is seen as a failure. + func run() async throws + /// Shutdown any active services. + func shutdown() } extension Sublimatory { - /// Empty implementation of ``didBoot(from:)-warq`` - /// - Parameter _: The Sever Application - public func didBoot(from _: @escaping @Sendable () -> any Application) async {} - - /// Empty implementation of ``shutdown(from:)-warq`` - /// - Parameter _: The Sever Application - public func shutdown(from _: @escaping @Sendable () -> any Application) async {} + /// Shutdown any active services. + public func shutdown() {} } diff --git a/Sources/SublimationKVdb/KVdb.swift b/Sources/SublimationKVdb/KVdb.swift deleted file mode 100644 index e7c37dc..0000000 --- a/Sources/SublimationKVdb/KVdb.swift +++ /dev/null @@ -1,105 +0,0 @@ -// -// KVdb.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation - -#if canImport(FoundationNetworking) - import FoundationNetworking -#endif - -/// A utility class for interacting with KVdb. -/// -/// KVdb is a key-value database service. -/// -/// - Note: This class requires the `Foundation` framework. -/// -/// - SeeAlso: [KVdb](https://kvdb.io/) -/// -/// - Author: Leo Dion -/// -/// - Version: 2024 -/// -/// - License: MIT License -public enum KVdb { - /// The base URL string for KVdb. - public static let baseString = "https://kvdb.io/" - - /// Constructs the path for a given key in a bucket. - /// - /// - Parameters: - /// - key: The key for the value. - /// - bucketName: The name of the bucket. - /// - /// - Returns: The constructed path. - public static func path(forKey key: some Any, atBucket bucketName: String) -> String { - "/\(bucketName)/\(key)" - } - - /// Constructs a URL for a given key in a bucket. - /// - /// - Parameters: - /// - URLType: The type of URL to construct. - /// - key: The key for the value. - /// - bucketName: The name of the bucket. - /// - /// - Returns: The constructed URL. - public static func construct( - _: URLType.Type, - forKey key: some Any, - atBucket bucketName: String - ) -> URLType { - URLType( - kvDBBase: baseString, - keyBucketPath: path(forKey: key, atBucket: bucketName) - ) - } - -// /// Retrieves the URL for a given key in a bucket. -// /// -// /// - Parameters: -// /// - key: The key for the value. -// /// - bucketName: The name of the bucket. -// /// - session: The URLSession to use for the request. Defaults to `.ephemeral`. -// /// -// /// - Returns: The URL for the key, or `nil` if it doesn't exist. -// /// -// /// - Throws: An error if the request fails. -// @available(*, deprecated) -// public static func url( -// withKey key: Key, -// atBucket bucketName: String, -// using session: URLSession = .ephemeral() -// ) async throws -> URL? { -// let repository = KVdbTunnelRepository( -// client: URLSessionClient(session: session), -// bucketName: bucketName -// ) -// return try await repository.tunnel(forKey: key) -// } -} diff --git a/Sources/SublimationKVdb/KVdbServerError.swift b/Sources/SublimationKVdb/KVdbServerError.swift deleted file mode 100644 index c4f2f61..0000000 --- a/Sources/SublimationKVdb/KVdbServerError.swift +++ /dev/null @@ -1,45 +0,0 @@ -// -// KVdbServerError.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation - -/// An error type representing various errors that can occur -/// when working with Ngrok server. -/// -/// - clientNotSetup: The Ngrok client is not properly set up. -/// - noTunnelFound: No tunnel was found. -/// - invalidURL: The URL is invalid. -/// - cantSaveTunnel: Unable to save the tunnel with the given ID and data. -public enum KVdbServerError: Error { -// case clientNotSetup -// case noTunnelFound - case invalidURL - case cantSaveTunnel(Int?, Data?) - case cantSaveTunnelError(any Error) -} diff --git a/Sources/SublimationKVdb/KVdbTunnelClient.swift b/Sources/SublimationKVdb/KVdbTunnelClient.swift deleted file mode 100644 index 6767497..0000000 --- a/Sources/SublimationKVdb/KVdbTunnelClient.swift +++ /dev/null @@ -1,103 +0,0 @@ -// -// KVdbTunnelClient.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation - -#if canImport(FoundationNetworking) - import FoundationNetworking -#endif - -/// A client for interacting with the VaporTunnel service. -/// -/// This client conforms to the `KVdbTunnelClient` protocol. -/// -/// - Note: This client requires the Vapor framework. -/// -/// - Warning: This client is only compatible with Swift 5.5 or later. -/// -/// - Important: Make sure to import the necessary dependencies before using this client. -/// -/// - SeeAlso: `KVdbTunnelClient` -public struct KVdbTunnelClient: Sendable { - private let get: @Sendable (URL) async throws -> Data? - private let post: @Sendable (URL, Data?) async throws -> Void - - public init( - keyType _: Key.Type, - get: @escaping @Sendable (URL) async throws -> Data?, - post: @escaping @Sendable (URL, Data?) async throws -> Void - ) { - self.get = get - self.post = post - } - - /// Retrieves the value associated with a key from a specific bucket. - /// - /// - Parameter key: The key used to access the value. - /// - Parameter bucketName: The name of the bucket where the value is stored. - /// - /// - Throws: `NgrokServerError.invalidURL` if the retrieved URL is invalid. - /// - /// - Returns: The URL associated with the key in the specified bucket. - public func getValue( - ofKey key: Key, - fromBucket bucketName: String - ) async throws -> URL { - let uri = KVdb.construct(URL.self, forKey: key, atBucket: bucketName) - let url: URL? - url = try await get(uri) - .map { String(decoding: $0, as: UTF8.self) } - .flatMap(URL.init(string:)) - - guard let url else { - throw KVdbServerError.invalidURL - } - return url - } - - /// Saves a value with a key in a specific bucket. - /// - /// - Parameter value: The URL value to save. - /// - Parameter key: The key used to associate the value. - /// - Parameter bucketName: The name of the bucket where the value will be stored. - /// - /// - Throws: `NgrokServerError.cantSaveTunnel` if the tunnel cannot be saved. - public func saveValue( - _ value: URL, - withKey key: Key, - inBucket bucketName: String - ) async throws { - let uri = KVdb.construct(URL.self, forKey: key, atBucket: bucketName) - do { - try await self.post(uri, value.absoluteString.data(using: .utf8)) - } catch { - throw KVdbServerError.cantSaveTunnelError(error) - } - } -} diff --git a/Sources/SublimationKVdb/KVdbURLConstructable.swift b/Sources/SublimationKVdb/KVdbURLConstructable.swift deleted file mode 100644 index c27276d..0000000 --- a/Sources/SublimationKVdb/KVdbURLConstructable.swift +++ /dev/null @@ -1,56 +0,0 @@ -// -// KVdbURLConstructable.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation - -/// A protocol for constructing URLs for interacting with a key-value database. -/// -/// Conforming types should provide an initializer -/// that takes a base URL and a key bucket path. -/// -/// Example usage: -/// ``` -/// struct MyKVdbURLConstructor: KVdbURLConstructable { -/// init(kvDBBase: String, keyBucketPath: String) { -/// // Implementation details -/// } -/// } -/// ``` -/// -/// - SeeAlso: `KVdbURLConstructor` -public protocol KVdbURLConstructable { - /// Initializes a URL constructor with the given base URL and key bucket path. - /// - /// - Parameters: - /// - kvDBBase: The base URL of the key-value database. - /// - keyBucketPath: The path to the key bucket. - /// - /// - Returns: An instance of the conforming type. - init(kvDBBase: String, keyBucketPath: String) -} diff --git a/Sources/SublimationKVdb/Optional.swift b/Sources/SublimationKVdb/Optional.swift deleted file mode 100644 index e1d1be8..0000000 --- a/Sources/SublimationKVdb/Optional.swift +++ /dev/null @@ -1,43 +0,0 @@ -// -// Optional.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -extension Optional { - /// Returns a tuple containing the wrapped value - /// of the optional and another optional value. - /// - /// - Parameter other: Another optional value. - /// - /// - Returns: A tuple containing the wrapped value of the optional and `other`, - /// or `nil` if either the optional or `other` is `nil`. - internal func flatTuple(_ other: OtherType?) -> (Wrapped, OtherType)? { - flatMap { wrapped in - other.map { (wrapped, $0) } - } - } -} diff --git a/Sources/SublimationKVdb/Result.swift b/Sources/SublimationKVdb/Result.swift deleted file mode 100644 index d79dc96..0000000 --- a/Sources/SublimationKVdb/Result.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// Result.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -extension Result { - internal struct EmptyError: Error {} - - internal init(success: Success?, failure: Failure?) where Failure == any Error { - if let failure { - self = .failure(failure) - } else if let success { - self = .success(success) - } else { - self = .failure(EmptyError()) - } - } -} diff --git a/Sources/SublimationKVdb/URL+KVdbURLConstructable.swift b/Sources/SublimationKVdb/URL+KVdbURLConstructable.swift deleted file mode 100644 index 3db041b..0000000 --- a/Sources/SublimationKVdb/URL+KVdbURLConstructable.swift +++ /dev/null @@ -1,58 +0,0 @@ -// -// URL+KVdbURLConstructable.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation - -#if canImport(FoundationNetworking) - import FoundationNetworking -#endif - -/// A type representing a URL. -/// -/// - Note: This type is an extension of `URL` and conforms to `KVdbURLConstructable`. -/// -/// - SeeAlso: `KVdbURLConstructable` -extension URL: KVdbURLConstructable { - /// Initializes a `URL` instance with the given KVDB base and key bucket path. - /// - /// - Parameters: - /// - kvDBBase: The base URL of the KVDB. - /// - keyBucketPath: The path to the key bucket. - /// - /// - Note: This initializer is only available if `FoundationNetworking` is imported. - /// - /// - Precondition: `kvDBBase` must be a valid URL. - /// - /// - Postcondition: The resulting `URL` instance is constructed - /// by appending `keyBucketPath` to `kvDBBase`. - public init(kvDBBase: String, keyBucketPath: String) { - // swiftlint:disable:next force_unwrapping - self = URL(string: kvDBBase)!.appendingPathComponent(keyBucketPath) - } -} diff --git a/Sources/SublimationKVdb/URLSession.swift b/Sources/SublimationKVdb/URLSession.swift deleted file mode 100644 index 7a3d19e..0000000 --- a/Sources/SublimationKVdb/URLSession.swift +++ /dev/null @@ -1,87 +0,0 @@ -// -// URLSession.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation - -#if canImport(FoundationNetworking) - import FoundationNetworking -#endif - -/// An extension to `URLSession` that provides asynchronous data fetching methods. -extension URLSession { - /// Creates a new ephemeral `URLSession` instance. - /// - /// - Returns: A new ephemeral `URLSession` instance. - public static func ephemeral() -> URLSession { - URLSession(configuration: .ephemeral) - } - - /// Fetches data asynchronously for the given request. - /// - /// - Parameter request: The request to fetch data for. - /// - /// - Returns: A tuple containing the fetched data and the URL response. - /// - /// - Throws: An error if the data fetching operation fails. - internal func dataAsync(for request: URLRequest) async throws -> (Data, URLResponse) { - #if !canImport(FoundationNetworking) - if #available(iOS 15, macOS 12, tvOS 15, watchOS 8, *) { - return try await self.data(for: request) - } - #endif - - return try await withCheckedThrowingContinuation { continuation in - let task = self.dataTask(with: request) { data, response, error in - continuation.resume( - with: .init( - success: data.flatTuple(response), - failure: error - ) - ) - } - task.resume() - } - } - - /// Fetches data asynchronously from the given URL. - /// - /// - Parameter url: The URL to fetch data from. - /// - /// - Returns: A tuple containing the fetched data and the URL response. - /// - /// - Throws: An error if the data fetching operation fails. - internal func dataAsync(from url: URL) async throws -> (Data, URLResponse) { - #if !canImport(FoundationNetworking) - if #available(iOS 15, macOS 12, tvOS 15, watchOS 8, *) { - return try await data(for: .init(url: url)) - } - #endif - return try await dataAsync(for: .init(url: url)) - } -} diff --git a/Sources/SublimationKVdb/URLSessionClient.swift b/Sources/SublimationKVdb/URLSessionClient.swift deleted file mode 100644 index f4bbbbe..0000000 --- a/Sources/SublimationKVdb/URLSessionClient.swift +++ /dev/null @@ -1,107 +0,0 @@ -// -// URLSessionClient.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation -import SublimationTunnel - -#if canImport(FoundationNetworking) - import FoundationNetworking -#endif - -/// A client for interacting with a KVdb tunnel using URLSession. -/// -/// This client conforms to the `KVdbTunnelClient` protocol. -/// -/// - Note: This client requires the `FoundationNetworking` module to be imported. -/// -/// - Warning: The `saveValue(_:withKey:inBucket:)` method will throw -/// a `NgrokServerError` if the save operation fails. -/// -/// - SeeAlso: `KVdbTunnelClient` -public struct URLSessionClient: TunnelClient { - private let session: URLSession - - /// Initializes a new `URLSessionClient` with the specified session. - /// - /// - Parameter session: The URLSession to use for network requests. - /// Defaults to an ephemeral session. - public init(session: URLSession = .ephemeral()) { - self.session = session - } - - /// Retrieves the value associated with a key from a specific bucket. - /// - /// - Parameters: - /// - key: The key to retrieve the value for. - /// - bucketName: The name of the bucket to retrieve the value from. - /// - /// - Returns: The URL value associated with the key. - /// - /// - Throws: A `NgrokServerError` if the retrieval operation fails. - public func getValue( - ofKey key: Key, - fromBucket bucketName: String - ) async throws -> URL { - let url = KVdb.construct(URL.self, forKey: key, atBucket: bucketName) - - let data = try await session.dataAsync(from: url).0 - - let urlString = String(decoding: data, as: UTF8.self) - guard let url = URL(string: urlString) else { - throw KVdbServerError.invalidURL - } - - return url - } - - /// Saves a URL value with a specified key in a specific bucket. - /// - /// - Parameters: - /// - value: The URL value to save. - /// - key: The key to associate with the value. - /// - bucketName: The name of the bucket to save the value in. - /// - /// - Throws: A `NgrokServerError` if the save operation fails. - public func saveValue( - _ value: URL, - withKey key: Key, - inBucket bucketName: String - ) async throws { - let url = KVdb.construct(URL.self, forKey: key, atBucket: bucketName) - var request = URLRequest(url: url) - request.httpBody = value.absoluteString.data(using: .utf8) - let (data, response) = try await session.dataAsync(for: request) - guard let httpResponse = response as? HTTPURLResponse else { - throw KVdbServerError.cantSaveTunnel(nil, nil) - } - guard httpResponse.statusCode / 100 == 2 else { - throw KVdbServerError.cantSaveTunnel(httpResponse.statusCode, data) - } - } -} diff --git a/Sources/SublimationMocks/MockError.swift b/Sources/SublimationMocks/MockError.swift index 74f2066..b234fa3 100644 --- a/Sources/SublimationMocks/MockError.swift +++ b/Sources/SublimationMocks/MockError.swift @@ -1,6 +1,6 @@ // // MockError.swift -// Sublimation +// SublimationBonjour // // Created by Leo Dion. // Copyright © 2024 BrightDigit. @@ -27,26 +27,18 @@ // OTHER DEALINGS IN THE SOFTWARE. // -package enum MockError: Error { - case value(T) -} +package enum MockError: Error { case value(T) } extension Result { package var error: (any Error)? { - guard case let .failure(failure) = self else { - return nil - } + guard case .failure(let failure) = self else { return nil } return failure } package func mockErrorValue() -> T? { - guard let mockError = error as? MockError else { - return nil - } + guard let mockError = error as? MockError else { return nil } - switch mockError { - case let .value(value): - return value + switch mockError { case .value(let value): return value } } } diff --git a/Sources/SublimationMocks/MockTunnelClient.swift b/Sources/SublimationMocks/MockTunnelClient.swift index f44810b..71a026c 100644 --- a/Sources/SublimationMocks/MockTunnelClient.swift +++ b/Sources/SublimationMocks/MockTunnelClient.swift @@ -1,6 +1,6 @@ // // MockTunnelClient.swift -// Sublimation +// SublimationBonjour // // Created by Leo Dion. // Copyright © 2024 BrightDigit. @@ -47,30 +47,19 @@ package actor MockTunnelClient: TunnelClient { package private(set) var getValuesPassed = [GetParameters]() package private(set) var saveValuesPassed = [SaveParameters]() - package init( - getValueResult: Result? = nil, saveValueError: (any Error)? = nil - ) { + package init(getValueResult: Result? = nil, saveValueError: (any Error)? = nil) { self.getValueResult = getValueResult self.saveValueError = saveValueError } - package func getValue( - ofKey key: Key, - fromBucket bucketName: String - ) async throws -> URL { + package func getValue(ofKey key: Key, fromBucket bucketName: String) async throws -> URL { getValuesPassed.append(.init(key: key, bucketName: bucketName)) - // swiftlint:disable:next force_unwrapping + // swift-format-ignore: NeverForceUnwrap return try getValueResult!.get() } - package func saveValue( - _ value: URL, - withKey key: Key, - inBucket bucketName: String - ) async throws { + package func saveValue(_ value: URL, withKey key: Key, inBucket bucketName: String) async throws { saveValuesPassed.append(.init(value: value, key: key, bucketName: bucketName)) - if let saveValueError { - throw saveValueError - } + if let saveValueError { throw saveValueError } } } diff --git a/Sources/SublimationMocks/MockURL.swift b/Sources/SublimationMocks/MockURL.swift index 8b4a18a..dd9f37d 100644 --- a/Sources/SublimationMocks/MockURL.swift +++ b/Sources/SublimationMocks/MockURL.swift @@ -1,6 +1,6 @@ // // MockURL.swift -// Sublimation +// SublimationBonjour // // Created by Leo Dion. // Copyright © 2024 BrightDigit. diff --git a/Sources/SublimationMocks/URL.swift b/Sources/SublimationMocks/URL.swift index e4438b6..aa4f8ce 100644 --- a/Sources/SublimationMocks/URL.swift +++ b/Sources/SublimationMocks/URL.swift @@ -1,6 +1,6 @@ // // URL.swift -// Sublimation +// SublimationBonjour // // Created by Leo Dion. // Copyright © 2024 BrightDigit. @@ -34,7 +34,5 @@ import Foundation #endif extension URL { - package static func random() -> URL { - URL(fileURLWithPath: NSTemporaryDirectory()) - } + package static func random() -> URL { URL(fileURLWithPath: NSTemporaryDirectory()) } } diff --git a/Sources/SublimationNgrok/NgrokCLIAPIConfiguration.swift b/Sources/SublimationNgrok/NgrokCLIAPIConfiguration.swift deleted file mode 100644 index ab35088..0000000 --- a/Sources/SublimationNgrok/NgrokCLIAPIConfiguration.swift +++ /dev/null @@ -1,72 +0,0 @@ -// -// NgrokCLIAPIConfiguration.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Logging -import SublimationCore -import SublimationTunnel - -/// Configuration for the Ngrok CLI API server. -/// -/// - Note: This configuration conforms to `NgrokServerConfiguration` protocol. -/// -/// - Note: This configuration conforms to `NgrokVaporConfiguration` protocol. -/// -/// - SeeAlso: `NgrokCLIAPIServer` -public struct NgrokCLIAPIConfiguration: TunnelServerConfiguration { - /// The type of server to use. - public typealias Server = NgrokCLIAPIServer - - /// The port number to run the server on. - public let port: Int - - /// The logger to use for logging. - public let logger: Logger -} - -extension NgrokCLIAPIConfiguration { - /// Initializes a new instance of - /// `NgrokCLIAPIConfiguration` using a `ServerApplication`. - /// - /// - Parameter serverApplication: The server application to use for configuration. - internal init(serverApplication: any Application) { - self.init( - port: serverApplication.httpServerConfigurationPort, - logger: serverApplication.logger - ) - } - - /// Initializes a new instance of `NgrokCLIAPIConfiguration` - /// using a `Vapor.Application`. - /// - /// - Parameter application: The Vapor application to use for configuration. - - public init(application: any Application) { - self.init(serverApplication: application) - } -} diff --git a/Sources/SublimationNgrok/NgrokCLIAPIServer+TunnelServer.swift b/Sources/SublimationNgrok/NgrokCLIAPIServer+TunnelServer.swift deleted file mode 100644 index 2993385..0000000 --- a/Sources/SublimationNgrok/NgrokCLIAPIServer+TunnelServer.swift +++ /dev/null @@ -1,59 +0,0 @@ -// -// NgrokCLIAPIServer+TunnelServer.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation -import OpenAPIRuntime -import SublimationTunnel - -extension NgrokCLIAPIServer { - /// Runs the server. - public func run( - isConnectionRefused: @escaping (ClientError) -> Bool) async { - let start = Date() - let newTunnel: any Tunnel - do { - newTunnel = try await self.newTunnel(isConnectionRefused: isConnectionRefused) - } catch { - delegate.server(self, errorDidOccur: error) - return - } - let seconds = Int(-start.timeIntervalSinceNow) - logger.notice("New Tunnel Created in \(seconds) secs: \(newTunnel.publicURL)") - - delegate.server(self, updatedTunnel: newTunnel) - } - - /// Starts the server. - public func start( - isConnectionRefused: @escaping @Sendable (ClientError) -> Bool) { - Task { - await run(isConnectionRefused: isConnectionRefused) - } - } -} diff --git a/Sources/SublimationNgrok/NgrokCLIAPIServer.swift b/Sources/SublimationNgrok/NgrokCLIAPIServer.swift deleted file mode 100644 index ed56dbe..0000000 --- a/Sources/SublimationNgrok/NgrokCLIAPIServer.swift +++ /dev/null @@ -1,162 +0,0 @@ -// -// NgrokCLIAPIServer.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation -import Logging -import Ngrokit -import OpenAPIRuntime -import SublimationTunnel - -/// A server implementation for Ngrok CLI API. -/// -/// - Note: This server conforms to the `NgrokServer` and `Sendable` protocols. -/// -/// - SeeAlso: `NgrokServer` -/// - SeeAlso: `Sendable` -public struct NgrokCLIAPIServer: TunnelServer, Sendable { - public typealias ConnectionErrorType = ClientError - - private struct TunnelResult { - let isOld: Bool - let tunnel: any Tunnel - } - - /// The delegate for the server. - internal let delegate: any TunnelServerDelegate - - /// The client for interacting with Ngrok. - private let client: NgrokClient - - /// The process for running Ngrok. - internal let process: any NgrokProcess - - /// The port number to use. - internal let port: Int - - /// The logger for logging server events. - internal let logger: Logger - - /// Initializes a new instance of `NgrokCLIAPIServer`. - /// - /// - Parameters: - /// - delegate: The delegate for the server. - /// - client: The client for interacting with Ngrok. - /// - process: The process for running Ngrok. - /// - port: The port number to use. - /// - logger: The logger for logging server events. - public init( - delegate: any TunnelServerDelegate, - client: NgrokClient, - process: any NgrokProcess, - port: Int, - logger: Logger - ) { - self.delegate = delegate - self.client = client - self.process = process - self.port = port - self.logger = logger - } - - /// Handles a CLI error. - /// - /// - Parameter error: The error that occurred. - @Sendable - private func cliError(_ error: any Error) { - delegate.server(self, errorDidOccur: error) - } - - private func searchForExistingTunnel( - within timeout: TimeInterval, - isConnectionRefused: @escaping (ClientError) -> Bool - ) async throws -> TunnelResult? { - logger.debug("Starting Search for Existing Tunnel") - - let result = await NetworkResult( - { try await client.listTunnels().first }, - isConnectionRefused: isConnectionRefused - ) - - if let tunnel = try await self.getTunnel(from: result) { - return tunnel - } - - return try await client.searchForCreatedTunnel( - within: timeout, - logger: logger, - isConnectionRefused: isConnectionRefused - ) - .map { - .init(isOld: false, tunnel: $0) - } - } - - private func getTunnel( - from result: NetworkResult - ) async throws -> TunnelResult?? { - switch result { - case .connectionRefused: - logger.notice( - "Ngrok not running. Waiting for Process and New Tunnel... (about 30 secs)" - ) - try await process.run(onError: cliError(_:)) - - case let .success(tunnel): - logger.debug("Process Already Running.") - return TunnelResult??.some(tunnel.map { .init(isOld: true, tunnel: $0) }) - - case let .failure(error): - throw error - } - return nil - } - - internal func newTunnel( - isConnectionRefused: @escaping (ClientError) -> Bool) async throws -> any Tunnel { - if let tunnel = try await searchForExistingTunnel( - within: 60.0, - - isConnectionRefused: isConnectionRefused - ) { - if tunnel.isOld { - try await client.stopTunnel(withName: tunnel.tunnel.name) - logger.info("Existing Tunnel Stopped. \(tunnel.tunnel.publicURL)") - } else { - return tunnel.tunnel - } - } - - return try await client.startTunnel( - .init( - port: port, - name: "vapor-development" - ) - ) - } -} diff --git a/Sources/SublimationNgrok/NgrokCLIAPIServerFactory.swift b/Sources/SublimationNgrok/NgrokCLIAPIServerFactory.swift deleted file mode 100644 index a27a498..0000000 --- a/Sources/SublimationNgrok/NgrokCLIAPIServerFactory.swift +++ /dev/null @@ -1,89 +0,0 @@ -// -// NgrokCLIAPIServerFactory.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation -import Ngrokit -import SublimationTunnel - -/// A factory for creating Ngrok CLI API servers. -/// -/// This factory conforms to the `NgrokServerFactory` protocol. -/// -/// - Note: This factory requires the `NgrokCLIAPI` type to be `Processable`. -/// -/// - SeeAlso: `NgrokServerFactory` -public struct NgrokCLIAPIServerFactory: TunnelServerFactory { - /// The configuration type for the Ngrok CLI API server. - public typealias Configuration = NgrokCLIAPIConfiguration - - /// The Ngrok CLI API instance. - private let cliAPI: any NgrokCLIAPI - - private let ngrokClient: @Sendable () -> NgrokClient - - public init( - cliAPI: any NgrokCLIAPI, - ngrokClient: @escaping @Sendable () -> NgrokClient - ) { - self.cliAPI = cliAPI - self.ngrokClient = ngrokClient - } - - public init( - ngrokPath: String, - ngrokClient: @escaping @Sendable () -> NgrokClient - ) { - self.init( - cliAPI: NgrokProcessCLIAPI(ngrokPath: ngrokPath), - ngrokClient: ngrokClient - ) - } - - /// Creates a new Ngrok CLI API server. - /// - /// - Parameters: - /// - configuration: The configuration for the server. - /// - handler: The delegate for the server. - /// - /// - Returns: A new `NgrokCLIAPIServer` instance. - - public func server( - from configuration: Configuration, - handler: any TunnelServerDelegate - ) -> NgrokCLIAPIServer { - let process = cliAPI.process(forHTTPPort: configuration.port) - return .init( - delegate: handler, - client: self.ngrokClient(), - process: process, - port: configuration.port, - logger: configuration.logger - ) - } -} diff --git a/Sources/SublimationNgrok/NgrokClient.swift b/Sources/SublimationNgrok/NgrokClient.swift deleted file mode 100644 index 2949de2..0000000 --- a/Sources/SublimationNgrok/NgrokClient.swift +++ /dev/null @@ -1,82 +0,0 @@ -// -// NgrokClient.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation -import Logging -import Ngrokit -import OpenAPIRuntime -import SublimationTunnel - -extension NgrokClient { - internal func attemptTunnel( - isConnectionRefused: @escaping (ClientError) -> Bool - ) async -> TunnelAttemptResult { - let networkResult = await AnyTunnelNetworkResult({ - try await self.listTunnels().first - }, isConnectionRefused: isConnectionRefused) - switch networkResult { - case let .connectionRefused(error): - return .error(error) - - default: - return .network(networkResult) - } - } - - internal func searchForCreatedTunnel( - within timeout: TimeInterval, - logger: Logger, - isConnectionRefused: @escaping (ClientError) -> Bool - ) async throws -> (any Tunnel)? { - let start = Date() - var networkResult: NetworkResult<(any Tunnel)?, ClientError>? - var lastError: ClientError? - var attempts = 0 - while networkResult == nil, (-start.timeIntervalSinceNow) < timeout { - logger.debug("Attempt #\(attempts + 1)") - try await Task.sleep(for: .seconds(5), tolerance: .seconds(5)) - let result = await self.attemptTunnel(isConnectionRefused: isConnectionRefused) - attempts += 1 - switch result { - case let .network(newNetworkResult): - networkResult = newNetworkResult - - case let .error(error): - lastError = error - } - } - - if let lastError, networkResult == nil { - logger.error("Timeout Occured After \(-start.timeIntervalSinceNow) seconds.") - throw lastError - } - - return try networkResult?.get()?.flatMap { $0 } - } -} diff --git a/Sources/SublimationNgrok/NgrokTunnel.swift b/Sources/SublimationNgrok/NgrokTunnel.swift deleted file mode 100644 index 56718de..0000000 --- a/Sources/SublimationNgrok/NgrokTunnel.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// NgrokTunnel.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Ngrokit -import SublimationTunnel - -extension NgrokTunnel: Tunnel {} diff --git a/Sources/SublimationNgrok/TunnelAttemptResult.swift b/Sources/SublimationNgrok/TunnelAttemptResult.swift deleted file mode 100644 index 928b089..0000000 --- a/Sources/SublimationNgrok/TunnelAttemptResult.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// TunnelAttemptResult.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import OpenAPIRuntime -import SublimationTunnel - -internal enum TunnelAttemptResult { - case network(AnyTunnelNetworkResult) - case error(ClientError) -} diff --git a/Sources/SublimationTunnel/NetworkResult.swift b/Sources/SublimationTunnel/NetworkResult.swift deleted file mode 100644 index ed6be7e..0000000 --- a/Sources/SublimationTunnel/NetworkResult.swift +++ /dev/null @@ -1,86 +0,0 @@ -// -// NetworkResult.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation - -package typealias AnyTunnelNetworkResult = - NetworkResult<(any Tunnel)?, ConnectionErrorType> - -/// Represents the result of a network operation. -/// -/// - success: The operation was successful and contains the result value. -/// - connectionRefused: The connection was refused by the server. -/// - failure: The operation failed with an error. -/// -/// - Note: This type is internal and should not be used outside of the framework. -package enum NetworkResult { - case success(T) - case connectionRefused(ConnectionErrorType) - case failure(any Error) -} - -extension NetworkResult { - package init(error: any Error, isConnectionRefused: @escaping (ConnectionErrorType) -> Bool) { - guard let error = error as? ConnectionErrorType else { - self = .failure(error) - return - } - - if isConnectionRefused(error) { - self = .connectionRefused(error) - return - } - - self = .failure(error) - } - - package init( - _ closure: @escaping () async throws -> T, - isConnectionRefused: @escaping (ConnectionErrorType) -> Bool - ) async { - do { - self = try await .success(closure()) - } catch { - self = .init(error: error, isConnectionRefused: isConnectionRefused) - } - } - - package func get() throws -> T? { - switch self { - case .connectionRefused: - return nil - - case let .failure(error): - throw error - - case let .success(item): - return item - } - } -} diff --git a/Sources/SublimationTunnel/Tunnel.swift b/Sources/SublimationTunnel/Tunnel.swift deleted file mode 100644 index 3a19690..0000000 --- a/Sources/SublimationTunnel/Tunnel.swift +++ /dev/null @@ -1,35 +0,0 @@ -// -// Tunnel.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation - -public protocol Tunnel: Sendable { - var name: String { get } - var publicURL: URL { get } -} diff --git a/Sources/SublimationTunnel/TunnelBucketRepositoryFactory.swift b/Sources/SublimationTunnel/TunnelBucketRepositoryFactory.swift deleted file mode 100644 index 4e03e4e..0000000 --- a/Sources/SublimationTunnel/TunnelBucketRepositoryFactory.swift +++ /dev/null @@ -1,63 +0,0 @@ -// -// TunnelBucketRepositoryFactory.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation - -#if canImport(FoundationNetworking) - import FoundationNetworking -#endif - -/// This factory is used to set up and configure a -/// `KVdbTunnelRepository` with a specific bucket name. -public struct TunnelBucketRepositoryFactory: - WritableTunnelRepositoryFactory { - /// The type of tunnel repository created by this factory. - public typealias TunnelRepositoryType = TunnelClientRepository - - /// The name of the bucket to use. - public let bucketName: String - - /// Initializes a new instance of the factory with the specified bucket name. - /// - /// - Parameter bucketName: The name of the bucket to use. - public init(bucketName: String) { - self.bucketName = bucketName - } - - /// Sets up a client and returns a new `KVdbTunnelRepository` instance. - /// - /// - Parameter client: The tunnel client to use. - /// - Returns: A new `KVdbTunnelRepository` instance. - public func setupClient( - _ client: TunnelClientType - ) -> TunnelClientRepository - where TunnelClientType: TunnelClient, TunnelClientType.Key == Key { - .init(client: client, bucketName: bucketName) - } -} diff --git a/Sources/SublimationTunnel/TunnelClient.swift b/Sources/SublimationTunnel/TunnelClient.swift deleted file mode 100644 index 3304867..0000000 --- a/Sources/SublimationTunnel/TunnelClient.swift +++ /dev/null @@ -1,71 +0,0 @@ -// -// TunnelClient.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation - -#if canImport(FoundationNetworking) - import FoundationNetworking -#endif - -/// A client for interacting with KVdb Tunnel. -/// -/// - Note: This client conforms to the `Sendable` protocol. -/// -/// - Note: The `Key` type must also conform to the `Sendable` protocol. -/// -/// - Warning: This client is not thread-safe. -/// -/// - Important: This client requires the `FoundationNetworking` module to be imported. -/// -/// - SeeAlso: `KVdbTunnelClientProtocol` -public protocol TunnelClient: Sendable { - /// The type of key used to access values in the KVdb Tunnel. - associatedtype Key: Sendable - - /// Retrieves the value associated with the specified key from the specified bucket. - /// - /// - Parameters: - /// - key: The key used to access the value. - /// - bucketName: The name of the bucket. - /// - /// - Returns: The URL of the retrieved value. - /// - /// - Throws: An error if the value cannot be retrieved. - func getValue(ofKey key: Key, fromBucket bucketName: String) async throws -> URL - - /// Saves the specified value with the specified key in the specified bucket. - /// - /// - Parameters: - /// - value: The URL of the value to be saved. - /// - key: The key used to access the value. - /// - bucketName: The name of the bucket. - /// - /// - Throws: An error if the value cannot be saved. - func saveValue(_ value: URL, withKey key: Key, inBucket bucketName: String) async throws -} diff --git a/Sources/SublimationTunnel/TunnelClientRepository.swift b/Sources/SublimationTunnel/TunnelClientRepository.swift deleted file mode 100644 index 2dd1981..0000000 --- a/Sources/SublimationTunnel/TunnelClientRepository.swift +++ /dev/null @@ -1,51 +0,0 @@ -// -// TunnelClientRepository.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation - -#if canImport(FoundationNetworking) - import FoundationNetworking -#endif - -public final class TunnelClientRepository: WritableTunnelRepository { - private let client: any TunnelClient - private let bucketName: String - public init(client: any TunnelClient, bucketName: String) { - self.client = client - self.bucketName = bucketName - } - - public func tunnel(forKey key: Key) async throws -> URL? { - try await client.getValue(ofKey: key, fromBucket: bucketName) - } - - public func saveURL(_ url: URL, withKey key: Key) async throws { - try await client.saveValue(url, withKey: key, inBucket: bucketName) - } -} diff --git a/Sources/SublimationTunnel/TunnelRepository.swift b/Sources/SublimationTunnel/TunnelRepository.swift deleted file mode 100644 index 7134168..0000000 --- a/Sources/SublimationTunnel/TunnelRepository.swift +++ /dev/null @@ -1,56 +0,0 @@ -// -// TunnelRepository.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation - -/// A repository for managing tunnels. -/// This protocol defines the basic functionality for -/// retrieving tunnels based on a given key. -/// -/// - Note: The `Key` type must conform to the `Sendable` protocol. -/// -/// - Important: This protocol inherits from the `Sendable` protocol. -/// -/// - Warning: The `Key` associated type must also conform to the `Sendable` protocol. -/// -/// - Requires: The `Key` associated type to be specified. -public protocol TunnelRepository: Sendable { - /// The type of key used to retrieve tunnels. - associatedtype Key: Sendable - - /// Retrieves a tunnel for the specified key. - /// - /// - Parameter key: The key used to retrieve the tunnel. - /// - /// - Throws: An error if the tunnel cannot be retrieved. - /// - /// - Returns: The URL of the retrieved tunnel, if available. - - func tunnel(forKey key: Key) async throws -> URL? -} diff --git a/Sources/SublimationTunnel/TunnelRepositoryFactory.swift b/Sources/SublimationTunnel/TunnelRepositoryFactory.swift deleted file mode 100644 index 648a580..0000000 --- a/Sources/SublimationTunnel/TunnelRepositoryFactory.swift +++ /dev/null @@ -1,69 +0,0 @@ -// -// TunnelRepositoryFactory.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation - -#if canImport(FoundationNetworking) - import FoundationNetworking -#endif - -/// A factory for creating tunnel repositories. -/// -/// The factory is responsible for -/// setting up the client and returning a tunnel repository. -/// -/// - Note: The factory must be `Sendable`. -/// -/// - Note: The associated type `TunnelRepositoryType` must conform to `TunnelRepository`. -/// -/// - Note: The factory must implement the `setupClient` method, -/// which takes a `TunnelClientType` and returns a `TunnelRepositoryType`. -/// -/// - Warning: The factory may require the `FoundationNetworking` module to be imported. -/// -/// - SeeAlso: `TunnelRepository` -/// - SeeAlso: `KVdbTunnelClient` -public protocol TunnelRepositoryFactory: Sendable { - /// The type of tunnel repository created by the factory. - associatedtype TunnelRepositoryType: TunnelRepository - - /// Sets up the client and returns a tunnel repository. - /// - /// - Parameter client: The tunnel client to use. - /// - /// - Returns: A tunnel repository. - /// - /// - Throws: An error if the setup fails. - /// - /// - Note: The `TunnelClientType` must have a `Key` type - /// that matches the `Key` type of the `TunnelRepositoryType`. - func setupClient( - _ client: TunnelClientType - ) -> TunnelRepositoryType where TunnelClientType.Key == TunnelRepositoryType.Key -} diff --git a/Sources/SublimationTunnel/TunnelServer.swift b/Sources/SublimationTunnel/TunnelServer.swift deleted file mode 100644 index b959c84..0000000 --- a/Sources/SublimationTunnel/TunnelServer.swift +++ /dev/null @@ -1,45 +0,0 @@ -// -// TunnelServer.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -/// A protocol for starting a Ngrok server. -/// -/// Implement this protocol to start a Ngrok server. -/// -/// - Note: The Ngrok server allows you to expose a local server to the internet. -/// -/// - Important: Make sure to call the `start()` method to start the Ngrok server. -public protocol TunnelServer { - /// Type of connection error which denotes whether the service isn't available. - associatedtype ConnectionErrorType: Error - /// Starts the Ngrok server. - /// - /// Call this method to start the Ngrok server and - /// expose your local server to the internet. - func start(isConnectionRefused: @escaping @Sendable (ConnectionErrorType) -> Bool) -} diff --git a/Sources/SublimationTunnel/TunnelServerConfiguration.swift b/Sources/SublimationTunnel/TunnelServerConfiguration.swift deleted file mode 100644 index 94767b4..0000000 --- a/Sources/SublimationTunnel/TunnelServerConfiguration.swift +++ /dev/null @@ -1,45 +0,0 @@ -// -// TunnelServerConfiguration.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import SublimationCore - -/// A protocol that defines the configuration for an Ngrok server. -/// -/// - Note: The associated type `Server` must conform to the `NgrokServer` protocol. -public protocol TunnelServerConfiguration { - /// Server for starting an `ngrok` process. - associatedtype Server: TunnelServer - /// Initializes a new instance of the configuration. - /// - /// - Parameter application: The Vapor application. - /// - /// - Note: This initializer is required to conform to - /// the `NgrokVaporConfiguration` protocol. - init(application: any Application) -} diff --git a/Sources/SublimationTunnel/TunnelServerDelegate.swift b/Sources/SublimationTunnel/TunnelServerDelegate.swift deleted file mode 100644 index e771035..0000000 --- a/Sources/SublimationTunnel/TunnelServerDelegate.swift +++ /dev/null @@ -1,51 +0,0 @@ -// -// TunnelServerDelegate.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation - -/// A delegate protocol for `NgrokServer` that handles server events and errors. -public protocol TunnelServerDelegate: AnyObject, Sendable { - /// Notifies the delegate that a tunnel has been updated. - /// - /// - Parameters: - /// - server: The `NgrokServer` instance that triggered the event. - /// - tunnel: The updated `Tunnel` object. - /// - /// - Note: This method is called whenever a tunnel's status or configuration changes. - func server(_ server: any TunnelServer, updatedTunnel tunnel: any Tunnel) - - /// Notifies the delegate that an error has occurred. - /// - /// - Parameters: - /// - server: The `NgrokServer` instance that triggered the event. - /// - error: The error that occurred. - /// - /// - Note: This method is called whenever an error occurs during server operations. - func server(_ server: any TunnelServer, errorDidOccur error: any Error) -} diff --git a/Sources/SublimationTunnel/TunnelServerFactory.swift b/Sources/SublimationTunnel/TunnelServerFactory.swift deleted file mode 100644 index 84ff8f4..0000000 --- a/Sources/SublimationTunnel/TunnelServerFactory.swift +++ /dev/null @@ -1,46 +0,0 @@ -// -// TunnelServerFactory.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -/// A factory protocol for creating Ngrok servers. -public protocol TunnelServerFactory: Sendable { - /// The associated type representing the configuration for the server. - associatedtype Configuration: TunnelServerConfiguration - - /// Creates a server instance based on the provided configuration. - /// - /// - Parameters: - /// - configuration: The configuration for the server. - /// - handler: The delegate object that handles server events. - /// - /// - Returns: A server instance based on the provided configuration. - func server( - from configuration: Configuration, - handler: any TunnelServerDelegate - ) -> Configuration.Server -} diff --git a/Sources/SublimationTunnel/TunnelSublimatory.swift b/Sources/SublimationTunnel/TunnelSublimatory.swift deleted file mode 100644 index d89d717..0000000 --- a/Sources/SublimationTunnel/TunnelSublimatory.swift +++ /dev/null @@ -1,196 +0,0 @@ -// -// TunnelSublimatory.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation -import Logging -import SublimationCore - -/// Closure which returns a ``TunnelClient`` from the ``Application``. -public typealias RepositoryClientFactory = - (@Sendable @escaping () -> any Application) -> any TunnelClient - -public actor TunnelSublimatory< - WritableTunnelRepositoryFactoryType: WritableTunnelRepositoryFactory, - TunnelServerFactoryType: TunnelServerFactory ->: Sublimatory, TunnelServerDelegate { - public typealias Key = WritableTunnelRepositoryFactoryType.TunnelRepositoryType.Key - public typealias ConnectionErrorType = TunnelServerFactoryType.Configuration.Server.ConnectionErrorType - private let factory: TunnelServerFactoryType - private let repoFactory: WritableTunnelRepositoryFactoryType - private let key: Key - private let repoClientFactory: RepositoryClientFactory - - private var tunnelRepo: WritableTunnelRepositoryFactoryType.TunnelRepositoryType? - private var logger: Logger? - private var server: (any TunnelServer)? - - private let isConnectionRefused: @Sendable (ConnectionErrorType) -> Bool - /// Initializes the Sublimation lifecycle handler. - /// - /// - Parameters: - /// - factory: The factory for creating an Ngrok server. - /// - repoFactory: The factory for creating a writable tunnel repository. - /// - key: The key for the tunnel repository. - public init( - factory: TunnelServerFactoryType, - repoFactory: WritableTunnelRepositoryFactoryType, - key: WritableTunnelRepositoryFactoryType.TunnelRepositoryType.Key, - repoClientFactory: @escaping RepositoryClientFactory, - isConnectionRefused: @escaping @Sendable (ConnectionErrorType) -> Bool - ) { - self.init( - factory: factory, - repoFactory: repoFactory, - key: key, - tunnelRepo: nil, - logger: nil, - server: nil, - repoClientFactory: repoClientFactory, - isConnectionRefused: isConnectionRefused - ) - } - - private init( - factory: TunnelServerFactoryType, - repoFactory: WritableTunnelRepositoryFactoryType, - key: Key, - tunnelRepo: WritableTunnelRepositoryFactoryType.TunnelRepositoryType?, - logger: Logger?, - server: (any TunnelServer)?, - repoClientFactory: @escaping RepositoryClientFactory, - isConnectionRefused: @escaping @Sendable (ConnectionErrorType) -> Bool - ) { - self.factory = factory - self.repoFactory = repoFactory - self.key = key - self.tunnelRepo = tunnelRepo - self.logger = logger - self.server = server - self.repoClientFactory = repoClientFactory - self.isConnectionRefused = isConnectionRefused - } - - /// Saves the tunnel URL to the tunnel repository. - /// - /// - Parameters: - /// - tunnel: The tunnel to save. - /// - /// - Note: This method is asynchronous. - /// - /// - SeeAlso: `Tunnel` - private func saveTunnel(_ tunnel: any Tunnel) async { - do { - try await tunnelRepo?.saveURL(tunnel.publicURL, withKey: key) - } catch { - logger?.error( - "Unable to save url to repository: \(error.localizedDescription)" - ) - return - } -// logger?.notice( -// "Saved url \(tunnel.publicURL) to repository with key \(key)" -// ) - } - - /// Handles an error that occurred during tunnel operation. - /// - /// - Parameters: - /// - error: The error that occurred. - /// - /// - Note: This method is asynchronous. - private func onError(_ error: any Error) async { - logger?.error("Error running tunnel: \(error.localizedDescription)") - } - - /// Called when an Ngrok server updates a tunnel. - /// - /// - Parameters: - /// - server: The Ngrok server. - /// - tunnel: The updated tunnel. - /// - /// - Note: This method is nonisolated. - /// - /// - SeeAlso: `NgrokServer` - /// - SeeAlso: `Tunnel` - public nonisolated func server(_: any TunnelServer, updatedTunnel tunnel: any Tunnel) { - Task { - await self.saveTunnel(tunnel) - } - } - - /// Called when an error occurs in the Ngrok server. - /// - /// - Parameters: - /// - server: The Ngrok server. - /// - error: The error that occurred. - /// - /// - Note: This method is nonisolated. - /// - /// - SeeAlso: `NgrokServer` - public nonisolated func server(_: any TunnelServer, errorDidOccur error: any Error) { - Task { - await self.onError(error) - } - } - - /// Begins the Sublimation application from the given application. - /// - /// - Parameters: - /// - application: The Vapor application. - /// - /// - Note: This method is private and asynchronous. - /// - /// - SeeAlso: `Application` - private func beginFromApplication(_ application: @Sendable @escaping () -> any Application) async { - let server = factory.server( - from: TunnelServerFactoryType.Configuration(application: application()), - handler: self - ) - logger = application().logger - tunnelRepo = repoFactory.setupClient( - repoClientFactory(application) - ) - self.server = server - server.start(isConnectionRefused: isConnectionRefused) - } - - /// Called when the application is about to boot. - /// - /// - Parameters: - /// - application: The Vapor application. - /// - /// - Throws: An error if the application fails to begin. - /// - /// - Note: This method is nonisolated. - /// - /// - SeeAlso: `Application` - public func willBoot(from application: @escaping @Sendable () -> any Application) async { - await self.beginFromApplication(application) - } -} diff --git a/Sources/SublimationTunnel/WritableTunnelRepository.swift b/Sources/SublimationTunnel/WritableTunnelRepository.swift deleted file mode 100644 index 664f5aa..0000000 --- a/Sources/SublimationTunnel/WritableTunnelRepository.swift +++ /dev/null @@ -1,52 +0,0 @@ -// -// WritableTunnelRepository.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation - -/// A repository for managing writable tunnels. -/// -/// This protocol extends the `TunnelRepository` protocol -/// and adds the ability to save a URL with a key. -/// -/// - Note: The `Key` type parameter -/// represents the type of key used to identify the tunnels. -/// -/// - SeeAlso: `TunnelRepository` -public protocol WritableTunnelRepository: TunnelRepository { - /// Saves a URL with a key. - /// - /// - Parameters: - /// - url: The URL to save. - /// - key: The key to associate with the URL. - /// - /// - Throws: An error if the save operation fails. - /// - /// - Note: This method is asynchronous. - func saveURL(_ url: URL, withKey key: Key) async throws -} diff --git a/Sources/SublimationTunnel/WritableTunnelRepositoryFactory.swift b/Sources/SublimationTunnel/WritableTunnelRepositoryFactory.swift deleted file mode 100644 index 5dd1ab6..0000000 --- a/Sources/SublimationTunnel/WritableTunnelRepositoryFactory.swift +++ /dev/null @@ -1,52 +0,0 @@ -// -// WritableTunnelRepositoryFactory.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -// #if os(macOS) -// @_exported import class Ngrokit.ProcessableProcess -// #endif -// @_exported import struct Ngrokit.NgrokClient - -import Foundation - -#if canImport(FoundationNetworking) - import FoundationNetworking -#endif - -/// A factory protocol for creating writable tunnel repositories. -/// -/// This protocol extends the `TunnelRepositoryFactory` protocol -/// and requires the associated `TunnelRepositoryType` -/// to conform to the `WritableTunnelRepository` protocol. -/// -/// - Note: This protocol is part of the `Sublimation` framework. -/// -/// - SeeAlso: `TunnelRepositoryFactory` -/// - SeeAlso: `WritableTunnelRepository` -public protocol WritableTunnelRepositoryFactory: TunnelRepositoryFactory - where TunnelRepositoryType: WritableTunnelRepository {} diff --git a/Sources/SublimationVapor/ClientError.swift b/Sources/SublimationVapor/ClientError.swift deleted file mode 100644 index 54f0db6..0000000 --- a/Sources/SublimationVapor/ClientError.swift +++ /dev/null @@ -1,47 +0,0 @@ -// -// ClientError.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import AsyncHTTPClient -import OpenAPIRuntime - -extension ClientError { - internal var isConnectionRefused: Bool { - #if canImport(Network) - if let posixError = self.underlyingError as? HTTPClient.NWPOSIXError { - return posixError.errorCode == .ECONNREFUSED - } - #endif - - if let clientError = self.underlyingError as? HTTPClientError { - return clientError == .connectTimeout - } - - return false - } -} diff --git a/Sources/SublimationVapor/KVdbTunnelClient.swift b/Sources/SublimationVapor/KVdbTunnelClient.swift deleted file mode 100644 index 84fbeac..0000000 --- a/Sources/SublimationVapor/KVdbTunnelClient.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// KVdbTunnelClient.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation -import SublimationKVdb -import SublimationTunnel - -extension KVdbTunnelClient: TunnelClient {} diff --git a/Sources/SublimationVapor/Sublimation+Ngrok.swift b/Sources/SublimationVapor/Sublimation+Ngrok.swift deleted file mode 100644 index 1ae83d6..0000000 --- a/Sources/SublimationVapor/Sublimation+Ngrok.swift +++ /dev/null @@ -1,90 +0,0 @@ -// -// Sublimation+Ngrok.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Ngrokit -import NIOCore -import OpenAPIAsyncHTTPClient -import OpenAPIRuntime -import Sublimation -import SublimationTunnel -import Vapor - -#if os(macOS) - extension Sublimation { - /// Initializes the Sublimation lifecycle handler using Ngrok with default values for macOS. - /// - /// - Parameters: - /// - ngrokPath: The path to the Ngrok executable. - /// - bucketName: The name of the bucket for the tunnel repository. - /// - key: The key for the tunnel repository. - /// - /// - Note: This initializer is only available on macOS. - /// - /// - SeeAlso: `KVdbTunnelRepositoryFactory` - /// - SeeAlso: `NgrokCLIAPIServerFactory` - public convenience init( - ngrokPath: String, - bucketName: String, - key: some Sendable, - isConnectionRefused: @escaping @Sendable (ClientError) -> Bool, - ngrokClient: @escaping @Sendable () -> NgrokClient - ) { - self.init( - sublimatory: TunnelSublimatory( - ngrokPath: ngrokPath, - bucketName: bucketName, - key: key, - isConnectionRefused: isConnectionRefused, - ngrokClient: ngrokClient - ) - ) - } - - /// Description Initializes the Sublimation lifecycle handler using Ngrok with default values for macOS. - /// - Parameters: - /// - ngrokPath: The path to the Ngrok executable. - /// - bucketName: The name of the bucket for the tunnel repository. - /// - key: The key for the tunnel repository. - /// - timeout: The amount of to wait for the ngrok to respond. - public convenience init( - ngrokPath: String, - bucketName: String, - key: some Sendable, - timeout: TimeAmount = .seconds(1) - ) { - let tunnelSublimatory = TunnelSublimatory( - ngrokPath: ngrokPath, - bucketName: bucketName, - key: key, - timeout: timeout - ) - self.init(sublimatory: tunnelSublimatory) - } - } -#endif diff --git a/Sources/SublimationVapor/Sublimation.swift b/Sources/SublimationVapor/Sublimation.swift deleted file mode 100644 index 62a63e3..0000000 --- a/Sources/SublimationVapor/Sublimation.swift +++ /dev/null @@ -1,54 +0,0 @@ -// -// Sublimation.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import NIOCore -import OpenAPIAsyncHTTPClient -import OpenAPIRuntime -import Sublimation -import Vapor - -extension Sublimation: LifecycleHandler { - public func willBoot(_ application: Vapor.Application) throws { - Task { - self.willBoot { application } - } - } - - public func didBoot(_ application: Vapor.Application) throws { - Task { - self.didBoot { application } - } - } - - public func shutdown(_ application: Vapor.Application) { - Task { - self.shutdown { application } - } - } -} diff --git a/Sources/SublimationVapor/TunnelSublimatory.swift b/Sources/SublimationVapor/TunnelSublimatory.swift deleted file mode 100644 index b2d2da5..0000000 --- a/Sources/SublimationVapor/TunnelSublimatory.swift +++ /dev/null @@ -1,111 +0,0 @@ -// -// TunnelSublimatory.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation -import Ngrokit -import NIOCore -import OpenAPIAsyncHTTPClient -import OpenAPIRuntime -import SublimationKVdb -import SublimationNgrok -import SublimationTunnel - -#if os(macOS) - extension TunnelSublimatory { - /// Initializes the Sublimation lifecycle handler with default values for macOS. - /// - /// - Parameters: - /// - ngrokPath: The path to the Ngrok executable. - /// - bucketName: The name of the bucket for the tunnel repository. - /// - key: The key for the tunnel repository. - /// - /// - Note: This initializer is only available on macOS. - /// - /// - SeeAlso: `KVdbTunnelRepositoryFactory` - /// - SeeAlso: `NgrokCLIAPIServerFactory` - public init( - ngrokPath: String, - bucketName: String, - key: Key, - isConnectionRefused: @escaping @Sendable (ClientError) -> Bool, - ngrokClient: @escaping @Sendable () -> NgrokClient - ) where WritableTunnelRepositoryFactoryType == TunnelBucketRepositoryFactory, - TunnelServerFactoryType == NgrokCLIAPIServerFactory, - WritableTunnelRepositoryFactoryType.TunnelRepositoryType.Key == Key { - self.init( - factory: NgrokCLIAPIServerFactory(ngrokPath: ngrokPath, ngrokClient: ngrokClient), - repoFactory: TunnelBucketRepositoryFactory(bucketName: bucketName), - key: key, - repoClientFactory: { application in - KVdbTunnelClient( - keyType: WritableTunnelRepositoryFactoryType.TunnelRepositoryType.Key.self, - get: { - try await application().get(from: $0) - }, post: { - try await application().post(to: $0, body: $1) - } - ) - }, - isConnectionRefused: isConnectionRefused - ) - } - - /// Initializes the Sublimation lifecycle handler with default values for macOS. - /// - /// - Parameters: - /// - ngrokPath: The path to the Ngrok executable. - /// - bucketName: The name of the bucket for the tunnel repository. - /// - key: The key for the tunnel repository. - /// - /// - Note: This initializer is only available on macOS. - /// - /// - SeeAlso: `KVdbTunnelRepositoryFactory` - /// - SeeAlso: `NgrokCLIAPIServerFactory` - public init( - ngrokPath: String, - bucketName: String, - key: Key, - timeout: TimeAmount = .seconds(1) - ) where TunnelServerFactoryType == NgrokCLIAPIServerFactory, - WritableTunnelRepositoryFactoryType == TunnelBucketRepositoryFactory { - self.init( - ngrokPath: ngrokPath, - bucketName: bucketName, - key: key, - isConnectionRefused: { $0.isConnectionRefused }, - ngrokClient: { - NgrokClient( - transport: AsyncHTTPClientTransport(configuration: .init(timeout: timeout)) - ) - } - ) - } - } - -#endif diff --git a/Sources/SublimationVapor/Vapor.Application.swift b/Sources/SublimationVapor/Vapor.Application.swift deleted file mode 100644 index e16f898..0000000 --- a/Sources/SublimationVapor/Vapor.Application.swift +++ /dev/null @@ -1,52 +0,0 @@ -// -// Vapor.Application.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import protocol SublimationCore.Application -import Vapor - -extension Vapor.Application: Application { - public var httpServerConfigurationPort: Int { - self.http.server.configuration.port - } - - public var httpServerTLS: Bool { - self.http.server.configuration.tlsConfiguration != nil - } - - public func post(to url: URL, body: Data?) async throws { - _ = try await client.post(.init(string: url.absoluteString)) { request in - request.body = body.map(ByteBuffer.init(data:)) - } - } - - public func get(from url: URL) async throws -> Data? { - let response = try await client.get(.init(string: url.absoluteString)) - return response.body.map { Data(buffer: $0) } - } -} diff --git a/Tests/NgrokitTests/DataHandleTests.swift b/Tests/NgrokitTests/DataHandleTests.swift deleted file mode 100644 index fef2105..0000000 --- a/Tests/NgrokitTests/DataHandleTests.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// DataHandleTests.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -@testable import Ngrokit -import NgrokitMocks -import XCTest - -internal class DataHandleTests: XCTestCase { - internal func testParseNgrokErrorCode() throws { - let dataHandle = MockDataHandle.withNgrokCode() - let error = try dataHandle.parseNgrokErrorCode() - XCTAssertEqual(error.rawValue, 108) - } -} diff --git a/Tests/NgrokitTests/NgrokClientTests.swift b/Tests/NgrokitTests/NgrokClientTests.swift deleted file mode 100644 index 3ce7c66..0000000 --- a/Tests/NgrokitTests/NgrokClientTests.swift +++ /dev/null @@ -1,130 +0,0 @@ -// -// NgrokClientTests.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -@testable import Ngrokit -import NgrokitMocks -import NgrokOpenAPIClient -import XCTest - -#if canImport(FoundationNetworking) - import FoundationNetworking -#endif - -internal class NgrokClientTests: XCTestCase { - private func assertTunnelEqual( - _ actualOutput: NgrokTunnel, - _ expectedOutput: Components.Schemas.TunnelResponse - ) { - XCTAssertEqual(actualOutput.publicURL.absoluteString, expectedOutput.public_url) - XCTAssertEqual(actualOutput.name, expectedOutput.name) - XCTAssertEqual(actualOutput.config.addr.absoluteString, expectedOutput.config.addr) - XCTAssertEqual(actualOutput.config.inspect, expectedOutput.config.inspect) - } - - // swiftlint:disable:next function_body_length - internal func testStartTunnel() async throws { - let publicURL = URL.temporaryDirectory() - let expectedInput = TunnelRequest( - port: .random(in: 10 ... 100), - name: UUID().uuidString, - proto: UUID().uuidString - ) - let expectedOutput: Components.Schemas.TunnelResponse = - .init( - name: UUID().uuidString, - public_url: publicURL.absoluteString, - config: .init(addr: UUID().uuidString, inspect: .random()) - ) - let request = Operations.startTunnel.Output.created( - .init(body: .json(expectedOutput)) - ) - let api = MockAPI(actualStartTunnelResult: .success(request)) - let client = NgrokClient(underlyingClient: api) - let actualOutput = try await client.startTunnel(expectedInput) - - assertTunnelEqual(actualOutput, expectedOutput) - - let body = await api.startTunnelPassed.last?.body - - guard case let .json(actualInput) = body else { - XCTFail("Incorrect result \(String(describing: body))") - return - } - - XCTAssertEqual(actualInput.name, expectedInput.name) - XCTAssertEqual(actualInput.addr, expectedInput.addr) - XCTAssertEqual(actualInput.proto, expectedInput.proto) - } - - internal func testStopTunnel() async throws { - let expectedInput = UUID().uuidString - let api = MockAPI(actualStopTunnelResult: .success(.noContent(.init()))) - let client = NgrokClient(underlyingClient: api) - - try await client.stopTunnel(withName: expectedInput) - - let name = await api.stopTunnelPassed.last?.path.name - - guard let actualInput = name else { - XCTFail("Incorrect name \(String(describing: name))") - return - } - - XCTAssertEqual(actualInput, expectedInput) - } - - internal func testListTunnel() async throws { - let expectedTunnels: [Components.Schemas.TunnelResponse] = [ - .init( - name: UUID().uuidString, - public_url: URL.temporaryDirectory().absoluteString, - config: .init(addr: UUID().uuidString, inspect: .random()) - ), - .init( - name: UUID().uuidString, - public_url: URL.temporaryDirectory().absoluteString, - config: .init(addr: UUID().uuidString, inspect: .random()) - ), - .init( - name: UUID().uuidString, - public_url: URL.temporaryDirectory().absoluteString, - config: .init(addr: UUID().uuidString, inspect: .random()) - ) - ] - let api = MockAPI( - actualListTunnelResult: .success( - .ok(.init(body: .json(.init(tunnels: expectedTunnels)))) - ) - ) - let client = NgrokClient(underlyingClient: api) - let actualOutput = try await client.listTunnels() - - zip(actualOutput, expectedTunnels).forEach(assertTunnelEqual) - } -} diff --git a/Tests/NgrokitTests/NgrokErrorTests.swift b/Tests/NgrokitTests/NgrokErrorTests.swift deleted file mode 100644 index 4673c88..0000000 --- a/Tests/NgrokitTests/NgrokErrorTests.swift +++ /dev/null @@ -1,95 +0,0 @@ -// -// NgrokErrorTests.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Ngrokit -import XCTest - -internal class NgrokErrorTests: XCTestCase { - // swiftlint:disable line_length - internal func testErrorDescriptions() { - XCTAssertEqual(NgrokError.invalidMetadataLength.errorDescription, "Invalid metadata length") - XCTAssertEqual(NgrokError.accountLimitExceeded.errorDescription, "You've hit your account limit for simultaneous ngrok agent sessions. Try stopping an existing agent or upgrading your account.") - XCTAssertEqual(NgrokError.unsupportedAgentVersion.errorDescription, "Your ngrok agent version is no longer supported. Only the most recent version of the ngrok agent is supported without an account. Update to a newer version with ngrok update or by downloading from https://ngrok.com/download. Sign up for an account to avoid forced version upgrades: https://ngrok.com/signup.") - XCTAssertEqual(NgrokError.captchaFailed.errorDescription, "You failed to solve the captcha, please try again.") - XCTAssertEqual(NgrokError.accountViolation.errorDescription, "You are disallowed from creating an ngrok account due to violation of the terms of service.") - XCTAssertEqual(NgrokError.gatewayError.errorDescription, "Ngrok gateway error. The server returned an invalid or incomplete HTTP response. Try starting ngrok with the full upstream service URL (e.g. ngrok http https://localhost:8081)") - XCTAssertEqual(NgrokError.tunnelNotFound.errorDescription, "Tunnel not found. This could be because your agent is not online or your tunnel has been flagged by our automated moderation system.") - XCTAssertEqual(NgrokError.accountBanned.errorDescription, "The account associated with this hostname has been banned. We've determined this account to be in violation of ngrok's terms of service. If you are the account owner and believe this is a mistake, please contact support@ngrok.com.") - XCTAssertEqual(NgrokError.passwordTooShort.errorDescription, "Your password must be at least 10 characters.") - XCTAssertEqual(NgrokError.accountCreationNotAllowed.errorDescription, "You may not create a new account because you are already a member of a free account. Upgrade or delete that account first before creating a new account.") - XCTAssertEqual(NgrokError.invalidCredentials.errorDescription, "The email or password you entered is not valid.") - XCTAssertEqual(NgrokError.userAlreadyExists.errorDescription, "A user with the email address already exists.") - XCTAssertEqual(NgrokError.disallowedEmailProvider.errorDescription, "Sign-ups are disallowed for the email provider. Please sign up with a different email provider.") - XCTAssertEqual(NgrokError.htmlContentSignupRequired.errorDescription, "Before you can serve HTML content, you must sign up for an ngrok account and install your authtoken.") - XCTAssertEqual(NgrokError.websiteVisitWarning.errorDescription, "You are about to visit HOSTPORT, served by SERVINGIP. This website is served for free through ngrok.com. You should only visit this website if you trust whoever sent the link to you.") - XCTAssertEqual(NgrokError.tunnelConnectionFailed.errorDescription, "Traffic was successfully tunneled to the ngrok agent, but the agent failed to establish a connection to the upstream web service") - } - - // swiftlint:disable nslocalizedstring_require_bundle - internal func testLocalizedDescriptions() { - XCTAssertEqual(NgrokError.invalidMetadataLength.localizedDescription, NSLocalizedString("Invalid metadata length", comment: "")) - XCTAssertEqual(NgrokError.accountLimitExceeded.localizedDescription, NSLocalizedString("You've hit your account limit for simultaneous ngrok agent sessions. Try stopping an existing agent or upgrading your account.", comment: "")) - XCTAssertEqual(NgrokError.unsupportedAgentVersion.localizedDescription, NSLocalizedString("Your ngrok agent version is no longer supported. Only the most recent version of the ngrok agent is supported without an account. Update to a newer version with ngrok update or by downloading from https://ngrok.com/download. Sign up for an account to avoid forced version upgrades: https://ngrok.com/signup.", comment: "")) - XCTAssertEqual(NgrokError.captchaFailed.localizedDescription, NSLocalizedString("You failed to solve the captcha, please try again.", comment: "")) - XCTAssertEqual(NgrokError.accountViolation.localizedDescription, NSLocalizedString("You are disallowed from creating an ngrok account due to violation of the terms of service.", comment: "")) - XCTAssertEqual(NgrokError.gatewayError.localizedDescription, NSLocalizedString("Ngrok gateway error. The server returned an invalid or incomplete HTTP response. Try starting ngrok with the full upstream service URL (e.g. ngrok http https://localhost:8081)", comment: "")) - XCTAssertEqual(NgrokError.tunnelNotFound.localizedDescription, NSLocalizedString("Tunnel not found. This could be because your agent is not online or your tunnel has been flagged by our automated moderation system.", comment: "")) - XCTAssertEqual(NgrokError.accountBanned.localizedDescription, NSLocalizedString("The account associated with this hostname has been banned. We've determined this account to be in violation of ngrok's terms of service. If you are the account owner and believe this is a mistake, please contact support@ngrok.com.", comment: "")) - XCTAssertEqual(NgrokError.passwordTooShort.localizedDescription, NSLocalizedString("Your password must be at least 10 characters.", comment: "")) - XCTAssertEqual(NgrokError.accountCreationNotAllowed.localizedDescription, NSLocalizedString("You may not create a new account because you are already a member of a free account. Upgrade or delete that account first before creating a new account.", comment: "")) - XCTAssertEqual(NgrokError.invalidCredentials.localizedDescription, NSLocalizedString("The email or password you entered is not valid.", comment: "")) - XCTAssertEqual(NgrokError.userAlreadyExists.localizedDescription, NSLocalizedString("A user with the email address already exists.", comment: "")) - XCTAssertEqual(NgrokError.disallowedEmailProvider.localizedDescription, NSLocalizedString("Sign-ups are disallowed for the email provider. Please sign up with a different email provider.", comment: "")) - XCTAssertEqual(NgrokError.htmlContentSignupRequired.localizedDescription, NSLocalizedString("Before you can serve HTML content, you must sign up for an ngrok account and install your authtoken.", comment: "")) - XCTAssertEqual(NgrokError.websiteVisitWarning.localizedDescription, NSLocalizedString("You are about to visit HOSTPORT, served by SERVINGIP. This website is served for free through ngrok.com. You should only visit this website if you trust whoever sent the link to you.", comment: "")) - XCTAssertEqual(NgrokError.tunnelConnectionFailed.localizedDescription, NSLocalizedString("Traffic was successfully tunneled to the ngrok agent, but the agent failed to establish a connection to the upstream web service", comment: "")) - } - - // swiftlint:enable nslocalizedstring_require_bundle - // swiftlint:enable line_length - - internal func testRawValues() { - XCTAssertEqual(NgrokError.invalidMetadataLength.rawValue, 100) - XCTAssertEqual(NgrokError.accountLimitExceeded.rawValue, 108) - XCTAssertEqual(NgrokError.unsupportedAgentVersion.rawValue, 120) - XCTAssertEqual(NgrokError.captchaFailed.rawValue, 1_205) - XCTAssertEqual(NgrokError.accountViolation.rawValue, 1_226) - XCTAssertEqual(NgrokError.gatewayError.rawValue, 3_004) - XCTAssertEqual(NgrokError.tunnelNotFound.rawValue, 3_200) - XCTAssertEqual(NgrokError.accountBanned.rawValue, 3_208) - XCTAssertEqual(NgrokError.passwordTooShort.rawValue, 4_011) - XCTAssertEqual(NgrokError.accountCreationNotAllowed.rawValue, 4_013) - XCTAssertEqual(NgrokError.invalidCredentials.rawValue, 4_100) - XCTAssertEqual(NgrokError.userAlreadyExists.rawValue, 4_101) - XCTAssertEqual(NgrokError.disallowedEmailProvider.rawValue, 4_108) - XCTAssertEqual(NgrokError.htmlContentSignupRequired.rawValue, 6_022) - XCTAssertEqual(NgrokError.websiteVisitWarning.rawValue, 6_024) - XCTAssertEqual(NgrokError.tunnelConnectionFailed.rawValue, 8_012) - } -} diff --git a/Tests/NgrokitTests/NgrokMacProcessTests.swift b/Tests/NgrokitTests/NgrokMacProcessTests.swift deleted file mode 100644 index bf28497..0000000 --- a/Tests/NgrokitTests/NgrokMacProcessTests.swift +++ /dev/null @@ -1,91 +0,0 @@ -// -// NgrokMacProcessTests.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -// -// NgrokMacProcessTests.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// -@testable import Ngrokit -import NgrokitMocks -import XCTest - -internal class NgrokMacProcessTests: XCTestCase { - internal func testInit() async { - let ngrokPath = UUID().uuidString - let httpPort = Int.random(in: 10 ... 10_000) - let process = NgrokMacProcess( - ngrokPath: ngrokPath, - httpPort: httpPort, - processType: MockProcess.self - ) - let actualProcess = await process.process - XCTAssertEqual(actualProcess.executableFilePath, ngrokPath) - XCTAssertEqual(actualProcess.port, httpPort) - XCTAssertEqual(actualProcess.scheme, "http") - } - - internal func testRunOnError() async throws { - let ngrokPath = UUID().uuidString - let httpPort = Int.random(in: 10 ... 10_000) - let process = NgrokMacProcess( - ngrokPath: ngrokPath, - httpPort: httpPort, - processType: MockProcess.self - ) - try await process.run { _ in } - - let actualProcess = await process.process - XCTAssertTrue(actualProcess.isRunCalled) - XCTAssertTrue(actualProcess.isTerminationHandlerSet) - } -} diff --git a/Tests/NgrokitTests/NgrokProcessCLIAPITests.swift b/Tests/NgrokitTests/NgrokProcessCLIAPITests.swift deleted file mode 100644 index ba23309..0000000 --- a/Tests/NgrokitTests/NgrokProcessCLIAPITests.swift +++ /dev/null @@ -1,53 +0,0 @@ -// -// NgrokProcessCLIAPITests.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -@testable import Ngrokit -import NgrokitMocks -import XCTest - -internal class NgrokProcessCLIAPITests: XCTestCase { - internal func testProcess() async throws { - let ngrokPath = UUID().uuidString - let httpPort = Int.random(in: 10 ... 10_000) - let api = NgrokProcessCLIAPI(ngrokPath: ngrokPath) - let process = api.process(forHTTPPort: httpPort) - - let macProcess = process as? NgrokMacProcess - - XCTAssertNotNil(macProcess) - - let mockProcess = await macProcess?.process - - XCTAssertNotNil(mockProcess) - - XCTAssertEqual(mockProcess?.executableFilePath, ngrokPath) - XCTAssertEqual(mockProcess?.port, httpPort) - XCTAssertEqual(mockProcess?.scheme, "http") - } -} diff --git a/Tests/SublimationBonjourTests/MockTXTRecord.swift b/Tests/SublimationBonjourTests/MockTXTRecord.swift deleted file mode 100644 index d13a284..0000000 --- a/Tests/SublimationBonjourTests/MockTXTRecord.swift +++ /dev/null @@ -1,108 +0,0 @@ -// -// MockTXTRecord.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation -@testable import SublimationBonjour -import XCTest - -internal struct MockTXTRecord: TXTRecord { - func getKeys() -> any Sequence { - dictionary.keys - } - - internal let dictionary: [String: String] - - internal var count: Int { - dictionary.count - } - - internal init(_ dictionary: [String: String]) { - self.dictionary = dictionary - } - - internal func getStringEntry(for key: String) -> String? { - dictionary[key] - } -} - -private let ipAddresses: [String] = [ - "192.168.0.1", "10.0.0.1", "172.16.254.1", "8.8.8.8", - "2001:0db8:85a3:0000:0000:8a2e:0370:7334", "fe80::1ff:fe23:4567:890a", - "2001:0db8:0000:0042:0000:8a2e:0370:7334", "::ffff:192.168.1.1", - "192.168.0.2", "10.0.0.2", "172.16.254.2", "8.8.4.4", - "2001:0db8:85a3:0000:0000:8a2e:0370:7335", "fe80::1ff:fe23:4567:890b", - "2001:0db8:0000:0042:0000:8a2e:0370:7335", "::ffff:192.168.1.2", - "192.168.0.3", "10.0.0.3", "172.16.254.3", "1.1.1.1", - "2001:0db8:85a3:0000:0000:8a2e:0370:7336", "fe80::1ff:fe23:4567:890c", - "2001:0db8:0000:0042:0000:8a2e:0370:7336", "::ffff:192.168.1.3", - "192.168.0.4", "10.0.0.4", "172.16.254.4", "9.9.9.9", - "2001:0db8:85a3:0000:0000:8a2e:0370:7337", "fe80::1ff:fe23:4567:890d", - "2001:0db8:0000:0042:0000:8a2e:0370:7337", "::ffff:192.168.1.4", - "192.168.0.5", "10.0.0.5", "172.16.254.5", "1.0.0.1", - "2001:0db8:85a3:0000:0000:8a2e:0370:7338", "fe80::1ff:fe23:4567:890e", - "2001:0db8:0000:0042:0000:8a2e:0370:7338", "::ffff:192.168.1.5", - "192.168.0.6", "10.0.0.6", "172.16.254.6", "4.2.2.2", - "2001:0db8:85a3:0000:0000:8a2e:0370:7339", "fe80::1ff:fe23:4567:890f", - "2001:0db8:0000:0042:0000:8a2e:0370:7339", "::ffff:192.168.1.6", - "192.168.0.7", "10.0.0.7", "172.16.254.7", "4.2.2.1", - "2001:0db8:85a3:0000:0000:8a2e:0370:7340", "fe80::1ff:fe23:4567:8910", - "2001:0db8:0000:0042:0000:8a2e:0370:7340", "::ffff:192.168.1.7", - "192.168.0.8", "10.0.0.8", "172.16.254.8", "208.67.222.222", - "2001:0db8:85a3:0000:0000:8a2e:0370:7341", "fe80::1ff:fe23:4567:8911", - "2001:0db8:0000:0042:0000:8a2e:0370:7341", "::ffff:192.168.1.8", - "192.168.0.9", "10.0.0.9", "172.16.254.9", "208.67.220.220", - "2001:0db8:85a3:0000:0000:8a2e:0370:7342", "fe80::1ff:fe23:4567:8912", - "2001:0db8:0000:0042:0000:8a2e:0370:7342", "::ffff:192.168.1.9", - "192.168.0.10", "10.0.0.10", "172.16.254.10", "198.51.100.1", - "2001:0db8:85a3:0000:0000:8a2e:0370:7343", "fe80::1ff:fe23:4567:8913", - "2001:0db8:0000:0042:0000:8a2e:0370:7343", "::ffff:192.168.1.10", - "192.168.0.11", "10.0.0.11", "172.16.254.11", "203.0.113.1", - "2001:0db8:85a3:0000:0000:8a2e:0370:7344", "fe80::1ff:fe23:4567:8914", - "2001:0db8:0000:0042:0000:8a2e:0370:7344", "::ffff:192.168.1.11", - "192.168.0.12", "10.0.0.12", "172.16.254.12", "192.0.2.1", - "2001:0db8:85a3:0000:0000:8a2e:0370:7345", "fe80::1ff:fe23:4567:8915", - "2001:0db8:0000:0042:0000:8a2e:0370:7345", "::ffff:192.168.1.12", - "192.168.0.13", "10.0.0.13", "172.16.254.13", "198.51.100.2", - "2001:0db8:85a3:0000:0000:8a2e:0370:7346", "fe80::1ff:fe23:4567:8916", - "2001:0db8:0000:0042:0000:8a2e:0370:7346", "::ffff:192.168.1.13", - "192.168.0.14", "10.0.0.14", "172.16.254.14", "203.0.113.2", - "2001:0db8:85a3:0000:0000:8a2e:0370:7347", "fe80::1ff:fe23:4567:8917", - "2001:0db8:0000:0042:0000:8a2e:0370:7347", "::ffff:192.168.1.14", - "192.168.0.15", "10.0.0.15", "172.16.254.15", "192.0.2.2", - "2001:0db8:85a3:0000:0000:8a2e:0370:7348", "fe80::1ff:fe23:4567:8918", - "2001:0db8:0000:0042:0000:8a2e:0370:7348", "::ffff:192.168.1.15" -] - -extension Array where Element == String { - internal static func randomIpAddresses(maxLength: Int) -> Self { - .init( - ipAddresses.shuffled().prefix(maxLength) - ) - } -} diff --git a/Tests/SublimationBonjourTests/TXTRecordTests.swift b/Tests/SublimationBonjourTests/TXTRecordTests.swift deleted file mode 100644 index 1f94432..0000000 --- a/Tests/SublimationBonjourTests/TXTRecordTests.swift +++ /dev/null @@ -1,120 +0,0 @@ -// -// TXTRecordTests.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Foundation -@testable import SublimationBonjour -import XCTest - -internal class TXTRecordTests: XCTestCase { - internal func testBadRecord() { - let record = MockTXTRecord(["Serial Number": UUID().uuidString]) - let urls = record.urls(defaultPort: 0, defaultTLS: false, logger: nil) - - XCTAssert(urls.isEmpty) - } - - internal func testInit() { - let expectation = expectation(description: "Filter") - - let expectedIsTLS: Bool = .random() - let expectedPort: Int = .random(in: 1_000 ... 9_000) - let expectedAddresses: [String] = .randomIpAddresses(maxLength: 5) - - expectation.expectedFulfillmentCount = expectedAddresses.count - - let record = MockTXTRecord( - isTLS: expectedIsTLS, - port: expectedPort, - maximumCount: nil, - addresses: expectedAddresses, - filter: { _ in - expectation.fulfill() - return true - } - ) - - XCTAssertEqual(record.dictionary[SublimationKey.port.stringValue], expectedPort.description) - XCTAssertEqual(record.dictionary[SublimationKey.tls.stringValue], expectedIsTLS.description) - - for (index, expectedAddress) in expectedAddresses.enumerated() { - XCTAssertEqual(record.dictionary[SublimationKey.address(index).stringValue], expectedAddress) - } - - XCTAssertEqual(record.count, expectedAddresses.count + 2) - - wait(for: [expectation], timeout: 1.0) - } - - internal func testGetEntry() { - let expectedIsTLS: Bool = .random() - let expectedPort: Int = .random(in: 1_000 ... 9_000) - let expectedAddresses: [String] = .randomIpAddresses(maxLength: 5) - - let record = MockTXTRecord( - isTLS: expectedIsTLS, - port: expectedPort, - maximumCount: nil, - addresses: expectedAddresses, - filter: { _ in - true - } - ) - - XCTAssertEqual(record.getEntry(for: .port).value, expectedPort) - XCTAssertEqual(record.getEntry(for: .tls).value, expectedIsTLS) - - for (index, expectedAddress) in expectedAddresses.enumerated() { - XCTAssertEqual(record.getEntry(for: .address(index)).value, expectedAddress) - } - } - - internal func testURLs() { - let expectedIsTLS: Bool = .random() - let expectedPort: Int = .random(in: 1_000 ... 9_000) - let expectedAddresses: [String] = .randomIpAddresses(maxLength: 5) - - let record = MockTXTRecord( - isTLS: expectedIsTLS, - port: expectedPort, - maximumCount: nil, - addresses: expectedAddresses, - filter: String.isIPv4NotLocalhost(_:) - ) - - let urls = record.urls(defaultPort: 0, defaultTLS: !expectedIsTLS, logger: nil) - let expectedURLs = expectedAddresses.compactMap { host -> URL? in - guard String.isIPv4NotLocalhost(host) else { - return nil - } - return URL(scheme: expectedIsTLS ? "https" : "http", host: host, port: expectedPort) - } - - XCTAssertEqual(urls, expectedURLs) - } -} diff --git a/Tests/SublimationKVdbTests/KVdbTests.swift b/Tests/SublimationKVdbTests/KVdbTests.swift deleted file mode 100644 index 0ace176..0000000 --- a/Tests/SublimationKVdbTests/KVdbTests.swift +++ /dev/null @@ -1,53 +0,0 @@ -// -// KVdbTests.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import SublimationKVdb -import SublimationMocks -import XCTest - -internal class KVdbTests: XCTestCase { - internal func testPath() { - let key = UUID() - let bucket = UUID().uuidString - let actual = KVdb.path(forKey: key, atBucket: bucket) - XCTAssertEqual(actual, "/\(bucket)/\(key)") - } - - internal func testConstruct() { - let key = UUID() - let bucket = UUID().uuidString - let url = KVdb.construct(MockURL.self, forKey: key, atBucket: bucket) - let expectedPath = KVdb.path(forKey: key, atBucket: bucket) - - XCTAssertEqual(url.kvDBBase, KVdb.baseString) - XCTAssertEqual(url.keyBucketPath, expectedPath) - } -} - -extension MockURL: KVdbURLConstructable {} diff --git a/Tests/SublimationKVdbTests/OptionalTests.swift b/Tests/SublimationKVdbTests/OptionalTests.swift deleted file mode 100644 index 8272e08..0000000 --- a/Tests/SublimationKVdbTests/OptionalTests.swift +++ /dev/null @@ -1,47 +0,0 @@ -// -// OptionalTests.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -@testable import SublimationKVdb -import XCTest - -internal class OptionalTests: XCTestCase { - internal func testFlatTuple() { - let nilValue: Int? = nil - let notNilValue: Int? = 12 - let expectedNotNil = (12, 12) - - XCTAssertNil(nilValue.flatTuple(notNilValue)) - XCTAssertNil(nilValue.flatTuple(nilValue)) - XCTAssertNil(notNilValue.flatTuple(nilValue)) - - let actualNotNil = notNilValue.flatTuple(notNilValue) - XCTAssertEqual(actualNotNil?.0, expectedNotNil.0) - XCTAssertEqual(actualNotNil?.1, expectedNotNil.1) - } -} diff --git a/Tests/SublimationKVdbTests/ResultTests.swift b/Tests/SublimationKVdbTests/ResultTests.swift deleted file mode 100644 index 819983c..0000000 --- a/Tests/SublimationKVdbTests/ResultTests.swift +++ /dev/null @@ -1,49 +0,0 @@ -// -// ResultTests.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -@testable import SublimationKVdb -import SublimationMocks -import XCTest - -internal class ResultTests: XCTestCase { - internal typealias MockResult = Result - internal func testInit() { - let successValue = UUID() - let errorValue = UUID() - - let successResult = MockResult(success: successValue, failure: nil) - let failedResult = MockResult(success: UUID(), failure: MockError.value(errorValue)) - let emptyResult = MockResult(success: nil, failure: nil) - - let actualErrorValue: UUID? = failedResult.mockErrorValue() - try XCTAssertEqual(successResult.get(), successValue) - XCTAssertEqual(actualErrorValue, errorValue) - XCTAssert(emptyResult.error is Result.EmptyError) - } -} diff --git a/Tests/SublimationKVdbTests/URLTests.swift b/Tests/SublimationKVdbTests/URLTests.swift deleted file mode 100644 index 312f609..0000000 --- a/Tests/SublimationKVdbTests/URLTests.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// URLTests.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import XCTest - -internal class URLTests: XCTestCase { - internal func testKVdbURLConstructable() { - let base = "http://www.apple.com" - let keyBucketPath = UUID().uuidString - let url = URL(kvDBBase: base, keyBucketPath: keyBucketPath) - XCTAssertEqual(url.absoluteString, "\(base)/\(keyBucketPath)") - } -} diff --git a/Tests/SublimationTests/SublimationTests.swift b/Tests/SublimationTests/SublimationTests.swift new file mode 100644 index 0000000..c925dc7 --- /dev/null +++ b/Tests/SublimationTests/SublimationTests.swift @@ -0,0 +1,35 @@ +// +// SublimationTests.swift +// Sublimation +// +// Created by Leo Dion on 7/30/24. +// + +import XCTest + +final class SublimationTests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + // Any test you write for XCTest can be annotated as throws and async. + // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. + // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. + } + + func testPerformanceExample() throws { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/Tests/SublimationTunnelTests/KVdbTunnelRepositoryFactoryTests.swift b/Tests/SublimationTunnelTests/KVdbTunnelRepositoryFactoryTests.swift deleted file mode 100644 index e693383..0000000 --- a/Tests/SublimationTunnelTests/KVdbTunnelRepositoryFactoryTests.swift +++ /dev/null @@ -1,69 +0,0 @@ -// -// KVdbTunnelRepositoryFactoryTests.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import SublimationMocks -import SublimationTunnel -import XCTest - -#if canImport(FoundationNetworking) - import FoundationNetworking -#endif - -internal class KVdbTunnelRepositoryFactoryTests: XCTestCase { - internal func testSetupClient() async throws { - let getURLExpected: URL = .random() - let client = MockTunnelClient( - getValueResult: .success(getURLExpected), - saveValueError: nil - ) - let saveKey = UUID() - let saveURL: URL = .random() - - let getKey = UUID() - - let bucketName = UUID().uuidString - let factory = TunnelBucketRepositoryFactory(bucketName: bucketName) - - let repository = factory.setupClient(client) - - try await repository.saveURL(saveURL, withKey: saveKey) - let getURLActual = try await repository.tunnel(forKey: getKey) - - let savedValue = await client.saveValuesPassed.last - XCTAssertEqual(saveKey, savedValue?.key) - XCTAssertEqual(saveURL, savedValue?.value) - XCTAssertEqual(bucketName, savedValue?.bucketName) - - let getValue = await client.getValuesPassed.last - XCTAssertEqual(getKey, getValue?.key) - XCTAssertEqual(bucketName, getValue?.bucketName) - - XCTAssertEqual(getURLActual, getURLExpected) - } -} diff --git a/Tests/SublimationVaporTests/MockServerApplication.swift b/Tests/SublimationVaporTests/MockServerApplication.swift deleted file mode 100644 index cc92cdc..0000000 --- a/Tests/SublimationVaporTests/MockServerApplication.swift +++ /dev/null @@ -1,58 +0,0 @@ -// -// MockServerApplication.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import Logging -import SublimationCore -import XCTest - -internal class MockServerApplication: Application { - internal let httpServerConfigurationPort: Int - internal let httpServerTLS: Bool - internal let logger: Logger - - internal private(set) var postRequests = [(URL, Data?)]() - internal private(set) var getRequests = [URL]() - internal private(set) var queuedGetResponses = [Result]() - internal private(set) var queuedPostResponses = [Result]() - internal init(httpServerConfigurationPort: Int, httpServerTLS: Bool, logger: Logger) { - self.httpServerConfigurationPort = httpServerConfigurationPort - self.httpServerTLS = httpServerTLS - self.logger = logger - } - - internal func post(to url: URL, body: Data?) async throws { - postRequests.append((url, body)) - try queuedPostResponses.remove(at: 0).get() - } - - internal func get(from url: URL) async throws -> Data? { - getRequests.append(url) - return try queuedGetResponses.remove(at: 0).get() - } -} diff --git a/Tests/SublimationVaporTests/MockServerDelegate.swift b/Tests/SublimationVaporTests/MockServerDelegate.swift deleted file mode 100644 index 3e392df..0000000 --- a/Tests/SublimationVaporTests/MockServerDelegate.swift +++ /dev/null @@ -1,45 +0,0 @@ -// -// MockServerDelegate.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -@testable import SublimationTunnel -import XCTest - -internal final class MockServerDelegate: TunnelServerDelegate { - internal let id: UUID - - internal init(id: UUID) { - self.id = id - } - - internal func server( - _: any TunnelServer, updatedTunnel _: any Tunnel - ) {} - - internal func server(_: any TunnelServer, errorDidOccur _: any Error) {} -} diff --git a/Tests/SublimationVaporTests/NetworkResultTests.swift b/Tests/SublimationVaporTests/NetworkResultTests.swift deleted file mode 100644 index 92c9666..0000000 --- a/Tests/SublimationVaporTests/NetworkResultTests.swift +++ /dev/null @@ -1,217 +0,0 @@ -// -// NetworkResultTests.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import AsyncHTTPClient -import OpenAPIRuntime -@testable import SublimationTunnel -@testable import SublimationVapor -import XCTest - -internal func XCTAsyncAssert( - _ expression: @escaping () async throws -> Bool, - _ message: @autoclosure () -> String = "", - file: StaticString = #filePath, - line: UInt = #line -) async rethrows { - let expressionResult = try await expression() - XCTAssert(expressionResult, message(), file: file, line: line) -} - -internal class NetworkResultTests: XCTestCase { - // swiftlint:disable:next function_body_length - internal func testError() { - #if canImport(Network) - let posixError = HTTPClient.NWPOSIXError(.ECONNREFUSED, reason: "") - let clientPosixError = ClientError( - operationID: "", - operationInput: (), - causeDescription: "", - underlyingError: posixError - ) - let actualPosixError: HTTPClient.NWPOSIXError? = - NetworkResult(error: clientPosixError, isConnectionRefused: { $0.isConnectionRefused }).underlyingClientError() - XCTAssertEqual( - actualPosixError?.errorCode, - posixError.errorCode - ) - #endif - let timeoutError = HTTPClientError.connectTimeout - - let clientTimeoutError = ClientError( - operationID: "", - operationInput: (), - causeDescription: "", - underlyingError: timeoutError - ) - - let actualTimeoutError: HTTPClientError? = - NetworkResult(error: clientTimeoutError, isConnectionRefused: { $0.isConnectionRefused }).underlyingClientError() - XCTAssertEqual( - actualTimeoutError, - timeoutError - ) - - #if canImport(Network) - XCTAssert(NetworkResult(error: posixError, isConnectionRefused: { $0.isConnectionRefused }).isFailure) - #endif - XCTAssert(NetworkResult(error: timeoutError, isConnectionRefused: { $0.isConnectionRefused }).isFailure) - } - - // swiftlint:disable:next function_body_length - internal func testClosure() async { - #if canImport(Network) - let posixError = HTTPClient.NWPOSIXError(.ECONNREFUSED, reason: "") - let clientPosixError = ClientError( - operationID: "", - operationInput: (), - causeDescription: "", - underlyingError: posixError - ) - #endif - let timeoutError = HTTPClientError.connectTimeout - - let clientTimeoutError = ClientError( - operationID: "", - operationInput: (), - causeDescription: "", - underlyingError: timeoutError - ) - - #if canImport(Network) - let actualPosixError: HTTPClient.NWPOSIXError? = - await NetworkResult { throw clientPosixError } isConnectionRefused: { $0.isConnectionRefused }.underlyingClientError() - XCTAssertEqual( - actualPosixError?.errorCode, - posixError.errorCode - ) - #endif - - let actualTimeoutError: HTTPClientError? = - await NetworkResult { throw clientTimeoutError } isConnectionRefused: { $0.isConnectionRefused }.underlyingClientError() - XCTAssertEqual( - actualTimeoutError, - timeoutError - ) - - #if canImport(Network) - - await XCTAsyncAssert { await NetworkResult { throw posixError } isConnectionRefused: { $0.isConnectionRefused }.isFailure } - #endif - await XCTAsyncAssert { await NetworkResult { throw timeoutError } isConnectionRefused: { $0.isConnectionRefused }.isFailure } - await XCTAsyncAssert { await NetworkResult { throw timeoutError } isConnectionRefused: { $0.isConnectionRefused }.isFailure } - - await XCTAsyncAssert { await NetworkResult {} isConnectionRefused: { $0.isConnectionRefused }.isSuccess } - } - - // swiftlint:disable:next function_body_length - internal func testGet() async { - #if canImport(Network) - let posixError = HTTPClient.NWPOSIXError(.ECONNREFUSED, reason: "") - #endif - let timeoutError = HTTPClientError.connectTimeout - - #if canImport(Network) - let clientPosixError = ClientError( - operationID: "", - operationInput: (), - causeDescription: "", - underlyingError: posixError - ) - #endif - let clientTimeoutError = ClientError( - operationID: "", - operationInput: (), - causeDescription: "", - underlyingError: timeoutError - ) - let clientOtherError = ClientError( - operationID: "", - operationInput: (), - causeDescription: "", - underlyingError: URLError(.unknown) - ) - - #if canImport(Network) - do { - let value: Void? = try await NetworkResult { throw clientPosixError } isConnectionRefused: { $0.isConnectionRefused }.get() - XCTAssertNil(value) - } catch { - XCTAssertNil(error) - } - #endif - - do { - let value: Void? = try await NetworkResult { throw clientTimeoutError } isConnectionRefused: { $0.isConnectionRefused }.get() - XCTAssertNil(value) - } catch { - XCTAssertNil(error) - } - - var error: (any Error)? - do { - _ = try await NetworkResult { throw clientOtherError } isConnectionRefused: { $0.isConnectionRefused }.get() - error = nil - } catch let caughtError as ClientError { - error = caughtError - } catch { - XCTAssertNil(error) - } - XCTAssertNotNil(error) - - do { - let value: ()? = try await NetworkResult {} isConnectionRefused: { $0.isConnectionRefused }.get() - XCTAssertNotNil(value) - } catch { - XCTAssertNil(error) - } - } -} - -extension NetworkResult { - internal var isSuccess: Bool { - guard case .success = self else { - return false - } - return true - } - - internal var isFailure: Bool { - guard case .failure = self else { - return false - } - return true - } - - internal func underlyingClientError() -> Failure? where ConnectionErrorType == ClientError { - guard case let .connectionRefused(clientError) = self else { - return nil - } - return clientError.underlyingError as? Failure - } -} diff --git a/Tests/SublimationVaporTests/NgrokCLIAPIConfigurationTests.swift b/Tests/SublimationVaporTests/NgrokCLIAPIConfigurationTests.swift deleted file mode 100644 index 93fce11..0000000 --- a/Tests/SublimationVaporTests/NgrokCLIAPIConfigurationTests.swift +++ /dev/null @@ -1,45 +0,0 @@ -// -// NgrokCLIAPIConfigurationTests.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -@testable import SublimationNgrok -import XCTest - -internal class NgrokCLIAPIConfigurationTests: XCTestCase { - internal func testInit() { - let loggerLabel = UUID().uuidString - let application = MockServerApplication( - httpServerConfigurationPort: .random(in: 10 ... 10_000), - httpServerTLS: .random(), - logger: .init(label: loggerLabel) - ) - let configuration = NgrokCLIAPIConfiguration(serverApplication: application) - XCTAssertEqual(configuration.logger.label, loggerLabel) - XCTAssertEqual(configuration.port, application.httpServerConfigurationPort) - } -} diff --git a/Tests/SublimationVaporTests/NgrokCLIAPIServerFactoryTests.swift b/Tests/SublimationVaporTests/NgrokCLIAPIServerFactoryTests.swift deleted file mode 100644 index 3407c5c..0000000 --- a/Tests/SublimationVaporTests/NgrokCLIAPIServerFactoryTests.swift +++ /dev/null @@ -1,85 +0,0 @@ -// -// NgrokCLIAPIServerFactoryTests.swift -// Sublimation -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import HTTPTypes -import Ngrokit -import NgrokitMocks -import OpenAPIRuntime -@testable import SublimationNgrok -import XCTest - -internal final class MockTransport: ClientTransport { - // swiftlint:disable:next unavailable_function - internal func send( - _: HTTPTypes.HTTPRequest, - body _: OpenAPIRuntime.HTTPBody?, - baseURL _: URL, - operationID _: String - ) async throws -> (HTTPTypes.HTTPResponse, OpenAPIRuntime.HTTPBody?) { - fatalError("Not implemented") - } -} - -internal class NgrokCLIAPIServerFactoryTests: XCTestCase { - // swiftlint:disable:next function_body_length - internal func testServer() { - let loggerLabel = UUID().uuidString - let application = MockServerApplication( - httpServerConfigurationPort: .random(in: 10 ... 10_000), - httpServerTLS: .random(), - logger: .init(label: loggerLabel) - ) - let delegateID = UUID() - let processID = UUID() - let configuration = NgrokCLIAPIConfiguration(serverApplication: application) - let factory = NgrokCLIAPIServerFactory( - cliAPI: MockNgrokCLIAPI(id: processID), ngrokClient: { - NgrokClient(transport: MockTransport()) - } - ) - let server = factory.server( - from: configuration, - handler: MockServerDelegate(id: delegateID) - ) - XCTAssertEqual( - (server.delegate as? MockServerDelegate)?.id, - delegateID - ) - - XCTAssertEqual( - server.port, - application.httpServerConfigurationPort - ) - - XCTAssertEqual( - (server.process as? MockNgrokProcess)?.id, - processID - ) - } -} diff --git a/generate.sh b/generate.sh deleted file mode 100755 index c7c3d15..0000000 --- a/generate.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh - -swift run swift-openapi-generator generate \ - --output-directory Sources/NgrokOpenAPIClient \ - --config openapi-generator-config.yaml \ - openapi.yaml diff --git a/openapi-generator-config.yaml b/openapi-generator-config.yaml deleted file mode 100644 index fb9b3e3..0000000 --- a/openapi-generator-config.yaml +++ /dev/null @@ -1,4 +0,0 @@ -generate: - - types - - client -accessModifier: package diff --git a/openapi.yaml b/openapi.yaml deleted file mode 100644 index 020da0c..0000000 --- a/openapi.yaml +++ /dev/null @@ -1,215 +0,0 @@ -openapi: 3.0.0 -info: - title: Ngrok Agent API - version: 1.0.0 -servers: - - url: http://127.0.0.1:4040 - description: Default Local Server -paths: - /api: - get: - summary: Access the root API resource of a running ngrok agent - responses: - '200': - description: Successful response - /api/tunnels: - get: - summary: List Tunnels - operationId: listTunnels - responses: - '200': - description: Successful response - content: - application/json: - schema: - $ref: '#/components/schemas/TunnelList' - example: - tunnels: - - name: command_line - uri: /api/tunnels/command_line - public_url: https://d95211d2.ngrok.io - proto: https - config: - addr: localhost:80 - inspect: true - metrics: - conns: - count: 0 - gauge: 0 - rate1: 0 - rate5: 0 - rate15: 0 - p50: 0 - p90: 0 - p95: 0 - p99: 0 - http: - count: 0 - rate1: 0 - rate5: 0 - rate15: 0 - p50: 0 - p90: 0 - p95: 0 - p99: 0 - uri: /api/tunnels - post: - summary: Start tunnel - operationId: startTunnel - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/TunnelRequest' - responses: - '201': - description: Tunnel started successfully - content: - application/json: - schema: - $ref: '#/components/schemas/TunnelResponse' - /api/tunnels/{name}: - get: - summary: Tunnel detail - operationId: getTunnel - parameters: - - name: name - in: path - required: true - schema: - type: string - responses: - '200': - description: Successful response - content: - application/json: - example: - $ref: '#/components/schemas/TunnelResponse' - delete: - summary: Stop tunnel - operationId: stopTunnel - parameters: - - name: name - in: path - required: true - schema: - type: string - responses: - '204': - description: Tunnel stopped successfully - -components: - schemas: - TunnelList: - type: object - required: - - tunnels - properties: - tunnels: - type: array - items: - $ref: '#/components/schemas/TunnelResponse' - TunnelRequest: - type: object - properties: - addr: - type: string - proto: - type: string - name: - type: string - required: - - addr - - proto - - name - - TunnelResponse: - type: object - required: - - name - - public_url - - config - properties: - name: - type: string - uri: - type: string - format: uri - public_url: - type: string - format: uri - proto: - type: string - config: - type: object - properties: - addr: - type: string - inspect: - type: boolean - required: - - addr - - inspect - metrics: - type: object - properties: - conns: - type: object - properties: - count: - type: integer - gauge: - type: integer - rate1: - type: integer - rate5: - type: integer - rate15: - type: integer - p50: - type: integer - p90: - type: integer - p95: - type: integer - p99: - type: integer - required: - - count - - gauge - - rate1 - - rate5 - - rate15 - - p50 - - p90 - - p95 - - p99 - http: - type: object - properties: - count: - type: integer - rate1: - type: integer - rate5: - type: integer - rate15: - type: integer - p50: - type: integer - p90: - type: integer - p95: - type: integer - p99: - type: integer - required: - - count - - rate1 - - rate5 - - rate15 - - p50 - - p90 - - p95 - - p99 diff --git a/project.yml b/project.yml index 5ef37e7..277f32d 100644 --- a/project.yml +++ b/project.yml @@ -4,9 +4,6 @@ settings: packages: Sublimation: path: . -projectReferences: - Demo: - path: ./Demo/SublimationDemoApp.xcodeproj aggregateTargets: Lint: buildScripts: diff --git a/scripts/generate.sh b/scripts/generate.sh deleted file mode 100755 index b88e2dc..0000000 --- a/scripts/generate.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -swift run swift-openapi-generator generate --output-directory Sources/Ngrokit/Generated --config openapi-generator-config.yaml openapi.yaml diff --git a/scripts/lint.sh b/scripts/lint.sh deleted file mode 100755 index d30285e..0000000 --- a/scripts/lint.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/sh - -if [ -z "$SRCROOT" ]; then - SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) - PACKAGE_DIR="${SCRIPT_DIR}/.." -else - PACKAGE_DIR="${SRCROOT}" -fi - -if [ -z "$GITHUB_ACTION" ]; then - MINT_CMD="/opt/homebrew/bin/mint" -else - MINT_CMD="mint" -fi - -export MINT_PATH="$PACKAGE_DIR/.mint" -MINT_ARGS="-n -m $PACKAGE_DIR/Mintfile --silent" -MINT_RUN="$MINT_CMD run $MINT_ARGS" - -pushd $PACKAGE_DIR - -$MINT_CMD bootstrap -m Mintfile - -if [ "$LINT_MODE" == "NONE" ]; then - exit -elif [ "$LINT_MODE" == "STRICT" ]; then - SWIFTFORMAT_OPTIONS="" - SWIFTLINT_OPTIONS="--strict" -else - SWIFTFORMAT_OPTIONS="" - SWIFTLINT_OPTIONS="" -fi - -pushd $PACKAGE_DIR - -if [ -z "$CI" ]; then - $MINT_RUN swiftformat . - $MINT_RUN swiftlint --fix -fi - -$MINT_RUN periphery scan -$MINT_RUN swiftformat --lint $SWIFTFORMAT_OPTIONS . -$MINT_RUN swiftlint lint $SWIFTLINT_OPTIONS - -popd