From a484e750e4f72a96d7d2024552f7f854fcb8a87a Mon Sep 17 00:00:00 2001 From: Max Howell Date: Sat, 2 Feb 2019 12:47:24 -0500 Subject: [PATCH 01/81] v7 initial changes * Swift 5 only * No ObjC * No deprecations * No submodules * Uses SwiftPM to generate an `.xcodeproj` --- .github/LinuxMain.stencil | 19 - .github/PromiseKit.podspec | 277 ---- .github/codecov.yml | 2 +- .github/jazzy.yml | 2 +- .github/release | 17 - .github/sourcery.yml | 12 - .gitignore | 9 +- .gitmodules | 69 - .travis.yml | 175 +-- {Documentation => Documents}/Appendix.md | 0 .../CommonPatterns.md | 0 .../Examples/ImageCache.md | 0 .../URLSession+BadResponseErrors.swift | 0 .../Examples/detweet.swift | 0 {Documentation => Documents}/FAQ.md | 0 .../GettingStarted.md | 2 +- {Documentation => Documents}/Installation.md | 0 {Documentation => Documents}/ObjectiveC.md | 0 {Documentation => Documents}/README.md | 2 +- .../Troubleshooting.md | 0 Extensions/AVFoundation | 1 - Extensions/Accounts | 1 - Extensions/AddressBook | 1 - Extensions/Alamofire | 1 - Extensions/AssetsLibrary | 1 - Extensions/Bolts | 1 - Extensions/CloudKit | 1 - Extensions/CoreBluetooth | 1 - Extensions/CoreLocation | 1 - Extensions/EventKit | 1 - Extensions/Foundation | 1 - Extensions/HealthKit | 1 - Extensions/HomeKit | 1 - Extensions/MapKit | 1 - Extensions/MessagesUI | 1 - Extensions/OMGHTTPURLRQ | 1 - Extensions/Photos | 1 - Extensions/QuartzCore | 1 - Extensions/Social | 1 - Extensions/StoreKit | 1 - Extensions/SystemConfiguration | 1 - Extensions/UIKit | 1 - Extensions/WatchConnectivity | 1 - Package.swift | 33 +- Package@swift-4.2.swift | 30 - Package@swift-5.0.swift | 33 - PromiseKit.playground/Contents.swift | 27 - PromiseKit.playground/contents.xcplayground | 7 - .../contents.xcworkspacedata | 7 - PromiseKit.xcodeproj/project.pbxproj | 1227 ----------------- .../contents.xcworkspacedata | 7 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../xcshareddata/WorkspaceSettings.xcsettings | 8 - .../xcschemes/PromiseKit.xcscheme | 150 -- README.md | 45 +- Sources/AnyPromise+Private.h | 32 - Sources/AnyPromise.h | 299 ---- Sources/AnyPromise.m | 175 --- Sources/AnyPromise.swift | 204 --- Sources/CustomStringConvertible.swift | 16 - Sources/Deprecations.swift | 93 -- Sources/Guarantee.swift | 15 +- Sources/Info.plist | 28 - Sources/NSMethodSignatureForBlock.m | 77 -- Sources/PMKCallVariadicBlock.m | 120 -- Sources/Promise.swift | 4 - Sources/PromiseKit.h | 7 - Sources/Resolver.swift | 12 +- Sources/Thenable.swift | 4 - Sources/after.m | 14 - Sources/after.swift | 12 +- Sources/dispatch_promise.m | 10 - Sources/fwd.h | 165 --- Sources/hang.m | 29 - Sources/join.m | 54 - Sources/race.m | 9 - Sources/when.m | 107 -- Sources/when.swift | 6 +- Tests/{JS-A+ => A+/JavaScript}/.gitignore | 0 Tests/{JS-A+ => A+/JavaScript}/AllTests.swift | 13 +- .../{JS-A+ => A+/JavaScript}/JSAdapter.swift | 4 + .../{JS-A+ => A+/JavaScript}/JSPromise.swift | 4 + Tests/{JS-A+ => A+/JavaScript}/JSUtils.swift | 12 +- .../JavaScript}/MockNodeEnvironment.swift | 18 +- Tests/{JS-A+ => A+/JavaScript}/README.md | 0 Tests/A+/JavaScript/XCTestManifests.swift | 18 + Tests/{JS-A+ => A+/JavaScript}/index.js | 0 .../JavaScript}/package-lock.json | 0 Tests/{JS-A+ => A+/JavaScript}/package.json | 0 .../JavaScript}/webpack.config.js | 0 Tests/A+/LinuxMain.swift | 12 + Tests/A+/{ => Swift}/0.0.0.swift | 7 +- Tests/A+/{ => Swift}/2.1.2.swift | 0 Tests/A+/{ => Swift}/2.1.3.swift | 0 Tests/A+/{ => Swift}/2.2.2.swift | 0 Tests/A+/{ => Swift}/2.2.3.swift | 0 Tests/A+/{ => Swift}/2.2.4.swift | 0 Tests/A+/{ => Swift}/2.2.6.swift | 0 Tests/A+/{ => Swift}/2.2.7.swift | 0 Tests/A+/{ => Swift}/2.3.1.swift | 0 Tests/A+/{ => Swift}/2.3.2.swift | 0 Tests/A+/{ => Swift}/2.3.4.swift | 0 Tests/A+/{ => Swift}/XCTestManifests.swift | 0 Tests/Bridging/BridgingTests.m | 34 - Tests/Bridging/BridgingTests.swift | 280 ---- Tests/Bridging/Infrastructure.h | 14 - Tests/Bridging/Infrastructure.m | 38 - Tests/Bridging/Infrastructure.swift | 24 - Tests/{CorePromise => Core}/AfterTests.swift | 18 - .../CancellableErrorTests.swift | 2 - .../CatchableTests.swift | 0 .../DefaultDispatchQueueTests.swift | 0 Tests/{CorePromise => Core}/ErrorTests.swift | 0 .../GuaranteeTests.swift | 0 Tests/{CorePromise => Core}/HangTests.swift | 0 .../{CorePromise => Core}/LoggingTests.swift | 0 .../{CorePromise => Core}/PromiseTests.swift | 2 - Tests/{CorePromise => Core}/RaceTests.swift | 0 .../RegressionTests.swift | 0 .../{CorePromise => Core}/ResolverTests.swift | 14 +- Tests/{CorePromise => Core}/StressTests.swift | 0 .../{CorePromise => Core}/ThenableTests.swift | 0 Tests/{CorePromise => Core}/Utilities.swift | 5 +- .../WhenConcurrentTests.swift | 0 .../WhenResolvedTests.swift | 6 +- Tests/{CorePromise => Core}/WhenTests.swift | 0 .../XCTestManifests.swift | 0 Tests/{CorePromise => Core}/ZalgoTests.swift | 0 Tests/CoreObjC/AnyPromiseTests.m | 896 ------------ Tests/CoreObjC/AnyPromiseTests.swift | 38 - Tests/CoreObjC/HangTests.m | 13 - Tests/CoreObjC/JoinTests.m | 90 -- Tests/CoreObjC/PMKManifoldTests.m | 83 -- Tests/CoreObjC/WhenTests.m | 265 ---- Tests/DeprecationTests.swift | 158 --- 135 files changed, 155 insertions(+), 5596 deletions(-) delete mode 100644 .github/LinuxMain.stencil delete mode 100644 .github/PromiseKit.podspec delete mode 100755 .github/release delete mode 100644 .github/sourcery.yml delete mode 100644 .gitmodules rename {Documentation => Documents}/Appendix.md (100%) rename {Documentation => Documents}/CommonPatterns.md (100%) rename {Documentation => Documents}/Examples/ImageCache.md (100%) rename {Documentation => Documents}/Examples/URLSession+BadResponseErrors.swift (100%) rename {Documentation => Documents}/Examples/detweet.swift (100%) rename {Documentation => Documents}/FAQ.md (100%) rename {Documentation => Documents}/GettingStarted.md (99%) rename {Documentation => Documents}/Installation.md (100%) rename {Documentation => Documents}/ObjectiveC.md (100%) rename {Documentation => Documents}/README.md (85%) rename {Documentation => Documents}/Troubleshooting.md (100%) delete mode 160000 Extensions/AVFoundation delete mode 160000 Extensions/Accounts delete mode 160000 Extensions/AddressBook delete mode 160000 Extensions/Alamofire delete mode 160000 Extensions/AssetsLibrary delete mode 160000 Extensions/Bolts delete mode 160000 Extensions/CloudKit delete mode 160000 Extensions/CoreBluetooth delete mode 160000 Extensions/CoreLocation delete mode 160000 Extensions/EventKit delete mode 160000 Extensions/Foundation delete mode 160000 Extensions/HealthKit delete mode 160000 Extensions/HomeKit delete mode 160000 Extensions/MapKit delete mode 160000 Extensions/MessagesUI delete mode 160000 Extensions/OMGHTTPURLRQ delete mode 160000 Extensions/Photos delete mode 160000 Extensions/QuartzCore delete mode 160000 Extensions/Social delete mode 160000 Extensions/StoreKit delete mode 160000 Extensions/SystemConfiguration delete mode 160000 Extensions/UIKit delete mode 160000 Extensions/WatchConnectivity delete mode 100644 Package@swift-4.2.swift delete mode 100644 Package@swift-5.0.swift delete mode 100644 PromiseKit.playground/Contents.swift delete mode 100644 PromiseKit.playground/contents.xcplayground delete mode 100644 PromiseKit.playground/playground.xcworkspace/contents.xcworkspacedata delete mode 100644 PromiseKit.xcodeproj/project.pbxproj delete mode 100644 PromiseKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata delete mode 100644 PromiseKit.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 PromiseKit.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings delete mode 100644 PromiseKit.xcodeproj/xcshareddata/xcschemes/PromiseKit.xcscheme delete mode 100644 Sources/AnyPromise+Private.h delete mode 100644 Sources/AnyPromise.h delete mode 100644 Sources/AnyPromise.m delete mode 100644 Sources/AnyPromise.swift delete mode 100644 Sources/Deprecations.swift delete mode 100644 Sources/Info.plist delete mode 100644 Sources/NSMethodSignatureForBlock.m delete mode 100644 Sources/PMKCallVariadicBlock.m delete mode 100644 Sources/PromiseKit.h delete mode 100644 Sources/after.m delete mode 100644 Sources/dispatch_promise.m delete mode 100644 Sources/fwd.h delete mode 100644 Sources/hang.m delete mode 100644 Sources/join.m delete mode 100644 Sources/race.m delete mode 100644 Sources/when.m rename Tests/{JS-A+ => A+/JavaScript}/.gitignore (100%) rename Tests/{JS-A+ => A+/JavaScript}/AllTests.swift (94%) rename Tests/{JS-A+ => A+/JavaScript}/JSAdapter.swift (94%) rename Tests/{JS-A+ => A+/JavaScript}/JSPromise.swift (97%) rename Tests/{JS-A+ => A+/JavaScript}/JSUtils.swift (91%) rename Tests/{JS-A+ => A+/JavaScript}/MockNodeEnvironment.swift (93%) rename Tests/{JS-A+ => A+/JavaScript}/README.md (100%) create mode 100644 Tests/A+/JavaScript/XCTestManifests.swift rename Tests/{JS-A+ => A+/JavaScript}/index.js (100%) rename Tests/{JS-A+ => A+/JavaScript}/package-lock.json (100%) rename Tests/{JS-A+ => A+/JavaScript}/package.json (100%) rename Tests/{JS-A+ => A+/JavaScript}/webpack.config.js (100%) create mode 100644 Tests/A+/LinuxMain.swift rename Tests/A+/{ => Swift}/0.0.0.swift (97%) rename Tests/A+/{ => Swift}/2.1.2.swift (100%) rename Tests/A+/{ => Swift}/2.1.3.swift (100%) rename Tests/A+/{ => Swift}/2.2.2.swift (100%) rename Tests/A+/{ => Swift}/2.2.3.swift (100%) rename Tests/A+/{ => Swift}/2.2.4.swift (100%) rename Tests/A+/{ => Swift}/2.2.6.swift (100%) rename Tests/A+/{ => Swift}/2.2.7.swift (100%) rename Tests/A+/{ => Swift}/2.3.1.swift (100%) rename Tests/A+/{ => Swift}/2.3.2.swift (100%) rename Tests/A+/{ => Swift}/2.3.4.swift (100%) rename Tests/A+/{ => Swift}/XCTestManifests.swift (100%) delete mode 100644 Tests/Bridging/BridgingTests.m delete mode 100644 Tests/Bridging/BridgingTests.swift delete mode 100644 Tests/Bridging/Infrastructure.h delete mode 100644 Tests/Bridging/Infrastructure.m delete mode 100644 Tests/Bridging/Infrastructure.swift rename Tests/{CorePromise => Core}/AfterTests.swift (63%) rename Tests/{CorePromise => Core}/CancellableErrorTests.swift (99%) rename Tests/{CorePromise => Core}/CatchableTests.swift (100%) rename Tests/{CorePromise => Core}/DefaultDispatchQueueTests.swift (100%) rename Tests/{CorePromise => Core}/ErrorTests.swift (100%) rename Tests/{CorePromise => Core}/GuaranteeTests.swift (100%) rename Tests/{CorePromise => Core}/HangTests.swift (100%) rename Tests/{CorePromise => Core}/LoggingTests.swift (100%) rename Tests/{CorePromise => Core}/PromiseTests.swift (99%) rename Tests/{CorePromise => Core}/RaceTests.swift (100%) rename Tests/{CorePromise => Core}/RegressionTests.swift (100%) rename Tests/{CorePromise => Core}/ResolverTests.swift (95%) rename Tests/{CorePromise => Core}/StressTests.swift (100%) rename Tests/{CorePromise => Core}/ThenableTests.swift (100%) rename Tests/{CorePromise => Core}/Utilities.swift (87%) rename Tests/{CorePromise => Core}/WhenConcurrentTests.swift (100%) rename Tests/{CorePromise => Core}/WhenResolvedTests.swift (94%) rename Tests/{CorePromise => Core}/WhenTests.swift (100%) rename Tests/{CorePromise => Core}/XCTestManifests.swift (100%) rename Tests/{CorePromise => Core}/ZalgoTests.swift (100%) delete mode 100644 Tests/CoreObjC/AnyPromiseTests.m delete mode 100644 Tests/CoreObjC/AnyPromiseTests.swift delete mode 100644 Tests/CoreObjC/HangTests.m delete mode 100644 Tests/CoreObjC/JoinTests.m delete mode 100644 Tests/CoreObjC/PMKManifoldTests.m delete mode 100644 Tests/CoreObjC/WhenTests.m delete mode 100644 Tests/DeprecationTests.swift diff --git a/.github/LinuxMain.stencil b/.github/LinuxMain.stencil deleted file mode 100644 index 11d1618ec..000000000 --- a/.github/LinuxMain.stencil +++ /dev/null @@ -1,19 +0,0 @@ -@testable import Core -@testable import A_ -import XCTest - -//TODO get this to run on CI and don’t have it committed -//NOTE problem is Sourcery doesn’t support Linux currently -//USAGE: cd PromiseKit/Sources/.. && sourcery --config .github/sourcery.yml - -{% for type in types.classes|based:"XCTestCase" %} -extension {{ type.name }} { - static var allTests = [ - {% for method in type.methods %}{% if method.parameters.count == 0 and method.shortName|hasPrefix:"test" %} ("{{ method.shortName }}", {{type.name}}.{{ method.shortName }}), - {% endif %}{% endfor %}] -} - -{% endfor %} -XCTMain([ -{% for type in types.classes|based:"XCTestCase" %}{% if not type.annotations.excludeFromLinuxMain %} testCase({{ type.name }}.allTests), -{% endif %}{% endfor %}]) diff --git a/.github/PromiseKit.podspec b/.github/PromiseKit.podspec deleted file mode 100644 index af2b0476d..000000000 --- a/.github/PromiseKit.podspec +++ /dev/null @@ -1,277 +0,0 @@ -Pod::Spec.new do |s| - s.name = "PromiseKit" - - s.version = '0.0.1' - - s.source = { - :git => "https://github.com/mxcl/#{s.name}.git", - :tag => s.version, - :submodules => true - } - - s.license = 'MIT' - s.summary = 'Promises for Swift & ObjC.' - s.homepage = 'http://mxcl.dev/PromiseKit/' - s.description = 'A thoughtful and complete implementation of promises for iOS, macOS, watchOS and tvOS with first-class support for both Objective-C and Swift.' - s.social_media_url = 'https://twitter.com/mxcl' - s.authors = { 'Max Howell' => 'mxcl@me.com' } - s.documentation_url = 'http://mxcl.dev/PromiseKit/reference/v6/Classes/Promise.html' - s.default_subspecs = 'CorePromise', 'UIKit', 'Foundation' - s.requires_arc = true - - s.swift_versions = ['3.1', '3.2', '3.3', '3.4', '4.0', '4.1', '4.2', '4.3', '4.4', '5.0'] - - # CocoaPods requires us to specify the root deployment targets - # even though for us it is nonsense. Our root spec has no - # sources. - s.ios.deployment_target = '8.0' - s.osx.deployment_target = '10.10' - s.watchos.deployment_target = '2.0' - s.tvos.deployment_target = '9.0' - - s.pod_target_xcconfig = { - 'OTHER_SWIFT_FLAGS' => '-DPMKCocoaPods', - } - - s.subspec 'Accounts' do |ss| - ss.ios.source_files = ss.osx.source_files = 'Extensions/Accounts/Sources/*' - ss.ios.frameworks = ss.osx.frameworks = 'Accounts' - ss.dependency 'PromiseKit/CorePromise' - ss.ios.deployment_target = '8.0' - ss.osx.deployment_target = '10.10' - end - - s.subspec 'Alamofire' do |ss| - ss.source_files = 'Extensions/Alamofire/Sources/*' - ss.dependency 'Alamofire', '~> 4.0' - ss.dependency 'PromiseKit/CorePromise' - ss.ios.deployment_target = '8.0' - ss.osx.deployment_target = '10.11' - ss.watchos.deployment_target = '2.0' - ss.tvos.deployment_target = '9.0' - end - - s.subspec 'AddressBook' do |ss| - ss.ios.source_files = 'Extensions/AddressBook/Sources/*' - ss.ios.frameworks = 'AddressBook' - ss.dependency 'PromiseKit/CorePromise' - ss.ios.deployment_target = '8.0' - end - - s.subspec 'AssetsLibrary' do |ss| - ss.ios.source_files = 'Extensions/AssetsLibrary/Sources/*' - ss.ios.frameworks = 'AssetsLibrary' - ss.dependency 'PromiseKit/CorePromise' - ss.ios.deployment_target = '8.0' - end - - s.subspec 'AVFoundation' do |ss| - ss.ios.source_files = 'Extensions/AVFoundation/Sources/*' - ss.ios.frameworks = 'AVFoundation' - ss.dependency 'PromiseKit/CorePromise' - ss.ios.deployment_target = '8.0' - end - - s.subspec 'Bolts' do |ss| - ss.source_files = 'Extensions/Bolts/Sources/*' - ss.dependency 'PromiseKit/CorePromise' - ss.dependency 'Bolts', '~> 1.9.0' - ss.ios.deployment_target = '8.0' - ss.osx.deployment_target = '10.10' - ss.watchos.deployment_target = '2.0' - ss.tvos.deployment_target = '9.0' - end - - s.subspec 'CloudKit' do |ss| - ss.source_files = 'Extensions/CloudKit/Sources/*' - ss.frameworks = 'CloudKit' - ss.dependency 'PromiseKit/CorePromise' - ss.ios.deployment_target = '10.0' - ss.osx.deployment_target = '10.12' - ss.tvos.deployment_target = '10.0' - ss.watchos.deployment_target = '3.0' - end - - s.subspec 'CoreBluetooth' do |ss| - ss.ios.source_files = ss.osx.source_files = ss.tvos.source_files = 'Extensions/CoreBluetooth/Sources/*' - ss.ios.frameworks = ss.osx.frameworks = ss.tvos.frameworks = 'CoreBluetooth' - ss.dependency 'PromiseKit/CorePromise' - ss.ios.deployment_target = '8.0' - ss.osx.deployment_target = '10.10' - ss.tvos.deployment_target = '9.0' - end - - s.subspec 'CorePromise' do |ss| - hh = Dir['Sources/*.h'] - Dir['Sources/*+Private.h'] - - cc = Dir['Sources/*.swift'] - ['Sources/SwiftPM.swift'] - cc << 'Sources/{after,AnyPromise,GlobalState,dispatch_promise,hang,join,PMKPromise,when,race}.m' - cc += hh - - ss.source_files = cc - ss.public_header_files = hh - ss.preserve_paths = 'Sources/AnyPromise+Private.h', 'Sources/PMKCallVariadicBlock.m', 'Sources/NSMethodSignatureForBlock.m' - ss.frameworks = 'Foundation' - - ss.ios.deployment_target = '8.0' - ss.osx.deployment_target = '10.10' - ss.watchos.deployment_target = '2.0' - ss.tvos.deployment_target = '9.0' - end - - s.subspec 'CoreLocation' do |ss| - ss.source_files = 'Extensions/CoreLocation/Sources/*' - ss.watchos.source_files = 'Extensions/CoreLocation/Sources/CLGeocoder*' - ss.dependency 'PromiseKit/CorePromise' - ss.frameworks = 'CoreLocation' - - ss.ios.deployment_target = '8.0' - ss.osx.deployment_target = '10.10' - ss.watchos.deployment_target = '3.0' - ss.tvos.deployment_target = '9.0' - end - - s.subspec 'EventKit' do |ss| - ss.ios.source_files = ss.osx.source_files = ss.watchos.source_files = 'Extensions/EventKit/Sources/*' - ss.ios.frameworks = ss.osx.frameworks = ss.watchos.frameworks = 'EventKit' - ss.dependency 'PromiseKit/CorePromise' - - ss.ios.deployment_target = '8.0' - ss.osx.deployment_target = '10.10' - ss.watchos.deployment_target = '2.0' - end - - s.subspec 'Foundation' do |ss| - ss.source_files = Dir['Extensions/Foundation/Sources/*'] - ss.dependency 'PromiseKit/CorePromise' - ss.frameworks = 'Foundation' - ss.ios.deployment_target = '8.0' - ss.osx.deployment_target = '10.10' - ss.watchos.deployment_target = '2.0' - ss.tvos.deployment_target = '9.0' - end - - s.subspec 'HealthKit' do |ss| - ss.source_files = Dir['Extensions/HealthKit/Sources/*'] - ss.dependency 'PromiseKit/CorePromise' - ss.frameworks = 'HealthKit' - ss.ios.deployment_target = '9.0' - ss.watchos.deployment_target = '2.0' - end - - s.subspec 'HomeKit' do |ss| - ss.source_files = Dir['Extensions/HomeKit/Sources/*'] - ss.dependency 'PromiseKit/CorePromise' - ss.frameworks = 'HomeKit' - ss.ios.deployment_target = '8.0' - ss.watchos.deployment_target = '3.0' - ss.tvos.deployment_target = '9.0' - end - - s.subspec 'MapKit' do |ss| - ss.ios.source_files = ss.osx.source_files = ss.tvos.source_files = 'Extensions/MapKit/Sources/*' - ss.ios.frameworks = ss.osx.frameworks = ss.tvos.frameworks = 'MapKit' - ss.dependency 'PromiseKit/CorePromise' - ss.ios.deployment_target = '8.0' - ss.osx.deployment_target = '10.10' - ss.watchos.deployment_target = '2.0' - ss.tvos.deployment_target = '9.2' - end - - s.subspec 'MessageUI' do |ss| - ss.ios.source_files = 'Extensions/MessagesUI/Sources/*' - ss.ios.frameworks = 'MessageUI' - ss.dependency 'PromiseKit/CorePromise' - ss.ios.deployment_target = '8.0' - end - - s.subspec 'OMGHTTPURLRQ' do |ss| - ss.source_files = 'Extensions/OMGHTTPURLRQ/Sources/*' - ss.dependency 'PromiseKit/Foundation' - ss.dependency 'OMGHTTPURLRQ', '~> 3.2' - ss.ios.deployment_target = '8.0' - ss.osx.deployment_target = '10.10' - ss.watchos.deployment_target = '2.0' - ss.tvos.deployment_target = '9.0' - end - - s.subspec 'Photos' do |ss| - ss.ios.source_files = ss.tvos.source_files = ss.osx.source_files = 'Extensions/Photos/Sources/*' - ss.ios.frameworks = ss.tvos.frameworks = ss.osx.frameworks = 'Photos' - ss.dependency 'PromiseKit/CorePromise' - - ss.ios.deployment_target = '8.0' - ss.osx.deployment_target = '10.13' - ss.tvos.deployment_target = '10.0' - end - - s.subspec 'QuartzCore' do |ss| - ss.osx.source_files = ss.ios.source_files = ss.tvos.source_files = 'Extensions/QuartzCore/Sources/*' - ss.osx.frameworks = ss.ios.frameworks = ss.tvos.frameworks = 'QuartzCore' - ss.dependency 'PromiseKit/CorePromise' - ss.ios.deployment_target = '8.0' - ss.osx.deployment_target = '10.10' - ss.tvos.deployment_target = '9.0' - end - - s.subspec 'Social' do |ss| - ss.ios.source_files = 'Extensions/Social/Sources/*' - ss.osx.source_files = Dir['Extensions/Social/Sources/*'] - ['Categories/Social/Sources/*SLComposeViewController+Promise.swift'] - ss.ios.frameworks = ss.osx.frameworks = 'Social' - ss.dependency 'PromiseKit/Foundation' - ss.ios.deployment_target = '8.0' - ss.osx.deployment_target = '10.10' - end - - s.subspec 'StoreKit' do |ss| - ss.ios.source_files = ss.osx.source_files = ss.tvos.source_files = 'Extensions/StoreKit/Sources/*' - ss.ios.frameworks = ss.osx.frameworks = ss.tvos.frameworks = 'StoreKit' - ss.dependency 'PromiseKit/CorePromise' - ss.ios.deployment_target = '8.0' - ss.osx.deployment_target = '10.10' - ss.tvos.deployment_target = '9.0' - end - - s.subspec 'SystemConfiguration' do |ss| - ss.ios.source_files = ss.osx.source_files = ss.tvos.source_files = 'Extensions/SystemConfiguration/Sources/*' - ss.ios.frameworks = ss.osx.frameworks = ss.tvos.frameworks = 'SystemConfiguration' - ss.dependency 'PromiseKit/CorePromise' - ss.ios.deployment_target = '8.0' - ss.osx.deployment_target = '10.10' - ss.tvos.deployment_target = '9.0' - end - - picker_cc = 'Extensions/UIKit/Sources/UIImagePickerController+Promise.swift' - - s.subspec 'UIKit' do |ss| - ss.ios.source_files = ss.tvos.source_files = Dir['Extensions/UIKit/Sources/*'] - [picker_cc] - ss.tvos.frameworks = ss.ios.frameworks = 'UIKit' - ss.dependency 'PromiseKit/CorePromise' - ss.ios.deployment_target = '8.0' - ss.tvos.deployment_target = '9.0' - end - - s.subspec 'UIImagePickerController' do |ss| - # Since iOS 10, App Store submissions that contain references to - # UIImagePickerController (even if unused in 3rd party libraries) - # are rejected unless an Info.plist key is specified, thus we - # moved this code to a sub-subspec. - # - # This *was* a subspec of UIKit, but bizarrely CocoaPods would - # include this when specifying *just* UIKit…! - - ss.ios.source_files = picker_cc - ss.ios.frameworks = 'UIKit' - ss.ios.xcconfig = { "GCC_PREPROCESSOR_DEFINITIONS" => '$(inherited) PMKImagePickerController=1' } - ss.dependency 'PromiseKit/UIKit' - ss.ios.deployment_target = '8.0' - end - - s.subspec 'WatchConnectivity' do |ss| - ss.ios.source_files = ss.watchos.source_files = 'Extensions/WatchConnectivity/Sources/*' - ss.ios.frameworks = ss.watchos.frameworks = 'WatchConnectivity' - ss.dependency 'PromiseKit/CorePromise' - ss.ios.deployment_target = '8.0' - ss.watchos.deployment_target = '2.0' - end -end diff --git a/.github/codecov.yml b/.github/codecov.yml index 8ffbde74d..12c801e77 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -1,7 +1,7 @@ ignore: - "Tests" - "README.md" - - "Documentation" + - "Documents" - ".travis.yml" codecov: diff --git a/.github/jazzy.yml b/.github/jazzy.yml index 9e8d71f05..027dd7fd1 100644 --- a/.github/jazzy.yml +++ b/.github/jazzy.yml @@ -13,6 +13,6 @@ output: ../output # output directory is relative to config file… ugh readme: - Documentation/README.md + Documents/README.md theme: fullwidth diff --git a/.github/release b/.github/release deleted file mode 100755 index 33538f0fb..000000000 --- a/.github/release +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh - -set -euxo pipefail - -if [ -z "$1" ]; then - echo "No argument supplied" - exit 1 -fi - -Vn=$1 -Vo=$(git tag | tail -1) -sed -i "" "s/CURRENT_PROJECT_VERSION = $Vo;/CURRENT_PROJECT_VERSION = $Vn;/" PromiseKit.xcodeproj/project.pbxproj -git add PromiseKit.xcodeproj/project.pbxproj -git commit -m "Tag $Vn" -git tag $Vn -git push origin $Vn -open "https://github.com/mxcl/PromiseKit/releases/tag/$Vn" diff --git a/.github/sourcery.yml b/.github/sourcery.yml deleted file mode 100644 index e965499c6..000000000 --- a/.github/sourcery.yml +++ /dev/null @@ -1,12 +0,0 @@ -sources: - include: - - ../Tests/A+ - - ../Tests/CorePromise - exclude: - - ../Tests/A+/0.0.0.swift - - ../Tests/CorePromise/Utilities.swift -templates: - include: - - LinuxMain.stencil -output: - ../Tests/LinuxMain.swift diff --git a/.gitignore b/.gitignore index a966dc1c4..dcbd654f6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,6 @@ -*.xcodeproj/**/xcuserdata/ -*.xcscmblueprint +/PromiseKit.xcodeproj /Carthage /.build .DS_Store -DerivedData -/PromiseKit.podspec -/Extensions/Carthage -/Tests/JS-A+/build +/build +/Tests/A+/JavaScript/build diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 9a18709e2..000000000 --- a/.gitmodules +++ /dev/null @@ -1,69 +0,0 @@ -[submodule "Extensions/Foundation"] - path = Extensions/Foundation - url = https://github.com/PromiseKit/Foundation.git -[submodule "Extensions/UIKit"] - path = Extensions/UIKit - url = https://github.com/PromiseKit/UIKit.git -[submodule "Extensions/Accounts"] - path = Extensions/Accounts - url = https://github.com/PromiseKit/Accounts.git -[submodule "Extensions/MessagesUI"] - path = Extensions/MessagesUI - url = https://github.com/PromiseKit/MessagesUI.git -[submodule "Extensions/WatchConnectivity"] - path = Extensions/WatchConnectivity - url = https://github.com/PromiseKit/WatchConnectivity.git -[submodule "Extensions/Photos"] - path = Extensions/Photos - url = https://github.com/PromiseKit/Photos.git -[submodule "Extensions/MapKit"] - path = Extensions/MapKit - url = https://github.com/PromiseKit/MapKit.git -[submodule "Extensions/CloudKit"] - path = Extensions/CloudKit - url = https://github.com/PromiseKit/CloudKit.git -[submodule "Extensions/AddressBook"] - path = Extensions/AddressBook - url = https://github.com/PromiseKit/AddressBook.git -[submodule "Extensions/AssetsLibrary"] - path = Extensions/AssetsLibrary - url = https://github.com/PromiseKit/AssetsLibrary.git -[submodule "Extensions/CoreLocation"] - path = Extensions/CoreLocation - url = https://github.com/PromiseKit/CoreLocation.git -[submodule "Extensions/QuartzCore"] - path = Extensions/QuartzCore - url = https://github.com/PromiseKit/QuartzCore.git -[submodule "Extensions/Social"] - path = Extensions/Social - url = https://github.com/PromiseKit/Social.git -[submodule "Extensions/StoreKit"] - path = Extensions/StoreKit - url = https://github.com/PromiseKit/StoreKit.git -[submodule "Extensions/Bolts"] - path = Extensions/Bolts - url = https://github.com/PromiseKit/Bolts.git -[submodule "Extensions/CoreBluetooth"] - path = Extensions/CoreBluetooth - url = https://github.com/PromiseKit/CoreBluetooth.git -[submodule "Extensions/EventKit"] - path = Extensions/EventKit - url = https://github.com/PromiseKit/EventKit.git -[submodule "Extensions/SystemConfiguration"] - path = Extensions/SystemConfiguration - url = https://github.com/PromiseKit/SystemConfiguration -[submodule "Extensions/Alamofire"] - path = Extensions/Alamofire - url = https://github.com/PromiseKit/Alamofire -[submodule "Extensions/OMGHTTPURLRQ"] - path = Extensions/OMGHTTPURLRQ - url = https://github.com/PromiseKit/OMGHTTPURLRQ -[submodule "Extensions/AVFoundation"] - path = Extensions/AVFoundation - url = https://github.com/PromiseKit/AVFoundation -[submodule "Extensions/HomeKit"] - path = Extensions/HomeKit - url = https://github.com/PromiseKit/HomeKit.git -[submodule "Extensions/HealthKit"] - path = Extensions/HealthKit - url = https://github.com/PromiseKit/PMKHealthKit diff --git a/.travis.yml b/.travis.yml index b1bff4b3b..7926b0438 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,169 +1,68 @@ os: osx -osx_image: xcode10.2 language: swift +osx_image: xcode10.2 branches: only: - v7 - master + - v6 - v4 - legacy-1.x - /^\d+\.\d+\.\d+$/ + stages: - name: pretest - if: type != push OR branch =~ /^\d+\.\d+\.\d+$/ - - name: lint - if: NOT branch =~ ^\d+\.\d+\.\d+$ - - name: carthage - if: type != push OR branch =~ /^\d+\.\d+\.\d+$/ - - name: swiftpm - if: type != push OR branch =~ /^\d+\.\d+\.\d+$/ - name: test - if: type != push OR branch =~ /^\d+\.\d+\.\d+$/ - name: deploy if: branch =~ ^\d+\.\d+\.\d+$ + +os: osx +osx_image: xcode10.2 +language: swift +xcode_project: PromiseKit.xcodeproj +xcode_scheme: PromiseKit-Package +xcode_destination: 'platform=macOS' + jobs: include: - stage: pretest name: Validate Linux test coverage completeness - install: swift test --generate-linuxmain -Xswiftc -target -Xswiftc x86_64-apple-macosx10.12 + install: swift test --generate-linuxmain script: git diff --exit-code - - &carthage - stage: carthage - osx_image: xcode9 - before_script: sed -i '' "s/SWIFT_TREAT_WARNINGS_AS_ERRORS = NO;/SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;/" *.xcodeproj/project.pbxproj - script: carthage build --no-skip-current --configuration Release - name: Swift 4.0.0 / Xcode 9.0 - - <<: *carthage - osx_image: xcode9.1 - name: Swift 4.0.2 / Xcode 9.1 - - <<: *carthage - osx_image: xcode9.2 - name: Swift 4.0.3 / Xcode 9.2 - - <<: *carthage - osx_image: xcode9.3 - name: Swift 4.1.0 / Xcode 9.3.1 - - <<: *carthage - osx_image: xcode9.4 - name: Swift 4.1.2 / Xcode 9.4.1 - - <<: *carthage - osx_image: xcode10.1 - name: Swift 4.1.50 / Xcode 10.1 - - <<: *carthage - osx_image: xcode10.2 - name: Swift 4.1.51 / Xcode 10.2 - - - &pod - stage: lint - osx_image: xcode8.3 - env: SWIFT=3.1 - cache: cocoapods - before_install: mv .github/PromiseKit.podspec . - install: gem install cocoapods --pre -v 1.7.0.beta.3 - script: pod lib lint --subspec=PromiseKit/CorePromise --fail-fast --swift-version=$SWIFT - - <<: *pod - osx_image: xcode9.2 - env: SWIFT=3.2 - - <<: *pod - osx_image: xcode9.4 - env: SWIFT=3.3 - - <<: *pod - osx_image: xcode10.1 - env: SWIFT=3.4 - - <<: *pod - osx_image: xcode9.2 - env: SWIFT=4.0 - - <<: *pod - osx_image: xcode9.4 - env: SWIFT=4.1 - - <<: *pod - osx_image: xcode10.1 - env: SWIFT=4.2 - - <<: *pod - osx_image: xcode10.2 - env: SWIFT=4.3 - - <<: *pod - osx_image: xcode10.2 - env: SWIFT=5.0 - - - &linux - stage: swiftpm - env: SWIFT_BUILD_VERSION=3 SWIFT_VERSION=4.0.3 - name: Linux / Swift 3.2 - os: linux - dist: trusty - sudo: false - language: generic - before_install: eval "$(curl -sL https://swiftenv.fuller.li/install.sh)" - install: swift build -Xswiftc -swift-version -Xswiftc $SWIFT_BUILD_VERSION - script: "true" - osx_image: null - - <<: *linux - env: SWIFT_BUILD_VERSION=3 SWIFT_VERSION=4.1.3 - name: Linux / Swift 3.3 - - <<: *linux - env: SWIFT_BUILD_VERSION=3 SWIFT_VERSION=4.2.4 - name: Linux / Swift 3.4 - - <<: *linux - env: SWIFT_BUILD_VERSION=4 SWIFT_VERSION=4.0.3 - name: Linux / Swift 4.0.3 - - <<: *linux - env: SWIFT_BUILD_VERSION=4 SWIFT_VERSION=4.1.3 - name: Linux / Swift 4.1.3 - - <<: *linux - env: SWIFT_BUILD_VERSION=4 SWIFT_VERSION=4.2.4 - name: Linux / Swift 4.1.50 - - <<: *linux - env: SWIFT_BUILD_VERSION=4.2 SWIFT_VERSION=4.2.4 - name: Linux / Swift 4.2.4 - - <<: *linux - env: SWIFT_BUILD_VERSION=4.2 SWIFT_VERSION=5.0 - name: Linux / Swift 4.3 - - <<: *linux - env: SWIFT_BUILD_VERSION=5 SWIFT_VERSION=5.0 - name: 'Linux / Swift 5.0 / +Tests' - script: swift test -Xswiftc -swift-version -Xswiftc $SWIFT_BUILD_VERSION - - &test + name: macOS stage: test - name: macOS / Xcode 10.2 - xcode_scheme: PromiseKit - xcode_project: PromiseKit.xcodeproj - xcode_destination: 'platform=macOS' + install: swift package generate-xcodeproj --enable-code-coverage after_success: bash <(curl -s https://codecov.io/bash) - - <<: *test - name: iOS / Xcode 10.2 - xcode_destination: 'platform=iOS Simulator,OS=12.2,name=iPhone SE' - + name: iOS + xcode_destination: 'OS=12.2,name=iPhone SE' - <<: *test - name: tvOS / Xcode 10.2 - xcode_destination: 'platform=tvOS Simulator,OS=12.2,name=Apple TV' + name: tvOS + xcode_destination: 'OS=12.2,name=Apple TV' - - name: Promises/A+ (via WebKit JavaScript Bridge) - before_install: - set -exo pipefail - install: - bash -c "cd Tests/JS-A+; npm ci &>/dev/null && npm run --hide-modules build" - script: - xcodebuild -scheme PromiseKit -target PMKJSA+Tests -enableCodeCoverage NO -only-testing:PMKJSA+Tests test | xcpretty + - name: Linux + env: SWIFT_VERSION='5.0-DEVELOPMENT-SNAPSHOT-2019-01-22-a' + os: linux + language: generic + osx_image: null + install: eval "$(curl -sL https://swiftenv.fuller.li/install.sh)" + script: swift test + + - name: JavaScript Promises/A+ + install: | + bash -c " + cd Tests/A+/JavaScript + npm ci &>/dev/null + npm run --hide-modules build" + script: swift test --filter A__js.AllTests + after_success: bash <(curl -s https://codecov.io/bash) cache.directories: - Tests/JS-A+/build - Tests/JS-A+/node_modules - - &swiftpm - stage: swiftpm - name: 'macOS / swift-tools-version: 4.0 / Swift 4.1.2' - osx_image: xcode9.4 - script: swift build - - <<: *swiftpm - osx_image: xcode10.1 - name: 'macOS / swift-tools-version: 4.2 / Swift 4.2.1' - - <<: *swiftpm - osx_image: xcode10.2 - name: 'macOS / swift-tools-version: 5.0 / Swift 5.0.0' - - name: '`pod trunk push`' stage: deploy install: gem install cocoapods --pre -v 1.7.0.beta.3 @@ -190,9 +89,9 @@ jobs: git remote update git fetch origin gh-pages:gh-pages --depth 1 git checkout gh-pages - rm -rf reference/v6 - mv output reference/v6 - git add reference/v6 + rm -rf reference/v7 + mv foo reference/v7 + git add reference/v7 git config user.name "Travis" git config user.email "jazzy@travis-ci.com" git commit -m "Updated docs for v$TRAVIS_TAG" diff --git a/Documentation/Appendix.md b/Documents/Appendix.md similarity index 100% rename from Documentation/Appendix.md rename to Documents/Appendix.md diff --git a/Documentation/CommonPatterns.md b/Documents/CommonPatterns.md similarity index 100% rename from Documentation/CommonPatterns.md rename to Documents/CommonPatterns.md diff --git a/Documentation/Examples/ImageCache.md b/Documents/Examples/ImageCache.md similarity index 100% rename from Documentation/Examples/ImageCache.md rename to Documents/Examples/ImageCache.md diff --git a/Documentation/Examples/URLSession+BadResponseErrors.swift b/Documents/Examples/URLSession+BadResponseErrors.swift similarity index 100% rename from Documentation/Examples/URLSession+BadResponseErrors.swift rename to Documents/Examples/URLSession+BadResponseErrors.swift diff --git a/Documentation/Examples/detweet.swift b/Documents/Examples/detweet.swift similarity index 100% rename from Documentation/Examples/detweet.swift rename to Documents/Examples/detweet.swift diff --git a/Documentation/FAQ.md b/Documents/FAQ.md similarity index 100% rename from Documentation/FAQ.md rename to Documents/FAQ.md diff --git a/Documentation/GettingStarted.md b/Documents/GettingStarted.md similarity index 99% rename from Documentation/GettingStarted.md rename to Documents/GettingStarted.md index 8f6af48dc..974098c45 100644 --- a/Documentation/GettingStarted.md +++ b/Documents/GettingStarted.md @@ -532,4 +532,4 @@ different API (sorry about that, but Swift has changed a lot over the years and we had to too). -[API Reference]: https://mxcl.dev/PromiseKit/reference/v6/Classes/Promise.html +[API Reference]: https://mxcl.dev/PromiseKit/reference/v7/Classes/Promise.html diff --git a/Documentation/Installation.md b/Documents/Installation.md similarity index 100% rename from Documentation/Installation.md rename to Documents/Installation.md diff --git a/Documentation/ObjectiveC.md b/Documents/ObjectiveC.md similarity index 100% rename from Documentation/ObjectiveC.md rename to Documents/ObjectiveC.md diff --git a/Documentation/README.md b/Documents/README.md similarity index 85% rename from Documentation/README.md rename to Documents/README.md index a03f4aa42..8164ddfec 100644 --- a/Documentation/README.md +++ b/Documents/README.md @@ -11,4 +11,4 @@ * [Troubleshooting](Troubleshooting.md) * [Appendix](Appendix.md) * [Examples](Examples) -* [API Reference](https://mxcl.dev/PromiseKit/reference/v6/Classes/Promise.html) +* [API Reference](https://mxcl.dev/PromiseKit/reference/v7/Classes/Promise.html) diff --git a/Documentation/Troubleshooting.md b/Documents/Troubleshooting.md similarity index 100% rename from Documentation/Troubleshooting.md rename to Documents/Troubleshooting.md diff --git a/Extensions/AVFoundation b/Extensions/AVFoundation deleted file mode 160000 index 6e6d18476..000000000 --- a/Extensions/AVFoundation +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6e6d184768c0d383e5ca8d7a1ff9710351e17dfd diff --git a/Extensions/Accounts b/Extensions/Accounts deleted file mode 160000 index 83ac28144..000000000 --- a/Extensions/Accounts +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 83ac281446b90a76357085e79616466cfc71a375 diff --git a/Extensions/AddressBook b/Extensions/AddressBook deleted file mode 160000 index 675857283..000000000 --- a/Extensions/AddressBook +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 675857283337f0c83dae6e0abeeebfbcbd84f6c5 diff --git a/Extensions/Alamofire b/Extensions/Alamofire deleted file mode 160000 index 7c6c6692f..000000000 --- a/Extensions/Alamofire +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7c6c6692fec51834128a7a8eb0f6a15bd93bfcf3 diff --git a/Extensions/AssetsLibrary b/Extensions/AssetsLibrary deleted file mode 160000 index 8492e28b5..000000000 --- a/Extensions/AssetsLibrary +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8492e28b521930227523b8a1b00e6f9134a9e6f5 diff --git a/Extensions/Bolts b/Extensions/Bolts deleted file mode 160000 index a14d7a64c..000000000 --- a/Extensions/Bolts +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a14d7a64cccbf777c0e4bd1de212a56c7ccddba6 diff --git a/Extensions/CloudKit b/Extensions/CloudKit deleted file mode 160000 index 06514c39b..000000000 --- a/Extensions/CloudKit +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 06514c39bf57e2a96a501948940ddb43f6bdd708 diff --git a/Extensions/CoreBluetooth b/Extensions/CoreBluetooth deleted file mode 160000 index 8e0bc9579..000000000 --- a/Extensions/CoreBluetooth +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8e0bc957996daad5c49f8809839f8699883ed364 diff --git a/Extensions/CoreLocation b/Extensions/CoreLocation deleted file mode 160000 index f3f3d8525..000000000 --- a/Extensions/CoreLocation +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f3f3d8525e9ea9fbc19ac269c80413a5454e9a8e diff --git a/Extensions/EventKit b/Extensions/EventKit deleted file mode 160000 index cf9095526..000000000 --- a/Extensions/EventKit +++ /dev/null @@ -1 +0,0 @@ -Subproject commit cf9095526ba0efc0903626ffab3e26c71cc11dec diff --git a/Extensions/Foundation b/Extensions/Foundation deleted file mode 160000 index e5c5d8471..000000000 --- a/Extensions/Foundation +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e5c5d8471da5b28ad436b3a80fac2aea1f9785eb diff --git a/Extensions/HealthKit b/Extensions/HealthKit deleted file mode 160000 index b7ad74638..000000000 --- a/Extensions/HealthKit +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b7ad74638b2831b8488f2ccfae2aebe1f7b71f21 diff --git a/Extensions/HomeKit b/Extensions/HomeKit deleted file mode 160000 index 4372659d7..000000000 --- a/Extensions/HomeKit +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4372659d7704b1d3c395511451e014ee44a12271 diff --git a/Extensions/MapKit b/Extensions/MapKit deleted file mode 160000 index 4199cb79e..000000000 --- a/Extensions/MapKit +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4199cb79e9af1f8741250ea53e1fbb5c35432fad diff --git a/Extensions/MessagesUI b/Extensions/MessagesUI deleted file mode 160000 index ae1a282c3..000000000 --- a/Extensions/MessagesUI +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ae1a282c3b29bc1115a7089437e2e75f2f74d222 diff --git a/Extensions/OMGHTTPURLRQ b/Extensions/OMGHTTPURLRQ deleted file mode 160000 index 5ec40a68f..000000000 --- a/Extensions/OMGHTTPURLRQ +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5ec40a68f168255dcc339b9620e2dcf62079ae6b diff --git a/Extensions/Photos b/Extensions/Photos deleted file mode 160000 index a20dddd6a..000000000 --- a/Extensions/Photos +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a20dddd6a8a36534e123edc251adef5b3de16989 diff --git a/Extensions/QuartzCore b/Extensions/QuartzCore deleted file mode 160000 index c034f528d..000000000 --- a/Extensions/QuartzCore +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c034f528da3309fa5f8b3f4a746ab100d6468389 diff --git a/Extensions/Social b/Extensions/Social deleted file mode 160000 index 95b086ce3..000000000 --- a/Extensions/Social +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 95b086ce353be7258ff5bcf8628cbe06186346fc diff --git a/Extensions/StoreKit b/Extensions/StoreKit deleted file mode 160000 index 6af114363..000000000 --- a/Extensions/StoreKit +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6af1143636fd1f50afe533ead4f7a863d91c11ba diff --git a/Extensions/SystemConfiguration b/Extensions/SystemConfiguration deleted file mode 160000 index f39adf82d..000000000 --- a/Extensions/SystemConfiguration +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f39adf82d349e175bddd99b748146d361b856328 diff --git a/Extensions/UIKit b/Extensions/UIKit deleted file mode 160000 index a7a236cf8..000000000 --- a/Extensions/UIKit +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a7a236cf81e8397aab37780af8d6624ab31fea06 diff --git a/Extensions/WatchConnectivity b/Extensions/WatchConnectivity deleted file mode 160000 index bdc99552c..000000000 --- a/Extensions/WatchConnectivity +++ /dev/null @@ -1 +0,0 @@ -Subproject commit bdc99552c392c04849f06b77b2afdf0df5cb5a50 diff --git a/Package.swift b/Package.swift index 76b516097..4c5e54ce9 100644 --- a/Package.swift +++ b/Package.swift @@ -1,30 +1,23 @@ -// swift-tools-version:4.0 +// swift-tools-version:5.0 import PackageDescription let pkg = Package(name: "PromiseKit") +pkg.platforms = [ + .macOS(.v10_12), //FIXME strictly 10.10 (only tests need 10.12) + .iOS(.v10), //FIXME strictly 8.0 + .tvOS(.v10), //FIXME strictly 9.0 + .watchOS(.v2) +] pkg.products = [ .library(name: "PromiseKit", targets: ["PromiseKit"]), ] - -let pmk: Target = .target(name: "PromiseKit") -pmk.path = "Sources" -pmk.exclude = [ - "AnyPromise.swift", - "AnyPromise.m", - "PMKCallVariadicBlock.m", - "dispatch_promise.m", - "join.m", - "when.m", - "NSMethodSignatureForBlock.m", - "after.m", - "hang.m", - "race.m", - "Deprecations.swift" +pkg.swiftLanguageVersions = [ + .v5 // grab PromiseKit-6.x if you want Swift 3.1‒4.2 ] -pkg.swiftLanguageVersions = [3, 4] pkg.targets = [ - pmk, - .testTarget(name: "A+", dependencies: ["PromiseKit"]), - .testTarget(name: "CorePromise", dependencies: ["PromiseKit"], path: "Tests/CorePromise"), + .target(name: "PromiseKit", path: "Sources"), + .testTarget(name: "Core", dependencies: ["PromiseKit"], path: "Tests/Core"), + .testTarget(name: "A+.swift", dependencies: ["PromiseKit"], path: "Tests/A+/Swift"), + .testTarget(name: "A+.js", dependencies: ["PromiseKit"], path: "Tests/A+/JavaScript"), ] diff --git a/Package@swift-4.2.swift b/Package@swift-4.2.swift deleted file mode 100644 index 7edd5509f..000000000 --- a/Package@swift-4.2.swift +++ /dev/null @@ -1,30 +0,0 @@ -// swift-tools-version:4.2 - -import PackageDescription - -let pkg = Package(name: "PromiseKit") -pkg.products = [ - .library(name: "PromiseKit", targets: ["PromiseKit"]), -] - -let pmk: Target = .target(name: "PromiseKit") -pmk.path = "Sources" -pmk.exclude = [ - "AnyPromise.swift", - "AnyPromise.m", - "PMKCallVariadicBlock.m", - "dispatch_promise.m", - "join.m", - "when.m", - "NSMethodSignatureForBlock.m", - "after.m", - "hang.m", - "race.m", - "Deprecations.swift" -] -pkg.swiftLanguageVersions = [.v3, .v4, .v4_2] -pkg.targets = [ - pmk, - .testTarget(name: "A+", dependencies: ["PromiseKit"]), - .testTarget(name: "CorePromise", dependencies: ["PromiseKit"], path: "Tests/CorePromise"), -] diff --git a/Package@swift-5.0.swift b/Package@swift-5.0.swift deleted file mode 100644 index 4594a8d44..000000000 --- a/Package@swift-5.0.swift +++ /dev/null @@ -1,33 +0,0 @@ -// swift-tools-version:5.0 - -import PackageDescription - -let pkg = Package(name: "PromiseKit") -pkg.platforms = [ - .macOS(.v10_10), .iOS(.v8), .tvOS(.v9), .watchOS(.v2) -] -pkg.products = [ - .library(name: "PromiseKit", targets: ["PromiseKit"]), -] - -let pmk: Target = .target(name: "PromiseKit") -pmk.path = "Sources" -pmk.exclude = [ - "AnyPromise.swift", - "AnyPromise.m", - "PMKCallVariadicBlock.m", - "dispatch_promise.m", - "join.m", - "when.m", - "NSMethodSignatureForBlock.m", - "after.m", - "hang.m", - "race.m", - "Deprecations.swift" -] -pkg.swiftLanguageVersions = [.v4, .v4_2, .v5] -pkg.targets = [ - pmk, - .testTarget(name: "A+", dependencies: ["PromiseKit"]), - .testTarget(name: "CorePromise", dependencies: ["PromiseKit"], path: "Tests/CorePromise"), -] diff --git a/PromiseKit.playground/Contents.swift b/PromiseKit.playground/Contents.swift deleted file mode 100644 index 3ade47e96..000000000 --- a/PromiseKit.playground/Contents.swift +++ /dev/null @@ -1,27 +0,0 @@ -import PlaygroundSupport - -// Is this erroring? If so open the `.xcodeproj` and build the -// framework for a macOS target (usually labeled: “My Mac”). -// Then select `PromiseKit.playground` from inside Xcode. -import PromiseKit - - -func promise3() -> Promise { - return after(.seconds(1)).map{ 3 } -} - -firstly { - Promise.value(1) -}.map { _ in - 2 -}.then { _ in - promise3() -}.done { - print($0) // => 3 -}.catch { error in - // only happens for errors -}.finally { - PlaygroundPage.current.finishExecution() -} - -PlaygroundPage.current.needsIndefiniteExecution = true diff --git a/PromiseKit.playground/contents.xcplayground b/PromiseKit.playground/contents.xcplayground deleted file mode 100644 index e6f503930..000000000 --- a/PromiseKit.playground/contents.xcplayground +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/PromiseKit.playground/playground.xcworkspace/contents.xcworkspacedata b/PromiseKit.playground/playground.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a62..000000000 --- a/PromiseKit.playground/playground.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/PromiseKit.xcodeproj/project.pbxproj b/PromiseKit.xcodeproj/project.pbxproj deleted file mode 100644 index 37e61ee80..000000000 --- a/PromiseKit.xcodeproj/project.pbxproj +++ /dev/null @@ -1,1227 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 085B96B321A6359500E5E22F /* LoggingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 085B96B121A6358900E5E22F /* LoggingTests.swift */; }; - 085B96BF21A9B37C00E5E22F /* LogEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 085B96BE21A9B37C00E5E22F /* LogEvent.swift */; }; - 0C42F31B1FCF86320051309C /* HangTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C42F3191FCF86240051309C /* HangTests.swift */; }; - 0CC3AF2B1FCF84F7000E98C9 /* hang.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CC3AF2A1FCF84F7000E98C9 /* hang.swift */; }; - 49A5584D1DC5185900E4D01B /* ResolverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49A5584B1DC5172F00E4D01B /* ResolverTests.swift */; }; - 630A8056203CEF6800D25F23 /* AnyPromiseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 630A8051203CEF6800D25F23 /* AnyPromiseTests.m */; settings = {COMPILER_FLAGS = "-fobjc-arc-exceptions"; }; }; - 630A8057203CEF6800D25F23 /* PMKManifoldTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 630A8052203CEF6800D25F23 /* PMKManifoldTests.m */; }; - 630A8058203CEF6800D25F23 /* JoinTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 630A8053203CEF6800D25F23 /* JoinTests.m */; }; - 630A8059203CEF6800D25F23 /* HangTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 630A8054203CEF6800D25F23 /* HangTests.m */; }; - 630A805A203CEF6800D25F23 /* WhenTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 630A8055203CEF6800D25F23 /* WhenTests.m */; }; - 630A805B203CF67800D25F23 /* DefaultDispatchQueueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 635D641B1D59635300BC0AF5 /* DefaultDispatchQueueTests.swift */; }; - 631411381D59795700E24B9E /* PromiseKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 63B0AC571D595E1B00FA21D9 /* PromiseKit.framework */; }; - 631411431D59797100E24B9E /* BridgingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6314113E1D59797100E24B9E /* BridgingTests.m */; }; - 631411441D59797100E24B9E /* BridgingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6314113F1D59797100E24B9E /* BridgingTests.swift */; }; - 631411451D59797100E24B9E /* Infrastructure.m in Sources */ = {isa = PBXBuildFile; fileRef = 631411411D59797100E24B9E /* Infrastructure.m */; }; - 631411461D59797100E24B9E /* Infrastructure.swift in Sources */ = {isa = PBXBuildFile; fileRef = 631411421D59797100E24B9E /* Infrastructure.swift */; }; - 631751A41D59766500A9DDDC /* PromiseKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 63B0AC571D595E1B00FA21D9 /* PromiseKit.framework */; }; - 631751B71D59768200A9DDDC /* 0.0.0.swift in Sources */ = {isa = PBXBuildFile; fileRef = 631751AB1D59768200A9DDDC /* 0.0.0.swift */; }; - 631751B81D59768200A9DDDC /* 2.1.2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 631751AC1D59768200A9DDDC /* 2.1.2.swift */; }; - 631751B91D59768200A9DDDC /* 2.1.3.swift in Sources */ = {isa = PBXBuildFile; fileRef = 631751AD1D59768200A9DDDC /* 2.1.3.swift */; }; - 631751BA1D59768200A9DDDC /* 2.2.2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 631751AE1D59768200A9DDDC /* 2.2.2.swift */; }; - 631751BB1D59768200A9DDDC /* 2.2.3.swift in Sources */ = {isa = PBXBuildFile; fileRef = 631751AF1D59768200A9DDDC /* 2.2.3.swift */; }; - 631751BC1D59768200A9DDDC /* 2.2.4.swift in Sources */ = {isa = PBXBuildFile; fileRef = 631751B01D59768200A9DDDC /* 2.2.4.swift */; }; - 631751BD1D59768200A9DDDC /* 2.2.6.swift in Sources */ = {isa = PBXBuildFile; fileRef = 631751B11D59768200A9DDDC /* 2.2.6.swift */; }; - 631751BE1D59768200A9DDDC /* 2.2.7.swift in Sources */ = {isa = PBXBuildFile; fileRef = 631751B21D59768200A9DDDC /* 2.2.7.swift */; }; - 631751BF1D59768200A9DDDC /* 2.3.1.swift in Sources */ = {isa = PBXBuildFile; fileRef = 631751B31D59768200A9DDDC /* 2.3.1.swift */; }; - 631751C01D59768200A9DDDC /* 2.3.2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 631751B41D59768200A9DDDC /* 2.3.2.swift */; }; - 631751C11D59768200A9DDDC /* 2.3.4.swift in Sources */ = {isa = PBXBuildFile; fileRef = 631751B51D59768200A9DDDC /* 2.3.4.swift */; }; - 632FBBE31F33B273008F8FBB /* Catchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 632FBBE21F33B273008F8FBB /* Catchable.swift */; }; - 632FBBE51F33B338008F8FBB /* CatchableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 632FBBE41F33B338008F8FBB /* CatchableTests.swift */; }; - 633027E6203CC0060037E136 /* PromiseKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 63B0AC571D595E1B00FA21D9 /* PromiseKit.framework */; }; - 6330B5E11F2E991200D60528 /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6330B5E01F2E991200D60528 /* Configuration.swift */; }; - 634AAD2B1EAE517C00B17855 /* fwd.h in Headers */ = {isa = PBXBuildFile; fileRef = 634AAD2A1EAE517C00B17855 /* fwd.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 635D641D1D59635300BC0AF5 /* PromiseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 635D64081D59635300BC0AF5 /* PromiseTests.swift */; }; - 635D641E1D59635300BC0AF5 /* CancellableErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 635D64091D59635300BC0AF5 /* CancellableErrorTests.swift */; }; - 635D64221D59635300BC0AF5 /* ZalgoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 635D640D1D59635300BC0AF5 /* ZalgoTests.swift */; }; - 635D64231D59635300BC0AF5 /* AfterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 635D640E1D59635300BC0AF5 /* AfterTests.swift */; }; - 635D64261D59635300BC0AF5 /* WhenResolvedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 635D64111D59635300BC0AF5 /* WhenResolvedTests.swift */; }; - 635D64271D59635300BC0AF5 /* RaceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 635D64121D59635300BC0AF5 /* RaceTests.swift */; }; - 635D64281D59635300BC0AF5 /* WhenConcurrentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 635D64131D59635300BC0AF5 /* WhenConcurrentTests.swift */; }; - 635D642A1D59635300BC0AF5 /* WhenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 635D64151D59635300BC0AF5 /* WhenTests.swift */; }; - 635D642B1D59635300BC0AF5 /* StressTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 635D64161D59635300BC0AF5 /* StressTests.swift */; }; - 635D642C1D59635300BC0AF5 /* RegressionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 635D64171D59635300BC0AF5 /* RegressionTests.swift */; }; - 635D64301D596E8500BC0AF5 /* PromiseKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 63B0AC571D595E1B00FA21D9 /* PromiseKit.framework */; }; - 636A291A1F1C156B001229C2 /* Promise.swift in Sources */ = {isa = PBXBuildFile; fileRef = 636A29191F1C156B001229C2 /* Promise.swift */; }; - 636A291F1F1C16FF001229C2 /* Box.swift in Sources */ = {isa = PBXBuildFile; fileRef = 636A291E1F1C16FF001229C2 /* Box.swift */; }; - 636A29211F1C1716001229C2 /* Thenable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 636A29201F1C1716001229C2 /* Thenable.swift */; }; - 636A29231F1C17A6001229C2 /* Guarantee.swift in Sources */ = {isa = PBXBuildFile; fileRef = 636A29221F1C17A6001229C2 /* Guarantee.swift */; }; - 636A29251F1C3089001229C2 /* race.swift in Sources */ = {isa = PBXBuildFile; fileRef = 636A29241F1C3089001229C2 /* race.swift */; }; - 636A29271F1C3927001229C2 /* Resolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 636A29261F1C3927001229C2 /* Resolver.swift */; }; - 639BF757203DF03100FA577B /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 639BF755203DF02C00FA577B /* Utilities.swift */; }; - 63B0AC7F1D595E6300FA21D9 /* after.m in Sources */ = {isa = PBXBuildFile; fileRef = 63B0AC611D595E6300FA21D9 /* after.m */; }; - 63B0AC801D595E6300FA21D9 /* after.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63B0AC621D595E6300FA21D9 /* after.swift */; }; - 63B0AC811D595E6300FA21D9 /* AnyPromise.h in Headers */ = {isa = PBXBuildFile; fileRef = 63B0AC631D595E6300FA21D9 /* AnyPromise.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 63B0AC821D595E6300FA21D9 /* AnyPromise.m in Sources */ = {isa = PBXBuildFile; fileRef = 63B0AC641D595E6300FA21D9 /* AnyPromise.m */; }; - 63B0AC831D595E6300FA21D9 /* AnyPromise.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63B0AC651D595E6300FA21D9 /* AnyPromise.swift */; }; - 63B0AC841D595E6300FA21D9 /* AnyPromise+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 63B0AC661D595E6300FA21D9 /* AnyPromise+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 63B0AC851D595E6300FA21D9 /* dispatch_promise.m in Sources */ = {isa = PBXBuildFile; fileRef = 63B0AC671D595E6300FA21D9 /* dispatch_promise.m */; }; - 63B0AC871D595E6300FA21D9 /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63B0AC691D595E6300FA21D9 /* Error.swift */; }; - 63B0AC891D595E6300FA21D9 /* hang.m in Sources */ = {isa = PBXBuildFile; fileRef = 63B0AC6B1D595E6300FA21D9 /* hang.m */; }; - 63B0AC8B1D595E6300FA21D9 /* join.m in Sources */ = {isa = PBXBuildFile; fileRef = 63B0AC6D1D595E6300FA21D9 /* join.m */; }; - 63B0AC931D595E6300FA21D9 /* PromiseKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 63B0AC761D595E6300FA21D9 /* PromiseKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 63B0AC991D595E6300FA21D9 /* when.m in Sources */ = {isa = PBXBuildFile; fileRef = 63B0AC7C1D595E6300FA21D9 /* when.m */; }; - 63B0AC9A1D595E6300FA21D9 /* when.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63B0AC7D1D595E6300FA21D9 /* when.swift */; }; - 63B18AEC1F2D205C00B79E37 /* CustomStringConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63B18AEB1F2D205C00B79E37 /* CustomStringConvertible.swift */; }; - 63B7C94B203E2B8200FBEC00 /* AnyPromiseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63B7C94A203E2B8200FBEC00 /* AnyPromiseTests.swift */; }; - 63B912AA1F1D7B1300D49110 /* firstly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63B912A91F1D7B1300D49110 /* firstly.swift */; }; - 63CF6D7A203CC66000EC8927 /* ErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63CF6D79203CC66000EC8927 /* ErrorTests.swift */; }; - 63CF6D7C203CCDAB00EC8927 /* GuaranteeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63CF6D7B203CCDAB00EC8927 /* GuaranteeTests.swift */; }; - 63CF6D7E203CD12700EC8927 /* DeprecationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63648C94203CB97400EBA011 /* DeprecationTests.swift */; }; - 63CF6D80203CD19200EC8927 /* ThenableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63CF6D7F203CD19200EC8927 /* ThenableTests.swift */; }; - 63D9B2EF203385FD0075C00B /* race.m in Sources */ = {isa = PBXBuildFile; fileRef = 63D9B2EE203385FD0075C00B /* race.m */; }; - 63D9B2F120338D5D0075C00B /* Deprecations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63D9B2F020338D5D0075C00B /* Deprecations.swift */; }; - C013F7382048E3B6006B57B1 /* MockNodeEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = C013F7372048E3B6006B57B1 /* MockNodeEnvironment.swift */; }; - C013F73A2049076A006B57B1 /* JSPromise.swift in Sources */ = {isa = PBXBuildFile; fileRef = C013F7392049076A006B57B1 /* JSPromise.swift */; }; - C013F73C20494291006B57B1 /* JSAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C013F73B20494291006B57B1 /* JSAdapter.swift */; }; - C013F740204E5064006B57B1 /* JSUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C013F73F204E5063006B57B1 /* JSUtils.swift */; }; - C0244E5E2047A6CB00ACB4AC /* PromiseKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 63B0AC571D595E1B00FA21D9 /* PromiseKit.framework */; }; - C0244E692047AC9F00ACB4AC /* AllTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0244E682047AC9F00ACB4AC /* AllTests.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 631411341D59795700E24B9E /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 6399A3721D595D9100D65233 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 63B0AC561D595E1B00FA21D9; - remoteInfo = PromiseKit; - }; - 6317518D1D59766500A9DDDC /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 6399A3721D595D9100D65233 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 63B0AC561D595E1B00FA21D9; - remoteInfo = PromiseKit; - }; - 633027E2203CC0060037E136 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 6399A3721D595D9100D65233 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 63B0AC561D595E1B00FA21D9; - remoteInfo = PromiseKit; - }; - 635D64041D5962F900BC0AF5 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 6399A3721D595D9100D65233 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 63B0AC561D595E1B00FA21D9; - remoteInfo = PromiseKit; - }; - C0244E502047A6CB00ACB4AC /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 6399A3721D595D9100D65233 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 63B0AC561D595E1B00FA21D9; - remoteInfo = PromiseKit; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - C0244E6E2047AF0B00ACB4AC /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 7; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 085B96B121A6358900E5E22F /* LoggingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggingTests.swift; sourceTree = ""; }; - 085B96BE21A9B37C00E5E22F /* LogEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = LogEvent.swift; path = Sources/LogEvent.swift; sourceTree = ""; }; - 0C42F3191FCF86240051309C /* HangTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HangTests.swift; sourceTree = ""; }; - 0CC3AF2A1FCF84F7000E98C9 /* hang.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = hang.swift; path = Sources/hang.swift; sourceTree = ""; }; - 49A5584B1DC5172F00E4D01B /* ResolverTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResolverTests.swift; sourceTree = ""; }; - 630019221D596292003B4E30 /* PMKCoreTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PMKCoreTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 630A8051203CEF6800D25F23 /* AnyPromiseTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AnyPromiseTests.m; sourceTree = ""; }; - 630A8052203CEF6800D25F23 /* PMKManifoldTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PMKManifoldTests.m; sourceTree = ""; }; - 630A8053203CEF6800D25F23 /* JoinTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JoinTests.m; sourceTree = ""; }; - 630A8054203CEF6800D25F23 /* HangTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HangTests.m; sourceTree = ""; }; - 630A8055203CEF6800D25F23 /* WhenTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WhenTests.m; sourceTree = ""; }; - 6314113C1D59795700E24B9E /* PMKBridgeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PMKBridgeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 6314113E1D59797100E24B9E /* BridgingTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BridgingTests.m; sourceTree = ""; }; - 6314113F1D59797100E24B9E /* BridgingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BridgingTests.swift; sourceTree = ""; }; - 631411401D59797100E24B9E /* Infrastructure.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Infrastructure.h; sourceTree = ""; }; - 631411411D59797100E24B9E /* Infrastructure.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Infrastructure.m; sourceTree = ""; }; - 631411421D59797100E24B9E /* Infrastructure.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Infrastructure.swift; sourceTree = ""; }; - 631751A81D59766500A9DDDC /* PMKA+Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "PMKA+Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; - 631751AB1D59768200A9DDDC /* 0.0.0.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 0.0.0.swift; sourceTree = ""; }; - 631751AC1D59768200A9DDDC /* 2.1.2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 2.1.2.swift; sourceTree = ""; }; - 631751AD1D59768200A9DDDC /* 2.1.3.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 2.1.3.swift; sourceTree = ""; }; - 631751AE1D59768200A9DDDC /* 2.2.2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 2.2.2.swift; sourceTree = ""; }; - 631751AF1D59768200A9DDDC /* 2.2.3.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 2.2.3.swift; sourceTree = ""; }; - 631751B01D59768200A9DDDC /* 2.2.4.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 2.2.4.swift; sourceTree = ""; }; - 631751B11D59768200A9DDDC /* 2.2.6.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 2.2.6.swift; sourceTree = ""; }; - 631751B21D59768200A9DDDC /* 2.2.7.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 2.2.7.swift; sourceTree = ""; }; - 631751B31D59768200A9DDDC /* 2.3.1.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 2.3.1.swift; sourceTree = ""; }; - 631751B41D59768200A9DDDC /* 2.3.2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 2.3.2.swift; sourceTree = ""; }; - 631751B51D59768200A9DDDC /* 2.3.4.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 2.3.4.swift; sourceTree = ""; }; - 631751B61D59768200A9DDDC /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; - 632FBBE21F33B273008F8FBB /* Catchable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Catchable.swift; path = Sources/Catchable.swift; sourceTree = ""; }; - 632FBBE41F33B338008F8FBB /* CatchableTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CatchableTests.swift; sourceTree = ""; }; - 633027EA203CC0060037E136 /* PMKDeprecatedTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PMKDeprecatedTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 6330B5E01F2E991200D60528 /* Configuration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Configuration.swift; path = Sources/Configuration.swift; sourceTree = ""; }; - 634AAD2A1EAE517C00B17855 /* fwd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = fwd.h; path = Sources/fwd.h; sourceTree = ""; }; - 635893921D5BE4E000F14B55 /* PromiseKit.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = PromiseKit.playground; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 635893941D5BE4F900F14B55 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; - 635893951D5BE4F900F14B55 /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; - 635893961D5BE4F900F14B55 /* PromiseKit.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; name = PromiseKit.podspec; path = .github/PromiseKit.podspec; sourceTree = ""; }; - 635893971D5BE4F900F14B55 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; - 635D64081D59635300BC0AF5 /* PromiseTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PromiseTests.swift; sourceTree = ""; }; - 635D64091D59635300BC0AF5 /* CancellableErrorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CancellableErrorTests.swift; sourceTree = ""; }; - 635D640D1D59635300BC0AF5 /* ZalgoTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZalgoTests.swift; sourceTree = ""; }; - 635D640E1D59635300BC0AF5 /* AfterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AfterTests.swift; sourceTree = ""; }; - 635D64111D59635300BC0AF5 /* WhenResolvedTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WhenResolvedTests.swift; sourceTree = ""; }; - 635D64121D59635300BC0AF5 /* RaceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RaceTests.swift; sourceTree = ""; }; - 635D64131D59635300BC0AF5 /* WhenConcurrentTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WhenConcurrentTests.swift; sourceTree = ""; }; - 635D64151D59635300BC0AF5 /* WhenTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WhenTests.swift; sourceTree = ""; }; - 635D64161D59635300BC0AF5 /* StressTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StressTests.swift; sourceTree = ""; }; - 635D64171D59635300BC0AF5 /* RegressionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RegressionTests.swift; sourceTree = ""; }; - 635D641B1D59635300BC0AF5 /* DefaultDispatchQueueTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultDispatchQueueTests.swift; sourceTree = ""; }; - 63648C94203CB97400EBA011 /* DeprecationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = DeprecationTests.swift; path = Tests/DeprecationTests.swift; sourceTree = ""; }; - 636A29191F1C156B001229C2 /* Promise.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Promise.swift; path = Sources/Promise.swift; sourceTree = ""; }; - 636A291E1F1C16FF001229C2 /* Box.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Box.swift; path = Sources/Box.swift; sourceTree = ""; }; - 636A29201F1C1716001229C2 /* Thenable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Thenable.swift; path = Sources/Thenable.swift; sourceTree = ""; }; - 636A29221F1C17A6001229C2 /* Guarantee.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Guarantee.swift; path = Sources/Guarantee.swift; sourceTree = ""; }; - 636A29241F1C3089001229C2 /* race.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = race.swift; path = Sources/race.swift; sourceTree = ""; }; - 636A29261F1C3927001229C2 /* Resolver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Resolver.swift; path = Sources/Resolver.swift; sourceTree = ""; }; - 639BF755203DF02C00FA577B /* Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utilities.swift; sourceTree = ""; }; - 63B0AC571D595E1B00FA21D9 /* PromiseKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PromiseKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 63B0AC611D595E6300FA21D9 /* after.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = after.m; path = Sources/after.m; sourceTree = ""; }; - 63B0AC621D595E6300FA21D9 /* after.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = after.swift; path = Sources/after.swift; sourceTree = ""; }; - 63B0AC631D595E6300FA21D9 /* AnyPromise.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AnyPromise.h; path = Sources/AnyPromise.h; sourceTree = ""; }; - 63B0AC641D595E6300FA21D9 /* AnyPromise.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AnyPromise.m; path = Sources/AnyPromise.m; sourceTree = ""; }; - 63B0AC651D595E6300FA21D9 /* AnyPromise.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AnyPromise.swift; path = Sources/AnyPromise.swift; sourceTree = ""; }; - 63B0AC661D595E6300FA21D9 /* AnyPromise+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "AnyPromise+Private.h"; path = "Sources/AnyPromise+Private.h"; sourceTree = ""; }; - 63B0AC671D595E6300FA21D9 /* dispatch_promise.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = dispatch_promise.m; path = Sources/dispatch_promise.m; sourceTree = ""; }; - 63B0AC691D595E6300FA21D9 /* Error.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Error.swift; path = Sources/Error.swift; sourceTree = ""; }; - 63B0AC6B1D595E6300FA21D9 /* hang.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = hang.m; path = Sources/hang.m; sourceTree = ""; }; - 63B0AC6C1D595E6300FA21D9 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Sources/Info.plist; sourceTree = ""; }; - 63B0AC6D1D595E6300FA21D9 /* join.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = join.m; path = Sources/join.m; sourceTree = ""; }; - 63B0AC6F1D595E6300FA21D9 /* NSMethodSignatureForBlock.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NSMethodSignatureForBlock.m; path = Sources/NSMethodSignatureForBlock.m; sourceTree = ""; }; - 63B0AC711D595E6300FA21D9 /* PMKCallVariadicBlock.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PMKCallVariadicBlock.m; path = Sources/PMKCallVariadicBlock.m; sourceTree = ""; }; - 63B0AC761D595E6300FA21D9 /* PromiseKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PromiseKit.h; path = Sources/PromiseKit.h; sourceTree = ""; }; - 63B0AC7C1D595E6300FA21D9 /* when.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = when.m; path = Sources/when.m; sourceTree = ""; }; - 63B0AC7D1D595E6300FA21D9 /* when.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = when.swift; path = Sources/when.swift; sourceTree = ""; }; - 63B18AEB1F2D205C00B79E37 /* CustomStringConvertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CustomStringConvertible.swift; path = Sources/CustomStringConvertible.swift; sourceTree = ""; }; - 63B7C94A203E2B8200FBEC00 /* AnyPromiseTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyPromiseTests.swift; sourceTree = ""; }; - 63B912A91F1D7B1300D49110 /* firstly.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = firstly.swift; path = Sources/firstly.swift; sourceTree = ""; }; - 63CF6D79203CC66000EC8927 /* ErrorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorTests.swift; sourceTree = ""; }; - 63CF6D7B203CCDAB00EC8927 /* GuaranteeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuaranteeTests.swift; sourceTree = ""; }; - 63CF6D7F203CD19200EC8927 /* ThenableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThenableTests.swift; sourceTree = ""; }; - 63D9B2EE203385FD0075C00B /* race.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = race.m; path = Sources/race.m; sourceTree = ""; }; - 63D9B2F020338D5D0075C00B /* Deprecations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Deprecations.swift; path = Sources/Deprecations.swift; sourceTree = ""; }; - C013F7372048E3B6006B57B1 /* MockNodeEnvironment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MockNodeEnvironment.swift; path = "Tests/JS-A+/MockNodeEnvironment.swift"; sourceTree = ""; }; - C013F7392049076A006B57B1 /* JSPromise.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = JSPromise.swift; path = "Tests/JS-A+/JSPromise.swift"; sourceTree = ""; }; - C013F73B20494291006B57B1 /* JSAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = JSAdapter.swift; path = "Tests/JS-A+/JSAdapter.swift"; sourceTree = ""; }; - C013F73F204E5063006B57B1 /* JSUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JSUtils.swift; path = "Tests/JS-A+/JSUtils.swift"; sourceTree = ""; }; - C0244E622047A6CB00ACB4AC /* PMKJSA+Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "PMKJSA+Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; - C0244E682047AC9F00ACB4AC /* AllTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AllTests.swift; path = "Tests/JS-A+/AllTests.swift"; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 630019181D596292003B4E30 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 635D64301D596E8500BC0AF5 /* PromiseKit.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 631411371D59795700E24B9E /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 631411381D59795700E24B9E /* PromiseKit.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 631751A31D59766500A9DDDC /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 631751A41D59766500A9DDDC /* PromiseKit.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 633027E5203CC0060037E136 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 633027E6203CC0060037E136 /* PromiseKit.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 63B0AC531D595E1B00FA21D9 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C0244E5D2047A6CB00ACB4AC /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - C0244E5E2047A6CB00ACB4AC /* PromiseKit.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 630A8050203CEF6800D25F23 /* CoreObjC */ = { - isa = PBXGroup; - children = ( - 630A8051203CEF6800D25F23 /* AnyPromiseTests.m */, - 63B7C94A203E2B8200FBEC00 /* AnyPromiseTests.swift */, - 630A8052203CEF6800D25F23 /* PMKManifoldTests.m */, - 630A8053203CEF6800D25F23 /* JoinTests.m */, - 630A8054203CEF6800D25F23 /* HangTests.m */, - 630A8055203CEF6800D25F23 /* WhenTests.m */, - ); - name = CoreObjC; - path = Tests/CoreObjC; - sourceTree = ""; - }; - 630B60BF1F2F739E00A1AEFE /* Features */ = { - isa = PBXGroup; - children = ( - 63B0AC611D595E6300FA21D9 /* after.m */, - 63B0AC6B1D595E6300FA21D9 /* hang.m */, - 63B0AC6D1D595E6300FA21D9 /* join.m */, - 63B0AC7C1D595E6300FA21D9 /* when.m */, - 63D9B2EE203385FD0075C00B /* race.m */, - ); - name = Features; - sourceTree = ""; - }; - 630B60C01F2F73B000A1AEFE /* Headers */ = { - isa = PBXGroup; - children = ( - 634AAD2A1EAE517C00B17855 /* fwd.h */, - 63B0AC631D595E6300FA21D9 /* AnyPromise.h */, - ); - name = Headers; - sourceTree = ""; - }; - 6314113D1D59797100E24B9E /* Bridging */ = { - isa = PBXGroup; - children = ( - 6314113E1D59797100E24B9E /* BridgingTests.m */, - 6314113F1D59797100E24B9E /* BridgingTests.swift */, - 631411401D59797100E24B9E /* Infrastructure.h */, - 631411411D59797100E24B9E /* Infrastructure.m */, - 631411421D59797100E24B9E /* Infrastructure.swift */, - ); - name = Bridging; - path = Tests/Bridging; - sourceTree = ""; - }; - 6317518A1D59765700A9DDDC /* Core */ = { - isa = PBXGroup; - children = ( - 63CF6D7F203CD19200EC8927 /* ThenableTests.swift */, - 632FBBE41F33B338008F8FBB /* CatchableTests.swift */, - 635D64081D59635300BC0AF5 /* PromiseTests.swift */, - 49A5584B1DC5172F00E4D01B /* ResolverTests.swift */, - 63CF6D7B203CCDAB00EC8927 /* GuaranteeTests.swift */, - 635D64091D59635300BC0AF5 /* CancellableErrorTests.swift */, - 63CF6D79203CC66000EC8927 /* ErrorTests.swift */, - 635D64151D59635300BC0AF5 /* WhenTests.swift */, - 635D64131D59635300BC0AF5 /* WhenConcurrentTests.swift */, - 635D64111D59635300BC0AF5 /* WhenResolvedTests.swift */, - 635D640E1D59635300BC0AF5 /* AfterTests.swift */, - 0C42F3191FCF86240051309C /* HangTests.swift */, - 635D64121D59635300BC0AF5 /* RaceTests.swift */, - 635D641B1D59635300BC0AF5 /* DefaultDispatchQueueTests.swift */, - 635D64171D59635300BC0AF5 /* RegressionTests.swift */, - 635D64161D59635300BC0AF5 /* StressTests.swift */, - 635D640D1D59635300BC0AF5 /* ZalgoTests.swift */, - 639BF755203DF02C00FA577B /* Utilities.swift */, - 085B96B121A6358900E5E22F /* LoggingTests.swift */, - ); - name = Core; - path = Tests/CorePromise; - sourceTree = ""; - }; - 631751AA1D59768200A9DDDC /* A+ */ = { - isa = PBXGroup; - children = ( - 631751AB1D59768200A9DDDC /* 0.0.0.swift */, - 631751AC1D59768200A9DDDC /* 2.1.2.swift */, - 631751AD1D59768200A9DDDC /* 2.1.3.swift */, - 631751AE1D59768200A9DDDC /* 2.2.2.swift */, - 631751AF1D59768200A9DDDC /* 2.2.3.swift */, - 631751B01D59768200A9DDDC /* 2.2.4.swift */, - 631751B11D59768200A9DDDC /* 2.2.6.swift */, - 631751B21D59768200A9DDDC /* 2.2.7.swift */, - 631751B31D59768200A9DDDC /* 2.3.1.swift */, - 631751B41D59768200A9DDDC /* 2.3.2.swift */, - 631751B51D59768200A9DDDC /* 2.3.4.swift */, - 631751B61D59768200A9DDDC /* README.md */, - ); - name = "A+"; - path = "Tests/A+"; - sourceTree = ""; - }; - 635893991D5BE51700F14B55 /* … */ = { - isa = PBXGroup; - children = ( - 63B0AC581D595E1B00FA21D9 /* Products */, - 63B0AC6C1D595E6300FA21D9 /* Info.plist */, - 635893941D5BE4F900F14B55 /* LICENSE */, - 635893951D5BE4F900F14B55 /* Package.swift */, - 635893961D5BE4F900F14B55 /* PromiseKit.podspec */, - ); - name = "…"; - sourceTree = ""; - }; - 635D64061D59630200BC0AF5 /* Tests */ = { - isa = PBXGroup; - children = ( - C0244E6B2047ACAF00ACB4AC /* JS/A+ */, - 631751AA1D59768200A9DDDC /* A+ */, - 6314113D1D59797100E24B9E /* Bridging */, - 6317518A1D59765700A9DDDC /* Core */, - 630A8050203CEF6800D25F23 /* CoreObjC */, - 63648C94203CB97400EBA011 /* DeprecationTests.swift */, - ); - name = Tests; - sourceTree = ""; - }; - 6399A3711D595D9100D65233 = { - isa = PBXGroup; - children = ( - 635893971D5BE4F900F14B55 /* README.md */, - 635893921D5BE4E000F14B55 /* PromiseKit.playground */, - 63B0AC761D595E6300FA21D9 /* PromiseKit.h */, - 63B0AC601D595E4C00FA21D9 /* Sources.swift */, - 63B912AB1F1E657400D49110 /* Sources.objc */, - 635D64061D59630200BC0AF5 /* Tests */, - 635893991D5BE51700F14B55 /* … */, - ); - sourceTree = ""; - }; - 63B0AC581D595E1B00FA21D9 /* Products */ = { - isa = PBXGroup; - children = ( - 63B0AC571D595E1B00FA21D9 /* PromiseKit.framework */, - 630019221D596292003B4E30 /* PMKCoreTests.xctest */, - 631751A81D59766500A9DDDC /* PMKA+Tests.xctest */, - 6314113C1D59795700E24B9E /* PMKBridgeTests.xctest */, - 633027EA203CC0060037E136 /* PMKDeprecatedTests.xctest */, - C0244E622047A6CB00ACB4AC /* PMKJSA+Tests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - 63B0AC601D595E4C00FA21D9 /* Sources.swift */ = { - isa = PBXGroup; - children = ( - 636A29191F1C156B001229C2 /* Promise.swift */, - 636A29221F1C17A6001229C2 /* Guarantee.swift */, - 636A29201F1C1716001229C2 /* Thenable.swift */, - 632FBBE21F33B273008F8FBB /* Catchable.swift */, - 63B912AC1F1E663E00D49110 /* Features */, - 63B0AC691D595E6300FA21D9 /* Error.swift */, - 636A29261F1C3927001229C2 /* Resolver.swift */, - 636A291E1F1C16FF001229C2 /* Box.swift */, - 6330B5E01F2E991200D60528 /* Configuration.swift */, - 63B18AEB1F2D205C00B79E37 /* CustomStringConvertible.swift */, - 63D9B2F020338D5D0075C00B /* Deprecations.swift */, - 085B96BE21A9B37C00E5E22F /* LogEvent.swift */, - ); - name = Sources.swift; - sourceTree = ""; - }; - 63B0AC9D1D595E6E00FA21D9 /* Internal Utilities */ = { - isa = PBXGroup; - children = ( - 63B0AC661D595E6300FA21D9 /* AnyPromise+Private.h */, - 63B0AC6F1D595E6300FA21D9 /* NSMethodSignatureForBlock.m */, - 63B0AC711D595E6300FA21D9 /* PMKCallVariadicBlock.m */, - ); - name = "Internal Utilities"; - sourceTree = ""; - }; - 63B912AB1F1E657400D49110 /* Sources.objc */ = { - isa = PBXGroup; - children = ( - 63B0AC641D595E6300FA21D9 /* AnyPromise.m */, - 63B0AC651D595E6300FA21D9 /* AnyPromise.swift */, - 63B0AC671D595E6300FA21D9 /* dispatch_promise.m */, - 630B60C01F2F73B000A1AEFE /* Headers */, - 630B60BF1F2F739E00A1AEFE /* Features */, - 63B0AC9D1D595E6E00FA21D9 /* Internal Utilities */, - ); - name = Sources.objc; - sourceTree = ""; - }; - 63B912AC1F1E663E00D49110 /* Features */ = { - isa = PBXGroup; - children = ( - 0CC3AF2A1FCF84F7000E98C9 /* hang.swift */, - 63B0AC621D595E6300FA21D9 /* after.swift */, - 63B912A91F1D7B1300D49110 /* firstly.swift */, - 636A29241F1C3089001229C2 /* race.swift */, - 63B0AC7D1D595E6300FA21D9 /* when.swift */, - ); - name = Features; - sourceTree = ""; - }; - C0244E6B2047ACAF00ACB4AC /* JS/A+ */ = { - isa = PBXGroup; - children = ( - C0244E682047AC9F00ACB4AC /* AllTests.swift */, - C013F7372048E3B6006B57B1 /* MockNodeEnvironment.swift */, - C013F7392049076A006B57B1 /* JSPromise.swift */, - C013F73B20494291006B57B1 /* JSAdapter.swift */, - C013F73F204E5063006B57B1 /* JSUtils.swift */, - ); - name = "JS/A+"; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - 63B0AC541D595E1B00FA21D9 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 63B0AC931D595E6300FA21D9 /* PromiseKit.h in Headers */, - 634AAD2B1EAE517C00B17855 /* fwd.h in Headers */, - 63B0AC811D595E6300FA21D9 /* AnyPromise.h in Headers */, - 63B0AC841D595E6300FA21D9 /* AnyPromise+Private.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - 630019011D596292003B4E30 /* PMKCoreTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 6300191F1D596292003B4E30 /* Build configuration list for PBXNativeTarget "PMKCoreTests" */; - buildPhases = ( - 630019021D596292003B4E30 /* Sources */, - 630019181D596292003B4E30 /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - 635D64051D5962F900BC0AF5 /* PBXTargetDependency */, - ); - name = PMKCoreTests; - productName = PromiseKit; - productReference = 630019221D596292003B4E30 /* PMKCoreTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - 631411321D59795700E24B9E /* PMKBridgeTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 631411391D59795700E24B9E /* Build configuration list for PBXNativeTarget "PMKBridgeTests" */; - buildPhases = ( - 631411351D59795700E24B9E /* Sources */, - 631411371D59795700E24B9E /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - 631411331D59795700E24B9E /* PBXTargetDependency */, - ); - name = PMKBridgeTests; - productName = PromiseKit; - productReference = 6314113C1D59795700E24B9E /* PMKBridgeTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - 6317518B1D59766500A9DDDC /* PMKA+Tests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 631751A51D59766500A9DDDC /* Build configuration list for PBXNativeTarget "PMKA+Tests" */; - buildPhases = ( - 6317518E1D59766500A9DDDC /* Sources */, - 631751A31D59766500A9DDDC /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - 6317518C1D59766500A9DDDC /* PBXTargetDependency */, - ); - name = "PMKA+Tests"; - productName = PromiseKit; - productReference = 631751A81D59766500A9DDDC /* PMKA+Tests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - 633027E0203CC0060037E136 /* PMKDeprecatedTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 633027E7203CC0060037E136 /* Build configuration list for PBXNativeTarget "PMKDeprecatedTests" */; - buildPhases = ( - 633027E3203CC0060037E136 /* Sources */, - 633027E5203CC0060037E136 /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - 633027E1203CC0060037E136 /* PBXTargetDependency */, - ); - name = PMKDeprecatedTests; - productName = PromiseKit; - productReference = 633027EA203CC0060037E136 /* PMKDeprecatedTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - 63B0AC561D595E1B00FA21D9 /* PromiseKit */ = { - isa = PBXNativeTarget; - buildConfigurationList = 63B0AC5F1D595E1B00FA21D9 /* Build configuration list for PBXNativeTarget "PromiseKit" */; - buildPhases = ( - 63B0AC521D595E1B00FA21D9 /* Sources */, - 63B0AC531D595E1B00FA21D9 /* Frameworks */, - 63B0AC541D595E1B00FA21D9 /* Headers */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = PromiseKit; - productName = PromiseKit; - productReference = 63B0AC571D595E1B00FA21D9 /* PromiseKit.framework */; - productType = "com.apple.product-type.framework"; - }; - C0244E4E2047A6CB00ACB4AC /* PMKJSA+Tests */ = { - isa = PBXNativeTarget; - buildConfigurationList = C0244E5F2047A6CB00ACB4AC /* Build configuration list for PBXNativeTarget "PMKJSA+Tests" */; - buildPhases = ( - C0244E512047A6CB00ACB4AC /* Sources */, - C0244E5D2047A6CB00ACB4AC /* Frameworks */, - C0244E6E2047AF0B00ACB4AC /* CopyFiles */, - ); - buildRules = ( - ); - dependencies = ( - C0244E4F2047A6CB00ACB4AC /* PBXTargetDependency */, - ); - name = "PMKJSA+Tests"; - productName = PromiseKit; - productReference = C0244E622047A6CB00ACB4AC /* PMKJSA+Tests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 6399A3721D595D9100D65233 /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 0930; - TargetAttributes = { - 630019011D596292003B4E30 = { - LastSwiftMigration = 0920; - }; - 631411321D59795700E24B9E = { - LastSwiftMigration = 0920; - }; - 6317518B1D59766500A9DDDC = { - LastSwiftMigration = 0920; - }; - 633027E0203CC0060037E136 = { - LastSwiftMigration = 0920; - }; - 63B0AC561D595E1B00FA21D9 = { - CreatedOnToolsVersion = 8.0; - LastSwiftMigration = 0920; - ProvisioningStyle = Automatic; - }; - C0244E4E2047A6CB00ACB4AC = { - LastSwiftMigration = 0920; - }; - }; - }; - buildConfigurationList = 6399A3751D595D9100D65233 /* Build configuration list for PBXProject "PromiseKit" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - English, - en, - Base, - ); - mainGroup = 6399A3711D595D9100D65233; - productRefGroup = 63B0AC581D595E1B00FA21D9 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 63B0AC561D595E1B00FA21D9 /* PromiseKit */, - 6317518B1D59766500A9DDDC /* PMKA+Tests */, - 631411321D59795700E24B9E /* PMKBridgeTests */, - 630019011D596292003B4E30 /* PMKCoreTests */, - 633027E0203CC0060037E136 /* PMKDeprecatedTests */, - C0244E4E2047A6CB00ACB4AC /* PMKJSA+Tests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXSourcesBuildPhase section */ - 630019021D596292003B4E30 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 0C42F31B1FCF86320051309C /* HangTests.swift in Sources */, - 635D641E1D59635300BC0AF5 /* CancellableErrorTests.swift in Sources */, - 630A8056203CEF6800D25F23 /* AnyPromiseTests.m in Sources */, - 635D64221D59635300BC0AF5 /* ZalgoTests.swift in Sources */, - 635D64271D59635300BC0AF5 /* RaceTests.swift in Sources */, - 632FBBE51F33B338008F8FBB /* CatchableTests.swift in Sources */, - 63CF6D80203CD19200EC8927 /* ThenableTests.swift in Sources */, - 635D642B1D59635300BC0AF5 /* StressTests.swift in Sources */, - 630A805A203CEF6800D25F23 /* WhenTests.m in Sources */, - 630A805B203CF67800D25F23 /* DefaultDispatchQueueTests.swift in Sources */, - 635D641D1D59635300BC0AF5 /* PromiseTests.swift in Sources */, - 63CF6D7C203CCDAB00EC8927 /* GuaranteeTests.swift in Sources */, - 639BF757203DF03100FA577B /* Utilities.swift in Sources */, - 635D64261D59635300BC0AF5 /* WhenResolvedTests.swift in Sources */, - 635D64231D59635300BC0AF5 /* AfterTests.swift in Sources */, - 63CF6D7A203CC66000EC8927 /* ErrorTests.swift in Sources */, - 49A5584D1DC5185900E4D01B /* ResolverTests.swift in Sources */, - 630A8057203CEF6800D25F23 /* PMKManifoldTests.m in Sources */, - 085B96B321A6359500E5E22F /* LoggingTests.swift in Sources */, - 63B7C94B203E2B8200FBEC00 /* AnyPromiseTests.swift in Sources */, - 630A8059203CEF6800D25F23 /* HangTests.m in Sources */, - 635D642A1D59635300BC0AF5 /* WhenTests.swift in Sources */, - 630A8058203CEF6800D25F23 /* JoinTests.m in Sources */, - 635D64281D59635300BC0AF5 /* WhenConcurrentTests.swift in Sources */, - 635D642C1D59635300BC0AF5 /* RegressionTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 631411351D59795700E24B9E /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 631411461D59797100E24B9E /* Infrastructure.swift in Sources */, - 631411431D59797100E24B9E /* BridgingTests.m in Sources */, - 631411441D59797100E24B9E /* BridgingTests.swift in Sources */, - 631411451D59797100E24B9E /* Infrastructure.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 6317518E1D59766500A9DDDC /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 631751C11D59768200A9DDDC /* 2.3.4.swift in Sources */, - 631751BA1D59768200A9DDDC /* 2.2.2.swift in Sources */, - 631751BF1D59768200A9DDDC /* 2.3.1.swift in Sources */, - 631751B91D59768200A9DDDC /* 2.1.3.swift in Sources */, - 631751BD1D59768200A9DDDC /* 2.2.6.swift in Sources */, - 631751B71D59768200A9DDDC /* 0.0.0.swift in Sources */, - 631751C01D59768200A9DDDC /* 2.3.2.swift in Sources */, - 631751B81D59768200A9DDDC /* 2.1.2.swift in Sources */, - 631751BE1D59768200A9DDDC /* 2.2.7.swift in Sources */, - 631751BC1D59768200A9DDDC /* 2.2.4.swift in Sources */, - 631751BB1D59768200A9DDDC /* 2.2.3.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 633027E3203CC0060037E136 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 63CF6D7E203CD12700EC8927 /* DeprecationTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 63B0AC521D595E1B00FA21D9 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 636A29251F1C3089001229C2 /* race.swift in Sources */, - 63B0AC9A1D595E6300FA21D9 /* when.swift in Sources */, - 636A29271F1C3927001229C2 /* Resolver.swift in Sources */, - 63B0AC991D595E6300FA21D9 /* when.m in Sources */, - 63D9B2EF203385FD0075C00B /* race.m in Sources */, - 63B0AC801D595E6300FA21D9 /* after.swift in Sources */, - 63B18AEC1F2D205C00B79E37 /* CustomStringConvertible.swift in Sources */, - 085B96BF21A9B37C00E5E22F /* LogEvent.swift in Sources */, - 6330B5E11F2E991200D60528 /* Configuration.swift in Sources */, - 63B912AA1F1D7B1300D49110 /* firstly.swift in Sources */, - 636A29211F1C1716001229C2 /* Thenable.swift in Sources */, - 632FBBE31F33B273008F8FBB /* Catchable.swift in Sources */, - 63B0AC851D595E6300FA21D9 /* dispatch_promise.m in Sources */, - 636A291F1F1C16FF001229C2 /* Box.swift in Sources */, - 63B0AC821D595E6300FA21D9 /* AnyPromise.m in Sources */, - 636A29231F1C17A6001229C2 /* Guarantee.swift in Sources */, - 636A291A1F1C156B001229C2 /* Promise.swift in Sources */, - 63B0AC8B1D595E6300FA21D9 /* join.m in Sources */, - 63B0AC891D595E6300FA21D9 /* hang.m in Sources */, - 63B0AC831D595E6300FA21D9 /* AnyPromise.swift in Sources */, - 63D9B2F120338D5D0075C00B /* Deprecations.swift in Sources */, - 63B0AC871D595E6300FA21D9 /* Error.swift in Sources */, - 0CC3AF2B1FCF84F7000E98C9 /* hang.swift in Sources */, - 63B0AC7F1D595E6300FA21D9 /* after.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C0244E512047A6CB00ACB4AC /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - C013F73C20494291006B57B1 /* JSAdapter.swift in Sources */, - C0244E692047AC9F00ACB4AC /* AllTests.swift in Sources */, - C013F740204E5064006B57B1 /* JSUtils.swift in Sources */, - C013F73A2049076A006B57B1 /* JSPromise.swift in Sources */, - C013F7382048E3B6006B57B1 /* MockNodeEnvironment.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 631411331D59795700E24B9E /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 63B0AC561D595E1B00FA21D9 /* PromiseKit */; - targetProxy = 631411341D59795700E24B9E /* PBXContainerItemProxy */; - }; - 6317518C1D59766500A9DDDC /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 63B0AC561D595E1B00FA21D9 /* PromiseKit */; - targetProxy = 6317518D1D59766500A9DDDC /* PBXContainerItemProxy */; - }; - 633027E1203CC0060037E136 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 63B0AC561D595E1B00FA21D9 /* PromiseKit */; - targetProxy = 633027E2203CC0060037E136 /* PBXContainerItemProxy */; - }; - 635D64051D5962F900BC0AF5 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 63B0AC561D595E1B00FA21D9 /* PromiseKit */; - targetProxy = 635D64041D5962F900BC0AF5 /* PBXContainerItemProxy */; - }; - C0244E4F2047A6CB00ACB4AC /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 63B0AC561D595E1B00FA21D9 /* PromiseKit */; - targetProxy = C0244E502047A6CB00ACB4AC /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - 630019201D596292003B4E30 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.12; - SWIFT_INSTALL_OBJC_HEADER = NO; - TVOS_DEPLOYMENT_TARGET = 10.0; - }; - name = Debug; - }; - 630019211D596292003B4E30 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.12; - SWIFT_INSTALL_OBJC_HEADER = NO; - TVOS_DEPLOYMENT_TARGET = 10.0; - }; - name = Release; - }; - 6314113A1D59795700E24B9E /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_OBJC_BRIDGING_HEADER = Tests/Bridging/Infrastructure.h; - }; - name = Debug; - }; - 6314113B1D59795700E24B9E /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_OBJC_BRIDGING_HEADER = Tests/Bridging/Infrastructure.h; - }; - name = Release; - }; - 631751A61D59766500A9DDDC /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - SWIFT_INSTALL_OBJC_HEADER = NO; - }; - name = Debug; - }; - 631751A71D59766500A9DDDC /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks @loader_path/Frameworks"; - "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - SWIFT_INSTALL_OBJC_HEADER = NO; - }; - name = Release; - }; - 633027E8203CC0060037E136 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_SUPPRESS_WARNINGS = YES; - }; - name = Debug; - }; - 633027E9203CC0060037E136 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_SUPPRESS_WARNINGS = YES; - }; - name = Release; - }; - 6399A3761D595D9100D65233 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 6.8.4; - DEBUG_INFORMATION_FORMAT = dwarf; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=1"; - GCC_TREAT_WARNINGS_AS_ERRORS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - LD_DYLIB_INSTALL_NAME = "@rpath"; - MACOSX_DEPLOYMENT_TARGET = 10.10; - ONLY_ACTIVE_ARCH = YES; - PRODUCT_BUNDLE_IDENTIFIER = org.promisekit; - PRODUCT_BUNDLE_PACKAGE_TYPE = BNDL; - PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "macosx appletvsimulator appletvos watchsimulator iphonesimulator watchos iphoneos"; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 4.0; - TVOS_DEPLOYMENT_TARGET = 9.0; - VERSIONING_SYSTEM = "apple-generic"; - WATCHOS_DEPLOYMENT_TARGET = 2.0; - }; - name = Debug; - }; - 6399A3771D595D9100D65233 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 6.8.4; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_NO_COMMON_BLOCKS = YES; - GCC_TREAT_WARNINGS_AS_ERRORS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - LD_DYLIB_INSTALL_NAME = "@rpath"; - MACOSX_DEPLOYMENT_TARGET = 10.10; - PRODUCT_BUNDLE_IDENTIFIER = org.promisekit; - PRODUCT_BUNDLE_PACKAGE_TYPE = BNDL; - PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "macosx appletvsimulator appletvos watchsimulator iphonesimulator watchos iphoneos"; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 4.0; - TVOS_DEPLOYMENT_TARGET = 9.0; - VERSIONING_SYSTEM = "apple-generic"; - WATCHOS_DEPLOYMENT_TARGET = 2.0; - }; - name = Release; - }; - 63B0AC5D1D595E1B00FA21D9 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - APPLICATION_EXTENSION_API_ONLY = YES; - CLANG_WARN_ASSIGN_ENUM = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_CXX0X_EXTENSIONS = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_EXPLICIT_OWNERSHIP_TYPE = YES; - CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS = YES; - CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES; - CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; - CLANG_WARN_UNREACHABLE_CODE = YES_AGGRESSIVE; - CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES; - DEFINES_MODULE = YES; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_TESTABILITY = YES; - GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; - GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; - GCC_WARN_ABOUT_MISSING_NEWLINE = YES; - GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; - GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; - GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; - GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES; - GCC_WARN_SIGN_COMPARE = YES; - GCC_WARN_STRICT_SELECTOR_MATCH = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNKNOWN_PRAGMAS = YES; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_LABEL = YES; - GCC_WARN_UNUSED_PARAMETER = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - LD_DYLIB_INSTALL_NAME = "$(DYLIB_INSTALL_NAME_BASE:standardizepath)/$(EXECUTABLE_PATH)"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_PACKAGE_TYPE = FMWK; - SKIP_INSTALL = YES; - SWIFT_TREAT_WARNINGS_AS_ERRORS = NO; - TARGETED_DEVICE_FAMILY = "1,2,3,4"; - }; - name = Debug; - }; - 63B0AC5E1D595E1B00FA21D9 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - APPLICATION_EXTENSION_API_ONLY = YES; - BITCODE_GENERATION_MODE = bitcode; - CLANG_WARN_ASSIGN_ENUM = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_CXX0X_EXTENSIONS = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_EXPLICIT_OWNERSHIP_TYPE = YES; - CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS = YES; - CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES; - CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; - CLANG_WARN_UNREACHABLE_CODE = YES_AGGRESSIVE; - CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES; - DEFINES_MODULE = YES; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; - GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; - GCC_WARN_ABOUT_MISSING_NEWLINE = YES; - GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; - GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; - GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; - GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES; - GCC_WARN_SIGN_COMPARE = YES; - GCC_WARN_STRICT_SELECTOR_MATCH = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNKNOWN_PRAGMAS = YES; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_LABEL = YES; - GCC_WARN_UNUSED_PARAMETER = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - LD_DYLIB_INSTALL_NAME = "$(DYLIB_INSTALL_NAME_BASE:standardizepath)/$(EXECUTABLE_PATH)"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_PACKAGE_TYPE = FMWK; - SKIP_INSTALL = YES; - SWIFT_TREAT_WARNINGS_AS_ERRORS = NO; - TARGETED_DEVICE_FAMILY = "1,2,3,4"; - }; - name = Release; - }; - C0244E602047A6CB00ACB4AC /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - CLANG_ENABLE_MODULES = YES; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - C0244E612047A6CB00ACB4AC /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - CLANG_ENABLE_MODULES = YES; - LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks @loader_path/Frameworks"; - "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_INSTALL_OBJC_HEADER = NO; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 6300191F1D596292003B4E30 /* Build configuration list for PBXNativeTarget "PMKCoreTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 630019201D596292003B4E30 /* Debug */, - 630019211D596292003B4E30 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 631411391D59795700E24B9E /* Build configuration list for PBXNativeTarget "PMKBridgeTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 6314113A1D59795700E24B9E /* Debug */, - 6314113B1D59795700E24B9E /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 631751A51D59766500A9DDDC /* Build configuration list for PBXNativeTarget "PMKA+Tests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 631751A61D59766500A9DDDC /* Debug */, - 631751A71D59766500A9DDDC /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 633027E7203CC0060037E136 /* Build configuration list for PBXNativeTarget "PMKDeprecatedTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 633027E8203CC0060037E136 /* Debug */, - 633027E9203CC0060037E136 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 6399A3751D595D9100D65233 /* Build configuration list for PBXProject "PromiseKit" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 6399A3761D595D9100D65233 /* Debug */, - 6399A3771D595D9100D65233 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 63B0AC5F1D595E1B00FA21D9 /* Build configuration list for PBXNativeTarget "PromiseKit" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 63B0AC5D1D595E1B00FA21D9 /* Debug */, - 63B0AC5E1D595E1B00FA21D9 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - C0244E5F2047A6CB00ACB4AC /* Build configuration list for PBXNativeTarget "PMKJSA+Tests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - C0244E602047A6CB00ACB4AC /* Debug */, - C0244E612047A6CB00ACB4AC /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 6399A3721D595D9100D65233 /* Project object */; -} diff --git a/PromiseKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/PromiseKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a62..000000000 --- a/PromiseKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/PromiseKit.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/PromiseKit.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003..000000000 --- a/PromiseKit.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/PromiseKit.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/PromiseKit.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index 08de0be8d..000000000 --- a/PromiseKit.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded - - - diff --git a/PromiseKit.xcodeproj/xcshareddata/xcschemes/PromiseKit.xcscheme b/PromiseKit.xcodeproj/xcshareddata/xcschemes/PromiseKit.xcscheme deleted file mode 100644 index d233c2c5a..000000000 --- a/PromiseKit.xcodeproj/xcshareddata/xcschemes/PromiseKit.xcscheme +++ /dev/null @@ -1,150 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/README.md b/README.md index c06b464fb..ea315deb1 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,7 @@ pod used in many of the most popular apps in the world. # PromiseKit 7 Alpha -We are testing PromiseKit 7 alpha, it is Swift 5 only. It is tagged and thus -importable in all package managers. +PromiseKit 7 is pre-release, if you’re using it: beware! # PromiseKit 6 @@ -50,20 +49,18 @@ In your [Podfile]: use_frameworks! target "Change Me!" do - pod "PromiseKit", "~> 6.8" + pod "PromiseKit", :git => 'https://github.com/mxcl/PromiseKit.git', :branch => 'v7' end ``` -> The above gives an Xcode warning? See our [Installation Guide]. +PromiseKit 7 supports Swift 5.x; Xcode >= 10.2; iOS, macOS, tvOS, watchOS, Linux +and Android; SwiftPM. -PromiseKit 6, 5 and 4 support Xcode 8.3, 9.x and 10.0; Swift 3.1, -3.2, 3.3, 3.4, 4.0, 4.1, 4.2, 4.3 and 5.0 (development snapshots); iOS, macOS, -tvOS, watchOS, Linux and Android; CocoaPods, Carthage and SwiftPM; -([CI Matrix](https://travis-ci.org/mxcl/PromiseKit)). +PromiseKits 6 and 4 support Xcode 8.3, 9.x and 10.0; Swift 3.1, 3.2, 3.3, 3.4, +4.0, 4.1, 4.2 and 5.0; iOS, macOS, tvOS, watchOS, Linux and Android; CocoaPods, +Carthage and SwiftPM; ([CI Matrix](https://travis-ci.org/mxcl/PromiseKit)). -For Carthage, SwiftPM, Accio, etc., or for instructions when using older Swifts or Xcodes, see our [Installation Guide]. We recommend -[Carthage](https://github.com/Carthage/Carthage) or -[Accio](https://github.com/JamitLabs/Accio). +For Carthage, SwiftPM, Accio, etc., or for instructions when using older Swifts or Xcodes, see our [Installation Guide]. # Professionally Supported PromiseKit is Now Available @@ -100,7 +97,7 @@ help me continue my work, I appreciate it 🙏🏻 * [Objective-C Guide](Documentation/ObjectiveC.md) * [Troubleshooting](Documentation/Troubleshooting.md) (e.g., solutions to common compile errors) * [Appendix](Documentation/Appendix.md) -* [API Reference](https://mxcl.dev/PromiseKit/reference/v6/Classes/Promise.html) +* [API Reference](https://mxcl.dev/PromiseKit/reference/v7/Classes/Promise.html) # Extensions @@ -111,29 +108,19 @@ extensions are available by specifying additional subspecs in your `Podfile`, e.g.: ```ruby -pod "PromiseKit/MapKit" # MKDirections().calculate().then { /*…*/ } -pod "PromiseKit/CoreLocation" # CLLocationManager.requestLocation().then { /*…*/ } +pod "PMKMapKit" # MKDirections().calculate().then { /*…*/ } +pod "PMKCoreLocation" # CLLocationManager.requestLocation().then { /*…*/ } ``` All our extensions are separate repositories at the [PromiseKit organization]. -## I don't want the extensions! - -Then don’t have them: - -```ruby -pod "PromiseKit/CorePromise", "~> 6.8" -``` - -> *Note:* Carthage installations come with no extensions by default. - ## Choose Your Networking Library Promise chains commonly start with a network operation. Thus, we offer extensions for `URLSession`: ```swift -// pod 'PromiseKit/Foundation' # https://github.com/PromiseKit/Foundation +// pod 'PMKFoundation' # https://github.com/PromiseKit/PMKFoundation firstly { URLSession.shared.dataTask(.promise, with: try makeUrlRequest()).validate() @@ -159,7 +146,7 @@ func makeUrlRequest() throws -> URLRequest { And [Alamofire]: ```swift -// pod 'PromiseKit/Alamofire' # https://github.com/PromiseKit/Alamofire- +// pod 'PMKAlamofire' # https://github.com/PromiseKit/PMKAlamofire firstly { Alamofire @@ -188,6 +175,12 @@ became true, but nowadays it isn’t really necessary. Please check our [Troubleshooting Guide](Documentation/Troubleshooting.md), and if after that you still have a question, ask at our [Gitter chat channel] or on [our bug tracker]. +# Contributing + +Generate the Xcode project: + + swift package generate-xcodeproj + [badge-pod]: https://img.shields.io/cocoapods/v/PromiseKit.svg?label=version [badge-pms]: https://img.shields.io/badge/supports-CocoaPods%20%7C%20Carthage%20%7C%20Accio%20%7C%20SwiftPM-green.svg diff --git a/Sources/AnyPromise+Private.h b/Sources/AnyPromise+Private.h deleted file mode 100644 index cd28c4183..000000000 --- a/Sources/AnyPromise+Private.h +++ /dev/null @@ -1,32 +0,0 @@ -@import Foundation.NSError; -@import Foundation.NSPointerArray; - -#if TARGET_OS_IPHONE - #define NSPointerArrayMake(N) ({ \ - NSPointerArray *aa = [NSPointerArray strongObjectsPointerArray]; \ - aa.count = N; \ - aa; \ - }) -#else - static inline NSPointerArray * __nonnull NSPointerArrayMake(NSUInteger count) { - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wdeprecated-declarations" - NSPointerArray *aa = [[NSPointerArray class] respondsToSelector:@selector(strongObjectsPointerArray)] - ? [NSPointerArray strongObjectsPointerArray] - : [NSPointerArray pointerArrayWithStrongObjects]; - #pragma clang diagnostic pop - aa.count = count; - return aa; - } -#endif - -#define IsError(o) [o isKindOfClass:[NSError class]] -#define IsPromise(o) [o isKindOfClass:[AnyPromise class]] - -#import "AnyPromise.h" - -@class PMKArray; - -@interface AnyPromise () -- (void)__pipe:(void(^ __nonnull)(__nullable id))block NS_REFINED_FOR_SWIFT; -@end diff --git a/Sources/AnyPromise.h b/Sources/AnyPromise.h deleted file mode 100644 index fd1ad3a9b..000000000 --- a/Sources/AnyPromise.h +++ /dev/null @@ -1,299 +0,0 @@ -#import -#import -#import "fwd.h" - -/// INTERNAL DO NOT USE -@class __AnyPromise; - -/// Provided to simplify some usage sites -typedef void (^PMKResolver)(id __nullable) NS_REFINED_FOR_SWIFT; - - -/// An Objective-C implementation of the promise pattern. -@interface AnyPromise: NSObject - -/** - Create a new promise that resolves with the provided block. - - Use this method when wrapping asynchronous code that does *not* use promises so that this code can be used in promise chains. - - If `resolve` is called with an `NSError` object, the promise is rejected, otherwise the promise is fulfilled. - - Don’t use this method if you already have promises! Instead, just return your promise. - - Should you need to fulfill a promise but have no sensical value to use: your promise is a `void` promise: fulfill with `nil`. - - The block you pass is executed immediately on the calling thread. - - - Parameter block: The provided block is immediately executed, inside the block call `resolve` to resolve this promise and cause any attached handlers to execute. If you are wrapping a delegate-based system, we recommend instead to use: initWithResolver: - - Returns: A new promise. - - Warning: Resolving a promise with `nil` fulfills it. - - SeeAlso: https://github.com/mxcl/PromiseKit/blob/master/Documentation/GettingStarted.md#making-promises - - SeeAlso: https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#wrapping-delegate-systems - */ -+ (instancetype __nonnull)promiseWithResolverBlock:(void (^ __nonnull)(__nonnull PMKResolver))resolveBlock NS_REFINED_FOR_SWIFT; - - -/// INTERNAL DO NOT USE -- (instancetype __nonnull)initWith__D:(__AnyPromise * __nonnull)d; - -/** - Creates a resolved promise. - - When developing your own promise systems, it is occasionally useful to be able to return an already resolved promise. - - - Parameter value: The value with which to resolve this promise. Passing an `NSError` will cause the promise to be rejected, passing an AnyPromise will return a new AnyPromise bound to that promise, otherwise the promise will be fulfilled with the value passed. - - Returns: A resolved promise. - */ -+ (instancetype __nonnull)promiseWithValue:(__nullable id)value NS_REFINED_FOR_SWIFT; - -/** - The value of the asynchronous task this promise represents. - - A promise has `nil` value if the asynchronous task it represents has not finished. If the value is `nil` the promise is still `pending`. - - - Warning: *Note* Our Swift variant’s value property returns nil if the promise is rejected where AnyPromise will return the error object. This fits with the pattern where AnyPromise is not strictly typed and is more dynamic, but you should be aware of the distinction. - - - Note: If the AnyPromise was fulfilled with a `PMKManifold`, returns only the first fulfillment object. - - - Returns: The value with which this promise was resolved or `nil` if this promise is pending. - */ -@property (nonatomic, readonly) __nullable id value NS_REFINED_FOR_SWIFT; - -/// - Returns: if the promise is pending resolution. -@property (nonatomic, readonly) BOOL pending NS_REFINED_FOR_SWIFT; - -/// - Returns: if the promise is resolved and fulfilled. -@property (nonatomic, readonly) BOOL fulfilled NS_REFINED_FOR_SWIFT; - -/// - Returns: if the promise is resolved and rejected. -@property (nonatomic, readonly) BOOL rejected NS_REFINED_FOR_SWIFT; - - -/** - The provided block is executed when its receiver is resolved. - - If you provide a block that takes a parameter, the value of the receiver will be passed as that parameter. - - [NSURLSession GET:url].then(^(NSData *data){ - // do something with data - }); - - @return A new promise that is resolved with the value returned from the provided block. For example: - - [NSURLSession GET:url].then(^(NSData *data){ - return data.length; - }).then(^(NSNumber *number){ - //… - }); - - @warning *Important* The block passed to `then` may take zero, one, two or three arguments, and return an object or return nothing. This flexibility is why the method signature for then is `id`, which means you will not get completion for the block parameter, and must type it yourself. It is safe to type any block syntax here, so to start with try just: `^{}`. - - @warning *Important* If an `NSError` or `NSString` is thrown inside your block, or you return an `NSError` object the next `Promise` will be rejected. See `catch` for documentation on error handling. - - @warning *Important* `then` is always executed on the main queue. - - @see thenOn - @see thenInBackground -*/ -- (AnyPromise * __nonnull (^ __nonnull)(id __nonnull))then NS_REFINED_FOR_SWIFT; - - -/** - The provided block is executed on the default queue when the receiver is fulfilled. - - This method is provided as a convenience for `thenOn`. - - @see then - @see thenOn -*/ -- (AnyPromise * __nonnull(^ __nonnull)(id __nonnull))thenInBackground NS_REFINED_FOR_SWIFT; - -/** - The provided block is executed on the dispatch queue of your choice when the receiver is fulfilled. - - @see then - @see thenInBackground -*/ -- (AnyPromise * __nonnull(^ __nonnull)(dispatch_queue_t __nonnull, id __nonnull))thenOn NS_REFINED_FOR_SWIFT; - -#ifndef __cplusplus -/** - The provided block is executed when the receiver is rejected. - - Provide a block of form `^(NSError *){}` or simply `^{}`. The parameter has type `id` to give you the freedom to choose either. - - The provided block always runs on the main queue. - - @warning *Note* Cancellation errors are not caught. - - @warning *Note* Since catch is a c++ keyword, this method is not available in Objective-C++ files. Instead use catchOn. - - @see catchOn - @see catchInBackground -*/ -- (AnyPromise * __nonnull(^ __nonnull)(id __nonnull))catch NS_REFINED_FOR_SWIFT; -#endif - -/** - The provided block is executed when the receiver is rejected. - - Provide a block of form `^(NSError *){}` or simply `^{}`. The parameter has type `id` to give you the freedom to choose either. - - The provided block always runs on the global background queue. - - @warning *Note* Cancellation errors are not caught. - - @warning *Note* Since catch is a c++ keyword, this method is not available in Objective-C++ files. Instead use catchWithPolicy. - - @see catch - @see catchOn - */ -- (AnyPromise * __nonnull(^ __nonnull)(id __nonnull))catchInBackground NS_REFINED_FOR_SWIFT; - - -/** - The provided block is executed when the receiver is rejected. - - Provide a block of form `^(NSError *){}` or simply `^{}`. The parameter has type `id` to give you the freedom to choose either. - - The provided block always runs on queue provided. - - @warning *Note* Cancellation errors are not caught. - - @see catch - @see catchInBackground - */ -- (AnyPromise * __nonnull(^ __nonnull)(dispatch_queue_t __nonnull, id __nonnull))catchOn NS_REFINED_FOR_SWIFT; - -/** - The provided block is executed when the receiver is resolved. - - The provided block always runs on the main queue. - - @see ensureOn -*/ -- (AnyPromise * __nonnull(^ __nonnull)(dispatch_block_t __nonnull))ensure NS_REFINED_FOR_SWIFT; - -/** - The provided block is executed on the dispatch queue of your choice when the receiver is resolved. - - @see ensure - */ -- (AnyPromise * __nonnull(^ __nonnull)(dispatch_queue_t __nonnull, dispatch_block_t __nonnull))ensureOn NS_REFINED_FOR_SWIFT; - -/** - Create a new promise with an associated resolver. - - Use this method when wrapping asynchronous code that does *not* use - promises so that this code can be used in promise chains. Generally, - prefer `promiseWithResolverBlock:` as the resulting code is more elegant. - - PMKResolver resolve; - AnyPromise *promise = [[AnyPromise alloc] initWithResolver:&resolve]; - - // later - resolve(@"foo"); - - @param resolver A reference to a block pointer of PMKResolver type. - You can then call your resolver to resolve this promise. - - @return A new promise. - - @warning *Important* The resolver strongly retains the promise. - - @see promiseWithResolverBlock: -*/ -- (instancetype __nonnull)initWithResolver:(PMKResolver __strong __nonnull * __nonnull)resolver NS_REFINED_FOR_SWIFT; - -@end - - -typedef void (^PMKAdapter)(id __nullable, NSError * __nullable) NS_REFINED_FOR_SWIFT; -typedef void (^PMKIntegerAdapter)(NSInteger, NSError * __nullable) NS_REFINED_FOR_SWIFT; -typedef void (^PMKBooleanAdapter)(BOOL, NSError * __nullable) NS_REFINED_FOR_SWIFT; - - -@interface AnyPromise (Adapters) - -/** - Create a new promise by adapting an existing asynchronous system. - - The pattern of a completion block that passes two parameters, the first - the result and the second an `NSError` object is so common that we - provide this convenience adapter to make wrapping such systems more - elegant. - - return [PMKPromise promiseWithAdapterBlock:^(PMKAdapter adapter){ - PFQuery *query = [PFQuery …]; - [query findObjectsInBackgroundWithBlock:adapter]; - }]; - - @warning *Important* If both parameters are nil, the promise fulfills, - if both are non-nil the promise rejects. This is per the convention. - - @see https://github.com/mxcl/PromiseKit/blob/master/Documentation/GettingStarted.md#making-promises - */ -+ (instancetype __nonnull)promiseWithAdapterBlock:(void (^ __nonnull)(PMKAdapter __nonnull adapter))block NS_REFINED_FOR_SWIFT; - -/** - Create a new promise by adapting an existing asynchronous system. - - Adapts asynchronous systems that complete with `^(NSInteger, NSError *)`. - NSInteger will cast to enums provided the enum has been wrapped with - `NS_ENUM`. All of Apple’s enums are, so if you find one that hasn’t you - may need to make a pull-request. - - @see promiseWithAdapter - */ -+ (instancetype __nonnull)promiseWithIntegerAdapterBlock:(void (^ __nonnull)(PMKIntegerAdapter __nonnull adapter))block NS_REFINED_FOR_SWIFT; - -/** - Create a new promise by adapting an existing asynchronous system. - - Adapts asynchronous systems that complete with `^(BOOL, NSError *)`. - - @see promiseWithAdapter - */ -+ (instancetype __nonnull)promiseWithBooleanAdapterBlock:(void (^ __nonnull)(PMKBooleanAdapter __nonnull adapter))block NS_REFINED_FOR_SWIFT; - -@end - - -#ifdef __cplusplus -extern "C" { -#endif - -/** - Whenever resolving a promise you may resolve with a tuple, eg. - returning from a `then` or `catch` handler or resolving a new promise. - - Consumers of your Promise are not compelled to consume any arguments and - in fact will often only consume the first parameter. Thus ensure the - order of parameters is: from most-important to least-important. - - Currently PromiseKit limits you to THREE parameters to the manifold. -*/ -#define PMKManifold(...) __PMKManifold(__VA_ARGS__, 3, 2, 1) -#define __PMKManifold(_1, _2, _3, N, ...) __PMKArrayWithCount(N, _1, _2, _3) -extern id __nonnull __PMKArrayWithCount(NSUInteger, ...); - -#ifdef __cplusplus -} // Extern C -#endif - - -@interface AnyPromise (Unavailable) - -- (instancetype __nonnull)init __attribute__((unavailable("It is illegal to create an unresolvable promise."))); -+ (instancetype __nonnull)new __attribute__((unavailable("It is illegal to create an unresolvable promise."))); -- (AnyPromise * __nonnull(^ __nonnull)(dispatch_block_t __nonnull))always __attribute__((unavailable("See -ensure"))); -- (AnyPromise * __nonnull(^ __nonnull)(dispatch_block_t __nonnull))alwaysOn __attribute__((unavailable("See -ensureOn"))); -- (AnyPromise * __nonnull(^ __nonnull)(dispatch_block_t __nonnull))finally __attribute__((unavailable("See -ensure"))); -- (AnyPromise * __nonnull(^ __nonnull)(dispatch_block_t __nonnull, dispatch_block_t __nonnull))finallyOn __attribute__((unavailable("See -ensureOn"))); - -@end - -__attribute__((unavailable("See AnyPromise"))) -@interface PMKPromise -@end diff --git a/Sources/AnyPromise.m b/Sources/AnyPromise.m deleted file mode 100644 index af6631004..000000000 --- a/Sources/AnyPromise.m +++ /dev/null @@ -1,175 +0,0 @@ -#if __has_include("PromiseKit-Swift.h") - #import "PromiseKit-Swift.h" -#else - #import -#endif -#import "PMKCallVariadicBlock.m" -#import "AnyPromise+Private.h" -#import "AnyPromise.h" - -NSString *const PMKErrorDomain = @"PMKErrorDomain"; - - -@implementation AnyPromise { - __AnyPromise *d; -} - -- (instancetype)initWith__D:(__AnyPromise *)dd { - self = [super init]; - if (self) self->d = dd; - return self; -} - -- (instancetype)initWithResolver:(PMKResolver __strong *)resolver { - self = [super init]; - if (self) - d = [[__AnyPromise alloc] initWithResolver:^(void (^resolve)(id)) { - *resolver = resolve; - }]; - return self; -} - -+ (instancetype)promiseWithResolverBlock:(void (^)(PMKResolver _Nonnull))resolveBlock { - id d = [[__AnyPromise alloc] initWithResolver:resolveBlock]; - return [[self alloc] initWith__D:d]; -} - -+ (instancetype)promiseWithValue:(id)value { - //TODO provide a more efficient route for sealed promises - id d = [[__AnyPromise alloc] initWithResolver:^(void (^resolve)(id)) { - resolve(value); - }]; - return [[self alloc] initWith__D:d]; -} - -//TODO remove if possible, but used by when.m -- (void)__pipe:(void (^)(id _Nullable))block { - [d __pipe:block]; -} - -//NOTE used by AnyPromise.swift -- (id)__d { - return d; -} - -- (AnyPromise *(^)(id))then { - return ^(id block) { - return [self->d __thenOn:dispatch_get_main_queue() execute:^(id obj) { - return PMKCallVariadicBlock(block, obj); - }]; - }; -} - -- (AnyPromise *(^)(dispatch_queue_t, id))thenOn { - return ^(dispatch_queue_t queue, id block) { - return [self->d __thenOn:queue execute:^(id obj) { - return PMKCallVariadicBlock(block, obj); - }]; - }; -} - -- (AnyPromise *(^)(id))thenInBackground { - return ^(id block) { - return [self->d __thenOn:dispatch_get_global_queue(0, 0) execute:^(id obj) { - return PMKCallVariadicBlock(block, obj); - }]; - }; -} - -- (AnyPromise *(^)(dispatch_queue_t, id))catchOn { - return ^(dispatch_queue_t q, id block) { - return [self->d __catchOn:q execute:^(id obj) { - return PMKCallVariadicBlock(block, obj); - }]; - }; -} - -- (AnyPromise *(^)(id))catch { - return ^(id block) { - return [self->d __catchOn:dispatch_get_main_queue() execute:^(id obj) { - return PMKCallVariadicBlock(block, obj); - }]; - }; -} - -- (AnyPromise *(^)(id))catchInBackground { - return ^(id block) { - return [self->d __catchOn:dispatch_get_global_queue(0, 0) execute:^(id obj) { - return PMKCallVariadicBlock(block, obj); - }]; - }; -} - -- (AnyPromise *(^)(dispatch_block_t))ensure { - return ^(dispatch_block_t block) { - return [self->d __ensureOn:dispatch_get_main_queue() execute:block]; - }; -} - -- (AnyPromise *(^)(dispatch_queue_t, dispatch_block_t))ensureOn { - return ^(dispatch_queue_t queue, dispatch_block_t block) { - return [self->d __ensureOn:queue execute:block]; - }; -} - -- (BOOL)pending { - return [[d valueForKey:@"__pending"] boolValue]; -} - -- (BOOL)rejected { - return IsError([d __value]); -} - -- (BOOL)fulfilled { - return !self.rejected; -} - -- (id)value { - id obj = [d __value]; - - if ([obj isKindOfClass:[PMKArray class]]) { - return obj[0]; - } else { - return obj; - } -} - -@end - - - -@implementation AnyPromise (Adapters) - -+ (instancetype)promiseWithAdapterBlock:(void (^)(PMKAdapter))block { - return [self promiseWithResolverBlock:^(PMKResolver resolve) { - block(^(id value, id error){ - resolve(error ?: value); - }); - }]; -} - -+ (instancetype)promiseWithIntegerAdapterBlock:(void (^)(PMKIntegerAdapter))block { - return [self promiseWithResolverBlock:^(PMKResolver resolve) { - block(^(NSInteger value, id error){ - if (error) { - resolve(error); - } else { - resolve(@(value)); - } - }); - }]; -} - -+ (instancetype)promiseWithBooleanAdapterBlock:(void (^)(PMKBooleanAdapter adapter))block { - return [self promiseWithResolverBlock:^(PMKResolver resolve) { - block(^(BOOL value, id error){ - if (error) { - resolve(error); - } else { - resolve(@(value)); - } - }); - }]; -} - -@end diff --git a/Sources/AnyPromise.swift b/Sources/AnyPromise.swift deleted file mode 100644 index 5dfa1df4c..000000000 --- a/Sources/AnyPromise.swift +++ /dev/null @@ -1,204 +0,0 @@ -import Foundation - -/** - __AnyPromise is an implementation detail. - - Because of how ObjC/Swift compatability work we have to compose our AnyPromise - with this internal object, however this is still part of the public interface. - Sadly. Please don’t use it. -*/ -@objc(__AnyPromise) public class __AnyPromise: NSObject { - fileprivate let box: Box - - @objc public init(resolver body: (@escaping (Any?) -> Void) -> Void) { - box = EmptyBox() - super.init() - body { - if let p = $0 as? AnyPromise { - p.d.__pipe(self.box.seal) - } else { - self.box.seal($0) - } - } - } - - @objc public func __thenOn(_ q: DispatchQueue, execute: @escaping (Any?) -> Any?) -> AnyPromise { - return AnyPromise(__D: __AnyPromise(resolver: { resolve in - self.__pipe { obj in - if !(obj is NSError) { - q.async { - resolve(execute(obj)) - } - } else { - resolve(obj) - } - } - })) - } - - @objc public func __catchOn(_ q: DispatchQueue, execute: @escaping (Any?) -> Any?) -> AnyPromise { - return AnyPromise(__D: __AnyPromise(resolver: { resolve in - self.__pipe { obj in - if obj is NSError { - q.async { - resolve(execute(obj)) - } - } else { - resolve(obj) - } - } - })) - } - - @objc public func __ensureOn(_ q: DispatchQueue, execute: @escaping () -> Void) -> AnyPromise { - return AnyPromise(__D: __AnyPromise(resolver: { resolve in - self.__pipe { obj in - q.async { - execute() - resolve(obj) - } - } - })) - } - - /// Internal, do not use! Some behaviors undefined. - @objc public func __pipe(_ to: @escaping (Any?) -> Void) { - let to = { (obj: Any?) -> Void in - if obj is NSError { - to(obj) // or we cannot determine if objects are errors in objc land - } else { - to(obj) - } - } - switch box.inspect() { - case .pending: - box.inspect { - switch $0 { - case .pending(let handlers): - handlers.append { obj in - to(obj) - } - case .resolved(let obj): - to(obj) - } - } - case .resolved(let obj): - to(obj) - } - } - - @objc public var __value: Any? { - switch box.inspect() { - case .resolved(let obj): - return obj - default: - return nil - } - } - - @objc public var __pending: Bool { - switch box.inspect() { - case .pending: - return true - case .resolved: - return false - } - } -} - -extension AnyPromise: Thenable, CatchMixin { - - /// - Returns: A new `AnyPromise` bound to a `Promise`. - public convenience init(_ bridge: U) { - self.init(__D: __AnyPromise(resolver: { resolve in - bridge.pipe { - switch $0 { - case .rejected(let error): - resolve(error as NSError) - case .fulfilled(let value): - resolve(value) - } - } - })) - } - - public func pipe(to body: @escaping (Result) -> Void) { - - func fulfill() { - // calling through to the ObjC `value` property unwraps (any) PMKManifold - // and considering this is the Swift pipe; we want that. - body(.fulfilled(self.value(forKey: "value"))) - } - - switch box.inspect() { - case .pending: - box.inspect { - switch $0 { - case .pending(let handlers): - handlers.append { - if let error = $0 as? Error { - body(.rejected(error)) - } else { - fulfill() - } - } - case .resolved(let error as Error): - body(.rejected(error)) - case .resolved: - fulfill() - } - } - case .resolved(let error as Error): - body(.rejected(error)) - case .resolved: - fulfill() - } - } - - fileprivate var d: __AnyPromise { - return value(forKey: "__d") as! __AnyPromise - } - - var box: Box { - return d.box - } - - public var result: Result? { - guard let value = __value else { - return nil - } - if let error = value as? Error { - return .rejected(error) - } else { - return .fulfilled(value) - } - } - - public typealias T = Any? -} - - -#if swift(>=3.1) -public extension Promise where T == Any? { - convenience init(_ anyPromise: AnyPromise) { - self.init { - anyPromise.pipe(to: $0.resolve) - } - } -} -#else -extension AnyPromise { - public func asPromise() -> Promise { - return Promise(.pending, resolver: { resolve in - pipe { result in - switch result { - case .rejected(let error): - resolve.reject(error) - case .fulfilled(let obj): - resolve.fulfill(obj) - } - } - }) - } -} -#endif diff --git a/Sources/CustomStringConvertible.swift b/Sources/CustomStringConvertible.swift index eee0b020a..639883e97 100644 --- a/Sources/CustomStringConvertible.swift +++ b/Sources/CustomStringConvertible.swift @@ -26,19 +26,3 @@ extension Promise: CustomDebugStringConvertible { } } } - -#if !SWIFT_PACKAGE -extension AnyPromise { - /// - Returns: A description of the state of this promise. - override open var description: String { - switch box.inspect() { - case .pending: - return "AnyPromise(…)" - case .resolved(let obj?): - return "AnyPromise(\(obj))" - case .resolved(nil): - return "AnyPromise(nil)" - } - } -} -#endif diff --git a/Sources/Deprecations.swift b/Sources/Deprecations.swift deleted file mode 100644 index a837dcb8d..000000000 --- a/Sources/Deprecations.swift +++ /dev/null @@ -1,93 +0,0 @@ -import Dispatch - -@available(*, deprecated, message: "See `init(resolver:)`") -public func wrap(_ body: (@escaping (T?, Error?) -> Void) throws -> Void) -> Promise { - return Promise { seal in - try body(seal.resolve) - } -} - -@available(*, deprecated, message: "See `init(resolver:)`") -public func wrap(_ body: (@escaping (T, Error?) -> Void) throws -> Void) -> Promise { - return Promise { seal in - try body(seal.resolve) - } -} - -@available(*, deprecated, message: "See `init(resolver:)`") -public func wrap(_ body: (@escaping (Error?, T?) -> Void) throws -> Void) -> Promise { - return Promise { seal in - try body(seal.resolve) - } -} - -@available(*, deprecated, message: "See `init(resolver:)`") -public func wrap(_ body: (@escaping (Error?) -> Void) throws -> Void) -> Promise { - return Promise { seal in - try body(seal.resolve) - } -} - -@available(*, deprecated, message: "See `init(resolver:)`") -public func wrap(_ body: (@escaping (T) -> Void) throws -> Void) -> Promise { - return Promise { seal in - try body(seal.fulfill) - } -} - -public extension Promise { - @available(*, deprecated, message: "See `ensure`") - func always(on q: DispatchQueue = .main, execute body: @escaping () -> Void) -> Promise { - return ensure(on: q, body) - } -} - -public extension Thenable { -#if PMKFullDeprecations - /// disabled due to ambiguity with the other `.flatMap` - @available(*, deprecated, message: "See: `compactMap`") - func flatMap(on: DispatchQueue? = conf.Q.map, _ transform: @escaping(T) throws -> U?) -> Promise { - return compactMap(on: on, transform) - } -#endif -} - -public extension Thenable where T: Sequence { -#if PMKFullDeprecations - /// disabled due to ambiguity with the other `.map` - @available(*, deprecated, message: "See: `mapValues`") - func map(on: DispatchQueue? = conf.Q.map, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U]> { - return mapValues(on: on, transform) - } - - /// disabled due to ambiguity with the other `.flatMap` - @available(*, deprecated, message: "See: `flatMapValues`") - func flatMap(on: DispatchQueue? = conf.Q.map, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U.Iterator.Element]> { - return flatMapValues(on: on, transform) - } -#endif - - @available(*, deprecated, message: "See: `filterValues`") - func filter(on: DispatchQueue? = conf.Q.map, test: @escaping (T.Iterator.Element) -> Bool) -> Promise<[T.Iterator.Element]> { - return filterValues(on: on, test) - } -} - -public extension Thenable where T: Collection { - @available(*, deprecated, message: "See: `firstValue`") - var first: Promise { - return firstValue - } - - @available(*, deprecated, message: "See: `lastValue`") - var last: Promise { - return lastValue - } -} - -public extension Thenable where T: Sequence, T.Iterator.Element: Comparable { - @available(*, deprecated, message: "See: `sortedValues`") - func sorted(on: DispatchQueue? = conf.Q.map) -> Promise<[T.Iterator.Element]> { - return sortedValues(on: on) - } -} diff --git a/Sources/Guarantee.swift b/Sources/Guarantee.swift index b87fad189..1897aa644 100644 --- a/Sources/Guarantee.swift +++ b/Sources/Guarantee.swift @@ -166,14 +166,22 @@ public extension Guarantee where T: Sequence { } } -#if swift(>=3.1) public extension Guarantee where T == Void { convenience init() { self.init(box: SealedBox(value: Void())) } -} -#endif +#if swift(>=5.1) + // ^^ ambiguous in Swift 5.0, testing again in next version + convenience init(resolver body: (@escaping() -> Void) -> Void) { + self.init(resolver: { seal in + body { + seal(()) + } + }) + } +#endif +} public extension DispatchQueue { /** @@ -199,7 +207,6 @@ public extension DispatchQueue { } } - #if os(Linux) import func CoreFoundation._CFIsMainThread diff --git a/Sources/Info.plist b/Sources/Info.plist deleted file mode 100644 index 3a619de4d..000000000 --- a/Sources/Info.plist +++ /dev/null @@ -1,28 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - $(CURRENT_PROJECT_VERSION) - CFBundleSignature - ???? - CFBundleVersion - 1 - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - - - diff --git a/Sources/NSMethodSignatureForBlock.m b/Sources/NSMethodSignatureForBlock.m deleted file mode 100644 index 700c1b37e..000000000 --- a/Sources/NSMethodSignatureForBlock.m +++ /dev/null @@ -1,77 +0,0 @@ -#import - -struct PMKBlockLiteral { - void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock - int flags; - int reserved; - void (*invoke)(void *, ...); - struct block_descriptor { - unsigned long int reserved; // NULL - unsigned long int size; // sizeof(struct Block_literal_1) - // optional helper functions - void (*copy_helper)(void *dst, void *src); // IFF (1<<25) - void (*dispose_helper)(void *src); // IFF (1<<25) - // required ABI.2010.3.16 - const char *signature; // IFF (1<<30) - } *descriptor; - // imported variables -}; - -typedef NS_OPTIONS(NSUInteger, PMKBlockDescriptionFlags) { - PMKBlockDescriptionFlagsHasCopyDispose = (1 << 25), - PMKBlockDescriptionFlagsHasCtor = (1 << 26), // helpers have C++ code - PMKBlockDescriptionFlagsIsGlobal = (1 << 28), - PMKBlockDescriptionFlagsHasStret = (1 << 29), // IFF BLOCK_HAS_SIGNATURE - PMKBlockDescriptionFlagsHasSignature = (1 << 30) -}; - -// It appears 10.7 doesn't support quotes in method signatures. Remove them -// via @rabovik's method. See https://github.com/OliverLetterer/SLObjectiveCRuntimeAdditions/pull/2 -#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_8 -NS_INLINE static const char * pmk_removeQuotesFromMethodSignature(const char *str){ - char *result = malloc(strlen(str) + 1); - BOOL skip = NO; - char *to = result; - char c; - while ((c = *str++)) { - if ('"' == c) { - skip = !skip; - continue; - } - if (skip) continue; - *to++ = c; - } - *to = '\0'; - return result; -} -#endif - -static NSMethodSignature *NSMethodSignatureForBlock(id block) { - if (!block) - return nil; - - struct PMKBlockLiteral *blockRef = (__bridge struct PMKBlockLiteral *)block; - PMKBlockDescriptionFlags flags = (PMKBlockDescriptionFlags)blockRef->flags; - - if (flags & PMKBlockDescriptionFlagsHasSignature) { - void *signatureLocation = blockRef->descriptor; - signatureLocation += sizeof(unsigned long int); - signatureLocation += sizeof(unsigned long int); - - if (flags & PMKBlockDescriptionFlagsHasCopyDispose) { - signatureLocation += sizeof(void(*)(void *dst, void *src)); - signatureLocation += sizeof(void (*)(void *src)); - } - - const char *signature = (*(const char **)signatureLocation); -#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_8 - signature = pmk_removeQuotesFromMethodSignature(signature); - NSMethodSignature *nsSignature = [NSMethodSignature signatureWithObjCTypes:signature]; - free((void *)signature); - - return nsSignature; -#endif - return [NSMethodSignature signatureWithObjCTypes:signature]; - } - return 0; -} diff --git a/Sources/PMKCallVariadicBlock.m b/Sources/PMKCallVariadicBlock.m deleted file mode 100644 index 1453a7d26..000000000 --- a/Sources/PMKCallVariadicBlock.m +++ /dev/null @@ -1,120 +0,0 @@ -#import "NSMethodSignatureForBlock.m" -#import -#import -#import "AnyPromise+Private.h" -#import -#import -#import - -#ifndef PMKLog -#define PMKLog NSLog -#endif - -@interface PMKArray : NSObject { -@public - id objs[3]; - NSUInteger count; -} @end - -@implementation PMKArray - -- (id)objectAtIndexedSubscript:(NSUInteger)idx { - if (count <= idx) { - // this check is necessary due to lack of checks in `pmk_safely_call_block` - return nil; - } - return objs[idx]; -} - -@end - -id __PMKArrayWithCount(NSUInteger count, ...) { - PMKArray *this = [PMKArray new]; - this->count = count; - va_list args; - va_start(args, count); - for (NSUInteger x = 0; x < count; ++x) - this->objs[x] = va_arg(args, id); - va_end(args); - return this; -} - - -static inline id _PMKCallVariadicBlock(id frock, id result) { - NSCAssert(frock, @""); - - NSMethodSignature *sig = NSMethodSignatureForBlock(frock); - const NSUInteger nargs = sig.numberOfArguments; - const char rtype = sig.methodReturnType[0]; - - #define call_block_with_rtype(type) ({^type{ \ - switch (nargs) { \ - case 1: \ - return ((type(^)(void))frock)(); \ - case 2: { \ - const id arg = [result class] == [PMKArray class] ? result[0] : result; \ - return ((type(^)(id))frock)(arg); \ - } \ - case 3: { \ - type (^block)(id, id) = frock; \ - return [result class] == [PMKArray class] \ - ? block(result[0], result[1]) \ - : block(result, nil); \ - } \ - case 4: { \ - type (^block)(id, id, id) = frock; \ - return [result class] == [PMKArray class] \ - ? block(result[0], result[1], result[2]) \ - : block(result, nil, nil); \ - } \ - default: \ - @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"PromiseKit: The provided block’s argument count is unsupported." userInfo:nil]; \ - }}();}) - - switch (rtype) { - case 'v': - call_block_with_rtype(void); - return nil; - case '@': - return call_block_with_rtype(id) ?: nil; - case '*': { - char *str = call_block_with_rtype(char *); - return str ? @(str) : nil; - } - case 'c': return @(call_block_with_rtype(char)); - case 'i': return @(call_block_with_rtype(int)); - case 's': return @(call_block_with_rtype(short)); - case 'l': return @(call_block_with_rtype(long)); - case 'q': return @(call_block_with_rtype(long long)); - case 'C': return @(call_block_with_rtype(unsigned char)); - case 'I': return @(call_block_with_rtype(unsigned int)); - case 'S': return @(call_block_with_rtype(unsigned short)); - case 'L': return @(call_block_with_rtype(unsigned long)); - case 'Q': return @(call_block_with_rtype(unsigned long long)); - case 'f': return @(call_block_with_rtype(float)); - case 'd': return @(call_block_with_rtype(double)); - case 'B': return @(call_block_with_rtype(_Bool)); - case '^': - if (strcmp(sig.methodReturnType, "^v") == 0) { - call_block_with_rtype(void); - return nil; - } - // else fall through! - default: - @throw [NSException exceptionWithName:@"PromiseKit" reason:@"PromiseKit: Unsupported method signature." userInfo:nil]; - } -} - -static id PMKCallVariadicBlock(id frock, id result) { - @try { - return _PMKCallVariadicBlock(frock, result); - } @catch (id thrown) { - if ([thrown isKindOfClass:[NSString class]]) - return thrown; - if ([thrown isKindOfClass:[NSError class]]) - return thrown; - - // we don’t catch objc exceptions: they are meant to crash your app - @throw thrown; - } -} diff --git a/Sources/Promise.swift b/Sources/Promise.swift index da9f6aa2b..177b2d008 100644 --- a/Sources/Promise.swift +++ b/Sources/Promise.swift @@ -130,15 +130,12 @@ public extension Promise { } } -#if swift(>=3.1) extension Promise where T == Void { /// Initializes a new promise fulfilled with `Void` public convenience init() { self.init(box: SealedBox(value: .fulfilled(Void()))) } } -#endif - public extension DispatchQueue { /** @@ -168,7 +165,6 @@ public extension DispatchQueue { } } - /// used by our extensions to provide unambiguous functions with the same name as the original function public enum PMKNamespacer { case promise diff --git a/Sources/PromiseKit.h b/Sources/PromiseKit.h deleted file mode 100644 index 2651530cb..000000000 --- a/Sources/PromiseKit.h +++ /dev/null @@ -1,7 +0,0 @@ -#import "fwd.h" -#import "AnyPromise.h" - -#import // `FOUNDATION_EXPORT` - -FOUNDATION_EXPORT double PromiseKitVersionNumber; -FOUNDATION_EXPORT const unsigned char PromiseKitVersionString[]; diff --git a/Sources/Resolver.swift b/Sources/Resolver.swift index 78531adbd..d8439da2c 100644 --- a/Sources/Resolver.swift +++ b/Sources/Resolver.swift @@ -55,7 +55,6 @@ public extension Resolver { } } -#if swift(>=3.1) extension Resolver where T == Void { /// Fulfills the promise unless error is non-nil public func resolve(_ error: Error?) { @@ -65,22 +64,13 @@ extension Resolver where T == Void { fulfill(()) } } -#if false - // disabled ∵ https://github.com/mxcl/PromiseKit/issues/990 - /// Fulfills the promise - public func fulfill() { - self.fulfill(()) - } -#else /// Fulfills the promise /// - Note: underscore is present due to: https://github.com/mxcl/PromiseKit/issues/990 - public func fulfill_() { + public func fulfill() { self.fulfill(()) } -#endif } -#endif public enum Result { case fulfilled(T) diff --git a/Sources/Thenable.swift b/Sources/Thenable.swift index 776237207..3c18db2f4 100644 --- a/Sources/Thenable.swift +++ b/Sources/Thenable.swift @@ -320,11 +320,7 @@ public extension Thenable where T: Sequence { */ func compactMapValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) throws -> U?) -> Promise<[U]> { return map(on: on, flags: flags) { foo -> [U] in - #if !swift(>=3.3) || (swift(>=4) && !swift(>=4.1)) - return try foo.flatMap(transform) - #else return try foo.compactMap(transform) - #endif } } diff --git a/Sources/after.m b/Sources/after.m deleted file mode 100644 index 25f9966fc..000000000 --- a/Sources/after.m +++ /dev/null @@ -1,14 +0,0 @@ -#import "AnyPromise.h" -@import Dispatch; -@import Foundation.NSDate; -@import Foundation.NSValue; - -/// @return A promise that fulfills after the specified duration. -AnyPromise *PMKAfter(NSTimeInterval duration) { - return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC)); - dispatch_after(time, dispatch_get_global_queue(0, 0), ^{ - resolve(@(duration)); - }); - }]; -} diff --git a/Sources/after.swift b/Sources/after.swift index cdaeccd9e..ca31911fc 100644 --- a/Sources/after.swift +++ b/Sources/after.swift @@ -11,11 +11,7 @@ import Dispatch public func after(seconds: TimeInterval) -> Guarantee { let (rg, seal) = Guarantee.pending() let when = DispatchTime.now() + seconds -#if swift(>=4.0) - q.asyncAfter(deadline: when) { seal(()) } -#else - q.asyncAfter(deadline: when, execute: seal) -#endif + q.asyncAfter(deadline: when, execute: { seal(()) }) return rg } @@ -29,11 +25,7 @@ public func after(seconds: TimeInterval) -> Guarantee { public func after(_ interval: DispatchTimeInterval) -> Guarantee { let (rg, seal) = Guarantee.pending() let when = DispatchTime.now() + interval -#if swift(>=4.0) - q.asyncAfter(deadline: when) { seal(()) } -#else - q.asyncAfter(deadline: when, execute: seal) -#endif + q.asyncAfter(deadline: when, execute: { seal(()) }) return rg } diff --git a/Sources/dispatch_promise.m b/Sources/dispatch_promise.m deleted file mode 100644 index ecb89f711..000000000 --- a/Sources/dispatch_promise.m +++ /dev/null @@ -1,10 +0,0 @@ -#import "AnyPromise.h" -@import Dispatch; - -AnyPromise *dispatch_promise_on(dispatch_queue_t queue, id block) { - return [AnyPromise promiseWithValue:nil].thenOn(queue, block); -} - -AnyPromise *dispatch_promise(id block) { - return dispatch_promise_on(dispatch_get_global_queue(0, 0), block); -} diff --git a/Sources/fwd.h b/Sources/fwd.h deleted file mode 100644 index 480d1480e..000000000 --- a/Sources/fwd.h +++ /dev/null @@ -1,165 +0,0 @@ -#import -#import - -@class AnyPromise; -extern NSString * __nonnull const PMKErrorDomain; - -#define PMKFailingPromiseIndexKey @"PMKFailingPromiseIndexKey" -#define PMKJoinPromisesKey @"PMKJoinPromisesKey" - -#define PMKUnexpectedError 1l -#define PMKInvalidUsageError 3l -#define PMKAccessDeniedError 4l -#define PMKOperationFailed 8l -#define PMKTaskError 9l -#define PMKJoinError 10l - - -#ifdef __cplusplus -extern "C" { -#endif - -/** - @return A new promise that resolves after the specified duration. - - @parameter duration The duration in seconds to wait before this promise is resolve. - - For example: - - PMKAfter(1).then(^{ - //… - }); -*/ -extern AnyPromise * __nonnull PMKAfter(NSTimeInterval duration) NS_REFINED_FOR_SWIFT; - - - -/** - `when` is a mechanism for waiting more than one asynchronous task and responding when they are all complete. - - `PMKWhen` accepts varied input. If an array is passed then when those promises fulfill, when’s promise fulfills with an array of fulfillment values. If a dictionary is passed then the same occurs, but when’s promise fulfills with a dictionary of fulfillments keyed as per the input. - - Interestingly, if a single promise is passed then when waits on that single promise, and if a single non-promise object is passed then when fulfills immediately with that object. If the array or dictionary that is passed contains objects that are not promises, then these objects are considered fulfilled promises. The reason we do this is to allow a pattern know as "abstracting away asynchronicity". - - If *any* of the provided promises reject, the returned promise is immediately rejected with that promise’s rejection. The error’s `userInfo` object is supplemented with `PMKFailingPromiseIndexKey`. - - For example: - - PMKWhen(@[promise1, promise2]).then(^(NSArray *results){ - //… - }); - - @warning *Important* In the event of rejection the other promises will continue to resolve and as per any other promise will either fulfill or reject. This is the right pattern for `getter` style asynchronous tasks, but often for `setter` tasks (eg. storing data on a server), you most likely will need to wait on all tasks and then act based on which have succeeded and which have failed. In such situations use `PMKJoin`. - - @param input The input upon which to wait before resolving this promise. - - @return A promise that is resolved with either: - - 1. An array of values from the provided array of promises. - 2. The value from the provided promise. - 3. The provided non-promise object. - - @see PMKJoin - -*/ -extern AnyPromise * __nonnull PMKWhen(id __nonnull input) NS_REFINED_FOR_SWIFT; - - - -/** - Creates a new promise that resolves only when all provided promises have resolved. - - Typically, you should use `PMKWhen`. - - For example: - - PMKJoin(@[promise1, promise2]).then(^(NSArray *resultingValues){ - //… - }).catch(^(NSError *error){ - assert(error.domain == PMKErrorDomain); - assert(error.code == PMKJoinError); - - NSArray *promises = error.userInfo[PMKJoinPromisesKey]; - for (AnyPromise *promise in promises) { - if (promise.rejected) { - //… - } - } - }); - - @param promises An array of promises. - - @return A promise that thens three parameters: - - 1) An array of mixed values and errors from the resolved input. - 2) An array of values from the promises that fulfilled. - 3) An array of errors from the promises that rejected or nil if all promises fulfilled. - - @see when -*/ -AnyPromise *__nonnull PMKJoin(NSArray * __nonnull promises) NS_REFINED_FOR_SWIFT; - - - -/** - Literally hangs this thread until the promise has resolved. - - Do not use hang… unless you are testing, playing or debugging. - - If you use it in production code I will literally and honestly cry like a child. - - @return The resolved value of the promise. - - @warning T SAFE. IT IS NOT SAFE. IT IS NOT SAFE. IT IS NOT SAFE. IT IS NO -*/ -extern id __nullable PMKHang(AnyPromise * __nonnull promise); - - - -/** - Executes the provided block on a background queue. - - dispatch_promise is a convenient way to start a promise chain where the - first step needs to run synchronously on a background queue. - - dispatch_promise(^{ - return md5(input); - }).then(^(NSString *md5){ - NSLog(@"md5: %@", md5); - }); - - @param block The block to be executed in the background. Returning an `NSError` will reject the promise, everything else (including void) fulfills the promise. - - @return A promise resolved with the return value of the provided block. - - @see dispatch_async -*/ -extern AnyPromise * __nonnull dispatch_promise(id __nonnull block) NS_SWIFT_UNAVAILABLE("Use our `DispatchQueue.async` override instead"); - - - -/** - Executes the provided block on the specified background queue. - - dispatch_promise_on(myDispatchQueue, ^{ - return md5(input); - }).then(^(NSString *md5){ - NSLog(@"md5: %@", md5); - }); - - @param block The block to be executed in the background. Returning an `NSError` will reject the promise, everything else (including void) fulfills the promise. - - @return A promise resolved with the return value of the provided block. - - @see dispatch_promise -*/ -extern AnyPromise * __nonnull dispatch_promise_on(dispatch_queue_t __nonnull queue, id __nonnull block) NS_SWIFT_UNAVAILABLE("Use our `DispatchQueue.async` override instead"); - -/** - Returns a new promise that resolves when the value of the first resolved promise in the provided array of promises. -*/ -extern AnyPromise * __nonnull PMKRace(NSArray * __nonnull promises) NS_REFINED_FOR_SWIFT; - -#ifdef __cplusplus -} // Extern C -#endif diff --git a/Sources/hang.m b/Sources/hang.m deleted file mode 100644 index 913339e51..000000000 --- a/Sources/hang.m +++ /dev/null @@ -1,29 +0,0 @@ -#import "AnyPromise.h" -#import "AnyPromise+Private.h" -@import CoreFoundation.CFRunLoop; - -/** - Suspends the active thread waiting on the provided promise. - - @return The value of the provided promise once resolved. - */ -id PMKHang(AnyPromise *promise) { - if (promise.pending) { - static CFRunLoopSourceContext context; - - CFRunLoopRef runLoop = CFRunLoopGetCurrent(); - CFRunLoopSourceRef runLoopSource = CFRunLoopSourceCreate(NULL, 0, &context); - CFRunLoopAddSource(runLoop, runLoopSource, kCFRunLoopDefaultMode); - - promise.ensure(^{ - CFRunLoopStop(runLoop); - }); - while (promise.pending) { - CFRunLoopRun(); - } - CFRunLoopRemoveSource(runLoop, runLoopSource, kCFRunLoopDefaultMode); - CFRelease(runLoopSource); - } - - return promise.value; -} diff --git a/Sources/join.m b/Sources/join.m deleted file mode 100644 index 979f092df..000000000 --- a/Sources/join.m +++ /dev/null @@ -1,54 +0,0 @@ -@import Foundation.NSDictionary; -#import "AnyPromise+Private.h" -#import -@import Foundation.NSError; -@import Foundation.NSNull; -#import "PromiseKit.h" -#import - -/** - Waits on all provided promises. - - `PMKWhen` rejects as soon as one of the provided promises rejects. `PMKJoin` waits on all provided promises, then rejects if any of those promises rejects, otherwise it fulfills with values from the provided promises. - - - Returns: A new promise that resolves once all the provided promises resolve. -*/ -AnyPromise *PMKJoin(NSArray *promises) { - if (promises == nil) - return [AnyPromise promiseWithValue:[NSError errorWithDomain:PMKErrorDomain code:PMKInvalidUsageError userInfo:@{NSLocalizedDescriptionKey: @"PMKJoin(nil)"}]]; - - if (promises.count == 0) - return [AnyPromise promiseWithValue:promises]; - - return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - NSPointerArray *results = NSPointerArrayMake(promises.count); - __block atomic_int countdown = promises.count; - __block BOOL rejected = NO; - - [promises enumerateObjectsUsingBlock:^(AnyPromise *promise, NSUInteger ii, BOOL *stop) { - [promise __pipe:^(id value) { - - if (IsError(value)) { - rejected = YES; - } - - //FIXME surely this isn't thread safe on multiple cores? - [results replacePointerAtIndex:ii withPointer:(__bridge void *)(value ?: [NSNull null])]; - - atomic_fetch_sub_explicit(&countdown, 1, memory_order_relaxed); - - if (countdown == 0) { - if (!rejected) { - resolve(results.allObjects); - } else { - id userInfo = @{PMKJoinPromisesKey: promises}; - id err = [NSError errorWithDomain:PMKErrorDomain code:PMKJoinError userInfo:userInfo]; - resolve(err); - } - } - }]; - - (void) stop; - }]; - }]; -} diff --git a/Sources/race.m b/Sources/race.m deleted file mode 100644 index cab38ec19..000000000 --- a/Sources/race.m +++ /dev/null @@ -1,9 +0,0 @@ -#import "AnyPromise+Private.h" - -AnyPromise *PMKRace(NSArray *promises) { - return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - for (AnyPromise *promise in promises) { - [promise __pipe:resolve]; - } - }]; -} diff --git a/Sources/when.m b/Sources/when.m deleted file mode 100644 index 43e5feddc..000000000 --- a/Sources/when.m +++ /dev/null @@ -1,107 +0,0 @@ -@import Foundation.NSDictionary; -#import "AnyPromise+Private.h" -@import Foundation.NSProgress; -#import -@import Foundation.NSError; -@import Foundation.NSNull; -#import "PromiseKit.h" - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" -// ^^ OSAtomicDecrement32 is deprecated on watchOS - - -// NSProgress resources: -// * https://robots.thoughtbot.com/asynchronous-nsprogress -// * http://oleb.net/blog/2014/03/nsprogress/ -// NSProgress! Beware! -// * https://github.com/AFNetworking/AFNetworking/issues/2261 - -/** - Wait for all promises in a set to resolve. - - @note If *any* of the provided promises reject, the returned promise is immediately rejected with that error. - @warning In the event of rejection the other promises will continue to resolve and, as per any other promise, will either fulfill or reject. This is the right pattern for `getter` style asynchronous tasks, but often for `setter` tasks (eg. storing data on a server), you most likely will need to wait on all tasks and then act based on which have succeeded and which have failed, in such situations use `when(resolved:)`. - @param promises The promises upon which to wait before the returned promise resolves. - @note PMKWhen provides NSProgress. - @return A new promise that resolves when all the provided promises fulfill or one of the provided promises rejects. -*/ -AnyPromise *PMKWhen(id promises) { - if (promises == nil) - return [AnyPromise promiseWithValue:[NSError errorWithDomain:PMKErrorDomain code:PMKInvalidUsageError userInfo:@{NSLocalizedDescriptionKey: @"PMKWhen(nil)"}]]; - - if ([promises isKindOfClass:[NSArray class]] || [promises isKindOfClass:[NSDictionary class]]) { - if ([promises count] == 0) - return [AnyPromise promiseWithValue:promises]; - } else if ([promises isKindOfClass:[AnyPromise class]]) { - promises = @[promises]; - } else { - return [AnyPromise promiseWithValue:promises]; - } - -#ifndef PMKDisableProgress - NSProgress *progress = [NSProgress progressWithTotalUnitCount:(int64_t)[promises count]]; - progress.pausable = NO; - progress.cancellable = NO; -#else - struct PMKProgress { - int completedUnitCount; - int totalUnitCount; - double fractionCompleted; - }; - __block struct PMKProgress progress; -#endif - - __block int32_t countdown = (int32_t)[promises count]; - BOOL const isdict = [promises isKindOfClass:[NSDictionary class]]; - - return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - NSInteger index = 0; - - for (__strong id key in promises) { - AnyPromise *promise = isdict ? promises[key] : key; - if (!isdict) key = @(index); - - if (![promise isKindOfClass:[AnyPromise class]]) - promise = [AnyPromise promiseWithValue:promise]; - - [promise __pipe:^(id value){ - if (progress.fractionCompleted >= 1) - return; - - if (IsError(value)) { - progress.completedUnitCount = progress.totalUnitCount; - - NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:[(NSError *)value userInfo] ?: @{}]; - userInfo[PMKFailingPromiseIndexKey] = key; - [userInfo setObject:value forKey:NSUnderlyingErrorKey]; - id err = [[NSError alloc] initWithDomain:[value domain] code:[value code] userInfo:userInfo]; - resolve(err); - } - else if (OSAtomicDecrement32(&countdown) == 0) { - progress.completedUnitCount = progress.totalUnitCount; - - id results; - if (isdict) { - results = [NSMutableDictionary new]; - for (id key in promises) { - id promise = promises[key]; - results[key] = IsPromise(promise) ? ((AnyPromise *)promise).value : promise; - } - } else { - results = [NSMutableArray new]; - for (AnyPromise *promise in promises) { - id value = IsPromise(promise) ? (promise.value ?: [NSNull null]) : promise; - [results addObject:value]; - } - } - resolve(results); - } else { - progress.completedUnitCount++; - } - }]; - } - }]; -} - -#pragma GCC diagnostic pop diff --git a/Sources/when.swift b/Sources/when.swift index 0913c64f3..bc54f6712 100644 --- a/Sources/when.swift +++ b/Sources/when.swift @@ -9,7 +9,7 @@ private func _when(_ thenables: [U]) -> Promise { let rp = Promise(.pending) -#if PMKDisableProgress || os(Linux) +#if PMKDisableProgress || os(Linux) || os(Android) var progress: (completedUnitCount: Int, totalUnitCount: Int) = (0, 0) #else let progress = Progress(totalUnitCount: Int64(thenables.count)) @@ -170,11 +170,7 @@ public func when(fulfilled promiseIterator: It, concurrent func testDone() { barrier.sync { if pendingPromises == 0 { - #if !swift(>=3.3) || (swift(>=4) && !swift(>=4.1)) - root.resolver.fulfill(promises.flatMap{ $0.value }) - #else root.resolver.fulfill(promises.compactMap{ $0.value }) - #endif } } } diff --git a/Tests/JS-A+/.gitignore b/Tests/A+/JavaScript/.gitignore similarity index 100% rename from Tests/JS-A+/.gitignore rename to Tests/A+/JavaScript/.gitignore diff --git a/Tests/JS-A+/AllTests.swift b/Tests/A+/JavaScript/AllTests.swift similarity index 94% rename from Tests/JS-A+/AllTests.swift rename to Tests/A+/JavaScript/AllTests.swift index a1fd6445e..c104a4b2e 100644 --- a/Tests/JS-A+/AllTests.swift +++ b/Tests/A+/JavaScript/AllTests.swift @@ -5,19 +5,21 @@ // Created by Lois Di Qual on 2/28/18. // -#if swift(>=3.2) - import XCTest import PromiseKit + +#if !os(Linux) +// can disable better when we don’t need --generate-linuxmain import JavaScriptCore +#endif class AllTests: XCTestCase { func testAll() { - + #if !os(Linux) let scriptPath = URL(fileURLWithPath: #file).deletingLastPathComponent().appendingPathComponent("build/build.js") guard FileManager.default.fileExists(atPath: scriptPath.path) else { - return print("Skipping JS-A+: see README for instructions on how to build") + return print("Skipping A+.js: see README for instructions on how to build") } guard let script = try? String(contentsOf: scriptPath) else { @@ -78,7 +80,6 @@ class AllTests: XCTestCase { // Call `runTests` runTests.call(withArguments: [adapter, onFailValue, onDoneValue, testName]) self.wait(for: [expectation], timeout: 60) + #endif } } - -#endif diff --git a/Tests/JS-A+/JSAdapter.swift b/Tests/A+/JavaScript/JSAdapter.swift similarity index 94% rename from Tests/JS-A+/JSAdapter.swift rename to Tests/A+/JavaScript/JSAdapter.swift index 6dcbe74c6..e26f59c3e 100644 --- a/Tests/JS-A+/JSAdapter.swift +++ b/Tests/A+/JavaScript/JSAdapter.swift @@ -5,6 +5,8 @@ // Created by Lois Di Qual on 3/2/18. // +#if !os(Linux) +// can disable better when we don’t need --generate-linuxmain import Foundation import JavaScriptCore import PromiseKit @@ -51,3 +53,5 @@ enum JSAdapter { return object } } + +#endif diff --git a/Tests/JS-A+/JSPromise.swift b/Tests/A+/JavaScript/JSPromise.swift similarity index 97% rename from Tests/JS-A+/JSPromise.swift rename to Tests/A+/JavaScript/JSPromise.swift index 70381fd86..036dc7171 100644 --- a/Tests/JS-A+/JSPromise.swift +++ b/Tests/A+/JavaScript/JSPromise.swift @@ -5,6 +5,8 @@ // Created by Lois Di Qual on 3/1/18. // +#if !os(Linux) +// can disable better when we don’t need --generate-linuxmain import Foundation import XCTest import PromiseKit @@ -92,3 +94,5 @@ class JSPromise: NSObject, JSPromiseProtocol { return JSPromise(promise: newPromise) } } + +#endif diff --git a/Tests/JS-A+/JSUtils.swift b/Tests/A+/JavaScript/JSUtils.swift similarity index 91% rename from Tests/JS-A+/JSUtils.swift rename to Tests/A+/JavaScript/JSUtils.swift index d08a44b49..200cc4612 100644 --- a/Tests/JS-A+/JSUtils.swift +++ b/Tests/A+/JavaScript/JSUtils.swift @@ -5,6 +5,8 @@ // Created by Lois Di Qual on 3/2/18. // +#if !os(Linux) +// can disable better when we don’t need --generate-linuxmain import Foundation import JavaScriptCore @@ -103,14 +105,4 @@ enum JSUtils { } } -#if !swift(>=3.2) -extension String { - func split(separator: Character, omittingEmptySubsequences: Bool = true) -> [String] { - return characters.split(separator: separator, omittingEmptySubsequences: omittingEmptySubsequences).map(String.init) - } - - var first: Character? { - return characters.first - } -} #endif diff --git a/Tests/JS-A+/MockNodeEnvironment.swift b/Tests/A+/JavaScript/MockNodeEnvironment.swift similarity index 93% rename from Tests/JS-A+/MockNodeEnvironment.swift rename to Tests/A+/JavaScript/MockNodeEnvironment.swift index 82808fe51..89c4cda34 100644 --- a/Tests/JS-A+/MockNodeEnvironment.swift +++ b/Tests/A+/JavaScript/MockNodeEnvironment.swift @@ -5,10 +5,10 @@ // Created by Lois Di Qual on 3/1/18. // -#if swift(>=3.2) - -import Foundation +#if !os(Linux) +// can disable better when we don’t need --generate-linuxmain import JavaScriptCore +import Foundation class MockNodeEnvironment { @@ -100,11 +100,7 @@ class MockNodeEnvironment { } let timer = Timer.scheduledTimer(timeInterval: interval, target: block, selector: #selector(Operation.main), userInfo: nil, repeats: repeats) let rawHash = UUID().uuidString.hashValue - #if swift(>=4.0) let hash = UInt32(truncatingIfNeeded: rawHash) - #else - let hash = UInt32(truncatingBitPattern: rawHash) - #endif timers[hash] = timer return hash } @@ -118,12 +114,4 @@ class MockNodeEnvironment { } } - -#if swift(>=4.0) && !swift(>=4.1) || !swift(>=3.3) -extension Sequence { - func compactMap(_ transform: (Self.Element) throws -> T?) rethrows -> [T] { - return try flatMap(transform) - } -} -#endif #endif diff --git a/Tests/JS-A+/README.md b/Tests/A+/JavaScript/README.md similarity index 100% rename from Tests/JS-A+/README.md rename to Tests/A+/JavaScript/README.md diff --git a/Tests/A+/JavaScript/XCTestManifests.swift b/Tests/A+/JavaScript/XCTestManifests.swift new file mode 100644 index 000000000..79a7d0f2a --- /dev/null +++ b/Tests/A+/JavaScript/XCTestManifests.swift @@ -0,0 +1,18 @@ +#if !canImport(ObjectiveC) +import XCTest + +extension AllTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__AllTests = [ + ("testAll", testAll), + ] +} + +public func __allTests() -> [XCTestCaseEntry] { + return [ + testCase(AllTests.__allTests__AllTests), + ] +} +#endif diff --git a/Tests/JS-A+/index.js b/Tests/A+/JavaScript/index.js similarity index 100% rename from Tests/JS-A+/index.js rename to Tests/A+/JavaScript/index.js diff --git a/Tests/JS-A+/package-lock.json b/Tests/A+/JavaScript/package-lock.json similarity index 100% rename from Tests/JS-A+/package-lock.json rename to Tests/A+/JavaScript/package-lock.json diff --git a/Tests/JS-A+/package.json b/Tests/A+/JavaScript/package.json similarity index 100% rename from Tests/JS-A+/package.json rename to Tests/A+/JavaScript/package.json diff --git a/Tests/JS-A+/webpack.config.js b/Tests/A+/JavaScript/webpack.config.js similarity index 100% rename from Tests/JS-A+/webpack.config.js rename to Tests/A+/JavaScript/webpack.config.js diff --git a/Tests/A+/LinuxMain.swift b/Tests/A+/LinuxMain.swift new file mode 100644 index 000000000..3ccd10feb --- /dev/null +++ b/Tests/A+/LinuxMain.swift @@ -0,0 +1,12 @@ +import XCTest + +import A__js +import A__swift +import Core + +var tests = [XCTestCaseEntry]() +tests += A__js.__allTests() +tests += A__swift.__allTests() +tests += Core.__allTests() + +XCTMain(tests) diff --git a/Tests/A+/0.0.0.swift b/Tests/A+/Swift/0.0.0.swift similarity index 97% rename from Tests/A+/0.0.0.swift rename to Tests/A+/Swift/0.0.0.swift index 5962caa0a..1790d1bb8 100644 --- a/Tests/A+/0.0.0.swift +++ b/Tests/A+/Swift/0.0.0.swift @@ -26,7 +26,7 @@ extension XCTestCase { let (pending, seal) = Promise.pending() do { - try body((pending, seal.fulfill_, seal.reject), expectation) + try body((pending, seal.fulfill, seal.reject), expectation) waitForExpectations(timeout: timeout) { err in if let _ = err { XCTFail("wait failed: \(description)", file: file, line: line) @@ -176,10 +176,7 @@ extension XCTestExpectation { extension XCTestCase { func wait(for: [XCTestExpectation], timeout: TimeInterval, file: StaticString = #file, line: UInt = #line) { - #if !(swift(>=4.0) && !swift(>=4.1)) - let line = Int(line) - #endif - waitForExpectations(timeout: timeout, file: file, line: line) + waitForExpectations(timeout: timeout, file: file, line: Int(line)) } } #endif diff --git a/Tests/A+/2.1.2.swift b/Tests/A+/Swift/2.1.2.swift similarity index 100% rename from Tests/A+/2.1.2.swift rename to Tests/A+/Swift/2.1.2.swift diff --git a/Tests/A+/2.1.3.swift b/Tests/A+/Swift/2.1.3.swift similarity index 100% rename from Tests/A+/2.1.3.swift rename to Tests/A+/Swift/2.1.3.swift diff --git a/Tests/A+/2.2.2.swift b/Tests/A+/Swift/2.2.2.swift similarity index 100% rename from Tests/A+/2.2.2.swift rename to Tests/A+/Swift/2.2.2.swift diff --git a/Tests/A+/2.2.3.swift b/Tests/A+/Swift/2.2.3.swift similarity index 100% rename from Tests/A+/2.2.3.swift rename to Tests/A+/Swift/2.2.3.swift diff --git a/Tests/A+/2.2.4.swift b/Tests/A+/Swift/2.2.4.swift similarity index 100% rename from Tests/A+/2.2.4.swift rename to Tests/A+/Swift/2.2.4.swift diff --git a/Tests/A+/2.2.6.swift b/Tests/A+/Swift/2.2.6.swift similarity index 100% rename from Tests/A+/2.2.6.swift rename to Tests/A+/Swift/2.2.6.swift diff --git a/Tests/A+/2.2.7.swift b/Tests/A+/Swift/2.2.7.swift similarity index 100% rename from Tests/A+/2.2.7.swift rename to Tests/A+/Swift/2.2.7.swift diff --git a/Tests/A+/2.3.1.swift b/Tests/A+/Swift/2.3.1.swift similarity index 100% rename from Tests/A+/2.3.1.swift rename to Tests/A+/Swift/2.3.1.swift diff --git a/Tests/A+/2.3.2.swift b/Tests/A+/Swift/2.3.2.swift similarity index 100% rename from Tests/A+/2.3.2.swift rename to Tests/A+/Swift/2.3.2.swift diff --git a/Tests/A+/2.3.4.swift b/Tests/A+/Swift/2.3.4.swift similarity index 100% rename from Tests/A+/2.3.4.swift rename to Tests/A+/Swift/2.3.4.swift diff --git a/Tests/A+/XCTestManifests.swift b/Tests/A+/Swift/XCTestManifests.swift similarity index 100% rename from Tests/A+/XCTestManifests.swift rename to Tests/A+/Swift/XCTestManifests.swift diff --git a/Tests/Bridging/BridgingTests.m b/Tests/Bridging/BridgingTests.m deleted file mode 100644 index 928c0481a..000000000 --- a/Tests/Bridging/BridgingTests.m +++ /dev/null @@ -1,34 +0,0 @@ -@import PromiseKit; -@import XCTest; -#import "Infrastructure.h" - - -@interface BridgingTests: XCTestCase @end @implementation BridgingTests - -- (void)testChainAnyPromiseFromSwiftCode { - XCTestExpectation *ex = [self expectationWithDescription:@""]; - AnyPromise *promise = PMKAfter(0.02); - for (int x = 0; x < 100; ++x) { - promise = promise.then(^{ - return [[[PromiseBridgeHelper alloc] init] bridge1]; - }); - } - promise.then(^{ - [ex fulfill]; - }); - [self waitForExpectationsWithTimeout:20 handler:nil]; -} - -- (void)test626 { - XCTestExpectation *ex = [self expectationWithDescription:@""]; - - testCase626().then(^{ - XCTFail(); - }).ensure(^{ - [ex fulfill]; - }); - - [self waitForExpectationsWithTimeout:20 handler:nil]; -} - -@end diff --git a/Tests/Bridging/BridgingTests.swift b/Tests/Bridging/BridgingTests.swift deleted file mode 100644 index 0e0433461..000000000 --- a/Tests/Bridging/BridgingTests.swift +++ /dev/null @@ -1,280 +0,0 @@ -import Foundation -import PromiseKit -import XCTest - -class BridgingTests: XCTestCase { - - func testCanBridgeAnyObject() { - let sentinel = NSURLRequest() - let p = Promise.value(sentinel) - let ap = AnyPromise(p) - - XCTAssertEqual(ap.value(forKey: "value") as? NSURLRequest, sentinel) - } - - func testCanBridgeOptional() { - let sentinel: NSURLRequest? = NSURLRequest() - let p = Promise.value(sentinel) - let ap = AnyPromise(p) - - XCTAssertEqual(ap.value(forKey: "value") as? NSURLRequest, sentinel) - } - - func testCanBridgeSwiftArray() { - let sentinel = [NSString(), NSString(), NSString()] - let p = Promise.value(sentinel) - let ap = AnyPromise(p) - - guard let foo = ap.value(forKey: "value") as? [NSString] else { return XCTFail() } - XCTAssertEqual(foo, sentinel) - } - - func testCanBridgeSwiftDictionary() { - let sentinel = [NSString(): NSString()] - let p = Promise.value(sentinel) - let ap = AnyPromise(p) - - guard let foo = ap.value(forKey: "value") as? [NSString: NSString] else { return XCTFail() } - XCTAssertEqual(foo, sentinel) - } - - func testCanBridgeInt() { - let sentinel = 3 - let p = Promise.value(sentinel) - let ap = AnyPromise(p) - XCTAssertEqual(ap.value(forKey: "value") as? Int, sentinel) - } - - func testCanBridgeString() { - let sentinel = "a" - let p = Promise.value(sentinel) - let ap = AnyPromise(p) - XCTAssertEqual(ap.value(forKey: "value") as? String, sentinel) - } - - func testCanBridgeBool() { - let sentinel = true - let p = Promise.value(sentinel) - let ap = AnyPromise(p) - XCTAssertEqual(ap.value(forKey: "value") as? Bool, sentinel) - } - - func testCanChainOffAnyPromiseFromObjC() { - let ex = expectation(description: "") - - firstly { - .value(1) - }.then { _ -> AnyPromise in - return PromiseBridgeHelper().value(forKey: "bridge2") as! AnyPromise - }.done { value in - XCTAssertEqual(123, value as? Int) - ex.fulfill() - }.silenceWarning() - - waitForExpectations(timeout: 1) - } - - func testCanThenOffAnyPromise() { - let ex = expectation(description: "") - - PMKDummyAnyPromise_YES().then { obj -> Promise in - if let value = obj as? NSNumber { - XCTAssertEqual(value, NSNumber(value: true)) - ex.fulfill() - } - return Promise() - }.silenceWarning() - - waitForExpectations(timeout: 1) - } - - func testCanThenOffManifoldAnyPromise() { - let ex = expectation(description: "") - - PMKDummyAnyPromise_Manifold().then { obj -> Promise in - defer { ex.fulfill() } - XCTAssertEqual(obj as? NSNumber, NSNumber(value: true), "\(obj ?? "nil") is not @YES") - return Promise() - }.silenceWarning() - - waitForExpectations(timeout: 1) - } - - func testCanAlwaysOffAnyPromise() { - let ex = expectation(description: "") - - PMKDummyAnyPromise_YES().then { obj -> Promise in - ex.fulfill() - return Promise() - }.silenceWarning() - - waitForExpectations(timeout: 1) - } - - func testCanCatchOffAnyPromise() { - let ex = expectation(description: "") - PMKDummyAnyPromise_Error().catch { err in - ex.fulfill() - } - waitForExpectations(timeout: 1) - } - - func testAsPromise() { - #if swift(>=3.1) - XCTAssertTrue(Promise(PMKDummyAnyPromise_Error()).isRejected) - XCTAssertEqual(Promise(PMKDummyAnyPromise_YES()).value as? NSNumber, NSNumber(value: true)) - #else - XCTAssertTrue(PMKDummyAnyPromise_Error().asPromise().isRejected) - XCTAssertEqual(PMKDummyAnyPromise_YES().asPromise().value as? NSNumber, NSNumber(value: true)) - #endif - } - - func testFirstlyReturningAnyPromiseSuccess() { - let ex = expectation(description: "") - firstly { - PMKDummyAnyPromise_Error() - }.catch { error in - ex.fulfill() - } - waitForExpectations(timeout: 1) - } - - func testFirstlyReturningAnyPromiseError() { - let ex = expectation(description: "") - firstly { - PMKDummyAnyPromise_YES() - }.done { _ in - ex.fulfill() - }.silenceWarning() - waitForExpectations(timeout: 1) - } - - func test1() { - let ex = expectation(description: "") - - // AnyPromise.then { return x } - - let input = after(seconds: 0).map{ 1 } - - AnyPromise(input).then { obj -> Promise in - XCTAssertEqual(obj as? Int, 1) - return .value(2) - }.done { value in - XCTAssertEqual(value, 2) - ex.fulfill() - }.silenceWarning() - - waitForExpectations(timeout: 1) - } - - func test2() { - let ex = expectation(description: "") - - // AnyPromise.then { return AnyPromise } - - let input = after(seconds: 0).map{ 1 } - - AnyPromise(input).then { obj -> AnyPromise in - XCTAssertEqual(obj as? Int, 1) - return AnyPromise(after(seconds: 0).map{ 2 }) - }.done { obj in - XCTAssertEqual(obj as? Int, 2) - ex.fulfill() - }.silenceWarning() - - waitForExpectations(timeout: 1) - } - - func test3() { - let ex = expectation(description: "") - - // AnyPromise.then { return Promise } - - let input = after(seconds: 0).map{ 1 } - - AnyPromise(input).then { obj -> Promise in - XCTAssertEqual(obj as? Int, 1) - return after(seconds: 0).map{ 2 } - }.done { value in - XCTAssertEqual(value, 2) - ex.fulfill() - }.silenceWarning() - - waitForExpectations(timeout: 1, handler: nil) - } - - - // can return AnyPromise (that fulfills) in then handler - func test4() { - let ex = expectation(description: "") - Promise.value(1).then { _ -> AnyPromise in - return AnyPromise(after(seconds: 0).map{ 1 }) - }.done { x in - XCTAssertEqual(x as? Int, 1) - ex.fulfill() - }.silenceWarning() - waitForExpectations(timeout: 1, handler: nil) - } - - // can return AnyPromise (that rejects) in then handler - func test5() { - let ex = expectation(description: "") - - Promise.value(1).then { _ -> AnyPromise in - let promise = after(.milliseconds(100)).done{ throw Error.dummy } - return AnyPromise(promise) - }.catch { err in - ex.fulfill() - } - waitForExpectations(timeout: 1) - } - - func testStandardSwiftBridgeIsUnambiguous() { - let p = Promise.value(1) - let q = Promise(p) - - XCTAssertEqual(p.value, q.value) - } - - /// testing NSError to Error for cancelledError types - func testErrorCancellationBridging() { - let ex = expectation(description: "") - - let p = Promise().done { - throw LocalError.cancel as NSError - } - p.catch { _ in - XCTFail() - } - p.catch(policy: .allErrors) { - XCTAssertTrue($0.isCancelled) - ex.fulfill() - } - waitForExpectations(timeout: 1) - - // here we verify that Swift’s NSError bridging works as advertised - - XCTAssertTrue(LocalError.cancel.isCancelled) - XCTAssertTrue((LocalError.cancel as NSError).isCancelled) - } -} - -private enum Error: Swift.Error { - case dummy -} - -extension Promise { - func silenceWarning() {} -} - -private enum LocalError: CancellableError { - case notCancel - case cancel - - var isCancelled: Bool { - switch self { - case .notCancel: return false - case .cancel: return true - } - } -} diff --git a/Tests/Bridging/Infrastructure.h b/Tests/Bridging/Infrastructure.h deleted file mode 100644 index 104378fa6..000000000 --- a/Tests/Bridging/Infrastructure.h +++ /dev/null @@ -1,14 +0,0 @@ -@import Foundation; -@class AnyPromise; - -AnyPromise *PMKDummyAnyPromise_YES(void); -AnyPromise *PMKDummyAnyPromise_Manifold(void); -AnyPromise *PMKDummyAnyPromise_Error(void); - -__attribute__((objc_runtime_name("PMKPromiseBridgeHelper"))) -__attribute__((objc_subclassing_restricted)) -@interface PromiseBridgeHelper: NSObject -- (AnyPromise *)bridge1; -@end - -AnyPromise *testCase626(void); diff --git a/Tests/Bridging/Infrastructure.m b/Tests/Bridging/Infrastructure.m deleted file mode 100644 index 5049310ae..000000000 --- a/Tests/Bridging/Infrastructure.m +++ /dev/null @@ -1,38 +0,0 @@ -@import Foundation; -@import PromiseKit; -#import "Infrastructure.h" - -AnyPromise *PMKDummyAnyPromise_YES() { - return [AnyPromise promiseWithValue:@YES]; -} - -AnyPromise *PMKDummyAnyPromise_Manifold() { - return [AnyPromise promiseWithValue:PMKManifold(@YES, @NO, @NO)]; -} - -AnyPromise *PMKDummyAnyPromise_Error() { - return [AnyPromise promiseWithValue:[NSError errorWithDomain:@"a" code:1 userInfo:nil]]; -} - -@implementation PromiseBridgeHelper (objc) - -- (AnyPromise *)bridge2 { - return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - resolve(@123); - }); - }]; -} - -@end - -#import "PMKBridgeTests-Swift.h" - -AnyPromise *testCase626() { - return PMKWhen(@[[TestPromise626 promise], [TestPromise626 promise]]).then(^(id value){ - NSLog(@"Success: %@", value); - }).catch(^(NSError *error) { - NSLog(@"Error: %@", error); - @throw error; - }); -} diff --git a/Tests/Bridging/Infrastructure.swift b/Tests/Bridging/Infrastructure.swift deleted file mode 100644 index 775fd31cb..000000000 --- a/Tests/Bridging/Infrastructure.swift +++ /dev/null @@ -1,24 +0,0 @@ -import PromiseKit - -// for BridgingTests.m -@objc(PMKPromiseBridgeHelper) class PromiseBridgeHelper: NSObject { - @objc func bridge1() -> AnyPromise { - let p = after(.milliseconds(10)) - return AnyPromise(p) - } -} - -enum MyError: Error { - case PromiseError -} - -@objc class TestPromise626: NSObject { - - @objc class func promise() -> AnyPromise { - let promise: Promise = Promise { seal in - seal.reject(MyError.PromiseError) - } - - return AnyPromise(promise) - } -} diff --git a/Tests/CorePromise/AfterTests.swift b/Tests/Core/AfterTests.swift similarity index 63% rename from Tests/CorePromise/AfterTests.swift rename to Tests/Core/AfterTests.swift index 6b587c954..5d016570b 100644 --- a/Tests/CorePromise/AfterTests.swift +++ b/Tests/Core/AfterTests.swift @@ -10,12 +10,6 @@ class AfterTests: XCTestCase { let ex3 = expectation(description: "") after(.seconds(0)).done(ex3.fulfill) waitForExpectations(timeout: 2, handler: nil) - - #if !SWIFT_PACKAGE - let ex4 = expectation(description: "") - __PMKAfter(0).done{ _ in ex4.fulfill() }.silenceWarning() - waitForExpectations(timeout: 2, handler: nil) - #endif } func testNegative() { @@ -26,12 +20,6 @@ class AfterTests: XCTestCase { let ex3 = expectation(description: "") after(.seconds(-1)).done(ex3.fulfill) waitForExpectations(timeout: 2, handler: nil) - - #if !SWIFT_PACKAGE - let ex4 = expectation(description: "") - __PMKAfter(-1).done{ _ in ex4.fulfill() }.silenceWarning() - waitForExpectations(timeout: 2, handler: nil) - #endif } func testPositive() { @@ -42,11 +30,5 @@ class AfterTests: XCTestCase { let ex3 = expectation(description: "") after(.seconds(1)).done(ex3.fulfill) waitForExpectations(timeout: 2, handler: nil) - - #if !SWIFT_PACKAGE - let ex4 = expectation(description: "") - __PMKAfter(1).done{ _ in ex4.fulfill() }.silenceWarning() - waitForExpectations(timeout: 2, handler: nil) - #endif } } diff --git a/Tests/CorePromise/CancellableErrorTests.swift b/Tests/Core/CancellableErrorTests.swift similarity index 99% rename from Tests/CorePromise/CancellableErrorTests.swift rename to Tests/Core/CancellableErrorTests.swift index fd2b4239d..774d05b28 100644 --- a/Tests/CorePromise/CancellableErrorTests.swift +++ b/Tests/Core/CancellableErrorTests.swift @@ -95,14 +95,12 @@ class CancellationTests: XCTestCase { waitForExpectations(timeout: 1) } -#if swift(>=3.2) func testIsCancelled() { XCTAssertTrue(PMKError.cancelled.isCancelled) XCTAssertTrue(URLError.cancelled.isCancelled) XCTAssertTrue(CocoaError.cancelled.isCancelled) XCTAssertFalse(CocoaError(_nsError: NSError(domain: NSCocoaErrorDomain, code: CocoaError.Code.coderInvalidValue.rawValue)).isCancelled) } -#endif } private enum LocalError: CancellableError { diff --git a/Tests/CorePromise/CatchableTests.swift b/Tests/Core/CatchableTests.swift similarity index 100% rename from Tests/CorePromise/CatchableTests.swift rename to Tests/Core/CatchableTests.swift diff --git a/Tests/CorePromise/DefaultDispatchQueueTests.swift b/Tests/Core/DefaultDispatchQueueTests.swift similarity index 100% rename from Tests/CorePromise/DefaultDispatchQueueTests.swift rename to Tests/Core/DefaultDispatchQueueTests.swift diff --git a/Tests/CorePromise/ErrorTests.swift b/Tests/Core/ErrorTests.swift similarity index 100% rename from Tests/CorePromise/ErrorTests.swift rename to Tests/Core/ErrorTests.swift diff --git a/Tests/CorePromise/GuaranteeTests.swift b/Tests/Core/GuaranteeTests.swift similarity index 100% rename from Tests/CorePromise/GuaranteeTests.swift rename to Tests/Core/GuaranteeTests.swift diff --git a/Tests/CorePromise/HangTests.swift b/Tests/Core/HangTests.swift similarity index 100% rename from Tests/CorePromise/HangTests.swift rename to Tests/Core/HangTests.swift diff --git a/Tests/CorePromise/LoggingTests.swift b/Tests/Core/LoggingTests.swift similarity index 100% rename from Tests/CorePromise/LoggingTests.swift rename to Tests/Core/LoggingTests.swift diff --git a/Tests/CorePromise/PromiseTests.swift b/Tests/Core/PromiseTests.swift similarity index 99% rename from Tests/CorePromise/PromiseTests.swift rename to Tests/Core/PromiseTests.swift index b5f745833..32c3f0a20 100644 --- a/Tests/CorePromise/PromiseTests.swift +++ b/Tests/Core/PromiseTests.swift @@ -83,12 +83,10 @@ class PromiseTests: XCTestCase { _ = Promise().map { Error.dummy } } -#if swift(>=3.1) func testCanMakeVoidPromise() { _ = Promise() _ = Guarantee() } -#endif enum Error: Swift.Error { case dummy diff --git a/Tests/CorePromise/RaceTests.swift b/Tests/Core/RaceTests.swift similarity index 100% rename from Tests/CorePromise/RaceTests.swift rename to Tests/Core/RaceTests.swift diff --git a/Tests/CorePromise/RegressionTests.swift b/Tests/Core/RegressionTests.swift similarity index 100% rename from Tests/CorePromise/RegressionTests.swift rename to Tests/Core/RegressionTests.swift diff --git a/Tests/CorePromise/ResolverTests.swift b/Tests/Core/ResolverTests.swift similarity index 95% rename from Tests/CorePromise/ResolverTests.swift rename to Tests/Core/ResolverTests.swift index 42c103e1b..09c78a354 100644 --- a/Tests/CorePromise/ResolverTests.swift +++ b/Tests/Core/ResolverTests.swift @@ -114,7 +114,6 @@ class WrapTests: XCTestCase { wait(for: [ex1, ex2] ,timeout: 1) } -#if swift(>=3.1) func testVoidCompletionValue() { let ex1 = expectation(description: "") let kf1 = KittenFetcher(value: nil, error: nil) @@ -130,7 +129,6 @@ class WrapTests: XCTestCase { wait(for: [ex1, ex2], timeout: 1) } -#endif func testIsFulfilled() { XCTAssertTrue(Promise.value(()).result?.isFulfilled ?? false) @@ -161,10 +159,6 @@ class WrapTests: XCTestCase { } func testVoidResolverFulfillAmbiguity() { - #if !swift(>=5) && swift(>=4.1) || swift(>=3.3) && !swift(>=4.0) - // ^^ this doesn’t work with Swift < 3.3 for some reason - // ^^ this doesn’t work with Swift 5.0-beta1 for some reason - // reference: https://github.com/mxcl/PromiseKit/issues/990 func foo(success: () -> Void, failure: (Error) -> Void) { @@ -180,6 +174,14 @@ class WrapTests: XCTestCase { let ex = expectation(description: "") bar().done(ex.fulfill).cauterize() wait(for: [ex], timeout: 10) + + #if swift(>=5.1) + // ^^ ambiguous in Swift 5.0, testing again in next version + let ex2 = expectation(description: "") + Guarantee { seal in + after(.microseconds(10)).done(seal) + }.done(ex2.fulfill) + wait(for: [ex2], timeout: 10) #endif } } diff --git a/Tests/CorePromise/StressTests.swift b/Tests/Core/StressTests.swift similarity index 100% rename from Tests/CorePromise/StressTests.swift rename to Tests/Core/StressTests.swift diff --git a/Tests/CorePromise/ThenableTests.swift b/Tests/Core/ThenableTests.swift similarity index 100% rename from Tests/CorePromise/ThenableTests.swift rename to Tests/Core/ThenableTests.swift diff --git a/Tests/CorePromise/Utilities.swift b/Tests/Core/Utilities.swift similarity index 87% rename from Tests/CorePromise/Utilities.swift rename to Tests/Core/Utilities.swift index 6e9bce694..6b4c5de68 100644 --- a/Tests/CorePromise/Utilities.swift +++ b/Tests/Core/Utilities.swift @@ -18,10 +18,7 @@ import XCTest extension XCTestCase { func wait(for: [XCTestExpectation], timeout: TimeInterval, file: StaticString = #file, line: UInt = #line) { - #if !(swift(>=4.0) && !swift(>=4.1)) - let line = Int(line) - #endif - waitForExpectations(timeout: timeout, file: file, line: line) + waitForExpectations(timeout: timeout, file: file, line: Int(line)) } } diff --git a/Tests/CorePromise/WhenConcurrentTests.swift b/Tests/Core/WhenConcurrentTests.swift similarity index 100% rename from Tests/CorePromise/WhenConcurrentTests.swift rename to Tests/Core/WhenConcurrentTests.swift diff --git a/Tests/CorePromise/WhenResolvedTests.swift b/Tests/Core/WhenResolvedTests.swift similarity index 94% rename from Tests/CorePromise/WhenResolvedTests.swift rename to Tests/Core/WhenResolvedTests.swift index 32bda7bb7..d65cf3728 100644 --- a/Tests/CorePromise/WhenResolvedTests.swift +++ b/Tests/Core/WhenResolvedTests.swift @@ -29,13 +29,13 @@ class JoinTests: XCTestCase { when(resolved: promise1, promise2, promise3).done(on: nil) { _ in finished = true } XCTAssertFalse(finished, "Not all promises have resolved") - seal1.fulfill_() + seal1.fulfill() XCTAssertFalse(finished, "Not all promises have resolved") - seal2.fulfill_() + seal2.fulfill() XCTAssertFalse(finished, "Not all promises have resolved") - seal3.fulfill_() + seal3.fulfill() XCTAssert(finished, "All promises have resolved") } } diff --git a/Tests/CorePromise/WhenTests.swift b/Tests/Core/WhenTests.swift similarity index 100% rename from Tests/CorePromise/WhenTests.swift rename to Tests/Core/WhenTests.swift diff --git a/Tests/CorePromise/XCTestManifests.swift b/Tests/Core/XCTestManifests.swift similarity index 100% rename from Tests/CorePromise/XCTestManifests.swift rename to Tests/Core/XCTestManifests.swift diff --git a/Tests/CorePromise/ZalgoTests.swift b/Tests/Core/ZalgoTests.swift similarity index 100% rename from Tests/CorePromise/ZalgoTests.swift rename to Tests/Core/ZalgoTests.swift diff --git a/Tests/CoreObjC/AnyPromiseTests.m b/Tests/CoreObjC/AnyPromiseTests.m deleted file mode 100644 index ebe41c0af..000000000 --- a/Tests/CoreObjC/AnyPromiseTests.m +++ /dev/null @@ -1,896 +0,0 @@ -@import PromiseKit; -@import XCTest; -#import "Infrastructure.h" -#define PMKTestErrorDomain @"PMKTestErrorDomain" - -static inline NSError *dummyWithCode(NSInteger code) { - return [NSError errorWithDomain:PMKTestErrorDomain code:rand() userInfo:@{NSLocalizedDescriptionKey: @(code).stringValue}]; -} - -static inline NSError *dummy() { - return dummyWithCode(rand()); -} - -static inline AnyPromise *rejectLater() { - return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - dispatch_async(dispatch_get_main_queue(), ^{ - resolve(dummy()); - }); - }); - }]; -} - -static inline AnyPromise *fulfillLater() { - return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - dispatch_async(dispatch_get_global_queue(0, 0), ^{ - resolve(@1); - }); - }]; -} - - - -@interface AnyPromiseTestSuite : XCTestCase @end @implementation AnyPromiseTestSuite - -- (void)test_01_resolve { - id ex1 = [self expectationWithDescription:@""]; - - AnyPromise *promise = [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - resolve(@1); - }]; - promise.then(^(NSNumber *o){ - [ex1 fulfill]; - XCTAssertEqual(o.intValue, 1); - }); - promise.catch(^{ - XCTFail(); - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_02_reject { - id ex1 = [self expectationWithDescription:@""]; - - AnyPromise *promise = [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - resolve(dummyWithCode(2)); - }]; - promise.then(^{ - XCTFail(); - }); - promise.catch(^(NSError *error){ - XCTAssertEqualObjects(error.localizedDescription, @"2"); - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_03_return_error { - id ex1 = [self expectationWithDescription:@""]; - - AnyPromise *promise = [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - resolve(@2); - }]; - promise.then(^{ - return [NSError errorWithDomain:@"a" code:3 userInfo:nil]; - }).catch(^(NSError *e){ - [ex1 fulfill]; - XCTAssertEqual(3, e.code); - }); - promise.catch(^{ - XCTFail(); - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_04_return_error_doesnt_compromise_result { - id ex1 = [self expectationWithDescription:@""]; - - AnyPromise *promise = [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - resolve(@4); - }].then(^{ - return dummy(); - }); - promise.then(^{ - XCTFail(); - }); - promise.catch(^{ - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_05_throw_and_bubble { - id ex1 = [self expectationWithDescription:@""]; - - [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - resolve(@5); - }].then(^(id ii){ - XCTAssertEqual(5, [ii intValue]); - return [NSError errorWithDomain:@"a" code:[ii intValue] userInfo:nil]; - }).catch(^(NSError *e){ - XCTAssertEqual(e.code, 5); - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_05_throw_and_bubble_more { - id ex1 = [self expectationWithDescription:@""]; - - [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - resolve(@5); - }].then(^{ - return dummy(); - }).then(^{ - //NOOP - }).catch(^(NSError *e){ - [ex1 fulfill]; - XCTAssertEqualObjects(e.domain, PMKTestErrorDomain); - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_06_return_error { - id ex1 = [self expectationWithDescription:@""]; - - [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - resolve(@5); - }].then(^{ - return dummy(); - }).catch(^{ - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_07_can_then_resolved { - id ex1 = [self expectationWithDescription:@""]; - - [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - resolve(@1); - }].then(^(id o){ - [ex1 fulfill]; - XCTAssertEqualObjects(@1, o); - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_07a_can_fail_rejected { - id ex1 = [self expectationWithDescription:@""]; - - [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - resolve(dummyWithCode(1)); - }].catch(^(NSError *e){ - [ex1 fulfill]; - XCTAssertEqualObjects(@"1", e.localizedDescription); - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_09_async { - id ex1 = [self expectationWithDescription:@""]; - - __block int x = 0; - [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - resolve(@1); - }].then(^{ - XCTAssertEqual(x, 0); - x++; - }).then(^{ - XCTAssertEqual(x, 1); - x++; - }).then(^{ - XCTAssertEqual(x, 2); - x++; - }).then(^{ - XCTAssertEqual(x, 3); - x++; - }).then(^{ - XCTAssertEqual(x, 4); - x++; - - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; - - XCTAssertEqual(x, 5); -} - -- (void)test_10_then_returns_resolved_promise { - id ex1 = [self expectationWithDescription:@""]; - - [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - resolve(@10); - }].then(^(id o){ - XCTAssertEqualObjects(@10, o); - return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - resolve(@100); - }]; - }).then(^(id o){ - XCTAssertEqualObjects(@100, o); - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_11_then_returns_pending_promise { - id ex1 = [self expectationWithDescription:@""]; - - [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - resolve(@1); - }].then(^{ - return fulfillLater(); - }).then(^(id o){ - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_12_then_returns_recursive_promises { - id ex1 = [self expectationWithDescription:@""]; - id ex2 = [self expectationWithDescription:@""]; - - __block int x = 0; - fulfillLater().then(^{ - NSLog(@"1"); - XCTAssertEqual(x++, 0); - return fulfillLater().then(^{ - NSLog(@"2"); - XCTAssertEqual(x++, 1); - return fulfillLater().then(^{ - NSLog(@"3"); - XCTAssertEqual(x++, 2); - return fulfillLater().then(^{ - NSLog(@"4"); - XCTAssertEqual(x++, 3); - [ex2 fulfill]; - return @"foo"; - }); - }); - }); - }).then(^(id o){ - NSLog(@"5"); - XCTAssertEqualObjects(@"foo", o); - XCTAssertEqual(x++, 4); - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; - - XCTAssertEqual(x, 5); -} - - - (void)test_13_then_returns_recursive_promises_that_fails { - id ex1 = [self expectationWithDescription:@""]; - id ex2 = [self expectationWithDescription:@""]; - - fulfillLater().then(^{ - return fulfillLater().then(^{ - return fulfillLater().then(^{ - return fulfillLater().then(^{ - [ex2 fulfill]; - return dummy(); - }); - }); - }); - }).then(^{ - XCTFail(); - }).catch(^(NSError *e){ - XCTAssertEqualObjects(e.domain, PMKTestErrorDomain); - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; - } - -- (void)test_14_fail_returns_value { - id ex1 = [self expectationWithDescription:@""]; - - [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - resolve(@1); - }].then(^{ - return [NSError errorWithDomain:@"a" code:1 userInfo:nil]; - }).catch(^(NSError *e){ - XCTAssertEqual(e.code, 1); - return @2; - }).then(^(id o){ - XCTAssertEqualObjects(o, @2); - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_15_fail_returns_promise { - id ex1 = [self expectationWithDescription:@""]; - - [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - resolve(@1); - }].then(^{ - return dummy(); - }).catch(^{ - return fulfillLater().then(^{ - return @123; - }); - }).then(^(id o){ - XCTAssertEqualObjects(o, @123); - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_23_add_another_fail_to_already_rejected { - id ex1 = [self expectationWithDescription:@""]; - id ex2 = [self expectationWithDescription:@""]; - - PMKResolver resolve; - AnyPromise *promise = [[AnyPromise alloc] initWithResolver:&resolve]; - - promise.then(^{ - XCTFail(); - }).catch(^(NSError *e){ - XCTAssertEqualObjects(e.localizedDescription, @"23"); - [ex1 fulfill]; - }); - - resolve(dummyWithCode(23)); - - promise.then(^{ - XCTFail(); - }).catch(^(NSError *e){ - XCTAssertEqualObjects(e.localizedDescription, @"23"); - [ex2 fulfill]; - }); - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_25_then_plus_deferred_plus_GCD { - id ex1 = [self expectationWithDescription:@""]; - id ex2 = [self expectationWithDescription:@""]; - id ex3 = [self expectationWithDescription:@""]; - - fulfillLater().then(^(id o){ - [ex1 fulfill]; - return fulfillLater().then(^{ - return @YES; - }); - }).then(^(id o){ - XCTAssertEqualObjects(@YES, o); - [ex2 fulfill]; - }).then(^(id o){ - XCTAssertNil(o); - [ex3 fulfill]; - }).catch(^{ - XCTFail(); - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_26_promise_then_promise_fail_promise_fail { - id ex1 = [self expectationWithDescription:@""]; - - fulfillLater().then(^{ - return fulfillLater().then(^{ - return dummy(); - }).catch(^{ - return fulfillLater().then(^{ - return dummy(); - }); - }); - }).then(^{ - XCTFail(); - }).catch(^{ - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil];} - -- (void)test_27_eat_failure { - id ex1 = [self expectationWithDescription:@""]; - - fulfillLater().then(^{ - return dummy(); - }).catch(^{ - return @YES; - }).then(^{ - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_28_deferred_rejected_catch_promise { - id ex1 = [self expectationWithDescription:@""]; - id ex2 = [self expectationWithDescription:@""]; - - rejectLater().catch(^{ - [ex1 fulfill]; - return fulfillLater(); - }).then(^(id o){ - [ex2 fulfill]; - }).catch(^{ - XCTFail(); - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_29_deferred_rejected_catch_promise { - id ex1 = [self expectationWithDescription:@""]; - id ex2 = [self expectationWithDescription:@""]; - - rejectLater().catch(^{ - [ex1 fulfill]; - return fulfillLater().then(^{ - return dummy(); - }); - }).then(^{ - XCTFail(@"1"); - }).catch(^(NSError *error){ - [ex2 fulfill]; - }).catch(^{ - XCTFail(@"2"); - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_30_dispatch_returns_pending_promise { - id ex1 = [self expectationWithDescription:@""]; - dispatch_promise(^{ - return fulfillLater(); - }).then(^{ - [ex1 fulfill]; - }); - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_31_dispatch_returns_promise { - id ex1 = [self expectationWithDescription:@""]; - dispatch_promise(^{ - return [AnyPromise promiseWithValue:@1]; - }).then(^(id o){ - XCTAssertEqualObjects(o, @1); - [ex1 fulfill]; - }); - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_32_return_primitive { - id ex1 = [self expectationWithDescription:@""]; - __block void (^fulfiller)(id) = nil; - [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - fulfiller = resolve; - }].then(^(id o){ - XCTAssertEqualObjects(o, @32); - return 3; - }).then(^(id o){ - XCTAssertEqualObjects(@3, o); - [ex1 fulfill]; - }); - fulfiller(@32); - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_33_return_nil { - id ex1 = [self expectationWithDescription:@""]; - [AnyPromise promiseWithValue:@1].then(^(id o){ - XCTAssertEqualObjects(o, @1); - return nil; - }).then(^{ - return nil; - }).then(^(id o){ - XCTAssertNil(o); - [ex1 fulfill]; - }); - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_33a_return_nil { - id ex1 = [self expectationWithDescription:@""]; - id ex2 = [self expectationWithDescription:@""]; - - [AnyPromise promiseWithValue:@"HI"].then(^(id o){ - XCTAssertEqualObjects(o, @"HI"); - [ex1 fulfill]; - return nil; - }).then(^{ - return nil; - }).then(^{ - [ex2 fulfill]; - return nil; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_36_promise_with_value_nil { - id ex1 = [self expectationWithDescription:@""]; - - [AnyPromise promiseWithValue:nil].then(^(id o){ - XCTAssertNil(o); - [ex1 fulfill]; - }); - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_42 { - id ex1 = [self expectationWithDescription:@""]; - - [AnyPromise promiseWithValue:@1].then(^{ - return fulfillLater(); - }).then(^{ - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_43_return_promise_from_itself { - id ex1 = [self expectationWithDescription:@""]; - - AnyPromise *p = fulfillLater().then(^{ return @1; }); - p.then(^{ - return p; - }).then(^{ - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_44_reseal { - id ex1 = [self expectationWithDescription:@""]; - - [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - resolve(@123); - resolve(@234); - }].then(^(id o){ - XCTAssertEqualObjects(o, @123); - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_46_test_then_on { - id ex1 = [self expectationWithDescription:@""]; - - dispatch_queue_t q1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); - dispatch_queue_t q2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - [AnyPromise promiseWithValue:@1].thenOn(q1, ^{ - XCTAssertFalse([NSThread isMainThread]); - return dispatch_get_current_queue(); - }).thenOn(q2, ^(id q){ - XCTAssertFalse([NSThread isMainThread]); - XCTAssertNotEqualObjects(q, dispatch_get_current_queue()); - [ex1 fulfill]; - }); -#pragma clang diagnostic pop - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_47_finally_plus { - id ex1 = [self expectationWithDescription:@""]; - - [AnyPromise promiseWithValue:@1].then(^{ - return @1; - }).ensure(^{ - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_48_finally_negative { - @autoreleasepool { - id ex1 = [self expectationWithDescription:@"always"]; - id ex2 = [self expectationWithDescription:@"errorUnhandler"]; - - [AnyPromise promiseWithValue:@1].then(^{ - return dummy(); - }).ensure(^{ - [ex1 fulfill]; - }).catch(^(NSError *err){ - XCTAssertEqualObjects(err.domain, PMKTestErrorDomain); - [ex2 fulfill]; - }); - } - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_49_finally_negative_later { - id ex1 = [self expectationWithDescription:@""]; - __block int x = 0; - - [AnyPromise promiseWithValue:@1].then(^{ - XCTAssertEqual(++x, 1); - return dummy(); - }).catch(^{ - XCTAssertEqual(++x, 2); - }).then(^{ - XCTAssertEqual(++x, 3); - }).ensure(^{ - XCTAssertEqual(++x, 4); - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_50_fulfill_with_pending_promise { - id ex1 = [self expectationWithDescription:@""]; - - [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - resolve(fulfillLater().then(^{ return @"HI"; })); - }].then(^(id hi){ - XCTAssertEqualObjects(hi, @"HI"); - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_51_fulfill_with_fulfilled_promise { - id ex1 = [self expectationWithDescription:@""]; - - [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - resolve([AnyPromise promiseWithValue:@1]); - }].then(^(id o){ - XCTAssertEqualObjects(o, @1); - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_52_fulfill_with_rejected_promise { //NEEDEDanypr - id ex1 = [self expectationWithDescription:@""]; - fulfillLater().then(^{ - return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - resolve([AnyPromise promiseWithValue:dummy()]); - }]; - }).catch(^(NSError *err){ - [ex1 fulfill]; - }); - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_53_return_rejected_promise { - id ex1 = [self expectationWithDescription:@""]; - fulfillLater().then(^{ - return @1; - }).then(^{ - return [AnyPromise promiseWithValue:dummy()]; - }).catch(^{ - [ex1 fulfill]; - }); - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_54_reject_with_rejected_promise { - id ex1 = [self expectationWithDescription:@""]; - - [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - id err = [NSError errorWithDomain:@"a" code:123 userInfo:nil]; - resolve([AnyPromise promiseWithValue:err]); - }].catch(^(NSError *err){ - XCTAssertEqual(err.code, 123); - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_58_just_finally { - id ex1 = [self expectationWithDescription:@""]; - - AnyPromise *promise = fulfillLater().then(^{ - return nil; - }).ensure(^{ - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; - - id ex2 = [self expectationWithDescription:@""]; - - promise.ensure(^{ - [ex2 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_59_catch_in_background { - id ex1 = [self expectationWithDescription:@""]; - - [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - id err = [NSError errorWithDomain:@"a" code:123 userInfo:nil]; - resolve(err); - }].catchInBackground(^(NSError *err){ - XCTAssertEqual(err.code, 123); - XCTAssertFalse([NSThread isMainThread]); - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_60_catch_on_specific_queue { - id ex1 = [self expectationWithDescription:@""]; - - NSString *expectedQueueName = @"specific queue 123"; - dispatch_queue_t q = dispatch_queue_create(expectedQueueName.UTF8String, DISPATCH_QUEUE_SERIAL); - - [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - id err = [NSError errorWithDomain:@"a" code:123 userInfo:nil]; - resolve(err); - }].catchOn(q, ^(NSError *err){ - XCTAssertEqual(err.code, 123); - XCTAssertFalse([NSThread isMainThread]); - NSString *currentQueueName = [NSString stringWithFormat:@"%s", dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)]; - XCTAssertEqualObjects(expectedQueueName, currentQueueName); - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_properties { - XCTAssertEqualObjects([AnyPromise promiseWithValue:@1].value, @1); - XCTAssertEqualObjects([[AnyPromise promiseWithValue:dummyWithCode(2)].value localizedDescription], @"2"); - XCTAssertNil([AnyPromise promiseWithResolverBlock:^(id a){}].value); - XCTAssertTrue([AnyPromise promiseWithResolverBlock:^(id a){}].pending); - XCTAssertFalse([AnyPromise promiseWithValue:@1].pending); - XCTAssertTrue([AnyPromise promiseWithValue:@1].fulfilled); - XCTAssertFalse([AnyPromise promiseWithValue:@1].rejected); -} - -- (void)test_promiseWithValue { - XCTAssertEqual([AnyPromise promiseWithValue:@1].value, @1); - XCTAssertEqualObjects([[AnyPromise promiseWithValue:dummyWithCode(2)].value localizedDescription], @"2"); - XCTAssertEqual([AnyPromise promiseWithValue:[AnyPromise promiseWithValue:@1]].value, @1); -} - -- (void)test_race { - id ex = [self expectationWithDescription:@""]; - id p = PMKAfter(0.1).then(^{ return @2; }); - PMKRace(@[PMKAfter(0.2), PMKAfter(0.5), p]).then(^(id obj){ - XCTAssertEqual(2, [obj integerValue]); - [ex fulfill]; - }); - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)testInBackground { - id ex = [self expectationWithDescription:@""]; - PMKAfter(0.1).thenInBackground(^{ [ex fulfill]; }); - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)testEnsureOn { - id ex = [self expectationWithDescription:@""]; - PMKAfter(0.1).ensureOn(dispatch_get_global_queue(0, 0), ^{ [ex fulfill]; }); - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)testAdapterBlock { - void (^fetch)(PMKAdapter) = ^(PMKAdapter block){ - block(@1, nil); - }; - id ex = [self expectationWithDescription:@""]; - [AnyPromise promiseWithAdapterBlock:fetch].then(^(id obj){ - XCTAssertEqualObjects(obj, @1); - [ex fulfill]; - }); - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)testIntegerAdapterBlock { - void (^fetch)(PMKIntegerAdapter) = ^(PMKIntegerAdapter block){ - block(1, nil); - }; - id ex = [self expectationWithDescription:@""]; - [AnyPromise promiseWithIntegerAdapterBlock:fetch].then(^(id obj){ - XCTAssertEqualObjects(obj, @1); - [ex fulfill]; - }); - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)testBooleanAdapterBlock { - void (^fetch)(PMKBooleanAdapter) = ^(PMKBooleanAdapter block){ - block(YES, nil); - }; - id ex = [self expectationWithDescription:@""]; - [AnyPromise promiseWithBooleanAdapterBlock:fetch].then(^(id obj){ - XCTAssertEqualObjects(obj, @YES); - [ex fulfill]; - }); - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -static NSHashTable *errorArray; - -- (void)setUp { - [super setUp]; - errorArray = [NSHashTable weakObjectsHashTable]; -} - -- (void)testErrorLeaks { - id ex1 = [self expectationWithDescription:@""]; - NSError *error = dummyWithCode(1001); - [errorArray addObject:error]; - [AnyPromise promiseWithValue:error] - .then(^{ - XCTFail(); - }).catch(^(NSError *e){ - XCTAssertEqual(e.localizedDescription.intValue, 1001); - }).then(^{ - NSError *err = dummyWithCode(1002); - [errorArray addObject:err]; - return err; - }).catch(^(NSError *e){ - XCTAssertEqual(e.localizedDescription.intValue, 1002); - }).then(^{ - NSError *err = dummyWithCode(1003); - [errorArray addObject:err]; - return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve){ - resolve(err); - }]; - }).catch(^(NSError *e){ - XCTAssertEqual(e.localizedDescription.intValue, 1003); - NSError *err = dummyWithCode(1004); - [errorArray addObject:err]; - return err; - }).catch(^(NSError *e){ - XCTAssertEqual(e.localizedDescription.intValue, 1004); - }).then(^{ - NSError *err = dummyWithCode(1005); - [errorArray addObject:err]; - // throw will lead to leak, if not use complie flag with "-fobjc-arc-exceptions" - @throw err; - }).catch(^(NSError *e){ - XCTAssertEqual(e.localizedDescription.intValue, 1005); - }).ensure(^{ - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)tearDown { - XCTAssertEqual(errorArray.allObjects.count, 0); - [super tearDown]; -} - -//- (void)test_nil_block { -// [AnyPromise promiseWithValue:@1].then(nil); -// [AnyPromise promiseWithValue:@1].thenOn(nil, nil); -// [AnyPromise promiseWithValue:@1].catch(nil); -// [AnyPromise promiseWithValue:@1].always(nil); -// [AnyPromise promiseWithValue:@1].alwaysOn(nil, nil); -//} - -@end diff --git a/Tests/CoreObjC/AnyPromiseTests.swift b/Tests/CoreObjC/AnyPromiseTests.swift deleted file mode 100644 index adbc68f78..000000000 --- a/Tests/CoreObjC/AnyPromiseTests.swift +++ /dev/null @@ -1,38 +0,0 @@ -import PromiseKit -import XCTest - -class AnyPromiseTests: XCTestCase { - func testFulfilledResult() { - switch AnyPromise(Promise.value(true)).result { - case .fulfilled(let obj as Bool)? where obj: - break - default: - XCTFail() - } - } - - func testRejectedResult() { - switch AnyPromise(Promise(error: PMKError.badInput)).result { - case .rejected(let err)?: - print(err) - break - default: - XCTFail() - } - } - - func testPendingResult() { - switch AnyPromise(Promise.pending().promise).result { - case nil: - break - default: - XCTFail() - } - } - - func testCustomStringConvertible() { - XCTAssertEqual("\(AnyPromise(Promise.pending().promise))", "AnyPromise(…)") - XCTAssertEqual("\(AnyPromise(Promise.value(1)))", "AnyPromise(1)") - XCTAssertEqual("\(AnyPromise(Promise.value(nil)))", "AnyPromise(nil)") - } -} diff --git a/Tests/CoreObjC/HangTests.m b/Tests/CoreObjC/HangTests.m deleted file mode 100644 index cf31ead97..000000000 --- a/Tests/CoreObjC/HangTests.m +++ /dev/null @@ -1,13 +0,0 @@ -@import PromiseKit; -@import XCTest; - -@interface HangTests: XCTestCase @end @implementation HangTests - -- (void)test { - __block int x = 0; - id value = PMKHang(PMKAfter(0.02).then(^{ x++; return 1; })); - XCTAssertEqual(x, 1); - XCTAssertEqualObjects(value, @1); -} - -@end diff --git a/Tests/CoreObjC/JoinTests.m b/Tests/CoreObjC/JoinTests.m deleted file mode 100644 index 1249cde0f..000000000 --- a/Tests/CoreObjC/JoinTests.m +++ /dev/null @@ -1,90 +0,0 @@ -@import Foundation; -@import PromiseKit; -@import XCTest; - - -@interface JoinTests: XCTestCase @end @implementation JoinTests - -- (void)test_73_join { - XCTestExpectation *ex1 = [self expectationWithDescription:@""]; - - __block void (^fulfiller)(id) = nil; - AnyPromise *promise = [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - fulfiller = resolve; - }]; - - PMKJoin(@[ - [AnyPromise promiseWithValue:[NSError errorWithDomain:@"dom" code:1 userInfo:nil]], - promise, - [AnyPromise promiseWithValue:[NSError errorWithDomain:@"dom" code:2 userInfo:nil]] - ]).then(^{ - XCTFail(); - }).catch(^(NSError *error){ - id promises = error.userInfo[PMKJoinPromisesKey]; - - int cume = 0, cumv = 0; - - for (AnyPromise *promise in promises) { - if ([promise.value isKindOfClass:[NSError class]]) { - cume |= [promise.value code]; - } else { - cumv |= [promise.value unsignedIntValue]; - } - } - - XCTAssertTrue(cumv == 4); - XCTAssertTrue(cume == 3); - - [ex1 fulfill]; - }); - fulfiller(@4); - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_74_join_no_errors { - XCTestExpectation *ex1 = [self expectationWithDescription:@""]; - PMKJoin(@[ - [AnyPromise promiseWithValue:@1], - [AnyPromise promiseWithValue:@2] - ]).then(^(NSArray *values, id errors) { - XCTAssertEqualObjects(values, (@[@1, @2])); - XCTAssertNil(errors); - [ex1 fulfill]; - }); - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - - -- (void)test_75_join_no_success { - XCTestExpectation *ex1 = [self expectationWithDescription:@""]; - PMKJoin(@[ - [AnyPromise promiseWithValue:[NSError errorWithDomain:@"dom" code:1 userInfo:nil]], - [AnyPromise promiseWithValue:[NSError errorWithDomain:@"dom" code:2 userInfo:nil]], - ]).then(^{ - XCTFail(); - }).catch(^(NSError *error){ - XCTAssertNotNil(error.userInfo[PMKJoinPromisesKey]); - [ex1 fulfill]; - }); - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_76_join_fulfills_if_empty_input { - XCTestExpectation *ex1 = [self expectationWithDescription:@""]; - PMKJoin(@[]).then(^(id a, id b, id c){ - XCTAssertEqualObjects(@[], a); - XCTAssertNil(b); - XCTAssertNil(c); - [ex1 fulfill]; - }); - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_join_nil { - NSArray *foo = nil; - NSError *err = PMKJoin(foo).value; - XCTAssertEqual(err.domain, PMKErrorDomain); - XCTAssertEqual(err.code, PMKInvalidUsageError); -} - -@end diff --git a/Tests/CoreObjC/PMKManifoldTests.m b/Tests/CoreObjC/PMKManifoldTests.m deleted file mode 100644 index 78248d6a4..000000000 --- a/Tests/CoreObjC/PMKManifoldTests.m +++ /dev/null @@ -1,83 +0,0 @@ -@import PromiseKit; -@import XCTest; - -@interface PMKManifoldTests: XCTestCase @end @implementation PMKManifoldTests - -- (void)test_62_access_extra_elements { - id ex1 = [self expectationWithDescription:@""]; - - [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - resolve(PMKManifold(@1)); - }].then(^(id o, id m, id n){ - XCTAssertNil(m, @"Accessing extra elements should not crash"); - XCTAssertNil(n, @"Accessing extra elements should not crash"); - XCTAssertEqualObjects(o, @1); - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_63_then_manifold { - id ex1 = [self expectationWithDescription:@""]; - - [AnyPromise promiseWithValue:@0].then(^{ - return PMKManifold(@1, @2, @3); - }).then(^(id o1, id o2, id o3){ - XCTAssertEqualObjects(o1, @1); - XCTAssertEqualObjects(o2, @2); - XCTAssertEqualObjects(o3, @3); - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_63_then_manifold_with_nil { - id ex1 = [self expectationWithDescription:@""]; - - [AnyPromise promiseWithValue:@0].then(^{ - return PMKManifold(@1, nil, @3); - }).then(^(id o1, id o2, id o3){ - XCTAssertEqualObjects(o1, @1); - XCTAssertEqualObjects(o2, nil); - XCTAssertEqualObjects(o3, @3); - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_65_manifold_fulfill_value { - id ex1 = [self expectationWithDescription:@""]; - - AnyPromise *promise = [AnyPromise promiseWithValue:@1].then(^{ - return PMKManifold(@123, @2); - }); - - promise.then(^(id a, id b){ - XCTAssertNotNil(a); - XCTAssertNotNil(b); - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; - - XCTAssertEqualObjects(promise.value, @123); -} - -- (void)test_37_PMKMany_2 { - id ex1 = [self expectationWithDescription:@""]; - - PMKAfter(0.02).then(^{ - return PMKManifold(@1, @2); - }).then(^(id a, id b){ - XCTAssertEqualObjects(a, @1); - XCTAssertEqualObjects(b, @2); - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -@end diff --git a/Tests/CoreObjC/WhenTests.m b/Tests/CoreObjC/WhenTests.m deleted file mode 100644 index f04a21a28..000000000 --- a/Tests/CoreObjC/WhenTests.m +++ /dev/null @@ -1,265 +0,0 @@ -@import Foundation; -@import PromiseKit; -@import XCTest; - - -@interface WhenTests: XCTestCase @end @implementation WhenTests - -- (void)testProgress { - - id ex = [self expectationWithDescription:@""]; - - XCTAssertNil([NSProgress currentProgress]); - - id p1 = PMKAfter(0.01); - id p2 = PMKAfter(0.02); - id p3 = PMKAfter(0.03); - id p4 = PMKAfter(0.04); - - NSProgress *progress = [NSProgress progressWithTotalUnitCount:1]; - [progress becomeCurrentWithPendingUnitCount:1]; - - PMKWhen(@[p1, p2, p3, p4]).then(^{ - XCTAssertEqual(progress.completedUnitCount, 1); - [ex fulfill]; - }); - - [progress resignCurrent]; - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)testProgressDoesNotExceed100Percent { - - id ex1 = [self expectationWithDescription:@""]; - id ex2 = [self expectationWithDescription:@""]; - - XCTAssertNil([NSProgress currentProgress]); - - id p1 = PMKAfter(0.01); - id p2 = PMKAfter(0.02).then(^{ return [NSError errorWithDomain:@"a" code:1 userInfo:nil]; }); - id p3 = PMKAfter(0.03); - id p4 = PMKAfter(0.04); - - id promises = @[p1, p2, p3, p4]; - - NSProgress *progress = [NSProgress progressWithTotalUnitCount:1]; - [progress becomeCurrentWithPendingUnitCount:1]; - - PMKWhen(promises).catch(^{ - [ex2 fulfill]; - }); - - [progress resignCurrent]; - - PMKJoin(promises).catch(^{ - XCTAssertLessThanOrEqual(1, progress.fractionCompleted); - XCTAssertEqual(progress.completedUnitCount, 1); - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)testWhenManifolds { - id ex = [self expectationWithDescription:@""]; - id p1 = dispatch_promise(^{ return PMKManifold(@1, @2); }); - id p2 = dispatch_promise(^{}); - PMKWhen(@[p1, p2]).then(^(NSArray *results){ - XCTAssertEqualObjects(results[0], @1); - XCTAssertEqualObjects(results[1], [NSNull null]); - [ex fulfill]; - }); - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_55_all_dictionary { - id ex1 = [self expectationWithDescription:@""]; - - id promises = @{ - @1: @2, - @2: @"abc", - @"a": PMKAfter(0.1).then(^{ return @"HI"; }) - }; - PMKWhen(promises).then(^(NSDictionary *dict){ - XCTAssertEqual(dict.count, 3ul); - XCTAssertEqualObjects(dict[@1], @2); - XCTAssertEqualObjects(dict[@2], @"abc"); - XCTAssertEqualObjects(dict[@"a"], @"HI"); - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_56_empty_array_when { - id ex1 = [self expectationWithDescription:@""]; - - PMKWhen(@[]).then(^(NSArray *array){ - XCTAssertEqual(array.count, 0ul); - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_57_empty_array_all { - id ex1 = [self expectationWithDescription:@""]; - - PMKWhen(@[]).then(^(NSArray *array){ - XCTAssertEqual(array.count, 0ul); - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_18_when { - id ex1 = [self expectationWithDescription:@""]; - - id a = PMKAfter(0.02).then(^{ return @345; }); - id b = PMKAfter(0.03).then(^{ return @345; }); - PMKWhen(@[a, b]).then(^(NSArray *objs){ - XCTAssertEqual(objs.count, 2ul); - XCTAssertEqualObjects(objs[0], objs[1]); - [ex1 fulfill]; - }); - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_21_recursive_when { - id domain = @"sdjhfg"; - - id ex1 = [self expectationWithDescription:@""]; - id a = PMKAfter(0.03).then(^{ - return [NSError errorWithDomain:domain code:123 userInfo:nil]; - }); - id b = PMKAfter(0.02); - id c = PMKWhen(@[a, b]); - PMKWhen(c).then(^{ - XCTFail(); - }).catch(^(NSError *e){ - XCTAssertEqualObjects(e.userInfo[PMKFailingPromiseIndexKey], @0); - XCTAssertEqualObjects(e.domain, domain); - [ex1 fulfill]; - }); - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_22_already_resolved_and_bubble { - id ex1 = [self expectationWithDescription:@""]; - id ex2 = [self expectationWithDescription:@""]; - - PMKResolver resolve; - AnyPromise *promise = [[AnyPromise alloc] initWithResolver:&resolve]; - - promise.then(^{ - XCTFail(); - }).catch(^(NSError *e){ - [ex1 fulfill]; - }); - - resolve([NSError errorWithDomain:@"a" code:1 userInfo:nil]); - - PMKWhen(promise).then(^{ - XCTFail(); - }).catch(^{ - [ex2 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_24_some_edge_case { - id ex1 = [self expectationWithDescription:@""]; - id a = PMKAfter(0.02).catch(^{}); - id b = PMKAfter(0.03); - PMKWhen(@[a, b]).then(^(NSArray *objs){ - [ex1 fulfill]; - }); - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_35_when_nil { - id ex1 = [self expectationWithDescription:@""]; - - AnyPromise *promise = [AnyPromise promiseWithValue:@"35"].then(^{ return nil; }); - PMKWhen(@[PMKAfter(0.02).then(^{ return @1; }), [AnyPromise promiseWithValue:nil], promise]).then(^(NSArray *results){ - XCTAssertEqual(results.count, 3ul); - XCTAssertEqualObjects(results[1], [NSNull null]); - [ex1 fulfill]; - }).catch(^(NSError *err){ - abort(); - }); - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - - -- (void)test_39_when_with_some_values { - id ex1 = [self expectationWithDescription:@""]; - - id p = PMKAfter(0.02); - id v = @1; - PMKWhen(@[p, v]).then(^(NSArray *aa){ - XCTAssertEqual(aa.count, 2ul); - XCTAssertEqualObjects(aa[1], @1); - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_40_when_with_all_values { - id ex1 = [self expectationWithDescription:@""]; - - PMKWhen(@[@1, @2]).then(^(NSArray *aa){ - XCTAssertEqualObjects(aa[0], @1); - XCTAssertEqualObjects(aa[1], @2); - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_41_when_with_repeated_promises { - id ex1 = [self expectationWithDescription:@""]; - - id p = PMKAfter(0.02); - id v = @1; - PMKWhen(@[p, v, p, v]).then(^(NSArray *aa){ - XCTAssertEqual(aa.count, 4ul); - XCTAssertEqualObjects(aa[1], @1); - XCTAssertEqualObjects(aa[3], @1); - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_45_when_which_returns_void { - id ex1 = [self expectationWithDescription:@""]; - - AnyPromise *promise = [AnyPromise promiseWithValue:@1].then(^{}); - PMKWhen(@[promise, [AnyPromise promiseWithValue:@1]]).then(^(NSArray *stuff){ - XCTAssertEqual(stuff.count, 2ul); - XCTAssertEqualObjects(stuff[0], [NSNull null]); - [ex1 fulfill]; - }); - - [self waitForExpectationsWithTimeout:1 handler:nil]; -} - -- (void)test_when_nil { - NSArray *foo = nil; - NSError *err = PMKWhen(foo).value; - XCTAssertEqual(err.domain, PMKErrorDomain); - XCTAssertEqual(err.code, PMKInvalidUsageError); -} - - -- (void)test_when_bad_input { - id foo = @"a"; - XCTAssertEqual(PMKWhen(foo).value, foo); -} - -@end diff --git a/Tests/DeprecationTests.swift b/Tests/DeprecationTests.swift deleted file mode 100644 index d382ce85e..000000000 --- a/Tests/DeprecationTests.swift +++ /dev/null @@ -1,158 +0,0 @@ -import PromiseKit -import XCTest - -class DeprecationTests: XCTestCase { - func testWrap1() { - let dummy = 10 - - func completion(_ body: (_ a: Int?, _ b: Error?) -> Void) { - body(dummy, nil) - } - - let ex = expectation(description: "") - wrap(completion).done { - XCTAssertEqual($0, dummy) - ex.fulfill() - } - wait(for: [ex], timeout: 10) - } - - func testWrap2() { - let dummy = 10 - - func completion(_ body: (_ a: Int, _ b: Error?) -> Void) { - body(dummy, nil) - } - - let ex = expectation(description: "") - wrap(completion).done { - XCTAssertEqual($0, dummy) - ex.fulfill() - } - wait(for: [ex], timeout: 10) - } - - func testWrap3() { - let dummy = 10 - - func completion(_ body: (_ a: Error?, _ b: Int?) -> Void) { - body(nil, dummy) - } - - let ex = expectation(description: "") - wrap(completion).done { - XCTAssertEqual($0, dummy) - ex.fulfill() - } - wait(for: [ex], timeout: 10) - } - - func testWrap4() { - let dummy = 10 - - func completion(_ body: (_ a: Error?) -> Void) { - body(nil) - } - - let ex = expectation(description: "") - wrap(completion).done { - ex.fulfill() - } - wait(for: [ex], timeout: 10) - } - - func testWrap5() { - let dummy = 10 - - func completion(_ body: (_ a: Int) -> Void) { - body(dummy) - } - - let ex = expectation(description: "") - wrap(completion).done { - XCTAssertEqual($0, dummy) - ex.fulfill() - } - wait(for: [ex], timeout: 10) - } - - func testAlways() { - let ex = expectation(description: "") - Promise.value(1).always(execute: ex.fulfill) - wait(for: [ex], timeout: 10) - } - -#if PMKFullDeprecations - func testFlatMap() { - let ex = expectation(description: "") - Promise.value(1).flatMap { _ -> Int? in - nil - }.catch { - //TODO should be `flatMap`, but how to enact that without causing - // compiler to warn when building PromiseKit for end-users? LOL - guard case PMKError.compactMap = $0 else { return XCTFail() } - ex.fulfill() - } - wait(for: [ex], timeout: 10) - } - - func testSequenceMap() { - let ex = expectation(description: "") - Promise.value([1, 2]).map { - $0 + 1 - }.done { - XCTAssertEqual($0, [2, 3]) - ex.fulfill() - }.silenceWarning() - wait(for: [ex], timeout: 10) - } - - func testSequenceFlatMap() { - let ex = expectation(description: "") - Promise.value([1, 2]).flatMap { - [$0 + 1, $0 + 2] - }.done { - XCTAssertEqual($0, [2, 3, 3, 4]) - ex.fulfill() - }.silenceWarning() - wait(for: [ex], timeout: 10) - } -#endif - - func testSequenceFilter() { - let ex = expectation(description: "") - Promise.value([0, 1, 2, 3]).filter { - $0 < 2 - }.done { - XCTAssertEqual($0, [0, 1]) - ex.fulfill() - }.silenceWarning() - wait(for: [ex], timeout: 10) - } - - func testSorted() { - let ex = expectation(description: "") - Promise.value([5, 2, 1, 8]).sorted().done { - XCTAssertEqual($0, [1,2,5,8]) - ex.fulfill() - } - wait(for: [ex], timeout: 10) - } - - func testFirst() { - XCTAssertEqual(Promise.value([1,2]).first.value, 1) - } - - func testLast() { - XCTAssertEqual(Promise.value([1,2]).last.value, 2) - } - - func testPMKErrorFlatMap() { - XCTAssertNotNil(PMKError.flatMap(1, Int.self).errorDescription) - } -} - - -extension Promise { - func silenceWarning() {} -} From 277944ff1943b12626a64e35da115e66a05adeb9 Mon Sep 17 00:00:00 2001 From: Garth Snyder Date: Tue, 12 Feb 2019 00:51:16 +0000 Subject: [PATCH 02/81] Generalize handler dispatch mechanisms --- Sources/Box.swift | 17 -- Sources/Catchable.swift | 47 ++- Sources/Configuration.swift | 10 +- Sources/Dispatcher.swift | 500 +++++++++++++++++++++++++++++++ Sources/Guarantee.swift | 45 ++- Sources/Promise.swift | 27 ++ Sources/Thenable.swift | 67 +++-- Tests/Core/DispatcherTests.swift | 148 +++++++++ 8 files changed, 774 insertions(+), 87 deletions(-) create mode 100644 Sources/Dispatcher.swift create mode 100644 Tests/Core/DispatcherTests.swift diff --git a/Sources/Box.swift b/Sources/Box.swift index 43cd3d1b0..7b53a9e1c 100644 --- a/Sources/Box.swift +++ b/Sources/Box.swift @@ -82,20 +82,3 @@ class EmptyBox: Box { } } } - - -extension Optional where Wrapped: DispatchQueue { - @inline(__always) - func async(flags: DispatchWorkItemFlags?, _ body: @escaping() -> Void) { - switch self { - case .none: - body() - case .some(let q): - if let flags = flags { - q.async(flags: flags, execute: body) - } else { - q.async(execute: body) - } - } - } -} diff --git a/Sources/Catchable.swift b/Sources/Catchable.swift index 596abdcba..a60ea4f60 100644 --- a/Sources/Catchable.swift +++ b/Sources/Catchable.swift @@ -14,14 +14,14 @@ public extension CatchMixin { of a chain. Often utility promises will not have a catch, instead delegating the error handling to the caller. - - Parameter on: The queue to which the provided closure dispatches. + - Parameter on: The dispatcher that executes the provided closure. - Parameter policy: The default policy does not execute your handler for cancellation errors. - Parameter execute: The handler to execute if this promise is rejected. - Returns: A promise finalizer. - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) */ @discardableResult - func `catch`(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) -> Void) -> PMKFinalizer { + func `catch`(on: Dispatcher = conf.D.return, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) -> Void) -> PMKFinalizer { let finalizer = PMKFinalizer() pipe { switch $0 { @@ -29,7 +29,7 @@ public extension CatchMixin { guard policy == .allErrors || !error.isCancelled else { fallthrough } - on.async(flags: flags) { + on.dispatch { body(error) finalizer.pending.resolve(()) } @@ -45,8 +45,8 @@ public class PMKFinalizer { let pending = Guarantee.pending() /// `finally` is the same as `ensure`, but it is not chainable - public func finally(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping () -> Void) { - pending.guarantee.done(on: on, flags: flags) { + public func finally(on: Dispatcher = conf.D.return, _ body: @escaping () -> Void) { + pending.guarantee.done(on: on) { body() } } @@ -68,11 +68,11 @@ public extension CatchMixin { return .value(CLLocation.chicago) } - - Parameter on: The queue to which the provided closure dispatches. + - Parameter on: The dispatcher that executes the provided closure. - Parameter body: The handler to execute if this promise is rejected. - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) */ - func recover(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) throws -> U) -> Promise where U.T == T { + func recover(on: Dispatcher = conf.D.map, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) throws -> U) -> Promise where U.T == T { let rp = Promise(.pending) pipe { switch $0 { @@ -80,7 +80,7 @@ public extension CatchMixin { rp.box.seal(.fulfilled(value)) case .rejected(let error): if policy == .allErrors || !error.isCancelled { - on.async(flags: flags) { + on.dispatch { do { let rv = try body(error) guard rv !== rp else { throw PMKError.returnedSelf } @@ -101,19 +101,19 @@ public extension CatchMixin { The provided closure executes when this promise rejects. This variant of `recover` requires the handler to return a Guarantee, thus it returns a Guarantee itself and your closure cannot `throw`. - Note it is logically impossible for this to take a `catchPolicy`, thus `allErrors` are handled. - - Parameter on: The queue to which the provided closure dispatches. + - Parameter on: The dispatcher that executes the provided closure. - Parameter body: The handler to execute if this promise is rejected. - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) */ @discardableResult - func recover(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Error) -> Guarantee) -> Guarantee { + func recover(on: Dispatcher = conf.D.map, _ body: @escaping(Error) -> Guarantee) -> Guarantee { let rg = Guarantee(.pending) pipe { switch $0 { case .fulfilled(let value): rg.box.seal(value) case .rejected(let error): - on.async(flags: flags) { + on.dispatch { body(error).pipe(to: rg.box.seal) } } @@ -134,14 +134,14 @@ public extension CatchMixin { //… } - - Parameter on: The queue to which the provided closure dispatches. + - Parameter on: The dispatcher that executes the provided closure. - Parameter body: The closure that executes when this promise resolves. - Returns: A new promise, resolved with this promise’s resolution. */ - func ensure(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping () -> Void) -> Promise { + func ensure(on: Dispatcher = conf.D.return, _ body: @escaping () -> Void) -> Promise { let rp = Promise(.pending) pipe { result in - on.async(flags: flags) { + on.dispatch { body() rp.box.seal(result) } @@ -163,14 +163,14 @@ public extension CatchMixin { //… } - - Parameter on: The queue to which the provided closure dispatches. + - Parameter on: The dispatcher that executes the provided closure. - Parameter body: The closure that executes when this promise resolves. - Returns: A new promise, resolved with this promise’s resolution. */ - func ensureThen(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping () -> Guarantee) -> Promise { + func ensureThen(on: Dispatcher = conf.D.return, _ body: @escaping () -> Guarantee) -> Promise { let rp = Promise(.pending) pipe { result in - on.async(flags: flags) { + on.dispatch { body().done { rp.box.seal(result) } @@ -180,7 +180,6 @@ public extension CatchMixin { } - /** Consumes the Swift unused-result warning. - Note: You should `catch`, but in situations where you know you don’t need a `catch`, `cauterize` makes your intentions clear. @@ -201,19 +200,19 @@ public extension CatchMixin where T == Void { This variant of `recover` is specialized for `Void` promises and de-errors your chain returning a `Guarantee`, thus you cannot `throw` and you must handle all errors including cancellation. - - Parameter on: The queue to which the provided closure dispatches. + - Parameter on: The dispatcher that executes the provided closure. - Parameter body: The handler to execute if this promise is rejected. - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) */ @discardableResult - func recover(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Error) -> Void) -> Guarantee { + func recover(on: Dispatcher = conf.D.map, _ body: @escaping(Error) -> Void) -> Guarantee { let rg = Guarantee(.pending) pipe { switch $0 { case .fulfilled: rg.box.seal(()) case .rejected(let error): - on.async(flags: flags) { + on.dispatch { body(error) rg.box.seal(()) } @@ -227,11 +226,11 @@ public extension CatchMixin where T == Void { This variant of `recover` ensures that no error is thrown from the handler and allows specifying a catch policy. - - Parameter on: The queue to which the provided closure dispatches. + - Parameter on: The dispatcher that executes the provided closure. - Parameter body: The handler to execute if this promise is rejected. - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) */ - func recover(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) throws -> Void) -> Promise { + func recover(on: Dispatcher = conf.D.map, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) throws -> Void) -> Promise { let rg = Promise(.pending) pipe { switch $0 { @@ -239,7 +238,7 @@ public extension CatchMixin where T == Void { rg.box.seal(.fulfilled(())) case .rejected(let error): if policy == .allErrors || !error.isCancelled { - on.async(flags: flags) { + on.dispatch { do { rg.box.seal(.fulfilled(try body(error))) } catch { diff --git a/Sources/Configuration.swift b/Sources/Configuration.swift index 9d4fc22fb..dea487e94 100644 --- a/Sources/Configuration.swift +++ b/Sources/Configuration.swift @@ -8,8 +8,14 @@ import Dispatch We would like it to be, but sadly `Swift` does not expose `dispatch_once` et al. which is what we used to use in order to make the configuration immutable once first used. */ public struct PMKConfiguration { - /// The default queues that promises handlers dispatch to - public var Q: (map: DispatchQueue?, return: DispatchQueue?) = (map: DispatchQueue.main, return: DispatchQueue.main) + /// Backward compatibility: default DispatchQueues that promise handlers dispatch to + public var Q: (map: DispatchQueue?, return: DispatchQueue?) { + get { return (map: D.map as? DispatchQueue, return: D.return as? DispatchQueue) } + set { D = (map: newValue.map ?? CurrentThreadDispatcher(), return: newValue.return ?? CurrentThreadDispatcher()) } + } + + /// The default Dispatchers that promise handlers dispatch to + public var D: (map: Dispatcher, return: Dispatcher) = (map: DispatchQueue.main, return: DispatchQueue.main) /// The default catch-policy for all `catch` and `resolve` public var catchPolicy = CatchPolicy.allErrorsExceptCancellation diff --git a/Sources/Dispatcher.swift b/Sources/Dispatcher.swift new file mode 100644 index 000000000..3da133f84 --- /dev/null +++ b/Sources/Dispatcher.swift @@ -0,0 +1,500 @@ +import Dispatch + +public protocol Dispatcher { + func dispatch(_ body: @escaping () -> Void) +} + +public class DispatchQueueDispatcher: Dispatcher { + + let queue: DispatchQueue + let flags: DispatchWorkItemFlags + + init(queue: DispatchQueue, flags: DispatchWorkItemFlags) { + self.queue = queue + self.flags = flags + } + + public func dispatch(_ body: @escaping () -> Void) { + queue.async(flags: flags, execute: body) + } + +} + +public struct CurrentThreadDispatcher: Dispatcher { + public func dispatch(_ body: @escaping () -> Void) { + body() + } +} + +extension DispatchQueue: Dispatcher { + /// Explicit declaration required; actual function signature is not identical to protocol + public func dispatch(_ body: @escaping () -> Void) { + async(execute: body) + } +} + +/// Used as default parameter for backward compatibility since clients may explicitly +/// specify "nil" to turn off dispatching. We need to distinguish three cases: explicit +/// queue, explicit nil, and no value specified. Dispatchers from conf.D cannot directly +/// be used as default parameter values because they are not necessarily DispatchQueues. + +public extension DispatchQueue { + static var pmkDefault = DispatchQueue(label: "org.promisekit.sentinel") +} + +public extension DispatchQueue { + func asDispatcher(withFlags flags: DispatchWorkItemFlags? = nil) -> Dispatcher { + if let flags = flags { + return DispatchQueueDispatcher(queue: self, flags: flags) + } + return self + } +} + +/// This hairball disambiguates all the various combinations of explicit arguments, default +/// arguments, and configured defaults. In particular, a method that is given explicit work item +/// flags but no DispatchQueue should still work (that is, the dispatcher should use those flags) +/// as long as the configured default is actually some kind of DispatchQueue. +/// +/// TODO: should conf.D = nil turn off dispatching even if explicit dispatch arguments are given? +/// TODO: Move log prints into LogError enum if they are kept + +fileprivate func selectDispatcher(given: DispatchQueue?, configured: Dispatcher, flags: DispatchWorkItemFlags?) -> Dispatcher { + guard let given = given else { + if flags != nil { + print("PromiseKit: warning: nil DispatchQueue specified, but DispatchWorkItemFlags were also supplied (ignored)") + } + return CurrentThreadDispatcher() + } + if given !== DispatchQueue.pmkDefault { + return given.asDispatcher(withFlags: flags) + } else if let flags = flags, let configured = configured as? DispatchQueue { + return configured.asDispatcher(withFlags: flags) + } else if flags != nil { + print("PromiseKit: warning: DispatchWorkItemFlags flags specified, but default dispatcher is not a DispatchQueue (ignored)") + } + return configured +} + +/// Backward compatibility for DispatchQueues in public API + +public extension Guarantee { + + @discardableResult + func done(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) -> Void) -> Guarantee { + let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) + return done(on: dispatcher, body) + } + + func get(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping (T) -> Void) -> Guarantee { + let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) + return get(on: dispatcher, body) + } + + func map(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) -> U) -> Guarantee { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return map(on: dispatcher, body) + } + + @discardableResult + func then(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) -> Guarantee) -> Guarantee { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return then(on: dispatcher, body) + } + +} + +public extension Guarantee where T: Sequence { + + func thenMap(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) -> Guarantee) -> Guarantee<[U]> { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return thenMap(on: dispatcher, transform) + } + +} + +public extension Thenable { + + /** + The provided closure executes when this promise resolves. + + This allows chaining promises. The promise returned by the provided closure is resolved before the promise returned by this closure resolves. + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter body: The closure that executes when this promise fulfills. It must return a promise. + - Returns: A new promise that resolves when the promise returned from the provided closure resolves. For example: + + firstly { + URLSession.shared.dataTask(.promise, with: url1) + }.then { response in + transform(data: response.data) + }.done { transformation in + //… + } + */ + func then(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) throws -> U) -> Promise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return then(on: dispatcher, body) + } + + /** + The provided closure is executed when this promise is resolved. + + This is like `then` but it requires the closure to return a non-promise. + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter transform: The closure that is executed when this Promise is fulfilled. It must return a non-promise. + - Returns: A new promise that is resolved with the value returned from the provided closure. For example: + + firstly { + URLSession.shared.dataTask(.promise, with: url1) + }.map { response in + response.data.length + }.done { length in + //… + } + */ + func map(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T) throws -> U) -> Promise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return map(on: dispatcher, transform) + } + + /** + The provided closure is executed when this promise is resolved. + + In your closure return an `Optional`, if you return `nil` the resulting promise is rejected with `PMKError.compactMap`, otherwise the promise is fulfilled with the unwrapped value. + + firstly { + URLSession.shared.dataTask(.promise, with: url) + }.compactMap { + try JSONSerialization.jsonObject(with: $0.data) as? [String: String] + }.done { dictionary in + //… + }.catch { + // either `PMKError.compactMap` or a `JSONError` + } + */ + func compactMap(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T) throws -> U?) -> Promise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return compactMap(on: dispatcher, transform) + } + + /** + The provided closure is executed when this promise is resolved. + + Equivalent to `map { x -> Void in`, but since we force the `Void` return Swift + is happier and gives you less hassle about your closure’s qualification. + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter body: The closure that is executed when this Promise is fulfilled. + - Returns: A new promise fulfilled as `Void`. + + firstly { + URLSession.shared.dataTask(.promise, with: url) + }.done { response in + print(response.data) + } + */ + func done(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) throws -> Void) -> Promise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) + return done(on: dispatcher, body) + } + + /** + The provided closure is executed when this promise is resolved. + + This is like `done` but it returns the same value that the handler is fed. + `get` immutably accesses the fulfilled value; the returned Promise maintains that value. + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter body: The closure that is executed when this Promise is fulfilled. + - Returns: A new promise that is resolved with the value that the handler is fed. For example: + + firstly { + .value(1) + }.get { foo in + print(foo, " is 1") + }.done { foo in + print(foo, " is 1") + }.done { foo in + print(foo, " is Void") + } + */ + func get(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping (T) throws -> Void) -> Promise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) + return get(on: dispatcher, body) + } + + /** + The provided closure is executed with promise result. + + This is like `get` but provides the Result of the Promise so you can inspect the value of the chain at this point without causing any side effects. + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter body: The closure that is executed with Result of Promise. + - Returns: A new promise that is resolved with the result that the handler is fed. For example: + + promise.tap{ print($0) }.then{ /*…*/ } + */ + func tap(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Result) -> Void) -> Promise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return tap(on: dispatcher, body) + } + +} + +public extension Thenable where T: Sequence { + /** + `Promise<[T]>` => `T` -> `U` => `Promise<[U]>` + + firstly { + .value([1,2,3]) + }.mapValues { integer in + integer * 2 + }.done { + // $0 => [2,4,6] + } + */ + func mapValues(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U]> { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return mapValues(on: dispatcher, transform) + } + + /** + `Promise<[T]>` => `T` -> `[U]` => `Promise<[U]>` + + firstly { + .value([1,2,3]) + }.flatMapValues { integer in + [integer, integer] + }.done { + // $0 => [1,1,2,2,3,3] + } + */ + func flatMapValues(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U.Iterator.Element]> { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return flatMapValues(on: dispatcher, transform) + } + + /** + `Promise<[T]>` => `T` -> `U?` => `Promise<[U]>` + + firstly { + .value(["1","2","a","3"]) + }.compactMapValues { + Int($0) + }.done { + // $0 => [1,2,3] + } + */ + func compactMapValues(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) throws -> U?) -> Promise<[U]> { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return compactMapValues(on: dispatcher, transform) + } + + /** + `Promise<[T]>` => `T` -> `Promise` => `Promise<[U]>` + + firstly { + .value([1,2,3]) + }.thenMap { integer in + .value(integer * 2) + }.done { + // $0 => [2,4,6] + } + */ + func thenMap(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U.T]> { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return thenMap(on: dispatcher, transform) + } + + /** + `Promise<[T]>` => `T` -> `Promise<[U]>` => `Promise<[U]>` + + firstly { + .value([1,2,3]) + }.thenFlatMap { integer in + .value([integer, integer]) + }.done { + // $0 => [1,1,2,2,3,3] + } + */ + func thenFlatMap(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U.T.Iterator.Element]> where U.T: Sequence { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return thenFlatMap(on: dispatcher, transform) + } + + /** + `Promise<[T]>` => `T` -> Bool => `Promise<[U]>` + + firstly { + .value([1,2,3]) + }.filterValues { + $0 > 1 + }.done { + // $0 => [2,3] + } + */ + func filterValues(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ isIncluded: @escaping (T.Iterator.Element) -> Bool) -> Promise<[T.Iterator.Element]> { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return filterValues(on: dispatcher, isIncluded) + } +} + +public extension Thenable where T: Collection { + func firstValue(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, where test: @escaping (T.Iterator.Element) -> Bool) -> Promise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return firstValue(on: dispatcher, where: test) + } +} + +public extension Thenable where T: Sequence, T.Iterator.Element: Comparable { + /// - Returns: a promise fulfilled with the sorted values of this `Sequence`. + func sortedValues(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil) -> Promise<[T.Iterator.Element]> { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return sortedValues(on: dispatcher) + } +} + +public extension CatchMixin { + /** + The provided closure executes when this promise rejects. + + Rejecting a promise cascades: rejecting all subsequent promises (unless + recover is invoked) thus you will typically place your catch at the end + of a chain. Often utility promises will not have a catch, instead + delegating the error handling to the caller. + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter policy: The default policy does not execute your handler for cancellation errors. + - Parameter execute: The handler to execute if this promise is rejected. + - Returns: A promise finalizer. + - SeeAlso: [Cancellation](http://https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation/docs/) + */ + @discardableResult + func `catch`(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) -> Void) -> PMKFinalizer { + let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) + return `catch`(on: dispatcher, policy: policy, body) + } + + /** + The provided closure executes when this promise rejects. + + Unlike `catch`, `recover` continues the chain. + Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: + + firstly { + CLLocationManager.requestLocation() + }.recover { error in + guard error == CLError.unknownLocation else { throw error } + return .value(CLLocation.chicago) + } + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter body: The handler to execute if this promise is rejected. + - SeeAlso: [Cancellation](http://https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation/docs/) + */ + func recover(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) throws -> U) -> Promise where U.T == T { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return recover(on: dispatcher, policy: policy, body) + } + + /** + The provided closure executes when this promise rejects. + This variant of `recover` requires the handler to return a Guarantee, thus it returns a Guarantee itself and your closure cannot `throw`. + - Note it is logically impossible for this to take a `catchPolicy`, thus `allErrors` are handled. + - Parameter on: The queue to which the provided closure dispatches. + - Parameter body: The handler to execute if this promise is rejected. + - SeeAlso: [Cancellation](http://https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation/docs/) + */ + @discardableResult + func recover(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Error) -> Guarantee) -> Guarantee { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return recover(on: dispatcher, body) + } + + /** + The provided closure executes when this promise resolves, whether it rejects or not. + + firstly { + UIApplication.shared.networkActivityIndicatorVisible = true + }.done { + //… + }.ensure { + UIApplication.shared.networkActivityIndicatorVisible = false + }.catch { + //… + } + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter body: The closure that executes when this promise resolves. + - Returns: A new promise, resolved with this promise’s resolution. + */ + func ensure(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping () -> Void) -> Promise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) + return ensure(on: dispatcher, body) + } + + /** + The provided closure executes when this promise resolves, whether it rejects or not. + The chain waits on the returned `Guarantee`. + + firstly { + setup() + }.done { + //… + }.ensureThen { + teardown() // -> Guarante + }.catch { + //… + } + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter body: The closure that executes when this promise resolves. + - Returns: A new promise, resolved with this promise’s resolution. + */ + func ensureThen(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping () -> Guarantee) -> Promise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) + return ensureThen(on: dispatcher, body) + } +} + +public extension PMKFinalizer { + /// `finally` is the same as `ensure`, but it is not chainable + func finally(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping () -> Void) { + let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) + return finally(on: dispatcher, body) + } +} + +public extension CatchMixin where T == Void { + + /** + The provided closure executes when this promise rejects. + + This variant of `recover` is specialized for `Void` promises and de-errors your chain returning a `Guarantee`, thus you cannot `throw` and you must handle all errors including cancellation. + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter body: The handler to execute if this promise is rejected. + - SeeAlso: [Cancellation](http://https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation/docs/) + */ + @discardableResult + func recover(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Error) -> Void) -> Guarantee { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return recover(on: dispatcher, body) + } + + /** + The provided closure executes when this promise rejects. + + This variant of `recover` ensures that no error is thrown from the handler and allows specifying a catch policy. + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter body: The handler to execute if this promise is rejected. + - SeeAlso: [Cancellation](http://https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation/docs/) + */ + func recover(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) throws -> Void) -> Promise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return recover(on: dispatcher, policy: policy, body) + } +} diff --git a/Sources/Guarantee.swift b/Sources/Guarantee.swift index 1897aa644..f97858210 100644 --- a/Sources/Guarantee.swift +++ b/Sources/Guarantee.swift @@ -77,10 +77,10 @@ public final class Guarantee: Thenable { public extension Guarantee { @discardableResult - func done(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) -> Void) -> Guarantee { + func done(on: Dispatcher = conf.D.return, _ body: @escaping(T) -> Void) -> Guarantee { let rg = Guarantee(.pending) pipe { (value: T) in - on.async(flags: flags) { + on.dispatch { body(value) rg.box.seal(()) } @@ -88,17 +88,17 @@ public extension Guarantee { return rg } - func get(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping (T) -> Void) -> Guarantee { - return map(on: on, flags: flags) { + func get(on: Dispatcher = conf.D.return, _ body: @escaping (T) -> Void) -> Guarantee { + return map(on: on) { body($0) return $0 } } - func map(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) -> U) -> Guarantee { + func map(on: Dispatcher = conf.D.map, _ body: @escaping(T) -> U) -> Guarantee { let rg = Guarantee(.pending) pipe { value in - on.async(flags: flags) { + on.dispatch { rg.box.seal(body(value)) } } @@ -106,10 +106,10 @@ public extension Guarantee { } @discardableResult - func then(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) -> Guarantee) -> Guarantee { + func then(on: Dispatcher = conf.D.map, _ body: @escaping(T) -> Guarantee) -> Guarantee { let rg = Guarantee(.pending) pipe { value in - on.async(flags: flags) { + on.dispatch { body(value).pipe(to: rg.box.seal) } } @@ -146,7 +146,7 @@ public extension Guarantee { public extension Guarantee where T: Sequence { /** - `Guarantee<[T]>` => `T` -> `Guarantee` => `Guaranetee<[U]>` + `Guarantee<[T]>` => `T` -> `Guarantee` => `Guarantee<[U]>` firstly { .value([1,2,3]) @@ -156,8 +156,8 @@ public extension Guarantee where T: Sequence { // $0 => [2,4,6] } */ - func thenMap(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) -> Guarantee) -> Guarantee<[U]> { - return then(on: on, flags: flags) { + func thenMap(on: Dispatcher = conf.D.map, _ transform: @escaping(T.Iterator.Element) -> Guarantee) -> Guarantee<[U]> { + return then(on: on) { when(fulfilled: $0.map(transform)) }.recover { // if happens then is bug inside PromiseKit @@ -207,6 +207,29 @@ public extension DispatchQueue { } } +public extension Dispatcher { + /** + Asynchronously executes the provided closure on a Dispatcher. + + dispatcher.guarantee { + md5(input) + }.done { md5 in + //… + } + + - Parameter body: The closure that resolves this promise. + - Returns: A new `Guarantee` resolved by the result of the provided closure. + - Note: There is no Promise/Thenable version of this due to Swift compiler ambiguity issues. + */ + func dispatch(_: PMKNamespacer, _ body: @escaping () -> T) -> Guarantee { + let rg = Guarantee(.pending) + dispatch { + rg.box.seal(body()) + } + return rg + } +} + #if os(Linux) import func CoreFoundation._CFIsMainThread diff --git a/Sources/Promise.swift b/Sources/Promise.swift index 177b2d008..654920985 100644 --- a/Sources/Promise.swift +++ b/Sources/Promise.swift @@ -165,6 +165,33 @@ public extension DispatchQueue { } } +public extension Dispatcher { + /** + Asynchronously executes the provided closure on a Dispatcher. + + dispatcher.promise { + try md5(input) + }.done { md5 in + //… + } + + - Parameter body: The closure that resolves this promise. + - Returns: A new `Promise` resolved by the result of the provided closure. + - Note: There is no Promise/Thenable version of this due to Swift compiler ambiguity issues. + */ + func dispatch(_: PMKNamespacer, _ body: @escaping () throws -> T) -> Promise { + let promise = Promise(.pending) + dispatch { + do { + promise.box.seal(.fulfilled(try body())) + } catch { + promise.box.seal(.rejected(error)) + } + } + return promise + } +} + /// used by our extensions to provide unambiguous functions with the same name as the original function public enum PMKNamespacer { case promise diff --git a/Sources/Thenable.swift b/Sources/Thenable.swift index 3c18db2f4..871c84686 100644 --- a/Sources/Thenable.swift +++ b/Sources/Thenable.swift @@ -18,7 +18,7 @@ public extension Thenable { This allows chaining promises. The promise returned by the provided closure is resolved before the promise returned by this closure resolves. - - Parameter on: The queue to which the provided closure dispatches. + - Parameter on: The dispatcher that executes the provided closure. - Parameter body: The closure that executes when this promise fulfills. It must return a promise. - Returns: A new promise that resolves when the promise returned from the provided closure resolves. For example: @@ -30,12 +30,12 @@ public extension Thenable { //… } */ - func then(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) throws -> U) -> Promise { + func then(on: Dispatcher = conf.D.map, _ body: @escaping(T) throws -> U) -> Promise { let rp = Promise(.pending) pipe { switch $0 { case .fulfilled(let value): - on.async(flags: flags) { + on.dispatch { do { let rv = try body(value) guard rv !== rp else { throw PMKError.returnedSelf } @@ -56,7 +56,7 @@ public extension Thenable { This is like `then` but it requires the closure to return a non-promise. - - Parameter on: The queue to which the provided closure dispatches. + - Parameter on: The dispatcher that executes the provided closure. - Parameter transform: The closure that is executed when this Promise is fulfilled. It must return a non-promise. - Returns: A new promise that is resolved with the value returned from the provided closure. For example: @@ -68,12 +68,12 @@ public extension Thenable { //… } */ - func map(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T) throws -> U) -> Promise { + func map(on: Dispatcher = conf.D.map, _ transform: @escaping(T) throws -> U) -> Promise { let rp = Promise(.pending) pipe { switch $0 { case .fulfilled(let value): - on.async(flags: flags) { + on.dispatch { do { rp.box.seal(.fulfilled(try transform(value))) } catch { @@ -102,12 +102,12 @@ public extension Thenable { // either `PMKError.compactMap` or a `JSONError` } */ - func compactMap(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T) throws -> U?) -> Promise { + func compactMap(on: Dispatcher = conf.D.map, _ transform: @escaping(T) throws -> U?) -> Promise { let rp = Promise(.pending) pipe { switch $0 { case .fulfilled(let value): - on.async(flags: flags) { + on.dispatch { do { if let rv = try transform(value) { rp.box.seal(.fulfilled(rv)) @@ -131,7 +131,7 @@ public extension Thenable { Equivalent to `map { x -> Void in`, but since we force the `Void` return Swift is happier and gives you less hassle about your closure’s qualification. - - Parameter on: The queue to which the provided closure dispatches. + - Parameter on: The dispatcher that executes the provided closure. - Parameter body: The closure that is executed when this Promise is fulfilled. - Returns: A new promise fulfilled as `Void`. @@ -141,12 +141,12 @@ public extension Thenable { print(response.data) } */ - func done(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) throws -> Void) -> Promise { + func done(on: Dispatcher = conf.D.return, _ body: @escaping(T) throws -> Void) -> Promise { let rp = Promise(.pending) pipe { switch $0 { case .fulfilled(let value): - on.async(flags: flags) { + on.dispatch { do { try body(value) rp.box.seal(.fulfilled(())) @@ -167,7 +167,7 @@ public extension Thenable { This is like `done` but it returns the same value that the handler is fed. `get` immutably accesses the fulfilled value; the returned Promise maintains that value. - - Parameter on: The queue to which the provided closure dispatches. + - Parameter on: The dispatcher that executes the provided closure. - Parameter body: The closure that is executed when this Promise is fulfilled. - Returns: A new promise that is resolved with the value that the handler is fed. For example: @@ -181,8 +181,8 @@ public extension Thenable { print(foo, " is Void") } */ - func get(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping (T) throws -> Void) -> Promise { - return map(on: on, flags: flags) { + func get(on: Dispatcher = conf.D.return, _ body: @escaping (T) throws -> Void) -> Promise { + return map(on: on) { try body($0) return $0 } @@ -193,16 +193,16 @@ public extension Thenable { This is like `get` but provides the Result of the Promise so you can inspect the value of the chain at this point without causing any side effects. - - Parameter on: The queue to which the provided closure dispatches. + - Parameter on: The dispatcher that executes the provided closure. - Parameter body: The closure that is executed with Result of Promise. - Returns: A new promise that is resolved with the result that the handler is fed. For example: promise.tap{ print($0) }.then{ /*…*/ } */ - func tap(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Result) -> Void) -> Promise { + func tap(on: Dispatcher = conf.D.map, _ body: @escaping(Result) -> Void) -> Promise { return Promise { seal in pipe { result in - on.async(flags: flags) { + on.dispatch { body(result) seal.resolve(result) } @@ -286,8 +286,8 @@ public extension Thenable where T: Sequence { // $0 => [2,4,6] } */ - func mapValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U]> { - return map(on: on, flags: flags){ try $0.map(transform) } + func mapValues(on: Dispatcher = conf.D.map, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U]> { + return map(on: on) { try $0.map(transform) } } /** @@ -301,8 +301,8 @@ public extension Thenable where T: Sequence { // $0 => [1,1,2,2,3,3] } */ - func flatMapValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U.Iterator.Element]> { - return map(on: on, flags: flags){ (foo: T) in + func flatMapValues(on: Dispatcher = conf.D.map, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U.Iterator.Element]> { + return map(on: on){ (foo: T) in try foo.flatMap{ try transform($0) } } } @@ -318,8 +318,9 @@ public extension Thenable where T: Sequence { // $0 => [1,2,3] } */ - func compactMapValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) throws -> U?) -> Promise<[U]> { - return map(on: on, flags: flags) { foo -> [U] in + + func compactMapValues(on: Dispatcher = conf.D.map, _ transform: @escaping(T.Iterator.Element) throws -> U?) -> Promise<[U]> { + return map(on: on) { foo -> [U] in return try foo.compactMap(transform) } } @@ -335,8 +336,8 @@ public extension Thenable where T: Sequence { // $0 => [2,4,6] } */ - func thenMap(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U.T]> { - return then(on: on, flags: flags) { + func thenMap(on: Dispatcher = conf.D.map, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U.T]> { + return then(on: on) { when(fulfilled: try $0.map(transform)) } } @@ -352,8 +353,8 @@ public extension Thenable where T: Sequence { // $0 => [1,1,2,2,3,3] } */ - func thenFlatMap(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U.T.Iterator.Element]> where U.T: Sequence { - return then(on: on, flags: flags) { + func thenFlatMap(on: Dispatcher = conf.D.map, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U.T.Iterator.Element]> where U.T: Sequence { + return then(on: on) { when(fulfilled: try $0.map(transform)) }.map(on: nil) { $0.flatMap{ $0 } @@ -371,8 +372,8 @@ public extension Thenable where T: Sequence { // $0 => [2,3] } */ - func filterValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ isIncluded: @escaping (T.Iterator.Element) -> Bool) -> Promise<[T.Iterator.Element]> { - return map(on: on, flags: flags) { + func filterValues(on: Dispatcher = conf.D.map, _ isIncluded: @escaping (T.Iterator.Element) -> Bool) -> Promise<[T.Iterator.Element]> { + return map(on: on) { $0.filter(isIncluded) } } @@ -390,8 +391,8 @@ public extension Thenable where T: Collection { } } - func firstValue(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, where test: @escaping (T.Iterator.Element) -> Bool) -> Promise { - return map(on: on, flags: flags) { + func firstValue(on: Dispatcher = conf.D.map, where test: @escaping (T.Iterator.Element) -> Bool) -> Promise { + return map(on: on) { for x in $0 where test(x) { return x } @@ -414,7 +415,7 @@ public extension Thenable where T: Collection { public extension Thenable where T: Sequence, T.Iterator.Element: Comparable { /// - Returns: a promise fulfilled with the sorted values of this `Sequence`. - func sortedValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil) -> Promise<[T.Iterator.Element]> { - return map(on: on, flags: flags){ $0.sorted() } + func sortedValues(on: Dispatcher = conf.D.map) -> Promise<[T.Iterator.Element]> { + return map(on: on){ $0.sorted() } } } diff --git a/Tests/Core/DispatcherTests.swift b/Tests/Core/DispatcherTests.swift new file mode 100644 index 000000000..60a399472 --- /dev/null +++ b/Tests/Core/DispatcherTests.swift @@ -0,0 +1,148 @@ +import Dispatch +import PromiseKit +import XCTest + +fileprivate let queueIDKey = DispatchSpecificKey() + +class RecordingDispatcher: Dispatcher { + + static var queueIndex = 1 + + var dispatchCount = 0 + let queue: DispatchQueue + + init() { + queue = DispatchQueue(label: "org.promisekit.testqueue \(RecordingDispatcher.queueIndex)") + RecordingDispatcher.queueIndex += 1 + } + + func dispatch(_ body: @escaping () -> Void) { + dispatchCount += 1 + queue.async(execute: body) + } + +} + +class DispatcherTests: XCTestCase { + + var dispatcher = RecordingDispatcher() + var dispatcherB = RecordingDispatcher() + + override func setUp() { + dispatcher = RecordingDispatcher() + dispatcherB = RecordingDispatcher() + } + + func testConfD() { + let ex = expectation(description: "conf.D") + let oldConf = PromiseKit.conf.D + PromiseKit.conf.D.map = dispatcher + PromiseKit.conf.D.return = dispatcherB + XCTAssertNil(PromiseKit.conf.Q.map, "conf.Q.map not nil") // Not representable as DispatchQueues + XCTAssertNil(PromiseKit.conf.Q.return, "conf.Q.return not nil") + Promise { seal in + seal.fulfill(42) + }.map { + $0 + 10 + }.done() { + XCTAssertEqual($0, 52, "summation result != 52") + XCTAssertEqual(self.dispatcher.dispatchCount, 1, "map dispatcher count != 1") + XCTAssertEqual(self.dispatcherB.dispatchCount, 1, "return dispatcher count != 1") + ex.fulfill() + }.cauterize() + waitForExpectations(timeout: 1) + let testQueue = DispatchQueue(label: "test queue") + PromiseKit.conf.D.map = testQueue // Assign DispatchQueue to Dispatcher variable + PromiseKit.conf.Q.return = testQueue // Assign DispatchQueue to DispatchQueue variable + XCTAssert(PromiseKit.conf.Q.map === testQueue, "did not get main DispatchQueue back from map") + XCTAssert((PromiseKit.conf.D.return as? DispatchQueue)! === testQueue, "did not get main DispatchQueue back from return") + PromiseKit.conf.D = oldConf + } + + func testDispatcherWithThrow() { + let ex = expectation(description: "Dispatcher with throw") + Promise { seal in + seal.fulfill(42) + }.map(on: dispatcher) { _ in + throw PMKError.badInput + }.catch(on: dispatcher) { _ in + ex.fulfill() + } + waitForExpectations(timeout: 1) + XCTAssertEqual(self.dispatcher.dispatchCount, 2) + } + + func testDispatchQueueSelection() { + + let ex = expectation(description: "DispatchQueue compatibility") + + let oldConf = PromiseKit.conf.D + PromiseKit.conf.D = (map: dispatcher, return: dispatcher) + + let background = DispatchQueue.global(qos: .background) + background.setSpecific(key: queueIDKey, value: 100) + DispatchQueue.main.setSpecific(key: queueIDKey, value: 102) + dispatcher.queue.setSpecific(key: queueIDKey, value: 103) + + Promise.value(42).map(on: .global(qos: .background), flags: .barrier) { (x: Int) -> Int in + let queueID = DispatchQueue.getSpecific(key: queueIDKey) + XCTAssertNotNil(queueID) + XCTAssertEqual(queueID!, 100) + return x + 10 + }.then(on: .main, flags: []) { (x: Int) -> Promise in + XCTAssertEqual(x, 52) + let queueID = DispatchQueue.getSpecific(key: queueIDKey) + XCTAssertNotNil(queueID) + XCTAssertEqual(queueID!, 102) + return Promise.value(50) + }.map(on: nil) { (x: Int) -> Int in + let queueID = DispatchQueue.getSpecific(key: queueIDKey) + XCTAssertNotNil(queueID) + XCTAssertEqual(queueID!, 102) + return x + 10 + }.map { (x: Int) -> Int in + XCTAssertEqual(x, 60) + let queueID = DispatchQueue.getSpecific(key: queueIDKey) + XCTAssertNotNil(queueID) + XCTAssertEqual(queueID!, 103) + return x + 10 + }.done(on: background) { + XCTAssertEqual($0, 70) + let queueID = DispatchQueue.getSpecific(key: queueIDKey) + XCTAssertNotNil(queueID) + XCTAssertEqual(queueID!, 100) + ex.fulfill() + }.cauterize() + + waitForExpectations(timeout: 1) + PromiseKit.conf.D = oldConf + + } + + @available(macOS 10.10, iOS 2.0, tvOS 10.0, watchOS 2.0, *) + func testDispatcherExtensionReturnsGuarantee() { + let ex = expectation(description: "Dispatcher.promise") + dispatcher.dispatch(.promise) { () -> Int in + XCTAssertFalse(Thread.isMainThread) + return 1 + }.done { one in + XCTAssertEqual(one, 1) + ex.fulfill() + } + waitForExpectations(timeout: 1) + } + + @available(macOS 10.10, iOS 2.0, tvOS 10.0, watchOS 2.0, *) + func testDispatcherExtensionCanThrowInBody() { + let ex = expectation(description: "Dispatcher.promise") + dispatcher.dispatch(.promise) { () -> Int in + throw PMKError.badInput + }.done { _ in + XCTFail() + }.catch { _ in + ex.fulfill() + } + waitForExpectations(timeout: 1) + } + +} \ No newline at end of file From 7d61133ebb6968ac9549777659f5859e7a262fa6 Mon Sep 17 00:00:00 2001 From: Max Howell Date: Sun, 10 Feb 2019 22:53:00 +0000 Subject: [PATCH 03/81] =?UTF-8?q?Use=20Swift=205=E2=80=99s=20Result?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Documents/CommonPatterns.md | 2 +- README.md | 2 ++ Sources/Catchable.swift | 34 +++++++++--------- Sources/CustomStringConvertible.swift | 12 +++---- Sources/Dispatcher.swift | 2 +- Sources/Guarantee.swift | 8 ++--- Sources/Promise.swift | 29 +++++++-------- Sources/Resolver.swift | 26 +++----------- Sources/Thenable.swift | 52 +++++++++++++-------------- Sources/hang.swift | 4 +-- Sources/when.swift | 20 +++++------ Tests/A+/JavaScript/JSPromise.swift | 6 ++-- Tests/A+/Swift/0.0.0.swift | 4 +-- Tests/Core/PromiseTests.swift | 4 +-- Tests/Core/ResolverTests.swift | 4 +-- 15 files changed, 95 insertions(+), 114 deletions(-) diff --git a/Documents/CommonPatterns.md b/Documents/CommonPatterns.md index 0768cf81b..ff8a60fd2 100644 --- a/Documents/CommonPatterns.md +++ b/Documents/CommonPatterns.md @@ -481,7 +481,7 @@ Use `when(resolved:)`: ```swift when(resolved: a, b).done { (results: [Result]) in - // `Result` is an enum of `.fulfilled` or `.rejected` + //… } // ^^ cannot call `catch` as `when(resolved:)` returns a `Guarantee` diff --git a/README.md b/README.md index ea315deb1..b86c955bd 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,8 @@ pod used in many of the most popular apps in the world. PromiseKit 7 is pre-release, if you’re using it: beware! +PromiseKit 7 uses Swift 5’s `Result`, PromiseKit <7 use our own `Result` type. + # PromiseKit 6 [Release notes and migration guide][PMK6]. diff --git a/Sources/Catchable.swift b/Sources/Catchable.swift index a60ea4f60..205682667 100644 --- a/Sources/Catchable.swift +++ b/Sources/Catchable.swift @@ -25,7 +25,7 @@ public extension CatchMixin { let finalizer = PMKFinalizer() pipe { switch $0 { - case .rejected(let error): + case .failure(let error): guard policy == .allErrors || !error.isCancelled else { fallthrough } @@ -33,7 +33,7 @@ public extension CatchMixin { body(error) finalizer.pending.resolve(()) } - case .fulfilled: + case .success: finalizer.pending.resolve(()) } } @@ -76,9 +76,9 @@ public extension CatchMixin { let rp = Promise(.pending) pipe { switch $0 { - case .fulfilled(let value): - rp.box.seal(.fulfilled(value)) - case .rejected(let error): + case .success(let value): + rp.box.seal(.success(value)) + case .failure(let error): if policy == .allErrors || !error.isCancelled { on.dispatch { do { @@ -86,11 +86,11 @@ public extension CatchMixin { guard rv !== rp else { throw PMKError.returnedSelf } rv.pipe(to: rp.box.seal) } catch { - rp.box.seal(.rejected(error)) + rp.box.seal(.failure(error)) } } } else { - rp.box.seal(.rejected(error)) + rp.box.seal(.failure(error)) } } } @@ -110,9 +110,9 @@ public extension CatchMixin { let rg = Guarantee(.pending) pipe { switch $0 { - case .fulfilled(let value): + case .success(let value): rg.box.seal(value) - case .rejected(let error): + case .failure(let error): on.dispatch { body(error).pipe(to: rg.box.seal) } @@ -209,9 +209,9 @@ public extension CatchMixin where T == Void { let rg = Guarantee(.pending) pipe { switch $0 { - case .fulfilled: + case .success: rg.box.seal(()) - case .rejected(let error): + case .failure(let error): on.dispatch { body(error) rg.box.seal(()) @@ -234,19 +234,19 @@ public extension CatchMixin where T == Void { let rg = Promise(.pending) pipe { switch $0 { - case .fulfilled: - rg.box.seal(.fulfilled(())) - case .rejected(let error): + case .success: + rg.box.seal(.success(())) + case .failure(let error): if policy == .allErrors || !error.isCancelled { on.dispatch { do { - rg.box.seal(.fulfilled(try body(error))) + rg.box.seal(.success(try body(error))) } catch { - rg.box.seal(.rejected(error)) + rg.box.seal(.failure(error)) } } } else { - rg.box.seal(.rejected(error)) + rg.box.seal(.failure(error)) } } } diff --git a/Sources/CustomStringConvertible.swift b/Sources/CustomStringConvertible.swift index 639883e97..56a1822e7 100644 --- a/Sources/CustomStringConvertible.swift +++ b/Sources/CustomStringConvertible.swift @@ -5,9 +5,9 @@ extension Promise: CustomStringConvertible { switch result { case nil: return "Promise(…\(T.self))" - case .rejected(let error)?: + case .failure(let error)?: return "Promise(\(error))" - case .fulfilled(let value)?: + case .success(let value)?: return "Promise(\(value))" } } @@ -19,10 +19,10 @@ extension Promise: CustomDebugStringConvertible { switch box.inspect() { case .pending(let handlers): return "Promise<\(T.self)>.pending(handlers: \(handlers.bodies.count))" - case .resolved(.rejected(let error)): - return "Promise<\(T.self)>.rejected(\(type(of: error)).\(error))" - case .resolved(.fulfilled(let value)): - return "Promise<\(T.self)>.fulfilled(\(value))" + case .resolved(.failure(let error)): + return "Promise<\(T.self)>.failure(\(type(of: error)).\(error))" + case .resolved(.success(let value)): + return "Promise<\(T.self)>.success(\(value))" } } } diff --git a/Sources/Dispatcher.swift b/Sources/Dispatcher.swift index 3da133f84..213c552b7 100644 --- a/Sources/Dispatcher.swift +++ b/Sources/Dispatcher.swift @@ -236,7 +236,7 @@ public extension Thenable { promise.tap{ print($0) }.then{ /*…*/ } */ - func tap(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Result) -> Void) -> Promise { + func tap(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Result) -> Void) -> Promise { let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) return tap(on: dispatcher, body) } diff --git a/Sources/Guarantee.swift b/Sources/Guarantee.swift index f97858210..cc219dcdb 100644 --- a/Sources/Guarantee.swift +++ b/Sources/Guarantee.swift @@ -24,8 +24,8 @@ public final class Guarantee: Thenable { } /// - See: `Thenable.pipe` - public func pipe(to: @escaping(Result) -> Void) { - pipe{ to(.fulfilled($0)) } + public func pipe(to: @escaping(Result) -> Void) { + pipe{ to(.success($0)) } } func pipe(to: @escaping(T) -> Void) { @@ -45,12 +45,12 @@ public final class Guarantee: Thenable { } /// - See: `Thenable.result` - public var result: Result? { + public var result: Result? { switch box.inspect() { case .pending: return nil case .resolved(let value): - return .fulfilled(value) + return .success(value) } } diff --git a/Sources/Promise.swift b/Sources/Promise.swift index 654920985..4ffcb4d19 100644 --- a/Sources/Promise.swift +++ b/Sources/Promise.swift @@ -6,9 +6,9 @@ import Dispatch - See: `Thenable` */ public final class Promise: Thenable, CatchMixin { - let box: Box> + let box: Box> - fileprivate init(box: SealedBox>) { + fileprivate init(box: SealedBox>) { self.box = box } @@ -39,12 +39,12 @@ public final class Promise: Thenable, CatchMixin { } */ public class func value(_ value: T) -> Promise { - return Promise(box: SealedBox(value: .fulfilled(value))) + return Promise(box: SealedBox(value: .success(value))) } /// Initialize a new rejected promise. public init(error: Error) { - box = SealedBox(value: .rejected(error)) + box = SealedBox(value: .failure(error)) } /// Initialize a new promise bound to the provided `Thenable`. @@ -70,7 +70,7 @@ public final class Promise: Thenable, CatchMixin { } /// - See: `Thenable.pipe` - public func pipe(to: @escaping(Result) -> Void) { + public func pipe(to: @escaping(Result) -> Void) { switch box.inspect() { case .pending: box.inspect { @@ -87,7 +87,7 @@ public final class Promise: Thenable, CatchMixin { } /// - See: `Thenable.result` - public var result: Result? { + public var result: Result? { switch box.inspect() { case .pending: return nil @@ -121,19 +121,14 @@ public extension Promise { group.wait() } - switch result! { - case .rejected(let error): - throw error - case .fulfilled(let value): - return value - } + return try result!.get() } } extension Promise where T == Void { /// Initializes a new promise fulfilled with `Void` public convenience init() { - self.init(box: SealedBox(value: .fulfilled(Void()))) + self.init(box: SealedBox(value: .success(Void()))) } } @@ -156,9 +151,9 @@ public extension DispatchQueue { let promise = Promise(.pending) async(group: group, qos: qos, flags: flags) { do { - promise.box.seal(.fulfilled(try body())) + promise.box.seal(.success(try body())) } catch { - promise.box.seal(.rejected(error)) + promise.box.seal(.failure(error)) } } return promise @@ -183,9 +178,9 @@ public extension Dispatcher { let promise = Promise(.pending) dispatch { do { - promise.box.seal(.fulfilled(try body())) + promise.box.seal(.success(try body())) } catch { - promise.box.seal(.rejected(error)) + promise.box.seal(.failure(error)) } } return promise diff --git a/Sources/Resolver.swift b/Sources/Resolver.swift index d8439da2c..d83d9cdaa 100644 --- a/Sources/Resolver.swift +++ b/Sources/Resolver.swift @@ -1,8 +1,8 @@ /// An object for resolving promises public final class Resolver { - let box: Box> + let box: Box> - init(_ box: Box>) { + init(_ box: Box>) { self.box = box } @@ -16,16 +16,16 @@ public final class Resolver { public extension Resolver { /// Fulfills the promise with the provided value func fulfill(_ value: T) { - box.seal(.fulfilled(value)) + box.seal(.success(value)) } /// Rejects the promise with the provided error func reject(_ error: Error) { - box.seal(.rejected(error)) + box.seal(.failure(error)) } /// Resolves the promise with the provided result - func resolve(_ result: Result) { + func resolve(_ result: Result) { box.seal(result) } @@ -71,19 +71,3 @@ extension Resolver where T == Void { self.fulfill(()) } } - -public enum Result { - case fulfilled(T) - case rejected(Error) -} - -public extension PromiseKit.Result { - var isFulfilled: Bool { - switch self { - case .fulfilled: - return true - case .rejected: - return false - } - } -} diff --git a/Sources/Thenable.swift b/Sources/Thenable.swift index 871c84686..055d956f9 100644 --- a/Sources/Thenable.swift +++ b/Sources/Thenable.swift @@ -6,10 +6,10 @@ public protocol Thenable: class { associatedtype T /// `pipe` is immediately executed when this `Thenable` is resolved - func pipe(to: @escaping(Result) -> Void) + func pipe(to: @escaping(Result) -> Void) /// The resolved result or nil if pending. - var result: Result? { get } + var result: Result? { get } } public extension Thenable { @@ -34,18 +34,18 @@ public extension Thenable { let rp = Promise(.pending) pipe { switch $0 { - case .fulfilled(let value): + case .success(let value): on.dispatch { do { let rv = try body(value) guard rv !== rp else { throw PMKError.returnedSelf } rv.pipe(to: rp.box.seal) } catch { - rp.box.seal(.rejected(error)) + rp.box.seal(.failure(error)) } } - case .rejected(let error): - rp.box.seal(.rejected(error)) + case .failure(let error): + rp.box.seal(.failure(error)) } } return rp @@ -72,16 +72,16 @@ public extension Thenable { let rp = Promise(.pending) pipe { switch $0 { - case .fulfilled(let value): + case .success(let value): on.dispatch { do { - rp.box.seal(.fulfilled(try transform(value))) + rp.box.seal(.success(try transform(value))) } catch { - rp.box.seal(.rejected(error)) + rp.box.seal(.failure(error)) } } - case .rejected(let error): - rp.box.seal(.rejected(error)) + case .failure(let error): + rp.box.seal(.failure(error)) } } return rp @@ -106,20 +106,20 @@ public extension Thenable { let rp = Promise(.pending) pipe { switch $0 { - case .fulfilled(let value): + case .success(let value): on.dispatch { do { if let rv = try transform(value) { - rp.box.seal(.fulfilled(rv)) + rp.box.seal(.success(rv)) } else { throw PMKError.compactMap(value, U.self) } } catch { - rp.box.seal(.rejected(error)) + rp.box.seal(.failure(error)) } } - case .rejected(let error): - rp.box.seal(.rejected(error)) + case .failure(let error): + rp.box.seal(.failure(error)) } } return rp @@ -145,17 +145,17 @@ public extension Thenable { let rp = Promise(.pending) pipe { switch $0 { - case .fulfilled(let value): + case .success(let value): on.dispatch { do { try body(value) - rp.box.seal(.fulfilled(())) + rp.box.seal(.success(())) } catch { - rp.box.seal(.rejected(error)) + rp.box.seal(.failure(error)) } } - case .rejected(let error): - rp.box.seal(.rejected(error)) + case .failure(let error): + rp.box.seal(.failure(error)) } } return rp @@ -199,7 +199,7 @@ public extension Thenable { promise.tap{ print($0) }.then{ /*…*/ } */ - func tap(on: Dispatcher = conf.D.map, _ body: @escaping(Result) -> Void) -> Promise { + func tap(on: Dispatcher = conf.D.map, _ body: @escaping(Result) -> Void) -> Promise { return Promise { seal in pipe { result in on.dispatch { @@ -224,9 +224,9 @@ public extension Thenable { switch result { case .none: return nil - case .some(.fulfilled): + case .some(.success): return nil - case .some(.rejected(let error)): + case .some(.failure(let error)): return error } } @@ -266,9 +266,9 @@ public extension Thenable { switch result { case .none: return nil - case .some(.fulfilled(let value)): + case .some(.success(let value)): return value - case .some(.rejected): + case .some(.failure): return nil } } diff --git a/Sources/hang.swift b/Sources/hang.swift index 53d54decb..f5b45f5de 100644 --- a/Sources/hang.swift +++ b/Sources/hang.swift @@ -47,9 +47,9 @@ public func hang(_ promise: Promise) throws -> T { } switch promise.result! { - case .rejected(let error): + case .failure(let error): throw error - case .fulfilled(let value): + case .success(let value): return value } } diff --git a/Sources/when.swift b/Sources/when.swift index bc54f6712..ed654e0c5 100644 --- a/Sources/when.swift +++ b/Sources/when.swift @@ -23,17 +23,17 @@ private func _when(_ thenables: [U]) -> Promise { promise.pipe { result in barrier.sync(flags: .barrier) { switch result { - case .rejected(let error): + case .failure(let error): if rp.isPending { progress.completedUnitCount = progress.totalUnitCount - rp.box.seal(.rejected(error)) + rp.box.seal(.failure(error)) } - case .fulfilled: + case .success: guard rp.isPending else { return } progress.completedUnitCount += 1 countdown -= 1 if countdown == 0 { - rp.box.seal(.fulfilled(())) + rp.box.seal(.success(())) } } } @@ -185,10 +185,10 @@ public func when(fulfilled promiseIterator: It, concurrent } switch resolution { - case .fulfilled: + case .success: dequeue() testDone() - case .rejected(let error): + case .failure(let error): root.resolver.reject(error) } } @@ -207,7 +207,7 @@ public func when(fulfilled promiseIterator: It, concurrent `when(fulfilled:)` rejects as soon as one of the provided promises rejects. `when(resolved:)` waits on all provided promises whatever their result, and then provides an array of `Result` so you can individually inspect the results. As a consequence this function returns a `Guarantee`, ie. errors are lifted from the individual promises into the results array of the returned `Guarantee`. when(resolved: promise1, promise2, promise3).then { results in - for result in results where case .fulfilled(let value) { + for result in results where case .success(let value) { //… } }.catch { error in @@ -218,12 +218,12 @@ public func when(fulfilled promiseIterator: It, concurrent - Note: we do not provide tuple variants for `when(resolved:)` but will accept a pull-request - Remark: Doesn't take Thenable due to protocol `associatedtype` paradox */ -public func when(resolved promises: Promise...) -> Guarantee<[Result]> { +public func when(resolved promises: Promise...) -> Guarantee<[Result]> { return when(resolved: promises) } /// - See: `when(resolved: Promise...)` -public func when(resolved promises: [Promise]) -> Guarantee<[Result]> { +public func when(resolved promises: [Promise]) -> Guarantee<[Result]> { guard !promises.isEmpty else { return .value([]) } @@ -231,7 +231,7 @@ public func when(resolved promises: [Promise]) -> Guarantee<[Result]> { var countdown = promises.count let barrier = DispatchQueue(label: "org.promisekit.barrier.join", attributes: .concurrent) - let rg = Guarantee<[Result]>(.pending) + let rg = Guarantee<[Result]>(.pending) for promise in promises { promise.pipe { result in barrier.sync(flags: .barrier) { diff --git a/Tests/A+/JavaScript/JSPromise.swift b/Tests/A+/JavaScript/JSPromise.swift index 036dc7171..f491f2a03 100644 --- a/Tests/A+/JavaScript/JSPromise.swift +++ b/Tests/A+/JavaScript/JSPromise.swift @@ -81,12 +81,12 @@ class JSPromise: NSObject, JSPromiseProtocol { } } - let newPromise = Promise> { resolver in + let newPromise = Promise> { resolver in _ = promise.tap(resolver.fulfill) }.then(on: nil) { result -> Promise in switch result { - case .fulfilled: return afterFulfill - case .rejected: return afterReject + case .success: return afterFulfill + case .failure: return afterReject } } returnedPromiseRef = newPromise diff --git a/Tests/A+/Swift/0.0.0.swift b/Tests/A+/Swift/0.0.0.swift index 1790d1bb8..578c6dd54 100644 --- a/Tests/A+/Swift/0.0.0.swift +++ b/Tests/A+/Swift/0.0.0.swift @@ -143,9 +143,9 @@ extension Promise { func test(onFulfilled: @escaping () -> Void, onRejected: @escaping () -> Void) { tap { result in switch result { - case .fulfilled: + case .success: onFulfilled() - case .rejected: + case .failure: onRejected() } }.silenceWarning() diff --git a/Tests/Core/PromiseTests.swift b/Tests/Core/PromiseTests.swift index 32c3f0a20..a278bcb0c 100644 --- a/Tests/Core/PromiseTests.swift +++ b/Tests/Core/PromiseTests.swift @@ -59,8 +59,8 @@ class PromiseTests: XCTestCase { func testCustomStringConvertible() { XCTAssertEqual(Promise.pending().promise.debugDescription, "Promise.pending(handlers: 0)") - XCTAssertEqual(Promise().debugDescription, "Promise<()>.fulfilled(())") - XCTAssertEqual(Promise(error: Error.dummy).debugDescription, "Promise.rejected(Error.dummy)") + XCTAssertEqual(Promise().debugDescription, "Promise<()>.success(())") + XCTAssertEqual(Promise(error: Error.dummy).debugDescription, "Promise.failure(Error.dummy)") XCTAssertEqual("\(Promise.pending().promise)", "Promise(…Int)") XCTAssertEqual("\(Promise.value(3))", "Promise(3)") diff --git a/Tests/Core/ResolverTests.swift b/Tests/Core/ResolverTests.swift index 09c78a354..d577f8d22 100644 --- a/Tests/Core/ResolverTests.swift +++ b/Tests/Core/ResolverTests.swift @@ -131,8 +131,8 @@ class WrapTests: XCTestCase { } func testIsFulfilled() { - XCTAssertTrue(Promise.value(()).result?.isFulfilled ?? false) - XCTAssertFalse(Promise(error: Error.test).result?.isFulfilled ?? true) + XCTAssertNotNil(try? Promise.value(()).result?.get()) + XCTAssertNil(try? Promise(error: Error.test).result?.get()) } func testPendingPromiseDeallocated() { From a90ce6b5e81c330ccda92ce30ff041c57126939b Mon Sep 17 00:00:00 2001 From: Garth Snyder Date: Thu, 28 Feb 2019 12:01:36 -0800 Subject: [PATCH 04/81] Final cleanup for Dispatcher conversion (#1016) * Additional comments for Dispatcher.swift * Remove .promise namespacer from Dispatcher.dispatch -> Guarantee/Promise * Convert to LogEvent system, groom logging and tests * Expand DispatchQueueDispatcher to hold both a DispatchGroup and a QoS * Finalize dispatcher.dispatch and related tests * Clean up DispatchQueue wrapping, avoid assuming any particular defaults * CurrentThreadDispatcher.dispatch needn't mark closure arg as @escaping * Test that DispatchQueue.pmkDefault is identifiable with === * Documentation updates * Build any v7* branch * Rebuild Linux test manifest, check return types of Dispatcher.dispatch {} --- .travis.yml | 2 +- Documents/Appendix.md | 42 +++++++++++ Documents/CommonPatterns.md | 6 +- Documents/FAQ.md | 58 ++++++++++----- Documents/GettingStarted.md | 20 ++--- Documents/Troubleshooting.md | 8 +- README.md | 22 +++--- Sources/Catchable.swift | 10 +-- Sources/Configuration.swift | 21 ++---- Sources/Dispatcher.swift | 105 ++++++++++++++++++-------- Sources/Guarantee.swift | 25 ++++--- Sources/LogEvent.swift | 25 +++++++ Sources/Promise.swift | 26 ++++--- Tests/Core/DispatcherTests.swift | 19 +++-- Tests/Core/LoggingTests.swift | 123 +++++++++++-------------------- Tests/Core/PromiseTests.swift | 96 +++++++++++++++++++++--- Tests/Core/XCTestManifests.swift | 22 +++++- 17 files changed, 414 insertions(+), 216 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7926b0438..32dc73c48 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ osx_image: xcode10.2 branches: only: - - v7 + - /^v7/ - master - v6 - v4 diff --git a/Documents/Appendix.md b/Documents/Appendix.md index d3797f4d9..cf67c2504 100644 --- a/Documents/Appendix.md +++ b/Documents/Appendix.md @@ -209,3 +209,45 @@ its work on a background thread. Promises abstract asynchronicity, so exploit and support that model. Design your APIs so that consumers don’t have to care what queue your functions run on. + +## `Dispatcher` Objects + +Some issues are best addressed at the level of dispatching. For example, you may need +to perform a series of HTTP transactions on a server that allows only a certain number of +API calls per minute. Or, you might want to allow only a certain number of memory-hungry +background tasks to run at once. These aren't really promise-level concerns; they just have +to do with the details of how closures are invoked once they become eligible to run. + +Unfortunately, `DispatchQueue`s don't directly support these types of restrictions, and because +of the way `DispatchQueue` is implemented, you can't create your own subclasses. PromiseKit 7 +adds an abstract `Dispatcher` protocol that generalizes the idea of a dispatch queue and allows for +alternate implementations: + +```swift +public protocol Dispatcher { + func dispatch(_ body: @escaping () -> Void) +} +``` + +Anywhere in PromiseKit that you can use a `DispatchQueue`, you're free to substitute a `Dispatcher`. + +PromiseKit doesn't care how you implement `dispatch()`. You can run the provided closure synchronously or +asynchronously, now or at some point in the future, on any thread you wish. Of course, your own code +must have a thread safety strategy and not create deadlocks. + +If you're setting a default dispatcher, assign your dispatcher to `PromiseKit.conf.D` rather than `PromiseKit.conf.Q`; the latter +accepts only `DispatchQueue`s. + +**FIXME: Check locations and availability of Dispatcher implementations before release** + +A few handy `Dispatcher` types are available in the `Dispatchers` extension library: + +* `RateLimitedDispatcher` implements general dispatch rate limits with an approximate "token bucket" strategy. +* `StrictRateLimitedDispatcher` is an exact and optimal "sliding window" rate limiter, but requires O(n) space (n = # of events/time) +* `ConcurrencyLimitedDispatcher` allows only *n* asynchronous closures to run at once. +* `CoreDataDispatcher` lets you dispatch onto threads associated with `NSManagedObjectContext`s. + +A couple of `Dispatcher`s are also included in the PromiseKit core: + +* `CurrentThreadDispatcher` runs closures immediately, on the current thread. +* `DispatchQueueDispatcher` forwards to a `DispatchQueue`, applying a static `DispatchGroup`, quality of service, and flag set. diff --git a/Documents/CommonPatterns.md b/Documents/CommonPatterns.md index ff8a60fd2..398affc60 100644 --- a/Documents/CommonPatterns.md +++ b/Documents/CommonPatterns.md @@ -82,8 +82,10 @@ class MyRestAPI { } ``` -All PromiseKit handlers take an `on` parameter that lets you designate the dispatch queue -on which to run the handler. The default is always the main queue. +All PromiseKit handlers take an `on` parameter that lets you designate a `Dispatcher` that +will run the handler. Usually, the dispatcher is just a plain-vanilla `DispatchQueue`, but you +can write your own if you like. The default is always `DispatchQueue.main`, which is a +serial (nonconcurrent) queue. PromiseKit is *entirely* thread safe. diff --git a/Documents/FAQ.md b/Documents/FAQ.md index 0c91162a8..634b68278 100644 --- a/Documents/FAQ.md +++ b/Documents/FAQ.md @@ -8,7 +8,7 @@ * Do you want a library that has been maintained continuously and passionately for 6 years? Then pick PromiseKit. * Do you want a library that the community has chosen to be their №1 Promises/Futures library? Then pick PromiseKit. * Do you want to be able to use Promises with Apple’s SDKs rather than having to do all the work of writing the Promise implementations yourself? Then pick PromiseKit. -* Do you want to be able to use Promises with Swift 3.x, Swift 4.x, ObjC, iOS, tvOS, watchOS, macOS, Android & Linux? Then pick PromiseKit. +* Do you want to be able to use Promises with Swift, ObjC, iOS, tvOS, watchOS, macOS, Android & Linux? Then pick PromiseKit. * PromiseKit verifies its correctness by testing against the entire [Promises/A+ test suite](https://github.com/promises-aplus/promises-tests). ## How do I create a fulfilled `Void` promise? @@ -229,8 +229,8 @@ So, RxSwift tries hard to supply every operator you might ever want to use right hundreds. PromiseKit supplies a few utilities to help with specific scenarios, but because it's trivial to write your own chain elements, there's no need for all this extra code in the library. -* PromiseKit dispatches the execution of every block. RxSwift dispatches only when told to do so. Moreover, the -current dispatching state is an attribute of the chain, not the specific block, as it is in PromiseKit. +* PromiseKit dispatches the execution of every block. RxSwift dispatches only when told to do so. Moreover, +in RxSwift, the current dispatching state is an attribute of the chain, not the specific block, as it is in PromiseKit. The RxSwift system is more powerful but more complex. PromiseKit is simple, predictable and safe. * In PromiseKit, both sides of a branched chain refer back to their shared common ancestors. In RxSwift, @@ -307,36 +307,56 @@ feature because it gives you guarantees about the flow of your chains. ## How do I change the default queues that handlers run on? -You can change the values of `PromiseKit.conf.Q`. There are two variables that -change the default queues that the two kinds of handler run on. A typical -pattern is to change all your `then`-type handlers to run on a background queue +You can change the values of `PromiseKit.conf.Q` or `PromiseKit.conf.D`. These +variables both access the same underlying state. However, `conf.Q` presents it in terms of +`DispatchQueue`s, while `conf.D` presents it in terms of the more general +`Dispatcher`-protocol objects that PromiseKit uses internally. (`DispatchQueue`s are +just one possible implementation of `Dispatcher`, although they are the ones that account +for nearly all actual use.) + +Each of these configuration variables is a two-tuple that identifies two separate dispatchers named `map` and `return`. + +```swift +public var Q: (map: DispatchQueue?, return: DispatchQueue?) +public var D: (map: Dispatcher, return: Dispatcher) +``` + +The `return` dispatcher is the default for chain-finalizing methods such as `done` +and `catch`. The `map` dispatcher is the default for everything else. A +typical pattern is to change all your `then`-type handlers to run on a background queue and to have all your “finalizers” run on the main queue: ``` PromiseKit.conf.Q.map = .global() -PromiseKit.conf.Q.return = .main //NOTE this is the default +PromiseKit.conf.Q.return = .main ``` -Be very careful about setting either of these queues to `nil`. It has the -effect of running *immediately*, and this is not what you usually want to do in -your application. This is, however, useful when you are running specs and want -your promises to resolve immediately. (This is basically the same idea as "stubbing" -an HTTP request.) +Note that `DispatchQueue.main` is the default for _both_ dispatchers. + +Be very careful about setting either part of `conf.Q` to `nil`. It has the +effect of running closures *immediately*, and this is not what you usually want to do in +your application. It is useful, however, when you are running specs and want +your promises to resolve immediately. (It's basically the same idea as "stubbing" +HTTP requests.) ```swift // in your test suite setup code -PromiseKit.conf.Q.map = nil -PromiseKit.conf.Q.return = nil +PromiseKit.conf.Q = (map: nil, return: nil) ``` ## How do I use PromiseKit on the server side? If your server framework requires that the main queue remain unused (e.g., Kitura), -then you must use PromiseKit 6 and you must tell PromiseKit not to dispatch to the -main queue by default. This is easy enough: +then you must tell PromiseKit not to dispatch there by default. This is easy enough: + +```swift +PromiseKit.conf.Q = (map: .global(), return: .global()) +``` +If you want to emulate the serializing behavior of `DispatchQueue.main`, just create and label +a new `DispatchQueue`. It'll be serial by default. ```swift -PromiseKit.conf.Q = (map: DispatchQueue.global(), return: DispatchQueue.global()) +PromiseKit.conf.Q.return = DispatchQueue(label: "virtual main queue") ``` > Note, we recommend using your own queue rather than `.global()`, we've seen better performance this way. @@ -372,12 +392,12 @@ Kitura.run() ## How do I control console output? -By default PromiseKit emits console messages when certain events occur. These events include: +By default, PromiseKit emits warning messages on the console when certain events occur. These events include: - A promise or guarantee has blocked the main thread - A promise has been deallocated without being fulfilled - An error which occurred while fulfilling a promise was swallowed using cauterize -You may turn off or redirect this output by setting a thread safe closure in [PMKConfiguration](https://github.com/mxcl/PromiseKit/blob/master/Sources/Configuration.swift) **before** processing any promises. For example, to turn off console output: +You may turn off or redirect this output by setting a thread-safe closure in [PMKConfiguration](https://github.com/mxcl/PromiseKit/blob/master/Sources/Configuration.swift) **before** processing any promises. For example, to turn off console output: ```swift conf.logHandler = { event in } diff --git a/Documents/GettingStarted.md b/Documents/GettingStarted.md index 974098c45..5f5a8f710 100644 --- a/Documents/GettingStarted.md +++ b/Documents/GettingStarted.md @@ -58,7 +58,7 @@ that represents the type of object it wraps. For example, in the example above, `login` is a function that returns a `Promise` that *will* represent an instance of `Creds`. -> *Note*: `done` is new to PromiseKit 5. We previously defined a variant of `then` that +> *Note*: `done` was introduced in PromiseKit 5. We previously defined a variant of `then` that did not require you to return a promise. Unfortunately, this convention often confused Swift and led to odd and hard-to-debug error messages. It also made using PromiseKit more painful. The introduction of `done` lets you type out promise chains that @@ -311,10 +311,10 @@ extra disambiguation for the Swift compiler. Sorry; we tried. typically just pass completion handler parameters to `resolve` and let Swift figure out which variant to apply to your particular case (as shown in the example above). -> *Note* `Guarantees` (below) have a slightly different initializer (since they -cannot error) so the parameter to the initializer closure is just a closure. Not -a `Resolver` object. Thus do `seal(value)` rather than `seal.fulfill(value)`. This -is because there is no variations in what guarantees can be sealed with, they can +> *Note*: `Guarantee`s (below) have a slightly different initializer since they +cannot error, so the parameter to the initializer closure is just a closure. Not +a `Resolver` object. Just do `seal(value)` rather than `seal.fulfill(value)`. It's +different because there is only one way to seal guarantees; they can *only* fulfill. # `Guarantee` @@ -346,7 +346,7 @@ if you find an issue. --- -If you are creating your own guarantees the syntax is simpler than that of promises; +If you are creating your own guarantees the syntax is simpler than that of promises: ```swift func fetch() -> Promise { @@ -507,7 +507,7 @@ However, this shorthand is both a blessing and a curse. You may find that the Sw often fails to infer return types properly. See our [Troubleshooting Guide](Troubleshooting.md) if you require further assistance. -> By adding `done` to PromiseKit 5, we have managed to avoid many of these common +> By adding `done` to PromiseKit 5, we were able to blunt many of these common pain points in using PromiseKit and Swift. @@ -527,9 +527,9 @@ Here are some recent articles that document PromiseKit 5+: * [Using Promises - Agostini.tech](https://agostini.tech/2018/10/08/using-promisekit) -Careful with general online references, many of them refer to PMK < 5 which has a subtly -different API (sorry about that, but Swift has changed a lot over the years and thus -we had to too). +Be careful when consulting general online references, as many of them refer to PMK < 5, which has a subtly +different API. (Sorry about that, but Swift has changed a lot over the years and thus +we had to as well.) [API Reference]: https://mxcl.dev/PromiseKit/reference/v7/Classes/Promise.html diff --git a/Documents/Troubleshooting.md b/Documents/Troubleshooting.md index 1e38b2676..417fe17c2 100644 --- a/Documents/Troubleshooting.md +++ b/Documents/Troubleshooting.md @@ -68,7 +68,7 @@ return firstly { } ``` -We have made great effort to reduce the need for explicit typing in PromiseKit 6, +We have made great effort to reduce the need for explicit typing in PromiseKit, but as with all Swift functions that return a generic type (e.g., `Array.map`), you may need to explicitly tell Swift what a closure returns if the closure's body is longer than one line. @@ -175,17 +175,17 @@ All PromiseKit functions are documented and provide examples. You have a `then`; you want a `done`. -## "Missing argument for parameter #1 in call" +## "Missing argument for parameter #1 in call" "Unable to infer closure type in the current context" This is part of Swift 4’s “tuplegate”. -You must specify your `Void` parameter: +You must fulfill a `Promise` with an explicit `Void` parameter: ```swift seal.fulfill(()) ``` -Yes: we hope they revert this change in Swift 5 too. +This wart remains in Swift 5, too. It's probably not going to change. ## "Ambiguous reference to 'firstly(execute:)'" diff --git a/README.md b/README.md index b86c955bd..a2563f76a 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,10 @@ PromiseKit 7 is pre-release, if you’re using it: beware! PromiseKit 7 uses Swift 5’s `Result`, PromiseKit <7 use our own `Result` type. +PromiseKit 7 generalizes `DispatchQueue`s to a `Dispatcher` protocol. However, +`DispatchQueue`s are `Dispatcher`-conformant, so existing code should not need +to change. Please report any issues related to this transition. + # PromiseKit 6 [Release notes and migration guide][PMK6]. @@ -91,14 +95,14 @@ help me continue my work, I appreciate it 🙏🏻 # Documentation * Handbook - * [Getting Started](Documentation/GettingStarted.md) - * [Promises: Common Patterns](Documentation/CommonPatterns.md) - * [Frequently Asked Questions](Documentation/FAQ.md) + * [Getting Started](Documents/GettingStarted.md) + * [Promises: Common Patterns](Documents/CommonPatterns.md) + * [Frequently Asked Questions](Documents/FAQ.md) * Manual - * [Installation Guide](Documentation/Installation.md) - * [Objective-C Guide](Documentation/ObjectiveC.md) - * [Troubleshooting](Documentation/Troubleshooting.md) (e.g., solutions to common compile errors) - * [Appendix](Documentation/Appendix.md) + * [Installation Guide](Documents/Installation.md) + * [Objective-C Guide](Documents/ObjectiveC.md) + * [Troubleshooting](Documents/Troubleshooting.md) (e.g., solutions to common compile errors) + * [Appendix](Documents/Appendix.md) * [API Reference](https://mxcl.dev/PromiseKit/reference/v7/Classes/Promise.html) # Extensions @@ -174,7 +178,7 @@ became true, but nowadays it isn’t really necessary. # Support -Please check our [Troubleshooting Guide](Documentation/Troubleshooting.md), and +Please check our [Troubleshooting Guide](Documents/Troubleshooting.md), and if after that you still have a question, ask at our [Gitter chat channel] or on [our bug tracker]. # Contributing @@ -196,7 +200,7 @@ Generate the Xcode project: [our bug tracker]: https://github.com/mxcl/PromiseKit/issues/new [Podfile]: https://guides.cocoapods.org/syntax/podfile.html [PMK6]: http://mxcl.dev/PromiseKit/news/2018/02/PromiseKit-6.0-Released/ -[Installation Guide]: Documentation/Installation.md +[Installation Guide]: Documents/Installation.md [badge-travis]: https://travis-ci.org/mxcl/PromiseKit.svg?branch=master [travis]: https://travis-ci.org/mxcl/PromiseKit [cocoapods]: https://cocoapods.org/pods/PromiseKit diff --git a/Sources/Catchable.swift b/Sources/Catchable.swift index 205682667..a0af9e61e 100644 --- a/Sources/Catchable.swift +++ b/Sources/Catchable.swift @@ -18,7 +18,7 @@ public extension CatchMixin { - Parameter policy: The default policy does not execute your handler for cancellation errors. - Parameter execute: The handler to execute if this promise is rejected. - Returns: A promise finalizer. - - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documents/CommonPatterns.md#cancellation) */ @discardableResult func `catch`(on: Dispatcher = conf.D.return, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) -> Void) -> PMKFinalizer { @@ -70,7 +70,7 @@ public extension CatchMixin { - Parameter on: The dispatcher that executes the provided closure. - Parameter body: The handler to execute if this promise is rejected. - - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documents/CommonPatterns.md#cancellation) */ func recover(on: Dispatcher = conf.D.map, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) throws -> U) -> Promise where U.T == T { let rp = Promise(.pending) @@ -103,7 +103,7 @@ public extension CatchMixin { - Note it is logically impossible for this to take a `catchPolicy`, thus `allErrors` are handled. - Parameter on: The dispatcher that executes the provided closure. - Parameter body: The handler to execute if this promise is rejected. - - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documents/CommonPatterns.md#cancellation) */ @discardableResult func recover(on: Dispatcher = conf.D.map, _ body: @escaping(Error) -> Guarantee) -> Guarantee { @@ -202,7 +202,7 @@ public extension CatchMixin where T == Void { - Parameter on: The dispatcher that executes the provided closure. - Parameter body: The handler to execute if this promise is rejected. - - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documents/CommonPatterns.md#cancellation) */ @discardableResult func recover(on: Dispatcher = conf.D.map, _ body: @escaping(Error) -> Void) -> Guarantee { @@ -228,7 +228,7 @@ public extension CatchMixin where T == Void { - Parameter on: The dispatcher that executes the provided closure. - Parameter body: The handler to execute if this promise is rejected. - - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documents/CommonPatterns.md#cancellation) */ func recover(on: Dispatcher = conf.D.map, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) throws -> Void) -> Promise { let rg = Promise(.pending) diff --git a/Sources/Configuration.swift b/Sources/Configuration.swift index dea487e94..801527414 100644 --- a/Sources/Configuration.swift +++ b/Sources/Configuration.swift @@ -8,13 +8,17 @@ import Dispatch We would like it to be, but sadly `Swift` does not expose `dispatch_once` et al. which is what we used to use in order to make the configuration immutable once first used. */ public struct PMKConfiguration { - /// Backward compatibility: default DispatchQueues that promise handlers dispatch to + /// Backward compatibility: the default Dispatcher to which handlers dispatch, represented as DispatchQueues. public var Q: (map: DispatchQueue?, return: DispatchQueue?) { - get { return (map: D.map as? DispatchQueue, return: D.return as? DispatchQueue) } + get { + let convertedMap = D.map is CurrentThreadDispatcher ? nil : D.map as? DispatchQueue + let convertedReturn = D.return is CurrentThreadDispatcher ? nil : D.return as? DispatchQueue + return (map: convertedMap, return: convertedReturn) + } set { D = (map: newValue.map ?? CurrentThreadDispatcher(), return: newValue.return ?? CurrentThreadDispatcher()) } } - /// The default Dispatchers that promise handlers dispatch to + /// The default Dispatchers to which promise handlers dispatch public var D: (map: Dispatcher, return: Dispatcher) = (map: DispatchQueue.main, return: DispatchQueue.main) /// The default catch-policy for all `catch` and `resolve` @@ -24,16 +28,7 @@ public struct PMKConfiguration { /// Not thread safe; change before processing any promises. /// - Note: The default handler calls `print()` public var logHandler: (LogEvent) -> () = { event in - switch event { - case .waitOnMainThread: - print("PromiseKit: warning: `wait()` called on main thread!") - case .pendingPromiseDeallocated: - print("PromiseKit: warning: pending promise deallocated") - case .pendingGuaranteeDeallocated: - print("PromiseKit: warning: pending guarantee deallocated") - case .cauterized (let error): - print("PromiseKit:cauterized-error: \(error)") - } + print(event.asString()) } } diff --git a/Sources/Dispatcher.swift b/Sources/Dispatcher.swift index 213c552b7..835cb6fd4 100644 --- a/Sources/Dispatcher.swift +++ b/Sources/Dispatcher.swift @@ -1,82 +1,125 @@ import Dispatch +/// A `PromiseKit` abstraction of a `DispatchQueue` that allows for a more +/// flexible variety of implementations. (For technical reasons, +/// `DispatchQueue` itself cannot be subclassed.) +/// +/// `Dispatcher`s define a `dispatch` method that executes a supplied closure. +/// Execution may be synchronous or asynchronous, serial +/// or concurrent, and can occur on any thread. +/// +/// All `DispatchQueue`s are also valid `Dispatcher`s. + public protocol Dispatcher { func dispatch(_ body: @escaping () -> Void) } -public class DispatchQueueDispatcher: Dispatcher { +/// A `Dispatcher` that bundles a `DispatchQueue` with +/// a `DispatchGroup`, a set of `DispatchWorkItemFlags`, and a +/// quality-of-service level. Closures dispatched through this +/// `Dispatcher` will be submitted to the underlying `DispatchQueue` +/// with the supplied components. + +public struct DispatchQueueDispatcher: Dispatcher { let queue: DispatchQueue - let flags: DispatchWorkItemFlags + let group: DispatchGroup? + let qos: DispatchQoS? + let flags: DispatchWorkItemFlags? - init(queue: DispatchQueue, flags: DispatchWorkItemFlags) { + init(queue: DispatchQueue, group: DispatchGroup? = nil, qos: DispatchQoS? = nil, flags: DispatchWorkItemFlags? = nil) { self.queue = queue + self.group = group + self.qos = qos self.flags = flags } public func dispatch(_ body: @escaping () -> Void) { - queue.async(flags: flags, execute: body) + queue.asyncD(group: group, qos: qos, flags: flags, execute: body) } +} +// Avoid having to hard-code any particular defaults for qos or flags +public extension DispatchQueue { + final func asyncD(group: DispatchGroup? = nil, qos: DispatchQoS? = nil, flags: DispatchWorkItemFlags? = nil, execute body: @escaping () -> Void) { + switch (qos, flags) { + case (nil, nil): + async(group: group, execute: body) + case (nil, let flags?): + async(group: group, flags: flags, execute: body) + case (let qos?, nil): + async(group: group, qos: qos, execute: body) + case (let qos?, let flags?): + async(group: group, qos: qos, flags: flags, execute: body) + } + } } +/// A `Dispatcher` class that executes all closures synchronously on +/// the current thread. +/// +/// Useful for temporarily disabling asynchrony and +/// multithreading while debugging `PromiseKit` chains. +/// +/// You can set `PromiseKit`'s default dispatching behavior to this mode +/// by setting `conf.Q.map` and/or `conf.Q.return` to `nil`. (This is the +/// same as assigning an instance of `CurrentThreadDispatcher` to these +/// variables.) + public struct CurrentThreadDispatcher: Dispatcher { - public func dispatch(_ body: @escaping () -> Void) { + public func dispatch(_ body: () -> Void) { body() } } extension DispatchQueue: Dispatcher { - /// Explicit declaration required; actual function signature is not identical to protocol public func dispatch(_ body: @escaping () -> Void) { async(execute: body) } } -/// Used as default parameter for backward compatibility since clients may explicitly -/// specify "nil" to turn off dispatching. We need to distinguish three cases: explicit -/// queue, explicit nil, and no value specified. Dispatchers from conf.D cannot directly -/// be used as default parameter values because they are not necessarily DispatchQueues. +// Used as default parameter for backward compatibility since clients may explicitly +// specify "nil" to turn off dispatching. We need to distinguish three cases: explicit +// queue, explicit nil, and no value specified. Dispatchers from conf.D cannot directly +// be used as default parameter values because they are not necessarily DispatchQueues. public extension DispatchQueue { static var pmkDefault = DispatchQueue(label: "org.promisekit.sentinel") } public extension DispatchQueue { - func asDispatcher(withFlags flags: DispatchWorkItemFlags? = nil) -> Dispatcher { - if let flags = flags { - return DispatchQueueDispatcher(queue: self, flags: flags) + /// Converts a `DispatchQueue` with given dispatching parameters into a `Dispatcher` + func asDispatcher(group: DispatchGroup? = nil, qos: DispatchQoS? = nil, flags: DispatchWorkItemFlags? = nil) -> Dispatcher { + if group == nil && qos == nil && flags == nil { + return self } - return self + return DispatchQueueDispatcher(queue: self, group: group, qos: qos, flags: flags) } } -/// This hairball disambiguates all the various combinations of explicit arguments, default -/// arguments, and configured defaults. In particular, a method that is given explicit work item -/// flags but no DispatchQueue should still work (that is, the dispatcher should use those flags) -/// as long as the configured default is actually some kind of DispatchQueue. -/// -/// TODO: should conf.D = nil turn off dispatching even if explicit dispatch arguments are given? -/// TODO: Move log prints into LogError enum if they are kept +// This hairball disambiguates all the various combinations of explicit arguments, default +// arguments, and configured defaults. In particular, a method that is given explicit work item +// flags but no DispatchQueue should still work (that is, the dispatcher should use those flags) +// as long as the configured default is actually some kind of DispatchQueue. fileprivate func selectDispatcher(given: DispatchQueue?, configured: Dispatcher, flags: DispatchWorkItemFlags?) -> Dispatcher { guard let given = given else { if flags != nil { - print("PromiseKit: warning: nil DispatchQueue specified, but DispatchWorkItemFlags were also supplied (ignored)") + conf.logHandler(.nilDispatchQueueWithFlags) } return CurrentThreadDispatcher() } if given !== DispatchQueue.pmkDefault { - return given.asDispatcher(withFlags: flags) + return given.asDispatcher(flags: flags) } else if let flags = flags, let configured = configured as? DispatchQueue { - return configured.asDispatcher(withFlags: flags) + return configured.asDispatcher(flags: flags) } else if flags != nil { - print("PromiseKit: warning: DispatchWorkItemFlags flags specified, but default dispatcher is not a DispatchQueue (ignored)") + conf.logHandler(.extraneousFlagsSpecified) } return configured } -/// Backward compatibility for DispatchQueues in public API +// Backward compatibility for DispatchQueues in public API public extension Guarantee { @@ -369,7 +412,7 @@ public extension CatchMixin { - Parameter policy: The default policy does not execute your handler for cancellation errors. - Parameter execute: The handler to execute if this promise is rejected. - Returns: A promise finalizer. - - SeeAlso: [Cancellation](http://https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation/docs/) + - SeeAlso: [Cancellation](http://https://github.com/mxcl/PromiseKit/blob/master/Documents/CommonPatterns.md#cancellation/docs/) */ @discardableResult func `catch`(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) -> Void) -> PMKFinalizer { @@ -392,7 +435,7 @@ public extension CatchMixin { - Parameter on: The queue to which the provided closure dispatches. - Parameter body: The handler to execute if this promise is rejected. - - SeeAlso: [Cancellation](http://https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation/docs/) + - SeeAlso: [Cancellation](http://https://github.com/mxcl/PromiseKit/blob/master/Documents/CommonPatterns.md#cancellation/docs/) */ func recover(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) throws -> U) -> Promise where U.T == T { let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) @@ -405,7 +448,7 @@ public extension CatchMixin { - Note it is logically impossible for this to take a `catchPolicy`, thus `allErrors` are handled. - Parameter on: The queue to which the provided closure dispatches. - Parameter body: The handler to execute if this promise is rejected. - - SeeAlso: [Cancellation](http://https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation/docs/) + - SeeAlso: [Cancellation](http://https://github.com/mxcl/PromiseKit/blob/master/Documents/CommonPatterns.md#cancellation/docs/) */ @discardableResult func recover(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Error) -> Guarantee) -> Guarantee { @@ -476,7 +519,7 @@ public extension CatchMixin where T == Void { - Parameter on: The queue to which the provided closure dispatches. - Parameter body: The handler to execute if this promise is rejected. - - SeeAlso: [Cancellation](http://https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation/docs/) + - SeeAlso: [Cancellation](http://https://github.com/mxcl/PromiseKit/blob/master/Documents/CommonPatterns.md#cancellation/docs/) */ @discardableResult func recover(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Error) -> Void) -> Guarantee { @@ -491,7 +534,7 @@ public extension CatchMixin where T == Void { - Parameter on: The queue to which the provided closure dispatches. - Parameter body: The handler to execute if this promise is rejected. - - SeeAlso: [Cancellation](http://https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation/docs/) + - SeeAlso: [Cancellation](http://https://github.com/mxcl/PromiseKit/blob/master/Documents/CommonPatterns.md#cancellation/docs/) */ func recover(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) throws -> Void) -> Promise { let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) diff --git a/Sources/Guarantee.swift b/Sources/Guarantee.swift index cc219dcdb..d007f8214 100644 --- a/Sources/Guarantee.swift +++ b/Sources/Guarantee.swift @@ -185,7 +185,7 @@ public extension Guarantee where T == Void { public extension DispatchQueue { /** - Asynchronously executes the provided closure on a dispatch queue. + Asynchronously executes the provided closure on a dispatch queue, yielding a `Guarantee`. DispatchQueue.global().async(.promise) { md5(input) @@ -193,14 +193,17 @@ public extension DispatchQueue { //… } - - Parameter body: The closure that resolves this promise. + - _: Must be `.promise` to distinguish from standard `DispatchQueue.async` + - group: A `DispatchGroup`, as for standard `DispatchQueue.async` + - qos: A quality-of-service grade, as for standard `DispatchQueue.async` + - flags: Work item flags, as for standard `DispatchQueue.async` + - body: A closure that yields a value to resolve the guarantee. - Returns: A new `Guarantee` resolved by the result of the provided closure. - - Note: There is no Promise/Thenable version of this due to Swift compiler ambiguity issues. */ @available(macOS 10.10, iOS 2.0, tvOS 10.0, watchOS 2.0, *) - final func async(_: PMKNamespacer, group: DispatchGroup? = nil, qos: DispatchQoS = .default, flags: DispatchWorkItemFlags = [], execute body: @escaping () -> T) -> Guarantee { + final func async(_: PMKNamespacer, group: DispatchGroup? = nil, qos: DispatchQoS? = nil, flags: DispatchWorkItemFlags? = nil, execute body: @escaping () -> T) -> Guarantee { let rg = Guarantee(.pending) - async(group: group, qos: qos, flags: flags) { + asyncD(group: group, qos: qos, flags: flags) { rg.box.seal(body()) } return rg @@ -209,19 +212,19 @@ public extension DispatchQueue { public extension Dispatcher { /** - Asynchronously executes the provided closure on a Dispatcher. - - dispatcher.guarantee { + Executes the provided closure on a `Dispatcher`, yielding a `Guarantee` + that represents the value ultimately returned by the closure. + + dispatcher.dispatch { md5(input) }.done { md5 in //… } - - Parameter body: The closure that resolves this promise. + - Parameter body: The closure that yields the value of the Guarantee. - Returns: A new `Guarantee` resolved by the result of the provided closure. - - Note: There is no Promise/Thenable version of this due to Swift compiler ambiguity issues. */ - func dispatch(_: PMKNamespacer, _ body: @escaping () -> T) -> Guarantee { + func dispatch(_ body: @escaping () -> T) -> Guarantee { let rg = Guarantee(.pending) dispatch { rg.box.seal(body()) diff --git a/Sources/LogEvent.swift b/Sources/LogEvent.swift index 99683bdb6..7dc4349e6 100644 --- a/Sources/LogEvent.swift +++ b/Sources/LogEvent.swift @@ -27,4 +27,29 @@ public enum LogEvent { /// An error which occurred while resolving a promise was swallowed case cauterized(Error) + + /// Odd arguments to DispatchQueue-compatibility layer + case nilDispatchQueueWithFlags + + /// DispatchWorkItem flags specified for non-DispatchQueue Dispatcher + case extraneousFlagsSpecified + + public func asString() -> String { + var message: String + switch self { + case .waitOnMainThread: + message = " warning: `wait()` called on main thread!" + case .pendingPromiseDeallocated: + message = " warning: pending promise deallocated" + case .pendingGuaranteeDeallocated: + message = " warning: pending guarantee deallocated" + case .cauterized(let error): + message = "cauterized-error: \(error)" + case .nilDispatchQueueWithFlags: + message = " warning: nil DispatchQueue specified, but DispatchWorkItemFlags were also supplied (ignored)" + case .extraneousFlagsSpecified: + message = " warning: DispatchWorkItemFlags flags specified, but default Dispatcher is not a DispatchQueue (ignored)" + } + return "PromiseKit:\(message)" + } } diff --git a/Sources/Promise.swift b/Sources/Promise.swift index 4ffcb4d19..792f2d4da 100644 --- a/Sources/Promise.swift +++ b/Sources/Promise.swift @@ -109,7 +109,7 @@ public extension Promise { func wait() throws -> T { if Thread.isMainThread { - conf.logHandler(LogEvent.waitOnMainThread) + conf.logHandler(.waitOnMainThread) } var result = self.result @@ -134,7 +134,7 @@ extension Promise where T == Void { public extension DispatchQueue { /** - Asynchronously executes the provided closure on a dispatch queue. + Asynchronously executes the provided closure on a dispatch queue, yielding a `Promise`. DispatchQueue.global().async(.promise) { try md5(input) @@ -142,14 +142,18 @@ public extension DispatchQueue { //… } - - Parameter body: The closure that resolves this promise. + - Parameters: + - _: Must be `.promise` to distinguish from standard `DispatchQueue.async` + - group: A `DispatchGroup`, as for standard `DispatchQueue.async` + - qos: A quality-of-service grade, as for standard `DispatchQueue.async` + - flags: Work item flags, as for standard `DispatchQueue.async` + - body: A closure that yields a value to resolve the promise. - Returns: A new `Promise` resolved by the result of the provided closure. - - Note: There is no Promise/Thenable version of this due to Swift compiler ambiguity issues. */ @available(macOS 10.10, iOS 8.0, tvOS 9.0, watchOS 2.0, *) - final func async(_: PMKNamespacer, group: DispatchGroup? = nil, qos: DispatchQoS = .default, flags: DispatchWorkItemFlags = [], execute body: @escaping () throws -> T) -> Promise { + final func async(_: PMKNamespacer, group: DispatchGroup? = nil, qos: DispatchQoS? = nil, flags: DispatchWorkItemFlags? = nil, execute body: @escaping () throws -> T) -> Promise { let promise = Promise(.pending) - async(group: group, qos: qos, flags: flags) { + asyncD(group: group, qos: qos, flags: flags) { do { promise.box.seal(.success(try body())) } catch { @@ -162,19 +166,19 @@ public extension DispatchQueue { public extension Dispatcher { /** - Asynchronously executes the provided closure on a Dispatcher. + Executes the provided closure on a `Dispatcher`, yielding a `Promise` + that represents the value ultimately returned by the closure. - dispatcher.promise { + dispatcher.dispatch { try md5(input) }.done { md5 in //… } - - Parameter body: The closure that resolves this promise. + - Parameter body: A closure that yields a value to resolve the promise. - Returns: A new `Promise` resolved by the result of the provided closure. - - Note: There is no Promise/Thenable version of this due to Swift compiler ambiguity issues. */ - func dispatch(_: PMKNamespacer, _ body: @escaping () throws -> T) -> Promise { + func dispatch(_ body: @escaping () throws -> T) -> Promise { let promise = Promise(.pending) dispatch { do { diff --git a/Tests/Core/DispatcherTests.swift b/Tests/Core/DispatcherTests.swift index 60a399472..c5551afa2 100644 --- a/Tests/Core/DispatcherTests.swift +++ b/Tests/Core/DispatcherTests.swift @@ -59,6 +59,11 @@ class DispatcherTests: XCTestCase { PromiseKit.conf.D = oldConf } + func testPMKDefaultIdentity() { + // If this identity does not hold, the DispatchQueue wrapper API will not behave correctly + XCTAssert(DispatchQueue.pmkDefault === DispatchQueue.pmkDefault, "DispatchQueues are not object-identity-preserving on this platform") + } + func testDispatcherWithThrow() { let ex = expectation(description: "Dispatcher with throw") Promise { seal in @@ -122,10 +127,12 @@ class DispatcherTests: XCTestCase { @available(macOS 10.10, iOS 2.0, tvOS 10.0, watchOS 2.0, *) func testDispatcherExtensionReturnsGuarantee() { let ex = expectation(description: "Dispatcher.promise") - dispatcher.dispatch(.promise) { () -> Int in + let object: Any = dispatcher.dispatch() { () -> Int in XCTAssertFalse(Thread.isMainThread) return 1 - }.done { one in + } + XCTAssert(object is Guarantee, "Guarantee not returned from Dispatcher.dispatch { () -> Int }") + (object as? Guarantee)?.done { one in XCTAssertEqual(one, 1) ex.fulfill() } @@ -135,9 +142,11 @@ class DispatcherTests: XCTestCase { @available(macOS 10.10, iOS 2.0, tvOS 10.0, watchOS 2.0, *) func testDispatcherExtensionCanThrowInBody() { let ex = expectation(description: "Dispatcher.promise") - dispatcher.dispatch(.promise) { () -> Int in + let object: Any = dispatcher.dispatch() { () -> Int in throw PMKError.badInput - }.done { _ in + } + XCTAssert(object is Promise, "Promise not returned from Dispatcher.dispatch { () throws -> Int }") + (object as? Promise)?.done { _ in XCTFail() }.catch { _ in ex.fulfill() @@ -145,4 +154,4 @@ class DispatcherTests: XCTestCase { waitForExpectations(timeout: 1) } -} \ No newline at end of file +} diff --git a/Tests/Core/LoggingTests.swift b/Tests/Core/LoggingTests.swift index 1cf65972c..955f43cb0 100644 --- a/Tests/Core/LoggingTests.swift +++ b/Tests/Core/LoggingTests.swift @@ -3,22 +3,30 @@ import Dispatch import XCTest class LoggingTests: XCTestCase { + + enum ForTesting: Error, CustomDebugStringConvertible { + case purposes + var debugDescription: String { + return "purposes" + } + } + + var logOutput: String? = nil + + func captureLogger(_ event: LogEvent) { + logOutput = "\(event)" + } + /** The test should emit the following log messages: PromiseKit: warning: `wait()` called on main thread! PromiseKit: warning: pending promise deallocated + PromiseKit: warning: pending guarantee deallocated PromiseKit:cauterized-error: purposes - This is an error message */ func testLogging() { - var logOutput: String? = nil - - enum ForTesting: Error { - case purposes - } - // Test Logging to Console, the default behavior conf.logHandler(.waitOnMainThread) conf.logHandler(.pendingPromiseDeallocated) @@ -33,16 +41,7 @@ class LoggingTests: XCTestCase { conf.logHandler(.cauterized(ForTesting.purposes)) XCTAssertNil(logOutput) - conf.logHandler = { event in - switch event { - case .waitOnMainThread, .pendingPromiseDeallocated, .pendingGuaranteeDeallocated: - logOutput = "\(event)" - case .cauterized: - // Using an enum with associated value does not convert to a string properly in - // earlier versions of swift - logOutput = "cauterized" - } - } + conf.logHandler = captureLogger conf.logHandler(.waitOnMainThread) XCTAssertEqual(logOutput!, "waitOnMainThread") logOutput = nil @@ -50,20 +49,13 @@ class LoggingTests: XCTestCase { XCTAssertEqual(logOutput!, "pendingPromiseDeallocated") logOutput = nil conf.logHandler(.cauterized(ForTesting.purposes)) - XCTAssertEqual(logOutput!, "cauterized") + XCTAssertEqual(logOutput!, "cauterized(purposes)") } // Verify waiting on main thread in Promise is logged func testPromiseWaitOnMainThreadLogged() throws { - enum ForTesting: Error { - case purposes - } - - var logOutput: String? = nil - conf.logHandler = { event in - logOutput = "\(event)" - } + conf.logHandler = captureLogger let promiseResolver = Promise.pending() let workQueue = DispatchQueue(label: "worker") workQueue.async { @@ -77,21 +69,7 @@ class LoggingTests: XCTestCase { // Verify Promise.cauterize() is logged func testCauterizeIsLogged() { - enum ForTesting: Error { - case purposes - } - - var logOutput: String? = nil - conf.logHandler = { event in - switch event { - case .waitOnMainThread, .pendingPromiseDeallocated, .pendingGuaranteeDeallocated: - logOutput = "\(event)" - case .cauterized: - // Using an enum with associated value does not convert to a string properly in - // earlier versions of swift - logOutput = "cauterized" - } - } + conf.logHandler = captureLogger func createPromise() -> Promise { let promiseResolver = Promise.pending() @@ -113,8 +91,8 @@ class LoggingTests: XCTestCase { readQueue.async { var outputSet = false while !outputSet { - if let logOutput = logOutput { - XCTAssertEqual(logOutput, "cauterized") + if let logOutput = self.logOutput { + XCTAssertEqual(logOutput, "cauterized(purposes)") outputSet = true ex.fulfill() } @@ -129,21 +107,7 @@ class LoggingTests: XCTestCase { // Verify waiting on main thread in Guarantee is logged func testGuaranteeWaitOnMainThreadLogged() { - enum ForTesting: Error { - case purposes - } - - var logOutput: String? = nil - conf.logHandler = { event in - switch event { - case .waitOnMainThread, .pendingPromiseDeallocated, .pendingGuaranteeDeallocated: - logOutput = "\(event)" - case .cauterized: - // Using an enum with associated value does not convert to a string properly in - // earlier versions of swift - logOutput = "cauterized" - } - } + conf.logHandler = captureLogger let guaranteeResolve = Guarantee.pending() let workQueue = DispatchQueue(label: "worker") workQueue.async { @@ -157,17 +121,7 @@ class LoggingTests: XCTestCase { // Verify pendingPromiseDeallocated is logged func testPendingPromiseDeallocatedIsLogged() { - var logOutput: String? = nil - conf.logHandler = { event in - switch event { - case .waitOnMainThread, .pendingPromiseDeallocated, .pendingGuaranteeDeallocated: - logOutput = "\(event)" - case .cauterized: - // Using an enum with associated value does not convert to a string properly in - // earlier versions of swift - logOutput = "cauterized" - } - } + conf.logHandler = captureLogger do { let _ = Promise.pending() } @@ -177,23 +131,28 @@ class LoggingTests: XCTestCase { // Verify pendingGuaranteeDeallocated is logged func testPendingGuaranteeDeallocatedIsLogged() { - var logOutput: String? = nil - let loggingClosure: (PromiseKit.LogEvent) -> () = { event in - switch event { - case .waitOnMainThread, .pendingPromiseDeallocated, .pendingGuaranteeDeallocated: - logOutput = "\(event)" - case .cauterized: - // Using an enum with associated value does not convert to a string properly in - // earlier versions of swift - logOutput = "cauterized" - } - } - conf.logHandler = loggingClosure + conf.logHandler = captureLogger do { let _ = Guarantee.pending() } XCTAssertEqual ("pendingGuaranteeDeallocated", logOutput!) } - //TODO Verify pending promise deallocation is logged + // Verify nilDispatchQueueWithFlags is logged + func testNilDispatchQueueWithFlags() { + + conf.logHandler = captureLogger + Guarantee.value(42).done(on: nil, flags: .barrier) { _ in } + XCTAssertEqual ("nilDispatchQueueWithFlags", logOutput!) + } + + // Verify extraneousFlagsSpecified is logged + func testExtraneousFlagsSpecified() { + + conf.logHandler = captureLogger + conf.D.return = CurrentThreadDispatcher() + Guarantee.value(42).done(flags: .barrier) { _ in } + XCTAssertEqual ("extraneousFlagsSpecified", logOutput!) + } + } diff --git a/Tests/Core/PromiseTests.swift b/Tests/Core/PromiseTests.swift index a278bcb0c..498e3a06f 100644 --- a/Tests/Core/PromiseTests.swift +++ b/Tests/Core/PromiseTests.swift @@ -28,32 +28,104 @@ class PromiseTests: XCTestCase { } @available(macOS 10.10, iOS 2.0, tvOS 10.0, watchOS 2.0, *) - func testDispatchQueueAsyncExtensionReturnsPromise() { + func testDispatchQueueAsyncExtensionReturnsGuarantee() { let ex = expectation(description: "") - - DispatchQueue.global().async(.promise) { () -> Int in + let returnedValue: Any = DispatchQueue.global().async(.promise) { () -> Int in XCTAssertFalse(Thread.isMainThread) return 1 - }.done { one in - XCTAssertEqual(one, 1) - ex.fulfill() } + // This is statically determined, but we want to promote it into something that fits into XCTest + XCTAssert(returnedValue is Guarantee, "DispatchQueue.async() returns non-Guarantee even when code doesn't throw") + if let guarantee = returnedValue as? Guarantee { + guarantee.done { one in + XCTAssertEqual(one, 1) + ex.fulfill() + } + waitForExpectations(timeout: 1) + } + } - waitForExpectations(timeout: 1) + @available(macOS 10.10, iOS 2.0, tvOS 10.0, watchOS 2.0, *) + func testDispatcherDispatchExtensionReturnsGuarantee() { + let ex = expectation(description: "Dispatcher.dispatch -> Guarantee") + let dispatcher: Dispatcher = DispatchQueue.global() + let returnedValue: Any = dispatcher.dispatch { () -> Int in + XCTAssertFalse(Thread.isMainThread) + return 1 + } + // This is statically determined, but we want to promote it into something that fits into XCTest + XCTAssert(returnedValue is Guarantee, "Dispatcher.dispatch() returns non-Guarantee even when code doesn't throw") + if let guarantee = returnedValue as? Guarantee { + guarantee.done { one in + XCTAssertEqual(one, 1) + ex.fulfill() + } + waitForExpectations(timeout: 1) + } } @available(macOS 10.10, iOS 2.0, tvOS 10.0, watchOS 2.0, *) func testDispatchQueueAsyncExtensionCanThrowInBody() { let ex = expectation(description: "") + let returnedValue: Any = DispatchQueue.global().async(.promise) { () -> Int in + throw Error.dummy + } + // This is statically determined, but we want to promote it into something that fits into XCTest + XCTAssert(returnedValue is Promise, "Dispatcher.dispatch() returns non-Promise even when code can throw") + XCTAssert(returnedValue is Promise, "DispatchQueue.async() returns non-Promise even when code can throw") + if let promise = returnedValue as? Promise { + promise.done { _ in + XCTFail("Promise should not complete normally") + }.catch { _ in + ex.fulfill() + } + waitForExpectations(timeout: 1) + } else { + XCTFail("Could not recover Promise from Any") + } + } - DispatchQueue.global().async(.promise) { () -> Int in + @available(macOS 10.10, iOS 2.0, tvOS 10.0, watchOS 2.0, *) + func testDispatcherDispatchExtensionCanThrowInBody() { + let ex = expectation(description: "Dispatcher.dispatch -> Promise") + let dispatcher: Dispatcher = DispatchQueue.global() + let returnedValue: Any = dispatcher.dispatch { () -> Int in throw Error.dummy - }.done { _ in - XCTFail() - }.catch { _ in - ex.fulfill() } + // This is statically determined, but we want to promote it into something that fits into XCTest + XCTAssert(returnedValue is Promise, "Dispatcher.dispatch() returns non-Promise even when code can throw") + if let promise = returnedValue as? Promise { + promise.done { _ in + XCTFail("Promise should not complete normally") + }.catch { _ in + ex.fulfill() + } + waitForExpectations(timeout: 1) + } else { + XCTFail("Could not recover Promise from Any") + } + } + @available(macOS 10.10, iOS 2.0, tvOS 10.0, watchOS 2.0, *) + func testDispatcherDispatchExtensionDoesNotInterfereWithRegularDispatch() { + let dispatcher: Dispatcher = DispatchQueue.global() + + let plain = expectation(description: "plain closure") + let plainReturn: Any = dispatcher.dispatch { + plain.fulfill() + } + // This is statically determined, but we want to promote it into something that fits into XCTest + XCTAssert(plainReturn is Void, "Dispatcher.dispatch() returns something other than Void") + + // With a throwing closure, the return should be a Promise, even without a return value. + // There's no standard Dispatcher API that accepts throwing closures for dispatch. + let throwing = expectation(description: "throwing closure") + let throwingReturn: Any = dispatcher.dispatch { + throwing.fulfill() + throw Error.dummy + } + // This is statically determined, but we want to promote it into something that fits into XCTest + XCTAssert(throwingReturn is Promise, "Dispatcher.dispatch() returns something other than Promise with plain throwing closure") waitForExpectations(timeout: 1) } diff --git a/Tests/Core/XCTestManifests.swift b/Tests/Core/XCTestManifests.swift index 0cf85fa37..9264dc7f2 100644 --- a/Tests/Core/XCTestManifests.swift +++ b/Tests/Core/XCTestManifests.swift @@ -50,6 +50,20 @@ extension CatchableTests { ] } +extension DispatcherTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__DispatcherTests = [ + ("testConfD", testConfD), + ("testDispatcherExtensionCanThrowInBody", testDispatcherExtensionCanThrowInBody), + ("testDispatcherExtensionReturnsGuarantee", testDispatcherExtensionReturnsGuarantee), + ("testDispatcherWithThrow", testDispatcherWithThrow), + ("testDispatchQueueSelection", testDispatchQueueSelection), + ("testPMKDefaultIdentity", testPMKDefaultIdentity), + ] +} + extension GuaranteeTests { // DO NOT MODIFY: This is autogenerated, use: // `swift test --generate-linuxmain` @@ -87,8 +101,10 @@ extension LoggingTests { // to regenerate. static let __allTests__LoggingTests = [ ("testCauterizeIsLogged", testCauterizeIsLogged), + ("testExtraneousFlagsSpecified", testExtraneousFlagsSpecified), ("testGuaranteeWaitOnMainThreadLogged", testGuaranteeWaitOnMainThreadLogged), ("testLogging", testLogging), + ("testNilDispatchQueueWithFlags", testNilDispatchQueueWithFlags), ("testPendingGuaranteeDeallocatedIsLogged", testPendingGuaranteeDeallocatedIsLogged), ("testPendingPromiseDeallocatedIsLogged", testPendingPromiseDeallocatedIsLogged), ("testPromiseWaitOnMainThreadLogged", testPromiseWaitOnMainThreadLogged), @@ -124,8 +140,11 @@ extension PromiseTests { ("testCanMakeVoidPromise", testCanMakeVoidPromise), ("testCannotFulfillWithError", testCannotFulfillWithError), ("testCustomStringConvertible", testCustomStringConvertible), + ("testDispatcherDispatchExtensionCanThrowInBody", testDispatcherDispatchExtensionCanThrowInBody), + ("testDispatcherDispatchExtensionDoesNotInterfereWithRegularDispatch", testDispatcherDispatchExtensionDoesNotInterfereWithRegularDispatch), + ("testDispatcherDispatchExtensionReturnsGuarantee", testDispatcherDispatchExtensionReturnsGuarantee), ("testDispatchQueueAsyncExtensionCanThrowInBody", testDispatchQueueAsyncExtensionCanThrowInBody), - ("testDispatchQueueAsyncExtensionReturnsPromise", testDispatchQueueAsyncExtensionReturnsPromise), + ("testDispatchQueueAsyncExtensionReturnsGuarantee", testDispatchQueueAsyncExtensionReturnsGuarantee), ("testIsFulfilled", testIsFulfilled), ("testIsPending", testIsPending), ("testIsRejected", testIsRejected), @@ -261,6 +280,7 @@ public func __allTests() -> [XCTestCaseEntry] { testCase(AfterTests.__allTests__AfterTests), testCase(CancellationTests.__allTests__CancellationTests), testCase(CatchableTests.__allTests__CatchableTests), + testCase(DispatcherTests.__allTests__DispatcherTests), testCase(GuaranteeTests.__allTests__GuaranteeTests), testCase(HangTests.__allTests__HangTests), testCase(JoinTests.__allTests__JoinTests), From c202ed68cc29d406281cdd3e1a9dd508037f5ece Mon Sep 17 00:00:00 2001 From: Garth Snyder Date: Thu, 7 Feb 2019 16:08:49 -0800 Subject: [PATCH 05/81] Add StrictRateLimitedDispatcher --- Sources/StrictRateLimitedDispatcher.swift | 90 +++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 Sources/StrictRateLimitedDispatcher.swift diff --git a/Sources/StrictRateLimitedDispatcher.swift b/Sources/StrictRateLimitedDispatcher.swift new file mode 100644 index 000000000..0c886c7d8 --- /dev/null +++ b/Sources/StrictRateLimitedDispatcher.swift @@ -0,0 +1,90 @@ +import Foundation + +// A PromiseKit Dispatcher that initiates no more than X executions every Y +// seconds. This version is accurate; see RateLimitedDispatcher for a more +// efficient approximation. + +public class StrictRateLimitedDispatcher: Dispatcher { + + let maxEvents: Int + let interval: TimeInterval + let queue: Dispatcher + + private let serializer = DispatchQueue(label: "SRLD serializer") + + private var pastDispatchTimes: [DispatchTime] = [] + private var nextPDT = 0 + + private var latestCleanupTime: DispatchTime = DispatchTime.distantFuture + private var cleanupTimer: Timer? { willSet { cleanupTimer?.invalidate() }} + + // A PromiseKit Dispatcher that initiates no more than X executions every Y + // seconds. This is a sliding window, so executions occur as rapidly as + // possible without exceeding X in any Y-second period. + // + // This version implements perfectly accurate timing, so it must keep + // track of up to X previous execution times at a cost of 8X bytes. Records are + // freed when they expire, so an idle scheduler incurs only trivial storage cost. + // + // For a "pretty good" approach to rate limiting that does not consume + // additional storage, see RateLimitedDispatcher. + // + // Executions are limited by start time, not completion, so it's possible to + // end up with more than X closures running concurrently in odd circumstances. + // + // 100% thread safe. + // + // - Parameter maxEvents: The number of executions that may be dispatched within any given period. + // - Parameter perInterval: The length of the reference interval, in seconds. + // - Parameter queue: The DispatchQueue or Dispatcher on which to perform executions. May be serial or concurrent. + + init(maxEvents: Int, perInterval interval: TimeInterval, queue: Dispatcher = DispatchQueue.main) { + self.maxEvents = maxEvents + self.interval = interval + self.queue = queue + } + + func dispatch(_ body: @escaping () -> Void) { + serializer.async { + self.enqueue(body) + } + } + + private func enqueue(_ body: @escaping () -> Void) { + let deadline = nextDeadline() + serializer.asyncAfter(deadline: deadline) { + self.queue.dispatch(body) + } + latestCleanupTime = deadline + interval // Must have a complete interval with no activity + let timeBeforeCleanup = max(latestCleanupTime - DispatchTime.now(), 0) + cleanupTimer = Timer.scheduledTimer(withTimeInterval: timeBeforeCleanup, repeats: false) { + serializer.async(execute: self.cleanup) + } + } + + private func cleanup() { + // Calls to cleanup() have to go through the serializer queue, so by by the time + // we get here, more blocks may have been enqueued. So double-check that our + // original deadline hasn't been superseded. + guard DispatchTime.now() >= latestCleanupTime else { return } + pastDispatchTimes.removeAll(keepingCapacity: false) + nextPDT = 0 + } + + private func nextDeadline() -> DispatchTime { + let now = DispatchTime.now() + if pastDispatchTimes.count < maxEvents { + pastDispatchTimes.append(now) + return now + } else { + let deadline = max(pastDispatchTimes[nextPDT] + interval, now) + pastDispatchTimes[nextPDT] = deadline + nextPDT += 1 + if nextPDT >= maxEvents { + nextPDT = 0 + } + return deadline + } + } + +} From 87e84e6fe3033c05668e5ee555e657cbb2ad212f Mon Sep 17 00:00:00 2001 From: Garth Snyder Date: Thu, 7 Feb 2019 17:24:38 -0800 Subject: [PATCH 06/81] Add ConcurrencyLimitedDispatcher --- Sources/ConcurrencyLimitedDispatcher.swift | 37 ++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 Sources/ConcurrencyLimitedDispatcher.swift diff --git a/Sources/ConcurrencyLimitedDispatcher.swift b/Sources/ConcurrencyLimitedDispatcher.swift new file mode 100644 index 000000000..971edced0 --- /dev/null +++ b/Sources/ConcurrencyLimitedDispatcher.swift @@ -0,0 +1,37 @@ +import Foundation + +// A PromiseKit Dispatcher that runs no more than X simultaneous +// executions at once. + +class ConcurrencyLimitedDispatcher: Dispatcher { + + let queue: Dispatcher + let serialEntryQueue: DispatchQueue + + private let semaphore: DispatchSemaphore + + // A PromiseKit Dispatcher that runs no more than X simultaneous + // executions at once. + // + // - Parameter limit: The number of executions that may run at once. + // - Parameter queue: The DispatchQueue or Dispatcher on which to perform executions. + // Should be some form of concurrent queue. + + public init(limit: Int, queue: Dispatcher = DispatchQueue.global(qos: .background)) { + self.queue = queue + serialEntryQueue = DispatchQueue(label: "CLD entryway") + semaphore = DispatchSemaphore(value: limit) + } + + public func dispatch(_ body: @escaping () -> Void) { + serialEntryQueue.async { + self.semaphore.wait() + self.queue.dispatch { + body() + self.semaphore.signal() + } + } + } + +} + From 2a549fa7af9c133076d7681030e249dc8101ddab Mon Sep 17 00:00:00 2001 From: Garth Snyder Date: Fri, 8 Feb 2019 10:57:40 -0800 Subject: [PATCH 07/81] StrictRateLimitedDispatcher now schedules only on execution --- Sources/Queue.swift | 64 +++++++++ Sources/RateLimitedDispatcher.swift | 139 +++++++++++++++++++ Sources/StrictRateLimitedDispatcher.swift | 162 +++++++++++++--------- Tests/Core/DispatcherTypeTests.swift | 66 +++++++++ 4 files changed, 369 insertions(+), 62 deletions(-) create mode 100644 Sources/Queue.swift create mode 100644 Sources/RateLimitedDispatcher.swift create mode 100644 Tests/Core/DispatcherTypeTests.swift diff --git a/Sources/Queue.swift b/Sources/Queue.swift new file mode 100644 index 000000000..53931c7ae --- /dev/null +++ b/Sources/Queue.swift @@ -0,0 +1,64 @@ +import Foundation + +// Simple queue implementation with storage recovery + +fileprivate let arraySizeWorthCompacting = 100 +fileprivate let minUtilization = 0.6 + +struct Queue { + + var elements: [T?] = [] + var head = 0 + let maxDepth: Int? + + init(maxDepth: Int? = nil) { + self.maxDepth = maxDepth + } + + var isEmpty: Bool { + return head >= elements.count + } + + var count: Int { + return elements.count - head + } + + mutating func enqueue(_ item: T) { + elements.append(item) + if let maxDepth = maxDepth, count > maxDepth { + _ = dequeue() + } + } + + mutating func dequeue() -> T { + assert(!isEmpty, "Dequeue attempt on an empty Queue") + defer { + elements[head] = nil + head += 1 + maybeCompactStorage() + } + return elements[head]! + } + + private mutating func maybeCompactStorage() { + let n = elements.count + if n > arraySizeWorthCompacting && head > Int(Double(n) * (1 - minUtilization)) { + compactStorage() + } + } + + mutating func compactStorage() { + if isEmpty { + elements.removeAll(keepingCapacity: false) + } else { + elements.removeFirst(head) + } + head = 0 + } + + mutating func purge() { + elements.removeAll(keepingCapacity: false) + head = 0 + } + +} diff --git a/Sources/RateLimitedDispatcher.swift b/Sources/RateLimitedDispatcher.swift new file mode 100644 index 000000000..fd1867368 --- /dev/null +++ b/Sources/RateLimitedDispatcher.swift @@ -0,0 +1,139 @@ +import Foundation + +/// A `PromiseKit` `Dispatcher` that executes, on average, no more than X +/// executions every Y seconds. +/// +/// This implementation uses a token bucket, so the transient burst rate may be +/// up to 2X in a Y-second period. If you need an ironclad guarantee of +/// conformance to the rate limit, you can specify an X that's half the true +/// value; however, this will halve the average throughput as well. +/// +/// For a completely accurate rate limiter that dispatches as rapidly as +/// possible, see `StrictRateLimitedDispatcher`. But note that this variant +/// incurs a + +a sliding window, so executions occur as rapidly as +/// possible without exceeding X in any Y-second period. +/// +/// This version implements perfectly accurate timing, so it must keep +/// track of up to X previous execution times. Records are freed when they expire, +/// so an idle scheduler does not incur this storage cost. +/// +/// For a "pretty good" approach to rate limiting that does not consume +/// additional storage, see RateLimitedDispatcher. +/// +/// Executions are limited by start time, not completion, so it's possible to +/// end up with more than X closures running concurrently in some circumstances. +/// +/// There is no guarantee that you will reach the given dispatch rate. There are not +/// an infinite number of threads available, and GCD scheduling has limited accuracy. +/// The only guarantee is that dispatching will never exceed the requested rate. +/// +/// 100% thread safe. + +public class StrictRateLimitedDispatcher: Dispatcher { + + let maxDispatches: Int + let interval: TimeInterval + let queue: Dispatcher + + private let serializer = DispatchQueue(label: "SRLD serializer") + + private var nScheduled = 0 + private var unscheduled = Queue<() -> Void>() + internal var startTimeHistory: Queue + private var latestDeadline = DispatchTime(uptimeNanoseconds: 0) + + private var cleanupNonce: Int64 = 0 + private var cleanupWorkItem: DispatchWorkItem? { willSet { cleanupWorkItem?.cancel() }} + + /// A `PromiseKit` Dispatcher that executes no more than X executions every Y + /// seconds. This is a sliding window, so executions occur as rapidly as + /// possible without exceeding X in any Y-second period. + /// + /// For a "pretty good" approach to rate limiting that does not consume + /// additional storage, see `RateLimitedDispatcher`. + /// + /// - Parameter maxDispatches: The number of executions that may be dispatched within a given interval. + /// - Parameter perInterval: The length of the reference interval, in seconds. + /// - Parameter queue: The DispatchQueue or Dispatcher on which to perform executions. May be serial or concurrent. + + public init(maxDispatches: Int, perInterval interval: TimeInterval, queue: Dispatcher = DispatchQueue.global()) { + self.maxDispatches = maxDispatches + self.interval = interval + self.queue = queue + startTimeHistory = Queue(maxDepth: maxDispatches) + } + + public func dispatch(_ body: @escaping () -> Void) { + serializer.async { + self.unscheduled.enqueue(body) + self.scheduleNext() + } + } + + private func scheduleNext() { + + cleanupNonce += 1 + + guard nScheduled < maxDispatches else { return } + guard !unscheduled.isEmpty else { return } + + var deadline = DispatchTime.now() + if !startTimeHistory.isEmpty { + // Use the start time of a previous closure as a time reference. In practice, + // past start times will normally be reported and recorded in monotonically + // increasing sequence, which yields optimal scheduling. However, this is all + // potentially multithreaded, so there are no order guarantees. However, if any + // start times ARE out of order, the algorithm is still correct: for each + // dispatched item, another may be scheduled interval seconds later. Like kanban. + deadline = max(deadline, startTimeHistory.dequeue() + interval) + } + // Enforce a monotonically increasing outbound schedule to keep calls in order + if deadline <= latestDeadline { + deadline = DispatchTime(uptimeNanoseconds: latestDeadline.uptimeNanoseconds + 1) + } + + let body = unscheduled.dequeue() + // A Dispatcher has no asyncAfter; use the serializer queue for timing + serializer.asyncAfter(deadline: deadline) { + self.queue.dispatch { + let now = DispatchTime.now() + self.serializer.async { + self.recordActualStartTime(now) + } + body() + } + } + + latestDeadline = deadline + nScheduled += 1 + + } + + private func recordActualStartTime(_ time: DispatchTime) { + nScheduled -= 1 + startTimeHistory.enqueue(time) + scheduleNext() + if nScheduled == 0 && unscheduled.isEmpty { + scheduleCleanup() + } + } + + private func scheduleCleanup() { + cleanupWorkItem = DispatchWorkItem { [ weak self, nonce = self.cleanupNonce ] in + self?.cleanup(nonce) + } + serializer.asyncAfter(deadline: DispatchTime.now() + interval, execute: cleanupWorkItem!) + } + + private func cleanup(_ nonce: Int64) { + // Calls to cleanup() have to go through the serializer queue, so by by the time + // we get here, more activity may have occurred. Ergo, verify nonce. + guard nonce == cleanupNonce else { return } + startTimeHistory.purge() // We're at least an interval past last start + unscheduled.compactStorage() + cleanupWorkItem = nil + } + +} diff --git a/Sources/StrictRateLimitedDispatcher.swift b/Sources/StrictRateLimitedDispatcher.swift index 0c886c7d8..adfd108cc 100644 --- a/Sources/StrictRateLimitedDispatcher.swift +++ b/Sources/StrictRateLimitedDispatcher.swift @@ -1,90 +1,128 @@ import Foundation -// A PromiseKit Dispatcher that initiates no more than X executions every Y -// seconds. This version is accurate; see RateLimitedDispatcher for a more -// efficient approximation. +/// A `PromiseKit` `Dispatcher` that executes no more than X executions every Y +/// seconds. This is a sliding window, so executions occur as rapidly as +/// possible without exceeding X in any Y-second period. +/// +/// This version implements perfectly accurate timing, so it must keep +/// track of up to X previous execution times. Records are freed when they expire, +/// so an idle scheduler does not incur this storage cost. +/// +/// For a "pretty good" approach to rate limiting that does not consume +/// additional storage, see `RateLimitedDispatcher`. +/// +/// Executions are limited by start time, not completion, so it's possible to +/// end up with more than X closures running concurrently in some circumstances. +/// +/// There is no guarantee that you will reach the given dispatch rate. There are not +/// an infinite number of threads available, and GCD scheduling has limited accuracy. +/// The only guarantee is that dispatching will never exceed the requested rate. +/// +/// 100% thread safe. public class StrictRateLimitedDispatcher: Dispatcher { - let maxEvents: Int + let maxDispatches: Int let interval: TimeInterval let queue: Dispatcher private let serializer = DispatchQueue(label: "SRLD serializer") - private var pastDispatchTimes: [DispatchTime] = [] - private var nextPDT = 0 + private var nScheduled = 0 + private var unscheduled = Queue<() -> Void>() + internal var startTimeHistory: Queue + private var latestDeadline = DispatchTime(uptimeNanoseconds: 0) - private var latestCleanupTime: DispatchTime = DispatchTime.distantFuture - private var cleanupTimer: Timer? { willSet { cleanupTimer?.invalidate() }} + private var cleanupNonce: Int64 = 0 + private var cleanupWorkItem: DispatchWorkItem? { willSet { cleanupWorkItem?.cancel() }} - // A PromiseKit Dispatcher that initiates no more than X executions every Y - // seconds. This is a sliding window, so executions occur as rapidly as - // possible without exceeding X in any Y-second period. - // - // This version implements perfectly accurate timing, so it must keep - // track of up to X previous execution times at a cost of 8X bytes. Records are - // freed when they expire, so an idle scheduler incurs only trivial storage cost. - // - // For a "pretty good" approach to rate limiting that does not consume - // additional storage, see RateLimitedDispatcher. - // - // Executions are limited by start time, not completion, so it's possible to - // end up with more than X closures running concurrently in odd circumstances. - // - // 100% thread safe. - // - // - Parameter maxEvents: The number of executions that may be dispatched within any given period. - // - Parameter perInterval: The length of the reference interval, in seconds. - // - Parameter queue: The DispatchQueue or Dispatcher on which to perform executions. May be serial or concurrent. - - init(maxEvents: Int, perInterval interval: TimeInterval, queue: Dispatcher = DispatchQueue.main) { - self.maxEvents = maxEvents + /// A `PromiseKit` `Dispatcher` that executes no more than X executions every Y + /// seconds. This is a sliding window, so executions occur as rapidly as + /// possible without exceeding X in any Y-second period. + /// + /// For a "pretty good" approach to rate limiting that does not consume + /// additional storage, see `RateLimitedDispatcher`. + /// + /// - Parameter maxDispatches: The number of executions that may be dispatched within a given interval. + /// - Parameter perInterval: The length of the reference interval, in seconds. + /// - Parameter queue: The DispatchQueue or Dispatcher on which to perform executions. May be serial or concurrent. + + public init(maxDispatches: Int, perInterval interval: TimeInterval, queue: Dispatcher = DispatchQueue.global()) { + self.maxDispatches = maxDispatches self.interval = interval self.queue = queue + startTimeHistory = Queue(maxDepth: maxDispatches) } - func dispatch(_ body: @escaping () -> Void) { + public func dispatch(_ body: @escaping () -> Void) { serializer.async { - self.enqueue(body) + self.unscheduled.enqueue(body) + self.scheduleNext() } } - private func enqueue(_ body: @escaping () -> Void) { - let deadline = nextDeadline() - serializer.asyncAfter(deadline: deadline) { - self.queue.dispatch(body) + private func scheduleNext() { + + cleanupNonce += 1 + + guard nScheduled < maxDispatches else { return } + guard !unscheduled.isEmpty else { return } + + var deadline = DispatchTime.now() + if !startTimeHistory.isEmpty { + // Use the start time of a previous closure as a time reference. In practice, + // past start times will normally be reported and recorded in monotonically + // increasing sequence, which yields optimal scheduling. However, this is all + // potentially multithreaded, so there are no order guarantees. However, if any + // start times ARE out of order, the algorithm is still correct: for each + // dispatched item, another may be scheduled interval seconds later. Like kanban. + deadline = max(deadline, startTimeHistory.dequeue() + interval) + } + // Enforce a monotonically increasing outbound schedule to keep calls in order + if deadline <= latestDeadline { + deadline = DispatchTime(uptimeNanoseconds: latestDeadline.uptimeNanoseconds + 1) } - latestCleanupTime = deadline + interval // Must have a complete interval with no activity - let timeBeforeCleanup = max(latestCleanupTime - DispatchTime.now(), 0) - cleanupTimer = Timer.scheduledTimer(withTimeInterval: timeBeforeCleanup, repeats: false) { - serializer.async(execute: self.cleanup) + + let body = unscheduled.dequeue() + // A Dispatcher has no asyncAfter; use the serializer queue for timing + serializer.asyncAfter(deadline: deadline) { + self.queue.dispatch { + let now = DispatchTime.now() + self.serializer.async { + self.recordActualStartTime(now) + } + body() + } } + + latestDeadline = deadline + nScheduled += 1 + } - private func cleanup() { - // Calls to cleanup() have to go through the serializer queue, so by by the time - // we get here, more blocks may have been enqueued. So double-check that our - // original deadline hasn't been superseded. - guard DispatchTime.now() >= latestCleanupTime else { return } - pastDispatchTimes.removeAll(keepingCapacity: false) - nextPDT = 0 + private func recordActualStartTime(_ time: DispatchTime) { + nScheduled -= 1 + startTimeHistory.enqueue(time) + scheduleNext() + if nScheduled == 0 && unscheduled.isEmpty { + scheduleCleanup() + } } - - private func nextDeadline() -> DispatchTime { - let now = DispatchTime.now() - if pastDispatchTimes.count < maxEvents { - pastDispatchTimes.append(now) - return now - } else { - let deadline = max(pastDispatchTimes[nextPDT] + interval, now) - pastDispatchTimes[nextPDT] = deadline - nextPDT += 1 - if nextPDT >= maxEvents { - nextPDT = 0 - } - return deadline + + private func scheduleCleanup() { + cleanupWorkItem = DispatchWorkItem { [ weak self, nonce = self.cleanupNonce ] in + self?.cleanup(nonce) } + serializer.asyncAfter(deadline: DispatchTime.now() + interval, execute: cleanupWorkItem!) } - + + private func cleanup(_ nonce: Int64) { + // Calls to cleanup() have to go through the serializer queue, so by by the time + // we get here, more activity may have occurred. Ergo, verify nonce. + guard nonce == cleanupNonce else { return } + startTimeHistory.purge() // We're at least an interval past last start + unscheduled.compactStorage() + cleanupWorkItem = nil + } + } diff --git a/Tests/Core/DispatcherTypeTests.swift b/Tests/Core/DispatcherTypeTests.swift new file mode 100644 index 000000000..16a7bbd4e --- /dev/null +++ b/Tests/Core/DispatcherTypeTests.swift @@ -0,0 +1,66 @@ +import Dispatch +@testable import PromiseKit +import XCTest + +class DispatcherTypeTests: XCTestCase { + + func testStrictRateLimitedDispatcher() { + + let most = 10 + let interval = 0.05 + let n = most * 20 + + let avgSlice = UInt32(interval * 1_000_000 * 0.9 / Double(most)) + let delayRange = 0...(2 * avgSlice) + + let dispatcher = StrictRateLimitedDispatcher(maxDispatches: most, perInterval: interval) + let delays = (1...n).map { _ in delayRange.randomElement()! } + let mostConcurrent = rateLimitTest(dispatcher, delays: delays, interval: interval) + + // Significantly less than the goal rate is also a potential concern + XCTAssertGreaterThan(mostConcurrent, (most * 3) / 4) + XCTAssertLessThanOrEqual(mostConcurrent, most) + + usleep(UInt32(interval * 1_000_000) + 100_000) + XCTAssert(dispatcher.startTimeHistory.count == 0, "Dispatcher did not clean up properly") + + } + + func rateLimitTest(_ dispatcher: Dispatcher, delays: [UInt32], interval: TimeInterval) -> Int { + + var startTimes: [DispatchTime] = [] + let lock = NSLock() + let ex = expectation(description: "Rate limit") + + for delay in delays { + usleep(delay) + Guarantee.value(42).done(on: dispatcher) { _ in + lock.lock() + startTimes.append(DispatchTime.now()) + if startTimes.count == delays.count { + ex.fulfill() + } + lock.unlock() + } + } + + let totalDelay = Double(delays.reduce(0, +)) / 1_000_000 + let expectedDuration = interval * Double(delays.count) + let adequateTime = max(expectedDuration, totalDelay) * 1.5 + waitForExpectations(timeout: adequateTime) + + return mostAtOnce(startTimes, interval: interval) + + } + + func mostAtOnce(_ times: [DispatchTime], interval: TimeInterval) -> Int { + var most = 0 + for start in times { + let timeRange = start...(start + interval) + let pruned = times.filter { timeRange.contains($0) } + most = max(most, pruned.count) + } + return most + } + +} From 6d2ea9cfe6e3a43fe9803466bcc06faf26b4e9b7 Mon Sep 17 00:00:00 2001 From: Garth Snyder Date: Thu, 14 Feb 2019 14:23:02 -0800 Subject: [PATCH 08/81] Refactor rate limited dispatchers onto common base --- Sources/RateLimitedDispatcher.swift | 132 +++++++--------------- Sources/RateLimitedDispatcherBase.swift | 59 ++++++++++ Sources/StrictRateLimitedDispatcher.swift | 65 ++++------- Tests/Core/DispatcherTypeTests.swift | 71 ++++++++++-- 4 files changed, 182 insertions(+), 145 deletions(-) create mode 100644 Sources/RateLimitedDispatcherBase.swift diff --git a/Sources/RateLimitedDispatcher.swift b/Sources/RateLimitedDispatcher.swift index fd1867368..ad69249a7 100644 --- a/Sources/RateLimitedDispatcher.swift +++ b/Sources/RateLimitedDispatcher.swift @@ -11,8 +11,8 @@ import Foundation /// For a completely accurate rate limiter that dispatches as rapidly as /// possible, see `StrictRateLimitedDispatcher`. But note that this variant /// incurs a - -a sliding window, so executions occur as rapidly as +/// +/// a sliding window, so executions occur as rapidly as /// possible without exceeding X in any Y-second period. /// /// This version implements perfectly accurate timing, so it must keep @@ -31,109 +31,59 @@ a sliding window, so executions occur as rapidly as /// /// 100% thread safe. -public class StrictRateLimitedDispatcher: Dispatcher { - - let maxDispatches: Int - let interval: TimeInterval - let queue: Dispatcher - - private let serializer = DispatchQueue(label: "SRLD serializer") - - private var nScheduled = 0 - private var unscheduled = Queue<() -> Void>() - internal var startTimeHistory: Queue - private var latestDeadline = DispatchTime(uptimeNanoseconds: 0) - - private var cleanupNonce: Int64 = 0 - private var cleanupWorkItem: DispatchWorkItem? { willSet { cleanupWorkItem?.cancel() }} +public class RateLimitedDispatcher: RateLimitedDispatcherBase { - /// A `PromiseKit` Dispatcher that executes no more than X executions every Y - /// seconds. This is a sliding window, so executions occur as rapidly as - /// possible without exceeding X in any Y-second period. - /// - /// For a "pretty good" approach to rate limiting that does not consume - /// additional storage, see `RateLimitedDispatcher`. - /// - /// - Parameter maxDispatches: The number of executions that may be dispatched within a given interval. - /// - Parameter perInterval: The length of the reference interval, in seconds. - /// - Parameter queue: The DispatchQueue or Dispatcher on which to perform executions. May be serial or concurrent. + private var tokensInBucket: Double = 0 + private var lastAccrual: DispatchTime = DispatchTime.now() + private var retryWorkItem: DispatchWorkItem? { willSet { retryWorkItem?.cancel() }} - public init(maxDispatches: Int, perInterval interval: TimeInterval, queue: Dispatcher = DispatchQueue.global()) { - self.maxDispatches = maxDispatches - self.interval = interval - self.queue = queue - startTimeHistory = Queue(maxDepth: maxDispatches) + override init(maxDispatches: Int, perInterval interval: TimeInterval, queue: Dispatcher = DispatchQueue.global()) { + lastAccrual = DispatchTime.now() + super.init(maxDispatches: maxDispatches, perInterval: interval, queue: queue) + tokensInBucket = Double(maxDispatches) } - public func dispatch(_ body: @escaping () -> Void) { - serializer.async { - self.unscheduled.enqueue(body) - self.scheduleNext() - } - } + override func dispatchFromQueue() { - private func scheduleNext() { - - cleanupNonce += 1 + let now = DispatchTime.now() + let tokensPerSecond = Double(maxDispatches) / interval + let tokensToAdd = (now - lastAccrual) * tokensPerSecond + tokensInBucket = min(Double(maxDispatches), tokensInBucket + tokensToAdd) + lastAccrual = now - guard nScheduled < maxDispatches else { return } - guard !unscheduled.isEmpty else { return } - - var deadline = DispatchTime.now() - if !startTimeHistory.isEmpty { - // Use the start time of a previous closure as a time reference. In practice, - // past start times will normally be reported and recorded in monotonically - // increasing sequence, which yields optimal scheduling. However, this is all - // potentially multithreaded, so there are no order guarantees. However, if any - // start times ARE out of order, the algorithm is still correct: for each - // dispatched item, another may be scheduled interval seconds later. Like kanban. - deadline = max(deadline, startTimeHistory.dequeue() + interval) - } - // Enforce a monotonically increasing outbound schedule to keep calls in order - if deadline <= latestDeadline { - deadline = DispatchTime(uptimeNanoseconds: latestDeadline.uptimeNanoseconds + 1) - } - - let body = unscheduled.dequeue() - // A Dispatcher has no asyncAfter; use the serializer queue for timing - serializer.asyncAfter(deadline: deadline) { - self.queue.dispatch { - let now = DispatchTime.now() + var didDispatch = false + while tokensInBucket >= 1.0 && !undispatched.isEmpty && nScheduled < maxDispatches { + didDispatch = true + tokensInBucket -= 1.0 + nScheduled += 1 + let body = unscheduled.dequeue() + queue.dispatch { self.serializer.async { - self.recordActualStartTime(now) + self.recordActualStart() } body() } } - - latestDeadline = deadline - nScheduled += 1 - - } - - private func recordActualStartTime(_ time: DispatchTime) { - nScheduled -= 1 - startTimeHistory.enqueue(time) - scheduleNext() - if nScheduled == 0 && unscheduled.isEmpty { - scheduleCleanup() + + if didDispatch { + cleanupNonce += 1 + } else { + scheduleRetry() } + } - private func scheduleCleanup() { - cleanupWorkItem = DispatchWorkItem { [ weak self, nonce = self.cleanupNonce ] in - self?.cleanup(nonce) + private func scheduleRetry() { + guard nScheduled == 0 && retryWorkItem == nil else { return } + let tokensPerSecond = Double(maxDispatches) / interval + let tokenDeficit = 1.0 - tokensInBucket + let secondsToGo = tokenDeficit / tokensPerSecond + let deadline = lastAccrual + secondsToGo + 1.0E-6 + let retryWorkItem = DispatchWorkItem { + self?.retryWorkItem = nil + self?.dispatchFromQueue() } - serializer.asyncAfter(deadline: DispatchTime.now() + interval, execute: cleanupWorkItem!) - } - - private func cleanup(_ nonce: Int64) { - // Calls to cleanup() have to go through the serializer queue, so by by the time - // we get here, more activity may have occurred. Ergo, verify nonce. - guard nonce == cleanupNonce else { return } - startTimeHistory.purge() // We're at least an interval past last start - unscheduled.compactStorage() - cleanupWorkItem = nil + serializer.asyncAfter(deadline: deadline, execute: retryWorkItem!) } - + } diff --git a/Sources/RateLimitedDispatcherBase.swift b/Sources/RateLimitedDispatcherBase.swift new file mode 100644 index 000000000..1c776f7e4 --- /dev/null +++ b/Sources/RateLimitedDispatcherBase.swift @@ -0,0 +1,59 @@ +import Foundation + +public class RateLimitedDispatcherBase: Dispatcher { + + let maxDispatches: Int + let interval: TimeInterval + let queue: Dispatcher + + internal let serializer = DispatchQueue(label: "RLD serializer") + + internal var nScheduled = 0 + internal var undispatched = Queue<() -> Void>() + + internal var cleanupNonce: Int64 = 0 + internal var cleanupWorkItem: DispatchWorkItem? { willSet { cleanupWorkItem?.cancel() }} + + public init(maxDispatches: Int, perInterval interval: TimeInterval, queue: Dispatcher = DispatchQueue.global()) { + self.maxDispatches = maxDispatches + self.interval = interval + self.queue = queue + } + + public func dispatch(_ body: @escaping () -> Void) { + serializer.async { + self.undispatched.enqueue(body) + self.dispatchFromQueue() + } + } + + internal func dispatchFromQueue() { + fatalError("Subclass responsibility") + } + + internal func recordActualStart() { + nScheduled -= 1 + dispatchFromQueue() + if nScheduled == 0 && undispatched.isEmpty { + scheduleCleanup() + } + } + + internal func scheduleCleanup() { + cleanupWorkItem = DispatchWorkItem { [ weak self, nonce = self.cleanupNonce ] in + self?.cleanup(nonce) + } + print("cleanup sched", (DispatchTime.now() + interval).rawValue) + serializer.asyncAfter(deadline: DispatchTime.now() + interval, execute: cleanupWorkItem!) + } + + internal func cleanup(_ nonce: Int64) { + // Calls to cleanup() have to go through the serializer queue, so by by the time + // we get here, more activity may have occurred. Ergo, verify nonce. + print("cleanup run at", DispatchTime.now().rawValue, "nonce OK:", nonce == cleanupNonce) + guard nonce == cleanupNonce else { return } + undispatched.compactStorage() + cleanupWorkItem = nil + } + +} diff --git a/Sources/StrictRateLimitedDispatcher.swift b/Sources/StrictRateLimitedDispatcher.swift index adfd108cc..c63d7d246 100644 --- a/Sources/StrictRateLimitedDispatcher.swift +++ b/Sources/StrictRateLimitedDispatcher.swift @@ -20,22 +20,12 @@ import Foundation /// /// 100% thread safe. -public class StrictRateLimitedDispatcher: Dispatcher { +public class StrictRateLimitedDispatcher: RateLimitedDispatcherBase { - let maxDispatches: Int - let interval: TimeInterval - let queue: Dispatcher - - private let serializer = DispatchQueue(label: "SRLD serializer") - - private var nScheduled = 0 - private var unscheduled = Queue<() -> Void>() internal var startTimeHistory: Queue + private var immediateDispatchesAvailable: Int private var latestDeadline = DispatchTime(uptimeNanoseconds: 0) - private var cleanupNonce: Int64 = 0 - private var cleanupWorkItem: DispatchWorkItem? { willSet { cleanupWorkItem?.cancel() }} - /// A `PromiseKit` `Dispatcher` that executes no more than X executions every Y /// seconds. This is a sliding window, so executions occur as rapidly as /// possible without exceeding X in any Y-second period. @@ -47,29 +37,26 @@ public class StrictRateLimitedDispatcher: Dispatcher { /// - Parameter perInterval: The length of the reference interval, in seconds. /// - Parameter queue: The DispatchQueue or Dispatcher on which to perform executions. May be serial or concurrent. - public init(maxDispatches: Int, perInterval interval: TimeInterval, queue: Dispatcher = DispatchQueue.global()) { - self.maxDispatches = maxDispatches - self.interval = interval - self.queue = queue + override init(maxDispatches: Int, perInterval interval: TimeInterval, queue: Dispatcher = DispatchQueue.global()) { startTimeHistory = Queue(maxDepth: maxDispatches) - } - - public func dispatch(_ body: @escaping () -> Void) { - serializer.async { - self.unscheduled.enqueue(body) - self.scheduleNext() - } + immediateDispatchesAvailable = maxDispatches + super.init(maxDispatches: maxDispatches, perInterval: interval, queue: queue) } - private func scheduleNext() { + override func dispatchFromQueue() { cleanupNonce += 1 guard nScheduled < maxDispatches else { return } - guard !unscheduled.isEmpty else { return } + guard !undispatched.isEmpty else { return } + let accountedFor = nScheduled + startTimeHistory.count + immediateDispatchesAvailable + assert(accountedFor == maxDispatches, "Dispatcher bookkeeping problem") + var deadline = DispatchTime.now() - if !startTimeHistory.isEmpty { + if immediateDispatchesAvailable > 0 { + immediateDispatchesAvailable -= 1 + } else { // Use the start time of a previous closure as a time reference. In practice, // past start times will normally be reported and recorded in monotonically // increasing sequence, which yields optimal scheduling. However, this is all @@ -83,8 +70,9 @@ public class StrictRateLimitedDispatcher: Dispatcher { deadline = DispatchTime(uptimeNanoseconds: latestDeadline.uptimeNanoseconds + 1) } - let body = unscheduled.dequeue() + let body = undispatched.dequeue() // A Dispatcher has no asyncAfter; use the serializer queue for timing + print("body sched", deadline.rawValue) serializer.asyncAfter(deadline: deadline) { self.queue.dispatch { let now = DispatchTime.now() @@ -101,28 +89,17 @@ public class StrictRateLimitedDispatcher: Dispatcher { } private func recordActualStartTime(_ time: DispatchTime) { - nScheduled -= 1 + print("body runat", time.rawValue) + print("body reportedat", DispatchTime.now().rawValue) startTimeHistory.enqueue(time) - scheduleNext() - if nScheduled == 0 && unscheduled.isEmpty { - scheduleCleanup() - } - } - - private func scheduleCleanup() { - cleanupWorkItem = DispatchWorkItem { [ weak self, nonce = self.cleanupNonce ] in - self?.cleanup(nonce) - } - serializer.asyncAfter(deadline: DispatchTime.now() + interval, execute: cleanupWorkItem!) + super.recordActualStart() } - private func cleanup(_ nonce: Int64) { - // Calls to cleanup() have to go through the serializer queue, so by by the time - // we get here, more activity may have occurred. Ergo, verify nonce. + override func cleanup(_ nonce: Int64) { + super.cleanup(nonce) guard nonce == cleanupNonce else { return } startTimeHistory.purge() // We're at least an interval past last start - unscheduled.compactStorage() - cleanupWorkItem = nil + immediateDispatchesAvailable = maxDispatches } } diff --git a/Tests/Core/DispatcherTypeTests.swift b/Tests/Core/DispatcherTypeTests.swift index 16a7bbd4e..d8df20864 100644 --- a/Tests/Core/DispatcherTypeTests.swift +++ b/Tests/Core/DispatcherTypeTests.swift @@ -4,28 +4,55 @@ import XCTest class DispatcherTypeTests: XCTestCase { - func testStrictRateLimitedDispatcher() { + func testStrictRateLimiter() { - let most = 10 - let interval = 0.05 - let n = most * 20 + var rng = Xoroshiro(0xBAD_DEFACED_FACADE, 0xDEAD_BEEF_CAFE_BABE) + // Hiatus = longer pause in inflow (on the order of the interval) + for hiatusLikelihoodPerInterval in [ 0.0, 0.3, 0.7 ] { + for noDelayLikelihood in [ 0.0, 0.2, 0.75 ] { + for interval in [ 0.025, 0.1 ] { + for most in [ 10, 30 ] { + + let n = most * 10 let avgSlice = UInt32(interval * 1_000_000 * 0.9 / Double(most)) - let delayRange = 0...(2 * avgSlice) + let normalDelayRange = 0...avgSlice + let hiatusRange = UInt32(interval * 0.5 * 1_000_000)...UInt32(interval * 2 * 1_000_000) + let hiatusLikelihoodPerDispatch = 1 - pow(1 - hiatusLikelihoodPerInterval, 1 / Double(most)) + + var delays: [UInt32] = [] + for _ in 1...n { + let rand = Double.random(in: 0...1, using: &rng) + if rand < hiatusLikelihoodPerDispatch { + delays.append(hiatusRange.randomElement(using: &rng)!) + } else if rand > (1 - noDelayLikelihood) { + delays.append(0) + } else { + delays.append(normalDelayRange.randomElement(using: &rng)!) + } + } + + let nHiatuses = delays.count { $0 > avgSlice } + print("\nNew run: n = \(n), most = \(most), interval = \(interval), pHiatus = \(hiatusLikelihoodPerInterval), nHiatuses = \(nHiatuses)\n") let dispatcher = StrictRateLimitedDispatcher(maxDispatches: most, perInterval: interval) - let delays = (1...n).map { _ in delayRange.randomElement()! } let mostConcurrent = rateLimitTest(dispatcher, delays: delays, interval: interval) // Significantly less than the goal rate is also a potential concern XCTAssertGreaterThan(mostConcurrent, (most * 3) / 4) XCTAssertLessThanOrEqual(mostConcurrent, most) - usleep(UInt32(interval * 1_000_000) + 100_000) + print("tail wait start", DispatchTime.now().rawValue) + usleep(UInt32(interval * 1_000_000 * 1.25)) // FIXME + print("tail wait end", DispatchTime.now().rawValue) XCTAssert(dispatcher.startTimeHistory.count == 0, "Dispatcher did not clean up properly") - - } + } + } + } + } + } + func rateLimitTest(_ dispatcher: Dispatcher, delays: [UInt32], interval: TimeInterval) -> Int { var startTimes: [DispatchTime] = [] @@ -36,7 +63,8 @@ class DispatcherTypeTests: XCTestCase { usleep(delay) Guarantee.value(42).done(on: dispatcher) { _ in lock.lock() - startTimes.append(DispatchTime.now()) + let now = DispatchTime.now() + startTimes.append(now) if startTimes.count == delays.count { ex.fulfill() } @@ -64,3 +92,26 @@ class DispatcherTypeTests: XCTestCase { } } + +// Reproducible, seedable RNG + +struct Xoroshiro: RandomNumberGenerator { + + typealias State = (UInt64, UInt64) + + var state: State + + init(_ a: UInt64, _ b: UInt64) { + state = (a, b) + } + + mutating func next() -> UInt64 { + let (l, k0, k1, k2): (UInt64, UInt64, UInt64, UInt64) = (64, 55, 14, 36) + let result = state.0 &+ state.1 + let x = state.0 ^ state.1 + state.0 = ((state.0 << k0) | (state.0 >> (l - k0))) ^ x ^ (x << k1) + state.1 = (x << k2) | (x >> (l - k2)) + return result + } + +} From 99b4804128309c50577def77c1a05f9331390c9f Mon Sep 17 00:00:00 2001 From: Garth Snyder Date: Mon, 11 Feb 2019 22:14:29 -0800 Subject: [PATCH 09/81] Limiter dispatchers complete and tested --- Sources/ConcurrencyLimitedDispatcher.swift | 16 +- Sources/RateLimitedDispatcher.swift | 94 +++++++----- Sources/RateLimitedDispatcherBase.swift | 8 +- Sources/StrictRateLimitedDispatcher.swift | 22 ++- Tests/Core/DispatcherTypeTests.swift | 162 ++++++++++++++++----- 5 files changed, 203 insertions(+), 99 deletions(-) diff --git a/Sources/ConcurrencyLimitedDispatcher.swift b/Sources/ConcurrencyLimitedDispatcher.swift index 971edced0..297e26e96 100644 --- a/Sources/ConcurrencyLimitedDispatcher.swift +++ b/Sources/ConcurrencyLimitedDispatcher.swift @@ -1,7 +1,7 @@ import Foundation -// A PromiseKit Dispatcher that runs no more than X simultaneous -// executions at once. +/// A PromiseKit Dispatcher that allows no more than X simultaneous +/// executions at once. class ConcurrencyLimitedDispatcher: Dispatcher { @@ -10,12 +10,12 @@ class ConcurrencyLimitedDispatcher: Dispatcher { private let semaphore: DispatchSemaphore - // A PromiseKit Dispatcher that runs no more than X simultaneous - // executions at once. - // - // - Parameter limit: The number of executions that may run at once. - // - Parameter queue: The DispatchQueue or Dispatcher on which to perform executions. - // Should be some form of concurrent queue. + /// A `PromiseKit` `Dispatcher` that allows no more than X simultaneous + /// executions at once. + /// + /// - Parameter limit: The number of executions that may run at once. + /// - Parameter queue: The DispatchQueue or Dispatcher on which to perform executions. + /// Should be some form of concurrent queue. public init(limit: Int, queue: Dispatcher = DispatchQueue.global(qos: .background)) { self.queue = queue diff --git a/Sources/RateLimitedDispatcher.swift b/Sources/RateLimitedDispatcher.swift index ad69249a7..2446743ee 100644 --- a/Sources/RateLimitedDispatcher.swift +++ b/Sources/RateLimitedDispatcher.swift @@ -1,62 +1,67 @@ import Foundation -/// A `PromiseKit` `Dispatcher` that executes, on average, no more than X -/// executions every Y seconds. +/// A `PromiseKit` `Dispatcher` that dispatches X closures every Y seconds, +/// on average. /// -/// This implementation uses a token bucket, so the transient burst rate may be -/// up to 2X in a Y-second period. If you need an ironclad guarantee of -/// conformance to the rate limit, you can specify an X that's half the true -/// value; however, this will halve the average throughput as well. +/// This implementation is O(1) in both space and time, but it uses approximate +/// time accounting. Over the long term, the rate converges to a rate of X/Y, +/// but the transient burst rate will be up to 2X/Y in some situations. /// /// For a completely accurate rate limiter that dispatches as rapidly as -/// possible, see `StrictRateLimitedDispatcher`. But note that this variant -/// incurs a +/// possible, see `StrictRateLimitedDispatcher`. That implementation requires +/// additional storage. /// -/// a sliding window, so executions occur as rapidly as -/// possible without exceeding X in any Y-second period. -/// -/// This version implements perfectly accurate timing, so it must keep -/// track of up to X previous execution times. Records are freed when they expire, -/// so an idle scheduler does not incur this storage cost. -/// -/// For a "pretty good" approach to rate limiting that does not consume -/// additional storage, see RateLimitedDispatcher. -/// -/// Executions are limited by start time, not completion, so it's possible to +/// Executions are paced by start time, not by completion, so it's possible to /// end up with more than X closures running concurrently in some circumstances. /// -/// There is no guarantee that you will reach the given dispatch rate. There are not +/// There is no guarantee that you will reach a given dispatch rate. There are not /// an infinite number of threads available, and GCD scheduling has limited accuracy. -/// The only guarantee is that dispatching will never exceed the requested rate. /// /// 100% thread safe. public class RateLimitedDispatcher: RateLimitedDispatcherBase { private var tokensInBucket: Double = 0 - private var lastAccrual: DispatchTime = DispatchTime.now() + private var latestAccrual: DispatchTime = DispatchTime.now() private var retryWorkItem: DispatchWorkItem? { willSet { retryWorkItem?.cancel() }} + private var tokensPerSecond: Double { return Double(maxDispatches) / interval } + + /// A `PromiseKit` `Dispatcher` that dispatches X executions every Y + /// seconds, on average. + /// + /// This version is O(1) in space and time but uses an approximate algorithm with + /// burst rates up to 2X per Y seconds. For a more accurate implementation, use + /// `StrictRateLimitedDispatcher`. + /// + /// - Parameter maxDispatches: The number of executions that may be dispatched within a given interval. + /// - Parameter perInterval: The length of the reference interval, in seconds. + /// - Parameter queue: The DispatchQueue or Dispatcher on which to perform executions. May be serial or concurrent. + override init(maxDispatches: Int, perInterval interval: TimeInterval, queue: Dispatcher = DispatchQueue.global()) { - lastAccrual = DispatchTime.now() + latestAccrual = DispatchTime.now() super.init(maxDispatches: maxDispatches, perInterval: interval, queue: queue) tokensInBucket = Double(maxDispatches) } override func dispatchFromQueue() { - let now = DispatchTime.now() - let tokensPerSecond = Double(maxDispatches) / interval - let tokensToAdd = (now - lastAccrual) * tokensPerSecond - tokensInBucket = min(Double(maxDispatches), tokensInBucket + tokensToAdd) - lastAccrual = now + guard undispatched.count > 0 else { return } + cleanupNonce += 1 + let now = DispatchTime.now() + let tokensToAdd = (now - latestAccrual) * tokensPerSecond + tokensInBucket = min(Double(maxDispatches - nDispatched), tokensInBucket + tokensToAdd) + latestAccrual = now + + // print("runqueue \(now.rawValue), nDispatched = \(nDispatched), tokens = \(tokensInBucket), undispatched = \(undispatched.count)") + var didDispatch = false - while tokensInBucket >= 1.0 && !undispatched.isEmpty && nScheduled < maxDispatches { + while tokensInBucket >= 1.0 && !undispatched.isEmpty && nDispatched < maxDispatches { didDispatch = true tokensInBucket -= 1.0 - nScheduled += 1 - let body = unscheduled.dequeue() + nDispatched += 1 + let body = undispatched.dequeue() queue.dispatch { self.serializer.async { self.recordActualStart() @@ -65,25 +70,36 @@ public class RateLimitedDispatcher: RateLimitedDispatcherBase { } } - if didDispatch { - cleanupNonce += 1 - } else { + if !didDispatch { scheduleRetry() } } private func scheduleRetry() { - guard nScheduled == 0 && retryWorkItem == nil else { return } - let tokensPerSecond = Double(maxDispatches) / interval - let tokenDeficit = 1.0 - tokensInBucket + guard retryWorkItem == nil && !undispatched.isEmpty && nDispatched < maxDispatches else { return } + let tokenDeficit = 1 - tokensInBucket let secondsToGo = tokenDeficit / tokensPerSecond - let deadline = lastAccrual + secondsToGo + 1.0E-6 - let retryWorkItem = DispatchWorkItem { + let deadline = latestAccrual + secondsToGo + 1.0E-6 + retryWorkItem = DispatchWorkItem { [weak self] in self?.retryWorkItem = nil self?.dispatchFromQueue() } serializer.asyncAfter(deadline: deadline, execute: retryWorkItem!) } + + override func cleanup(_ nonce: Int64) { + super.cleanup(nonce) + guard nonce == cleanupNonce else { return } + tokensInBucket = Double(maxDispatches) // Avoid accumulating roundoff errors + } } + +internal extension DispatchTime { + static func -(a: DispatchTime, b: DispatchTime) -> TimeInterval { + let delta = a.uptimeNanoseconds - b.uptimeNanoseconds + return TimeInterval(delta) / 1_000_000_000 + } +} + diff --git a/Sources/RateLimitedDispatcherBase.swift b/Sources/RateLimitedDispatcherBase.swift index 1c776f7e4..042b5559c 100644 --- a/Sources/RateLimitedDispatcherBase.swift +++ b/Sources/RateLimitedDispatcherBase.swift @@ -8,7 +8,7 @@ public class RateLimitedDispatcherBase: Dispatcher { internal let serializer = DispatchQueue(label: "RLD serializer") - internal var nScheduled = 0 + internal var nDispatched = 0 internal var undispatched = Queue<() -> Void>() internal var cleanupNonce: Int64 = 0 @@ -32,9 +32,9 @@ public class RateLimitedDispatcherBase: Dispatcher { } internal func recordActualStart() { - nScheduled -= 1 + nDispatched -= 1 dispatchFromQueue() - if nScheduled == 0 && undispatched.isEmpty { + if nDispatched == 0 && undispatched.isEmpty { scheduleCleanup() } } @@ -43,14 +43,12 @@ public class RateLimitedDispatcherBase: Dispatcher { cleanupWorkItem = DispatchWorkItem { [ weak self, nonce = self.cleanupNonce ] in self?.cleanup(nonce) } - print("cleanup sched", (DispatchTime.now() + interval).rawValue) serializer.asyncAfter(deadline: DispatchTime.now() + interval, execute: cleanupWorkItem!) } internal func cleanup(_ nonce: Int64) { // Calls to cleanup() have to go through the serializer queue, so by by the time // we get here, more activity may have occurred. Ergo, verify nonce. - print("cleanup run at", DispatchTime.now().rawValue, "nonce OK:", nonce == cleanupNonce) guard nonce == cleanupNonce else { return } undispatched.compactStorage() cleanupWorkItem = nil diff --git a/Sources/StrictRateLimitedDispatcher.swift b/Sources/StrictRateLimitedDispatcher.swift index c63d7d246..fd784332c 100644 --- a/Sources/StrictRateLimitedDispatcher.swift +++ b/Sources/StrictRateLimitedDispatcher.swift @@ -1,20 +1,19 @@ import Foundation -/// A `PromiseKit` `Dispatcher` that executes no more than X executions every Y +/// A `PromiseKit` `Dispatcher` that dispatches no more than X closures every Y /// seconds. This is a sliding window, so executions occur as rapidly as /// possible without exceeding X in any Y-second period. /// -/// This version implements perfectly accurate timing, so it must keep -/// track of up to X previous execution times. Records are freed when they expire, -/// so an idle scheduler does not incur this storage cost. +/// This version implements perfectly accurate timing, so it must (temporarily) +/// keep track of up to X previous execution times. /// /// For a "pretty good" approach to rate limiting that does not consume /// additional storage, see `RateLimitedDispatcher`. /// -/// Executions are limited by start time, not completion, so it's possible to +/// Executions are paced by start time, not by completion, so it's possible to /// end up with more than X closures running concurrently in some circumstances. /// -/// There is no guarantee that you will reach the given dispatch rate. There are not +/// There is no guarantee that you will reach a given dispatch rate. There are not /// an infinite number of threads available, and GCD scheduling has limited accuracy. /// The only guarantee is that dispatching will never exceed the requested rate. /// @@ -26,7 +25,7 @@ public class StrictRateLimitedDispatcher: RateLimitedDispatcherBase { private var immediateDispatchesAvailable: Int private var latestDeadline = DispatchTime(uptimeNanoseconds: 0) - /// A `PromiseKit` `Dispatcher` that executes no more than X executions every Y + /// A `PromiseKit` `Dispatcher` that dispatches no more than X executions every Y /// seconds. This is a sliding window, so executions occur as rapidly as /// possible without exceeding X in any Y-second period. /// @@ -47,10 +46,10 @@ public class StrictRateLimitedDispatcher: RateLimitedDispatcherBase { cleanupNonce += 1 - guard nScheduled < maxDispatches else { return } + guard nDispatched < maxDispatches else { return } guard !undispatched.isEmpty else { return } - let accountedFor = nScheduled + startTimeHistory.count + immediateDispatchesAvailable + let accountedFor = nDispatched + startTimeHistory.count + immediateDispatchesAvailable assert(accountedFor == maxDispatches, "Dispatcher bookkeeping problem") var deadline = DispatchTime.now() @@ -72,7 +71,6 @@ public class StrictRateLimitedDispatcher: RateLimitedDispatcherBase { let body = undispatched.dequeue() // A Dispatcher has no asyncAfter; use the serializer queue for timing - print("body sched", deadline.rawValue) serializer.asyncAfter(deadline: deadline) { self.queue.dispatch { let now = DispatchTime.now() @@ -84,13 +82,11 @@ public class StrictRateLimitedDispatcher: RateLimitedDispatcherBase { } latestDeadline = deadline - nScheduled += 1 + nDispatched += 1 } private func recordActualStartTime(_ time: DispatchTime) { - print("body runat", time.rawValue) - print("body reportedat", DispatchTime.now().rawValue) startTimeHistory.enqueue(time) super.recordActualStart() } diff --git a/Tests/Core/DispatcherTypeTests.swift b/Tests/Core/DispatcherTypeTests.swift index d8df20864..12d9f857b 100644 --- a/Tests/Core/DispatcherTypeTests.swift +++ b/Tests/Core/DispatcherTypeTests.swift @@ -4,21 +4,120 @@ import XCTest class DispatcherTypeTests: XCTestCase { - func testStrictRateLimiter() { + lazy var scenarios = generateRateLimitScenarios() + var rng = Xoroshiro(0x80D0082B8A9651BA, 0x49A8092CFD464A11) // Arbitrary seed + let debug = false + + struct RateLimitScenario { + let maxDispatches: Int + let interval: Double + let hiatusLikelihood: Double + let nHiatuses: Int + let noDelayLikelihood: Double + let delays: [UInt32] + } + + func testRateLimitedDispatcher() { + for scenario in scenarios { + printScenarioDetails(scenario) + let dispatcher = RateLimitedDispatcher(maxDispatches: scenario.maxDispatches, perInterval: scenario.interval) + let (deltaT, mostConcurrent) = rateLimitTest(dispatcher, delays: scenario.delays, interval: scenario.interval) + // For the nonstrict RateLimitedDispatcher, burst rate may be up to 2X the goal. + XCTAssertLessThanOrEqual(mostConcurrent, scenario.maxDispatches * 2) + // Significantly under the goal rate is also a concern + XCTAssertGreaterThan(mostConcurrent, (scenario.maxDispatches * 3) / 4) + printTestResults(deltaT, mostConcurrent, scenario) + } + } + + func printScenarioDetails(_ scenario: RateLimitScenario) { + guard debug else { return } + print("\nNew run: n = \(scenario.delays.count), most = \(scenario.maxDispatches),", + "interval = \(scenario.interval), pNoDelay = \(scenario.noDelayLikelihood),", + "pHiatus = \(scenario.hiatusLikelihood), nHiatuses = \(scenario.nHiatuses)\n") + } + + func printTestResults(_ deltaT: TimeInterval, _ concurrent: Int, _ scenario: RateLimitScenario) { + guard debug else { return } + let rateAvg = Double(scenario.delays.count) * scenario.interval / deltaT + print("result actual max = \(concurrent), target max = \(scenario.maxDispatches), average rate = \(rateAvg)") + } + + func testStrictRateLimitedDispatcher() { + for scenario in scenarios { + printScenarioDetails(scenario) + let dispatcher = StrictRateLimitedDispatcher(maxDispatches: scenario.maxDispatches, perInterval: scenario.interval) + let (deltaT, mostConcurrent) = rateLimitTest(dispatcher, delays: scenario.delays, interval: scenario.interval) + XCTAssertLessThanOrEqual(mostConcurrent, scenario.maxDispatches) + // Significantly under the goal rate is also a concern + XCTAssertGreaterThan(mostConcurrent, (scenario.maxDispatches * 3) / 4) + printTestResults(deltaT, mostConcurrent, scenario) + // print("tail wait start", DispatchTime.now().rawValue) + usleep(UInt32(scenario.interval * 1_000_000 * 1.25)) + // print("tail wait end", DispatchTime.now().rawValue) + XCTAssert(dispatcher.startTimeHistory.count == 0, "Dispatcher did not clean up properly") + } + } + + func testConcurrencyLimitedDispatcher() { + + for scenario in scenarios { + + printScenarioDetails(scenario) + let dispatcher = ConcurrencyLimitedDispatcher(limit: scenario.maxDispatches) + + var nConcurrent = 0 + var maxNConcurrent = 0 + var nRun = 0 + let lock = NSLock() + let ex = expectation(description: "Concurrency limit") + + for delay in scenario.delays { + usleep(delay) + Guarantee.value(42).done(on: dispatcher) { _ in + lock.lock() + nConcurrent += 1 + maxNConcurrent = max(maxNConcurrent, nConcurrent) + lock.unlock() + usleep(UInt32.random(in: 10_000...100_000, using: &self.rng)) + lock.lock() + nConcurrent -= 1 + nRun += 1 + if nRun == scenario.delays.count { + ex.fulfill() + } + lock.unlock() + } + } + + waitForExpectations(timeout: Double(scenario.delays.count) * 0.1) + XCTAssertEqual(maxNConcurrent, scenario.maxDispatches) + + } + } + + func generateRateLimitScenarios() -> [RateLimitScenario] { - var rng = Xoroshiro(0xBAD_DEFACED_FACADE, 0xDEAD_BEEF_CAFE_BABE) + var rng = Xoroshiro(0x80D0082B8A9651BA, 0x49A8092CFD464A11) // Arbitrary seed + var scenarios: [RateLimitScenario] = [] - // Hiatus = longer pause in inflow (on the order of the interval) - for hiatusLikelihoodPerInterval in [ 0.0, 0.3, 0.7 ] { - for noDelayLikelihood in [ 0.0, 0.2, 0.75 ] { - for interval in [ 0.025, 0.1 ] { - for most in [ 10, 30 ] { + // for hiatusLikelihoodPerInterval in [ 0.0, 0.3, 0.7 ] { + // for noDelayLikelihood in [ 0.0, 0.2, 0.75 ] { + // for interval in [ 0.02, 0.1, 1.0 ] { + // for maxDispatches in [ 1, 2, 5, 20 ] { - let n = most * 10 - let avgSlice = UInt32(interval * 1_000_000 * 0.9 / Double(most)) + for hiatusLikelihoodPerInterval in [ 0.3 ] { + for noDelayLikelihood in [ 0.75 ] { + for interval in [ 0.02, 0.1 ] { + for maxDispatches in [ 20 ] { + + // <------------ + + let n = maxDispatches * 10 + let avgSlice = UInt32(interval * 1_000_000 * 0.9 / Double(maxDispatches)) let normalDelayRange = 0...avgSlice - let hiatusRange = UInt32(interval * 0.5 * 1_000_000)...UInt32(interval * 2 * 1_000_000) - let hiatusLikelihoodPerDispatch = 1 - pow(1 - hiatusLikelihoodPerInterval, 1 / Double(most)) + let hiatusRange = UInt32(interval * 0.5 * 1_000_000)...UInt32(interval * 2.5 * 1_000_000) + let hiatusLikelihoodPerDispatch = 1 - pow(1 - hiatusLikelihoodPerInterval, 1 / Double(maxDispatches)) var delays: [UInt32] = [] for _ in 1...n { @@ -33,29 +132,22 @@ class DispatcherTypeTests: XCTestCase { } let nHiatuses = delays.count { $0 > avgSlice } - print("\nNew run: n = \(n), most = \(most), interval = \(interval), pHiatus = \(hiatusLikelihoodPerInterval), nHiatuses = \(nHiatuses)\n") + + scenarios.append(RateLimitScenario(maxDispatches: maxDispatches, interval: interval, + hiatusLikelihood: hiatusLikelihoodPerInterval, nHiatuses: nHiatuses, + noDelayLikelihood: noDelayLikelihood, delays: delays)) - let dispatcher = StrictRateLimitedDispatcher(maxDispatches: most, perInterval: interval) - let mostConcurrent = rateLimitTest(dispatcher, delays: delays, interval: interval) + // <------------- - // Significantly less than the goal rate is also a potential concern - XCTAssertGreaterThan(mostConcurrent, (most * 3) / 4) - XCTAssertLessThanOrEqual(mostConcurrent, most) - - print("tail wait start", DispatchTime.now().rawValue) - usleep(UInt32(interval * 1_000_000 * 1.25)) // FIXME - print("tail wait end", DispatchTime.now().rawValue) - XCTAssert(dispatcher.startTimeHistory.count == 0, "Dispatcher did not clean up properly") - - } - } - } - } + }}}} + + return scenarios } - - func rateLimitTest(_ dispatcher: Dispatcher, delays: [UInt32], interval: TimeInterval) -> Int { + + func rateLimitTest(_ dispatcher: Dispatcher, delays: [UInt32], interval: TimeInterval) -> (TimeInterval, Int) { - var startTimes: [DispatchTime] = [] + let testStart = DispatchTime.now() + var closureStartTimes: [DispatchTime] = [] let lock = NSLock() let ex = expectation(description: "Rate limit") @@ -64,8 +156,8 @@ class DispatcherTypeTests: XCTestCase { Guarantee.value(42).done(on: dispatcher) { _ in lock.lock() let now = DispatchTime.now() - startTimes.append(now) - if startTimes.count == delays.count { + closureStartTimes.append(now) + if closureStartTimes.count == delays.count { ex.fulfill() } lock.unlock() @@ -77,8 +169,10 @@ class DispatcherTypeTests: XCTestCase { let adequateTime = max(expectedDuration, totalDelay) * 1.5 waitForExpectations(timeout: adequateTime) - return mostAtOnce(startTimes, interval: interval) + let most = mostAtOnce(closureStartTimes, interval: interval) + let duration = DispatchTime.now() - testStart + return (duration, most) } func mostAtOnce(_ times: [DispatchTime], interval: TimeInterval) -> Int { @@ -90,7 +184,7 @@ class DispatcherTypeTests: XCTestCase { } return most } - + } // Reproducible, seedable RNG From ff06e0d1e787776bc9544e45d939c4dbc34af1ec Mon Sep 17 00:00:00 2001 From: Garth Snyder Date: Fri, 15 Feb 2019 20:49:25 -0800 Subject: [PATCH 10/81] Add CoreDataDispatcher --- Sources/CoreDataDispatcher.swift | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 Sources/CoreDataDispatcher.swift diff --git a/Sources/CoreDataDispatcher.swift b/Sources/CoreDataDispatcher.swift new file mode 100644 index 000000000..af7e98808 --- /dev/null +++ b/Sources/CoreDataDispatcher.swift @@ -0,0 +1,31 @@ +#if !os(Linux) + +import Foundation +import CoreData + +public extension NSManagedObjectContext { + var dispatcher: CoreDataDispatcher { + return CoreDataDispatcher(self) + } +} + +/// A `Dispatcher` that dispatches onto the threads associated with +/// `NSManagedObjectContext`s, allowing Core Data operations to be +/// handled using promises. + +public struct CoreDataDispatcher: Dispatcher { + + let context: NSManagedObjectContext + + public init(_ context: NSManagedObjectContext) { + self.context = context + } + + public func dispatch(_ body: @escaping () -> Void) { + context.perform(body) + } + +} + +#endif + From ddb9677a2f985297630e7c759a4fbe0e4a000f93 Mon Sep 17 00:00:00 2001 From: Garth Snyder Date: Tue, 12 Feb 2019 16:09:11 -0800 Subject: [PATCH 11/81] Grooming, doc comments, NSLock -> DispatchQueue --- Sources/ConcurrencyLimitedDispatcher.swift | 12 ++++---- Sources/RateLimitedDispatcher.swift | 7 +++-- Sources/StrictRateLimitedDispatcher.swift | 11 ++++--- Tests/Core/DispatcherTypeTests.swift | 36 +++++++++++----------- 4 files changed, 34 insertions(+), 32 deletions(-) diff --git a/Sources/ConcurrencyLimitedDispatcher.swift b/Sources/ConcurrencyLimitedDispatcher.swift index 297e26e96..2a745c59f 100644 --- a/Sources/ConcurrencyLimitedDispatcher.swift +++ b/Sources/ConcurrencyLimitedDispatcher.swift @@ -6,25 +6,25 @@ import Foundation class ConcurrencyLimitedDispatcher: Dispatcher { let queue: Dispatcher - let serialEntryQueue: DispatchQueue + let serializer: DispatchQueue = DispatchQueue(label: "CLD serializer") private let semaphore: DispatchSemaphore /// A `PromiseKit` `Dispatcher` that allows no more than X simultaneous /// executions at once. /// - /// - Parameter limit: The number of executions that may run at once. - /// - Parameter queue: The DispatchQueue or Dispatcher on which to perform executions. - /// Should be some form of concurrent queue. + /// - Parameters: + /// - limit: The number of executions that may run at once. + /// - queue: The DispatchQueue or Dispatcher on which to perform executions. + /// Should be some form of concurrent queue. public init(limit: Int, queue: Dispatcher = DispatchQueue.global(qos: .background)) { self.queue = queue - serialEntryQueue = DispatchQueue(label: "CLD entryway") semaphore = DispatchSemaphore(value: limit) } public func dispatch(_ body: @escaping () -> Void) { - serialEntryQueue.async { + serializer.async { self.semaphore.wait() self.queue.dispatch { body() diff --git a/Sources/RateLimitedDispatcher.swift b/Sources/RateLimitedDispatcher.swift index 2446743ee..addc55524 100644 --- a/Sources/RateLimitedDispatcher.swift +++ b/Sources/RateLimitedDispatcher.swift @@ -34,9 +34,10 @@ public class RateLimitedDispatcher: RateLimitedDispatcherBase { /// burst rates up to 2X per Y seconds. For a more accurate implementation, use /// `StrictRateLimitedDispatcher`. /// - /// - Parameter maxDispatches: The number of executions that may be dispatched within a given interval. - /// - Parameter perInterval: The length of the reference interval, in seconds. - /// - Parameter queue: The DispatchQueue or Dispatcher on which to perform executions. May be serial or concurrent. + /// - Parameters: + /// - maxDispatches: The number of executions that may be dispatched within a given interval. + /// - perInterval: The length of the reference interval, in seconds. + /// - queue: The DispatchQueue or Dispatcher on which to perform executions. May be serial or concurrent. override init(maxDispatches: Int, perInterval interval: TimeInterval, queue: Dispatcher = DispatchQueue.global()) { latestAccrual = DispatchTime.now() diff --git a/Sources/StrictRateLimitedDispatcher.swift b/Sources/StrictRateLimitedDispatcher.swift index fd784332c..87a175c4f 100644 --- a/Sources/StrictRateLimitedDispatcher.swift +++ b/Sources/StrictRateLimitedDispatcher.swift @@ -5,7 +5,7 @@ import Foundation /// possible without exceeding X in any Y-second period. /// /// This version implements perfectly accurate timing, so it must (temporarily) -/// keep track of up to X previous execution times. +/// track up to X previous execution times and is thus O(X) in space. /// /// For a "pretty good" approach to rate limiting that does not consume /// additional storage, see `RateLimitedDispatcher`. @@ -27,14 +27,15 @@ public class StrictRateLimitedDispatcher: RateLimitedDispatcherBase { /// A `PromiseKit` `Dispatcher` that dispatches no more than X executions every Y /// seconds. This is a sliding window, so executions occur as rapidly as - /// possible without exceeding X in any Y-second period. + /// possible without exceeding X in any Y-second period. O(X) in space. /// /// For a "pretty good" approach to rate limiting that does not consume /// additional storage, see `RateLimitedDispatcher`. /// - /// - Parameter maxDispatches: The number of executions that may be dispatched within a given interval. - /// - Parameter perInterval: The length of the reference interval, in seconds. - /// - Parameter queue: The DispatchQueue or Dispatcher on which to perform executions. May be serial or concurrent. + /// - Parameters: + /// - maxDispatches: The number of executions that may be dispatched within a given interval. + /// - perInterval: The length of the reference interval, in seconds. + /// - queue: The DispatchQueue or Dispatcher on which to perform executions. May be serial or concurrent. override init(maxDispatches: Int, perInterval interval: TimeInterval, queue: Dispatcher = DispatchQueue.global()) { startTimeHistory = Queue(maxDepth: maxDispatches) diff --git a/Tests/Core/DispatcherTypeTests.swift b/Tests/Core/DispatcherTypeTests.swift index 12d9f857b..a9aecceb3 100644 --- a/Tests/Core/DispatcherTypeTests.swift +++ b/Tests/Core/DispatcherTypeTests.swift @@ -69,24 +69,24 @@ class DispatcherTypeTests: XCTestCase { var nConcurrent = 0 var maxNConcurrent = 0 var nRun = 0 - let lock = NSLock() + let serializer = DispatchQueue(label: "Concurrency test") let ex = expectation(description: "Concurrency limit") for delay in scenario.delays { usleep(delay) Guarantee.value(42).done(on: dispatcher) { _ in - lock.lock() - nConcurrent += 1 - maxNConcurrent = max(maxNConcurrent, nConcurrent) - lock.unlock() + serializer.sync { + nConcurrent += 1 + maxNConcurrent = max(maxNConcurrent, nConcurrent) + } usleep(UInt32.random(in: 10_000...100_000, using: &self.rng)) - lock.lock() - nConcurrent -= 1 - nRun += 1 - if nRun == scenario.delays.count { - ex.fulfill() + serializer.sync { + nConcurrent -= 1 + nRun += 1 + if nRun == scenario.delays.count { + ex.fulfill() + } } - lock.unlock() } } @@ -148,19 +148,19 @@ class DispatcherTypeTests: XCTestCase { let testStart = DispatchTime.now() var closureStartTimes: [DispatchTime] = [] - let lock = NSLock() + let serializer = DispatchQueue(label: "Rate limit") let ex = expectation(description: "Rate limit") for delay in delays { usleep(delay) Guarantee.value(42).done(on: dispatcher) { _ in - lock.lock() - let now = DispatchTime.now() - closureStartTimes.append(now) - if closureStartTimes.count == delays.count { - ex.fulfill() + serializer.sync { + let now = DispatchTime.now() + closureStartTimes.append(now) + if closureStartTimes.count == delays.count { + ex.fulfill() + } } - lock.unlock() } } From 50f05b5ace9b14b496a747f0979883846cfd8077 Mon Sep 17 00:00:00 2001 From: Garth Snyder Date: Fri, 15 Feb 2019 20:50:18 -0800 Subject: [PATCH 12/81] Break Dispatcher implementation tests into separate classes to allow parallelism --- Tests/Core/DispatcherTypeTests.swift | 147 +++++++++++++++------------ 1 file changed, 80 insertions(+), 67 deletions(-) diff --git a/Tests/Core/DispatcherTypeTests.swift b/Tests/Core/DispatcherTypeTests.swift index a9aecceb3..7c0f762f2 100644 --- a/Tests/Core/DispatcherTypeTests.swift +++ b/Tests/Core/DispatcherTypeTests.swift @@ -2,7 +2,7 @@ import Dispatch @testable import PromiseKit import XCTest -class DispatcherTypeTests: XCTestCase { +class DispatcherTestBase: XCTestCase { lazy var scenarios = generateRateLimitScenarios() var rng = Xoroshiro(0x80D0082B8A9651BA, 0x49A8092CFD464A11) // Arbitrary seed @@ -17,19 +17,6 @@ class DispatcherTypeTests: XCTestCase { let delays: [UInt32] } - func testRateLimitedDispatcher() { - for scenario in scenarios { - printScenarioDetails(scenario) - let dispatcher = RateLimitedDispatcher(maxDispatches: scenario.maxDispatches, perInterval: scenario.interval) - let (deltaT, mostConcurrent) = rateLimitTest(dispatcher, delays: scenario.delays, interval: scenario.interval) - // For the nonstrict RateLimitedDispatcher, burst rate may be up to 2X the goal. - XCTAssertLessThanOrEqual(mostConcurrent, scenario.maxDispatches * 2) - // Significantly under the goal rate is also a concern - XCTAssertGreaterThan(mostConcurrent, (scenario.maxDispatches * 3) / 4) - printTestResults(deltaT, mostConcurrent, scenario) - } - } - func printScenarioDetails(_ scenario: RateLimitScenario) { guard debug else { return } print("\nNew run: n = \(scenario.delays.count), most = \(scenario.maxDispatches),", @@ -42,59 +29,6 @@ class DispatcherTypeTests: XCTestCase { let rateAvg = Double(scenario.delays.count) * scenario.interval / deltaT print("result actual max = \(concurrent), target max = \(scenario.maxDispatches), average rate = \(rateAvg)") } - - func testStrictRateLimitedDispatcher() { - for scenario in scenarios { - printScenarioDetails(scenario) - let dispatcher = StrictRateLimitedDispatcher(maxDispatches: scenario.maxDispatches, perInterval: scenario.interval) - let (deltaT, mostConcurrent) = rateLimitTest(dispatcher, delays: scenario.delays, interval: scenario.interval) - XCTAssertLessThanOrEqual(mostConcurrent, scenario.maxDispatches) - // Significantly under the goal rate is also a concern - XCTAssertGreaterThan(mostConcurrent, (scenario.maxDispatches * 3) / 4) - printTestResults(deltaT, mostConcurrent, scenario) - // print("tail wait start", DispatchTime.now().rawValue) - usleep(UInt32(scenario.interval * 1_000_000 * 1.25)) - // print("tail wait end", DispatchTime.now().rawValue) - XCTAssert(dispatcher.startTimeHistory.count == 0, "Dispatcher did not clean up properly") - } - } - - func testConcurrencyLimitedDispatcher() { - - for scenario in scenarios { - - printScenarioDetails(scenario) - let dispatcher = ConcurrencyLimitedDispatcher(limit: scenario.maxDispatches) - - var nConcurrent = 0 - var maxNConcurrent = 0 - var nRun = 0 - let serializer = DispatchQueue(label: "Concurrency test") - let ex = expectation(description: "Concurrency limit") - - for delay in scenario.delays { - usleep(delay) - Guarantee.value(42).done(on: dispatcher) { _ in - serializer.sync { - nConcurrent += 1 - maxNConcurrent = max(maxNConcurrent, nConcurrent) - } - usleep(UInt32.random(in: 10_000...100_000, using: &self.rng)) - serializer.sync { - nConcurrent -= 1 - nRun += 1 - if nRun == scenario.delays.count { - ex.fulfill() - } - } - } - } - - waitForExpectations(timeout: Double(scenario.delays.count) * 0.1) - XCTAssertEqual(maxNConcurrent, scenario.maxDispatches) - - } - } func generateRateLimitScenarios() -> [RateLimitScenario] { @@ -187,6 +121,85 @@ class DispatcherTypeTests: XCTestCase { } +class RateLimitTests: DispatcherTestBase { + + func testRateLimitedDispatcher() { + for scenario in scenarios { + printScenarioDetails(scenario) + let dispatcher = RateLimitedDispatcher(maxDispatches: scenario.maxDispatches, perInterval: scenario.interval) + let (deltaT, mostConcurrent) = rateLimitTest(dispatcher, delays: scenario.delays, interval: scenario.interval) + // For the nonstrict RateLimitedDispatcher, burst rate may be up to 2X the goal. + XCTAssertLessThanOrEqual(mostConcurrent, scenario.maxDispatches * 2) + // Significantly under the goal rate is also a concern + XCTAssertGreaterThan(mostConcurrent, (scenario.maxDispatches * 3) / 4) + printTestResults(deltaT, mostConcurrent, scenario) + } + } + +} + +class StrictRateLimitTests: DispatcherTestBase { + + func testStrictRateLimitedDispatcher() { + for scenario in scenarios { + printScenarioDetails(scenario) + let dispatcher = StrictRateLimitedDispatcher(maxDispatches: scenario.maxDispatches, perInterval: scenario.interval) + let (deltaT, mostConcurrent) = rateLimitTest(dispatcher, delays: scenario.delays, interval: scenario.interval) + XCTAssertLessThanOrEqual(mostConcurrent, scenario.maxDispatches) + // Significantly under the goal rate is also a concern + XCTAssertGreaterThan(mostConcurrent, (scenario.maxDispatches * 3) / 4) + printTestResults(deltaT, mostConcurrent, scenario) + // print("tail wait start", DispatchTime.now().rawValue) + usleep(UInt32(scenario.interval * 1_000_000 * 1.25)) + // print("tail wait end", DispatchTime.now().rawValue) + XCTAssert(dispatcher.startTimeHistory.count == 0, "Dispatcher did not clean up properly") + } + } + +} + +class ConcurrencyLimitTests: DispatcherTestBase { + + func testConcurrencyLimitedDispatcher() { + + for scenario in scenarios { + + printScenarioDetails(scenario) + let dispatcher = ConcurrencyLimitedDispatcher(limit: scenario.maxDispatches) + + var nConcurrent = 0 + var maxNConcurrent = 0 + var nRun = 0 + let serializer = DispatchQueue(label: "Concurrency test") + let ex = expectation(description: "Concurrency limit") + + for delay in scenario.delays { + usleep(delay) + Guarantee.value(42).done(on: dispatcher) { _ in + serializer.sync { + nConcurrent += 1 + maxNConcurrent = max(maxNConcurrent, nConcurrent) + } + usleep(UInt32.random(in: 10_000...100_000, using: &self.rng)) + serializer.sync { + nConcurrent -= 1 + nRun += 1 + if nRun == scenario.delays.count { + ex.fulfill() + } + } + } + } + + waitForExpectations(timeout: Double(scenario.delays.count) * 0.1) + XCTAssertEqual(maxNConcurrent, scenario.maxDispatches) + + } + } + +} + + // Reproducible, seedable RNG struct Xoroshiro: RandomNumberGenerator { From 442a6181df9b85d38ac576f14090e3299bf8fa33 Mon Sep 17 00:00:00 2001 From: Garth Snyder Date: Fri, 15 Feb 2019 21:36:01 -0800 Subject: [PATCH 13/81] Move time measurement outside of serialization for rate limit test --- Tests/Core/DispatcherTypeTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Core/DispatcherTypeTests.swift b/Tests/Core/DispatcherTypeTests.swift index 7c0f762f2..5f0cf3a42 100644 --- a/Tests/Core/DispatcherTypeTests.swift +++ b/Tests/Core/DispatcherTypeTests.swift @@ -88,8 +88,8 @@ class DispatcherTestBase: XCTestCase { for delay in delays { usleep(delay) Guarantee.value(42).done(on: dispatcher) { _ in + let now = DispatchTime.now() serializer.sync { - let now = DispatchTime.now() closureStartTimes.append(now) if closureStartTimes.count == delays.count { ex.fulfill() From d963bde01a49998207d2955da40ec276614bd880 Mon Sep 17 00:00:00 2001 From: Garth Snyder Date: Fri, 22 Feb 2019 22:51:43 -0800 Subject: [PATCH 14/81] Update testing for Linux and slow Travis environments --- Tests/A+/LinuxMain.swift | 12 ----- Tests/Core/DispatcherTypeTests.swift | 72 +++++++++++++++++++++------- Tests/Core/XCTestManifests.swift | 44 +++++++++++++++++ Tests/LinuxMain.swift | 10 ++-- 4 files changed, 104 insertions(+), 34 deletions(-) delete mode 100644 Tests/A+/LinuxMain.swift diff --git a/Tests/A+/LinuxMain.swift b/Tests/A+/LinuxMain.swift deleted file mode 100644 index 3ccd10feb..000000000 --- a/Tests/A+/LinuxMain.swift +++ /dev/null @@ -1,12 +0,0 @@ -import XCTest - -import A__js -import A__swift -import Core - -var tests = [XCTestCaseEntry]() -tests += A__js.__allTests() -tests += A__swift.__allTests() -tests += Core.__allTests() - -XCTMain(tests) diff --git a/Tests/Core/DispatcherTypeTests.swift b/Tests/Core/DispatcherTypeTests.swift index 5f0cf3a42..dcf2b5e2f 100644 --- a/Tests/Core/DispatcherTypeTests.swift +++ b/Tests/Core/DispatcherTypeTests.swift @@ -4,9 +4,40 @@ import XCTest class DispatcherTestBase: XCTestCase { - lazy var scenarios = generateRateLimitScenarios() + struct ScenarioParameters { + let hiatusLikelihoods: [Double] + let noDelayLikelihoods: [Double] + let intervals: [Double] + let dispatches: [Int] + } + + let standardParams = ScenarioParameters( + hiatusLikelihoods: [ 0.3 ], + noDelayLikelihoods: [ 0.75 ], + intervals: [ 0.02, 0.1 ], + dispatches: [ 20 ] + ) + + // Low-CPU, low-parallelism test environment + let travisParams = ScenarioParameters( + hiatusLikelihoods: [ 0.3 ], + noDelayLikelihoods: [ 0.75 ], + intervals: [ 0.1, 0.3 ], + dispatches: [ 10 ] + ) + + // More thorough testing, but takes longer to run + let tortureParams = ScenarioParameters( + hiatusLikelihoods: [ 0.0, 0.3, 0.7 ], + noDelayLikelihoods: [ 0.3, 0.75, 1.0 ], + intervals: [ 0.02, 0.1 ], + dispatches: [ 20 ] + ) + + lazy var scenarios = generateRateLimitScenarios(travisParams) var rng = Xoroshiro(0x80D0082B8A9651BA, 0x49A8092CFD464A11) // Arbitrary seed let debug = false + let laxity = 1 // For Travis and low-parallelism environments, elsewhere use 0 struct RateLimitScenario { let maxDispatches: Int @@ -30,20 +61,15 @@ class DispatcherTestBase: XCTestCase { print("result actual max = \(concurrent), target max = \(scenario.maxDispatches), average rate = \(rateAvg)") } - func generateRateLimitScenarios() -> [RateLimitScenario] { + func generateRateLimitScenarios(_ params: ScenarioParameters) -> [RateLimitScenario] { var rng = Xoroshiro(0x80D0082B8A9651BA, 0x49A8092CFD464A11) // Arbitrary seed var scenarios: [RateLimitScenario] = [] - // for hiatusLikelihoodPerInterval in [ 0.0, 0.3, 0.7 ] { - // for noDelayLikelihood in [ 0.0, 0.2, 0.75 ] { - // for interval in [ 0.02, 0.1, 1.0 ] { - // for maxDispatches in [ 1, 2, 5, 20 ] { - - for hiatusLikelihoodPerInterval in [ 0.3 ] { - for noDelayLikelihood in [ 0.75 ] { - for interval in [ 0.02, 0.1 ] { - for maxDispatches in [ 20 ] { + for hiatusLikelihoodPerInterval in params.hiatusLikelihoods { + for noDelayLikelihood in params.noDelayLikelihoods { + for interval in params.intervals { + for maxDispatches in params.dispatches { // <------------ @@ -64,8 +90,8 @@ class DispatcherTestBase: XCTestCase { delays.append(normalDelayRange.randomElement(using: &rng)!) } } - - let nHiatuses = delays.count { $0 > avgSlice } + + let nHiatuses = delays.reduce(0) { $1 > avgSlice ? $0 + 1 : $0 } scenarios.append(RateLimitScenario(maxDispatches: maxDispatches, interval: interval, hiatusLikelihood: hiatusLikelihoodPerInterval, nHiatuses: nHiatuses, @@ -129,7 +155,11 @@ class RateLimitTests: DispatcherTestBase { let dispatcher = RateLimitedDispatcher(maxDispatches: scenario.maxDispatches, perInterval: scenario.interval) let (deltaT, mostConcurrent) = rateLimitTest(dispatcher, delays: scenario.delays, interval: scenario.interval) // For the nonstrict RateLimitedDispatcher, burst rate may be up to 2X the goal. - XCTAssertLessThanOrEqual(mostConcurrent, scenario.maxDispatches * 2) + // There is, unavoidably, a potential lag between the time a closure is dispatched and the + // time it actually starts to run and has its start-time measured. This redistribution in time + // makes it impossible to verify rates with perfect accuracy because of bunching. For desktop + // testing the issue essentially never occurs and laxity should be set to 0. + XCTAssertLessThanOrEqual(mostConcurrent, scenario.maxDispatches * 2 + laxity) // Significantly under the goal rate is also a concern XCTAssertGreaterThan(mostConcurrent, (scenario.maxDispatches * 3) / 4) printTestResults(deltaT, mostConcurrent, scenario) @@ -145,12 +175,16 @@ class StrictRateLimitTests: DispatcherTestBase { printScenarioDetails(scenario) let dispatcher = StrictRateLimitedDispatcher(maxDispatches: scenario.maxDispatches, perInterval: scenario.interval) let (deltaT, mostConcurrent) = rateLimitTest(dispatcher, delays: scenario.delays, interval: scenario.interval) - XCTAssertLessThanOrEqual(mostConcurrent, scenario.maxDispatches) + // There is, unavoidably, a potential lag between the time a closure is dispatched and the + // time it actually starts to run and has its start-time measured. This redistribution in time + // makes it impossible to verify rates with perfect accuracy because of bunching. For desktop + // testing the issue essentially never occurs and laxity should be set to 0. + XCTAssertLessThanOrEqual(mostConcurrent, scenario.maxDispatches + laxity) // Significantly under the goal rate is also a concern XCTAssertGreaterThan(mostConcurrent, (scenario.maxDispatches * 3) / 4) printTestResults(deltaT, mostConcurrent, scenario) // print("tail wait start", DispatchTime.now().rawValue) - usleep(UInt32(scenario.interval * 1_000_000 * 1.25)) + usleep(UInt32(scenario.interval * 1_000_000 * 2)) // print("tail wait end", DispatchTime.now().rawValue) XCTAssert(dispatcher.startTimeHistory.count == 0, "Dispatcher did not clean up properly") } @@ -192,8 +226,10 @@ class ConcurrencyLimitTests: DispatcherTestBase { } waitForExpectations(timeout: Double(scenario.delays.count) * 0.1) - XCTAssertEqual(maxNConcurrent, scenario.maxDispatches) - + // Usually maxNConcurrent will == target, but some platforms have inherent limits on parallelism, at least in test + XCTAssertLessThanOrEqual(maxNConcurrent, scenario.maxDispatches, "More concurrent tasks than allowed") + XCTAssertGreaterThanOrEqual(maxNConcurrent, 2, "Concurrent executions not concurrent") + } } diff --git a/Tests/Core/XCTestManifests.swift b/Tests/Core/XCTestManifests.swift index 0cf85fa37..985e3860d 100644 --- a/Tests/Core/XCTestManifests.swift +++ b/Tests/Core/XCTestManifests.swift @@ -50,6 +50,28 @@ extension CatchableTests { ] } +extension ConcurrencyLimitTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__ConcurrencyLimitTests = [ + ("testConcurrencyLimitedDispatcher", testConcurrencyLimitedDispatcher), + ] +} + +extension DispatcherTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__DispatcherTests = [ + ("testConfD", testConfD), + ("testDispatcherExtensionCanThrowInBody", testDispatcherExtensionCanThrowInBody), + ("testDispatcherExtensionReturnsGuarantee", testDispatcherExtensionReturnsGuarantee), + ("testDispatcherWithThrow", testDispatcherWithThrow), + ("testDispatchQueueSelection", testDispatchQueueSelection), + ] +} + extension GuaranteeTests { // DO NOT MODIFY: This is autogenerated, use: // `swift test --generate-linuxmain` @@ -150,6 +172,15 @@ extension RaceTests { ] } +extension RateLimitTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__RateLimitTests = [ + ("testRateLimitedDispatcher", testRateLimitedDispatcher), + ] +} + extension RegressionTests { // DO NOT MODIFY: This is autogenerated, use: // `swift test --generate-linuxmain` @@ -170,6 +201,15 @@ extension StressTests { ] } +extension StrictRateLimitTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__StrictRateLimitTests = [ + ("testStrictRateLimitedDispatcher", testStrictRateLimitedDispatcher), + ] +} + extension ThenableTests { // DO NOT MODIFY: This is autogenerated, use: // `swift test --generate-linuxmain` @@ -261,6 +301,8 @@ public func __allTests() -> [XCTestCaseEntry] { testCase(AfterTests.__allTests__AfterTests), testCase(CancellationTests.__allTests__CancellationTests), testCase(CatchableTests.__allTests__CatchableTests), + testCase(ConcurrencyLimitTests.__allTests__ConcurrencyLimitTests), + testCase(DispatcherTests.__allTests__DispatcherTests), testCase(GuaranteeTests.__allTests__GuaranteeTests), testCase(HangTests.__allTests__HangTests), testCase(JoinTests.__allTests__JoinTests), @@ -269,8 +311,10 @@ public func __allTests() -> [XCTestCaseEntry] { testCase(PMKErrorTests.__allTests__PMKErrorTests), testCase(PromiseTests.__allTests__PromiseTests), testCase(RaceTests.__allTests__RaceTests), + testCase(RateLimitTests.__allTests__RateLimitTests), testCase(RegressionTests.__allTests__RegressionTests), testCase(StressTests.__allTests__StressTests), + testCase(StrictRateLimitTests.__allTests__StrictRateLimitTests), testCase(ThenableTests.__allTests__ThenableTests), testCase(WhenConcurrentTestCase_Swift.__allTests__WhenConcurrentTestCase_Swift), testCase(WhenTests.__allTests__WhenTests), diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index a91a609ff..3ccd10feb 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -1,10 +1,12 @@ import XCTest -import A_ -import CorePromise +import A__js +import A__swift +import Core var tests = [XCTestCaseEntry]() -tests += A_.__allTests() -tests += CorePromise.__allTests() +tests += A__js.__allTests() +tests += A__swift.__allTests() +tests += Core.__allTests() XCTMain(tests) From aa473f1cea46c73e48ccfc55ba5f69e1a19028d7 Mon Sep 17 00:00:00 2001 From: Garth Snyder Date: Fri, 22 Feb 2019 22:47:43 -0800 Subject: [PATCH 15/81] Build all v7 branches --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7926b0438..32dc73c48 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ osx_image: xcode10.2 branches: only: - - v7 + - /^v7/ - master - v6 - v4 From 7038ecc4de2ff9dd2f8e70e848c1263b9a6a95f7 Mon Sep 17 00:00:00 2001 From: Max Howell Date: Thu, 28 Feb 2019 18:52:14 -0500 Subject: [PATCH 16/81] Use my deploy script --- .travis.yml | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/.travis.yml b/.travis.yml index 32dc73c48..42510fc62 100644 --- a/.travis.yml +++ b/.travis.yml @@ -63,20 +63,15 @@ jobs: - Tests/JS-A+/build - Tests/JS-A+/node_modules - - name: '`pod trunk push`' - stage: deploy - install: gem install cocoapods --pre -v 1.7.0.beta.3 - before_script: | - mv .github/PromiseKit.podspec . - sed -i '' "s/s.version = '0.0.1'/s.version = '$TRAVIS_TAG'/g" PromiseKit.podspec - script: | - set -exo pipefail - pod trunk push --verbose --allow-warnings | tee pod.log | ruby -e 'ARGF.each{ print "." }' - # ^^ pipe because Travis times us out if there is no output - # AND `pod` defaults to hardly any output - # BUT `--verbose` generates so much output that Travis kills our script due to *too much* output! - # --allow-warnings because Bolts generates warnings and CocoaPods fails you even if your deps emit warnings - after_failure: cat pod.log | grep error + - stage: deploy + before_install: | + curl -O https://raw.githubusercontent.com/mxcl/Path.swift/master/.github/deploy + chmod u+x deploy + install: brew install mxcl/made/swift-sh + before_script: ./deploy generate-podspec + script: pod trunk push --allow-warnings + after_success: | + ./deploy publish-release - name: Generate Documentation git.depth: false From 9ca62ff50512173805560d18e4b8c134aeea6342 Mon Sep 17 00:00:00 2001 From: Max Howell Date: Fri, 1 Mar 2019 05:09:46 +0000 Subject: [PATCH 17/81] [ci] Spell check markdown --- .github/spelling-skip-words | 70 +++++++++++++++++++++++++++++++++++++ .travis.yml | 9 +++++ README.md | 2 +- 3 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 .github/spelling-skip-words diff --git a/.github/spelling-skip-words b/.github/spelling-skip-words new file mode 100644 index 000000000..8e48a9eca --- /dev/null +++ b/.github/spelling-skip-words @@ -0,0 +1,70 @@ +PromiseKit +NSError +ObjC +AnyPromise +AnyPromises +iOS +Xcode +CocoaPods +Submodules +Agostini.tech +1.x +2.x +3.x +4.x +5.x +6.x +7.x +8.x +9.x +10.x +Alamofire +APIs +PMKConfiguration +Kitura +RxSwift +Zalgo +macOS +watchOS +CoreLocation +CancellablePromiseKit +UIKit +tvOS +SDKs +hair-raisingly +deallocate +deallocated +initializer +initializers +work-arounds +i.e. +e.g. +SwiftPM +CorePromise +submodules +CorePromise +tuplegate +Swift-ese +codebase +subspecs +finalizers +ReactiveSwift +asynchronicity +composable +presentee +Optionals +StackOverflow +backtrace +backport +reframe +runtime +_ +misinference +Podfile +Xcodes +TideLift +Gitter +top-100 +CocoaPod +conformant +PromiseKits diff --git a/.travis.yml b/.travis.yml index 42510fc62..4ec9e78d9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,7 @@ branches: stages: - name: pretest + - name: lint - name: test - name: deploy if: branch =~ ^\d+\.\d+\.\d+$ @@ -63,6 +64,14 @@ jobs: - Tests/JS-A+/build - Tests/JS-A+/node_modules + - stage: lint + name: Spell Check Markdown + install: npm install markdown-spellcheck --global + before_script: mv .github/spelling-skip-words .spelling + script: mdspell -r -n -a --en-us *.md **/*.md + os: linux + language: generic + - stage: deploy before_install: | curl -O https://raw.githubusercontent.com/mxcl/Path.swift/master/.github/deploy diff --git a/README.md b/README.md index a2563f76a..683b39f7f 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ pod used in many of the most popular apps in the world. # PromiseKit 7 Alpha -PromiseKit 7 is pre-release, if you’re using it: beware! +PromiseKit 7 is prerelease, if you’re using it: beware! PromiseKit 7 uses Swift 5’s `Result`, PromiseKit <7 use our own `Result` type. From 2b90cb3d4ef00176d9156f3648c9b30e8cad7d8e Mon Sep 17 00:00:00 2001 From: Doug Date: Mon, 18 Mar 2019 19:10:28 -0700 Subject: [PATCH 18/81] 'Cancel' for PromiseKit (#899) This patch adds the concept of a `CancelContext` to PromiseKit allowing chains to handle cancellation properly for each independent promise after the cancel point. --- .github/spelling-skip-words | 59 +++ Documents/Cancel.md | 464 +++++++++++++++++ Documents/CommonPatterns.md | 81 ++- Documents/README.md | 1 + Documents/Troubleshooting.md | 78 +++ Package.swift | 1 + README.md | 3 + Sources/CancelContext.swift | 257 +++++++++ Sources/CancellableCatchable.swift | 301 +++++++++++ Sources/CancellablePromise.swift | 143 +++++ Sources/CancellableTask.swift | 12 + Sources/CancellableThenable.swift | 520 +++++++++++++++++++ Sources/Dispatcher.swift | 478 +++++++++++++++++ Sources/Error.swift | 9 +- Sources/Guarantee.swift | 40 +- Sources/Promise.swift | 21 + Sources/Thenable.swift | 12 + Sources/after.swift | 16 +- Sources/firstly.swift | 50 ++ Sources/hang.swift | 11 + Sources/race.swift | 87 ++++ Sources/when.swift | 229 +++++++- Tests/Cancel/AfterTests.swift | 121 +++++ Tests/Cancel/CancelChain.swift | 246 +++++++++ Tests/Cancel/CancellableErrorTests.swift | 136 +++++ Tests/Cancel/CancellablePromiseTests.swift | 205 ++++++++ Tests/Cancel/CatchableTests.swift | 395 ++++++++++++++ Tests/Cancel/DefaultDispatchQueueTests.swift | 74 +++ Tests/Cancel/DispatcherTests.swift | 205 ++++++++ Tests/Cancel/ErrorTests.swift | 12 + Tests/Cancel/GuaranteeTests.swift | 119 +++++ Tests/Cancel/HangTests.swift | 48 ++ Tests/Cancel/PromiseTests.swift | 286 ++++++++++ Tests/Cancel/RaceTests.swift | 107 ++++ Tests/Cancel/RegressionTests.swift | 57 ++ Tests/Cancel/ResolverTests.swift | 340 ++++++++++++ Tests/Cancel/StressTests.swift | 173 ++++++ Tests/Cancel/ThenableTests.swift | 164 ++++++ Tests/Cancel/TimeoutTests.swift | 125 +++++ Tests/Cancel/Utilities.swift | 40 ++ Tests/Cancel/ValueTests.swift | 139 +++++ Tests/Cancel/WhenConcurrentTests.swift | 275 ++++++++++ Tests/Cancel/WhenResolvedTests.swift | 69 +++ Tests/Cancel/WhenTests.swift | 348 +++++++++++++ Tests/Cancel/XCTestManifests.swift | 380 ++++++++++++++ Tests/Cancel/ZalgoTests.swift | 89 ++++ Tests/Core/DispatcherTests.swift | 77 +++ Tests/Core/RaceTests.swift | 10 + Tests/Core/XCTestManifests.swift | 3 + Tests/LinuxMain.swift | 2 + 50 files changed, 7062 insertions(+), 56 deletions(-) create mode 100644 Documents/Cancel.md create mode 100644 Sources/CancelContext.swift create mode 100644 Sources/CancellableCatchable.swift create mode 100644 Sources/CancellablePromise.swift create mode 100644 Sources/CancellableTask.swift create mode 100644 Sources/CancellableThenable.swift create mode 100644 Tests/Cancel/AfterTests.swift create mode 100644 Tests/Cancel/CancelChain.swift create mode 100644 Tests/Cancel/CancellableErrorTests.swift create mode 100644 Tests/Cancel/CancellablePromiseTests.swift create mode 100644 Tests/Cancel/CatchableTests.swift create mode 100644 Tests/Cancel/DefaultDispatchQueueTests.swift create mode 100644 Tests/Cancel/DispatcherTests.swift create mode 100644 Tests/Cancel/ErrorTests.swift create mode 100644 Tests/Cancel/GuaranteeTests.swift create mode 100644 Tests/Cancel/HangTests.swift create mode 100644 Tests/Cancel/PromiseTests.swift create mode 100644 Tests/Cancel/RaceTests.swift create mode 100644 Tests/Cancel/RegressionTests.swift create mode 100644 Tests/Cancel/ResolverTests.swift create mode 100644 Tests/Cancel/StressTests.swift create mode 100644 Tests/Cancel/ThenableTests.swift create mode 100644 Tests/Cancel/TimeoutTests.swift create mode 100644 Tests/Cancel/Utilities.swift create mode 100644 Tests/Cancel/ValueTests.swift create mode 100644 Tests/Cancel/WhenConcurrentTests.swift create mode 100644 Tests/Cancel/WhenResolvedTests.swift create mode 100644 Tests/Cancel/WhenTests.swift create mode 100644 Tests/Cancel/XCTestManifests.swift create mode 100644 Tests/Cancel/ZalgoTests.swift diff --git a/.github/spelling-skip-words b/.github/spelling-skip-words index 8e48a9eca..f0f87c808 100644 --- a/.github/spelling-skip-words +++ b/.github/spelling-skip-words @@ -68,3 +68,62 @@ top-100 CocoaPod conformant PromiseKits +CancellablePromise +CancellableThenable +thenable +CancellableCatchable +catchable +cancellableRecover +ensureThen +Alamofire.request +responseDecodable +DecodableObject.self +BFTask +CLLocationManager.requestLocation +URLSession.shared.dataTask +MapKit +MKDirections +URLSession.shared.GET +StoreKit +SKProductsRequest +SystemConfiguration +SCNetworkReachability.promise +UIViewPropertyAnimator +startAnimation +Alamofire.DataRequest +responseData +responseString +responseJSON +responsePropertyList +Alamofire.DownloadRequest +CLLocationManager +requestLocation +authorizationType +requestAuthorization +requestedAuthorizationType +NotificationCenter +NSObject +keyPath +URLSession +dataTask +uploadTask +fromFile +downloadTask +HomeKit +HMPromiseAccessoryBrowser +scanInterval +HMHomeManager +calculateETA +MKMapSnapshotter +SKReceiptRefreshRequest +SCNetworkReachability +Cancellability +HealthKit +enum +cancellize +UIImage +PMKFinalizercancelthendonePromise +VoidPending +PromiseURLSessionresumewait +unusedResult +discardableResultcatchreturncauterize diff --git a/Documents/Cancel.md b/Documents/Cancel.md new file mode 100644 index 000000000..2811019cc --- /dev/null +++ b/Documents/Cancel.md @@ -0,0 +1,464 @@ +# Cancelling Promises + +PromiseKit 7 adds clear and concise cancellation abilities to promises and to the [PromiseKit extensions](#extensions-pane). Cancelling promises and their associated tasks is now simple and straightforward. Promises and promise chains can safely and efficiently be cancelled from any thread at any time. + +```swift +UIApplication.shared.isNetworkActivityIndicatorVisible = true + +let fetchImage = URLSession.shared.dataTask(.promise, with: url).cancellize().compactMap{ UIImage(data: $0.data) } +let fetchLocation = CLLocationManager.requestLocation().cancellize().lastValue + +let finalizer = firstly { + when(fulfilled: fetchImage, fetchLocation) +}.done { image, location in + self.imageView.image = image + self.label.text = "\(location)" +}.ensure { + UIApplication.shared.isNetworkActivityIndicatorVisible = false +}.catch(policy: .allErrors) { error in + /* 'catch' will be invoked with 'PMKError.cancelled' when cancel is called on the context. + Use the default policy of '.allErrorsExceptCancellation' to ignore cancellation errors. */ + self.show(UIAlertController(for: error), sender: self) +} + +//… + +// Cancel currently active tasks and reject all cancellable promises with 'PMKError.cancelled'. +// 'cancel()' can be called from any thread at any time. +finalizer.cancel() + +/* 'finalizer' here refers to the 'CancellableFinalizer' for the chain. Calling 'cancel' on + any promise in the chain or on the finalizer cancels the entire chain. Therefore + calling 'cancel' on the finalizer cancels everything. */ +``` + +# Cancel Chains + +Promises can be cancelled using a `CancellablePromise`. The `cancellize()` method on `Promise` is used to convert a `Promise` into a `CancellablePromise`. If a promise chain is initialized with a `CancellablePromise`, then the entire chain is cancellable. Calling `cancel()` on any promise in the chain cancels the entire chain. + +Creating a chain where the entire chain can be cancelled is the recommended usage for cancellable promises. + +The `CancellablePromise` contains a `CancelContext` that keeps track of the tasks and promises for the chain. Promise chains can be cancelled either by calling the `cancel()` method on any `CancellablePromise` in the chain, or by calling `cancel()` on the `CancelContext` for the chain. It may be desirable to hold on to the `CancelContext` directly rather than a promise so that the promise can be deallocated by ARC when it is resolved. + +For example: + +```swift +let context = firstly { + login() + /* The 'Thenable.cancellize' method initiates a cancellable promise chain by + returning a 'CancellablePromise'. */ +}.cancellize().then { creds in + fetch(avatar: creds.user) +}.done { image in + self.imageView = image +}.catch(policy: .allErrors) { error in + if error.isCancelled { + // the chain has been cancelled! + } +}.cancelContext + +// … + +/* Note: Promises can be cancelled using the 'cancel()' method on the 'CancellablePromise'. + However, it may be desirable to hold on to the 'CancelContext' directly rather than a + promise so that the promise can be deallocated by ARC when it is resolved. */ +context.cancel() +``` + +### Creating a partially cancellable chain + +A `CancellablePromise` can be placed at the start of a chain, but it cannot be embedded directly in the middle of a standard (non-cancellable) promise chain. Instead, a partially cancellable promise chain can be used. A partially cancellable chain is not the recommended way to use cancellable promises, although there may be cases where this is useful. + +**Convert a cancellable chain to a standard chain** + +`CancellablePromise` wraps a delegate `Promise`, which can be accessed with the `promise` property. The above example can be modified as follows so that once `login()` completes, the chain can no longer be cancelled: + +```swift +/* Here, by calling 'promise.then' rather than 'then' the chain is converted from a cancellable + promise chain to a standard promise chain. In this example, calling 'cancel()' during 'login' + will cancel the chain but calling 'cancel()' during the 'fetch' operation will have no effect: */ +let cancellablePromise = firstly { + login().cancellize() +} +cancellablePromise.promise.then { + fetch(avatar: creds.user) +}.done { image in + self.imageView = image +}.catch(policy: .allErrors) { error in + if error.isCancelled { + // the chain has been cancelled! + } +} + +// … + +/* This will cancel the 'login' but will not cancel the 'fetch'. So whether or not the + chain is cancelled depends on how far the chain has progressed. */ +cancellablePromise.cancel() +``` + +**Convert a standard chain to a cancellable chain** + +A non-cancellable chain can be converted to a cancellable chain in the middle of the chain as follows: + +```swift +/* In this example, calling 'cancel()' during 'login' will not cancel the login. However, + the chain will be cancelled immediately, and the 'fetch' will not be executed. If 'cancel()' + is called during the 'fetch' then both the 'fetch' itself and the promise chain will be + cancelled immediately. */ +let promise = firstly { + login() +}.then { + fetch(avatar: creds.user).cancellize() +}.done { image in + self.imageView = image +}.catch(policy: .allErrors) { error in + if error.isCancelled { + // the chain has been cancelled! + } +} + +// … + +promise.cancel() +``` + +# Core Cancellable PromiseKit API + +The following classes, methods and functions have been added to PromiseKit to support cancellation. Existing functions or methods with underlying tasks that can be cancelled are indicated by being appended with '.cancellize()'. + +
Thenable
+    cancellize(_:)                 - Converts the Promise or Guarantee (Thenable) into a
+                                     CancellablePromise, which is a cancellable variant of the given
+                                     Promise or Guarantee (Thenable)
+
+Global functions
+    after(seconds:).cancellize()   - 'after' with seconds can be cancelled
+    after(_:).cancellize           - 'after' with interval can be cancelled
+
+    firstly(execute:)               - Accepts body returning Promise or CancellablePromise
+    hang(_:)                        - Accepts Promise and CancellablePromise
+    race(_:)                        - Accepts [Promise] and [CancellablePromise]
+    when(fulfilled:)                - Accepts [Promise] and [CancellablePromise]
+    when(fulfilled:concurrently:)   - Accepts iterator of type Promise or CancellablePromise
+    when(resolved:)                 - Accepts [Promise] and [CancellablePromise]
+
+CancellablePromise properties and methods
+    promise                         - Delegate Promise for this CancellablePromise
+    result                          - The current Result
+
+    init(_ bridge:cancelContext:)   - Initialize a new cancellable promise bound to the provided Thenable
+    init(cancellable:resolver body:).  - Initialize a new cancellable promise that can be resolved with
+                                       the provided '(Resolver) throws -> Void' body
+    init(cancellable:promise:resolver:)  - Initialize a new cancellable promise using the given Promise
+                                       and its Resolver
+    init(cancellable:error:)          - Initialize a new rejected cancellable promise
+    init(cancellable:)                - Initializes a new cancellable promise fulfilled with Void
+
+    pending() -> (promise:resolver:)  - Returns a tuple of a new cancellable pending promise and its
+                                        Resolver
+
+CancellableThenable properties and methods
+    thenable                        - Delegate Thenable for this CancellableThenable
+
+    cancel(error:)                  - Cancels all members of the promise chain
+    cancelContext                   - The CancelContext associated with this CancellableThenable
+    cancelItemList                  - Tracks the cancel items for this CancellableThenable
+    isCancelled                     - True if all members of the promise chain have been successfully
+                                      cancelled, false otherwise
+    cancelAttempted                 - True if 'cancel' has been called on the promise chain associated
+                                      with this CancellableThenable, false otherwise
+    cancelledError                  - The error generated when the promise is cancelled
+    appendCancellable(cancellable:reject:)  - Append the Cancellable task to our cancel context
+    appendCancelContext(from:)      - Append the cancel context associated with 'from' to our
+                                      CancelContext
+
+    then(on:flags:_ body:)           - Accepts body returning CancellableThenable
+    cancellableThen(on:flags:_ body:)  - Accepts body returning Thenable
+    map(on:flags:_ transform:)
+    compactMap(on:flags:_ transform:)
+    done(on:flags:_ body:)
+    get(on:flags:_ body:)
+    tap(on:flags:_ body:)
+    asVoid()
+
+    error
+    isPending
+    isResolved
+    isFulfilled
+    isRejected
+    value
+
+    mapValues(on:flags:_ transform:)
+    flatMapValues(on:flags:_ transform:)
+    compactMapValues(on:flags:_ transform:)
+    thenMap(on:flags:_ transform:)                 - Accepts transform returning CancellableThenable
+    cancellableThenMap(on:flags:_ transform:)      - Accepts transform returning Thenable
+    thenFlatMap(on:flags:_ transform:)             - Accepts transform returning CancellableThenable
+    cancellableThenFlatMap(on:flags:_ transform:)  - Accepts transform returning Thenable
+    filterValues(on:flags:_ isIncluded:)
+    firstValue
+    lastValue
+    sortedValues(on:flags:)
+
+CancellableCatchable properties and methods
+    catchable                                      - Delegate Catchable for this CancellableCatchable
+    catch(on:flags:policy::_ body:)                - Accepts body returning Void
+    recover(on:flags:policy::_ body:)              - Accepts body returning CancellableThenable
+    cancellableRecover(on:flags:policy::_ body:)   - Accepts body returning Thenable
+    ensure(on:flags:_ body:)                       - Accepts body returning Void
+    ensureThen(on:flags:_ body:)                   - Accepts body returning CancellablePromise
+    finally(_ body:)
+    cauterize()
+
+ +# Extensions + +Cancellation support has been added to the PromiseKit extensions, but only where the underlying asynchronous tasks can be cancelled. This example Podfile lists the PromiseKit extensions that support cancellation along with a usage example: + +
pod "PromiseKit/Alamofire"
+# Alamofire.request("http://example.com", method: .get).responseDecodable(DecodableObject.self).cancellize()
+
+pod "PromiseKit/Bolts"
+# CancellablePromise(…).then() { _ -> BFTask in /*…*/ }  // Returns CancellablePromise
+
+pod "PromiseKit/CoreLocation"
+# CLLocationManager.requestLocation().cancellize().then { /*…*/ }
+
+pod "PromiseKit/Foundation"
+# URLSession.shared.dataTask(.promise, with: request).cancellize().then { /*…*/ }
+
+pod "PromiseKit/MapKit"
+# MKDirections(…).calculate().cancellize().then { /*…*/ }
+
+pod "PromiseKit/OMGHTTPURLRQ"
+# URLSession.shared.GET("http://example.com").cancellize().then { /*…*/ }
+
+pod "PromiseKit/StoreKit"
+# SKProductsRequest(…).start(.promise).cancellize().then { /*…*/ }
+
+pod "PromiseKit/SystemConfiguration"
+# SCNetworkReachability.promise().cancellize().then { /*…*/ }
+
+pod "PromiseKit/UIKit"
+# UIViewPropertyAnimator(…).startAnimation(.promise).cancellize().then { /*…*/ }
+
+ +Here is a complete list of PromiseKit extension methods that support cancellation: + +[Alamofire](http://github.com/PromiseKit/Alamofire-) + +
Alamofire.DataRequest
+    response(_:queue:).cancellize()
+    responseData(queue:).cancellize()
+    responseString(queue:).cancellize()
+    responseJSON(queue:options:).cancellize()
+    responsePropertyList(queue:options:).cancellize()
+    responseDecodable(queue::decoder:).cancellize()
+    responseDecodable(_ type:queue:decoder:).cancellize()
+
+Alamofire.DownloadRequest
+    response(_:queue:).cancellize()
+    responseData(queue:).cancellize()
+
+ +[Bolts](http://github.com/PromiseKit/Bolts) + +
CancellablePromise<T>
+    then<U>(on: DispatchQueue?, body: (T) -> BFTask<U>) -> CancellablePromise
+
+ +[CoreLocation](http://github.com/PromiseKit/CoreLocation) + +
CLLocationManager
+    requestLocation(authorizationType:satisfying:).cancellize()
+    requestAuthorization(type requestedAuthorizationType:).cancellize()
+
+ +[Foundation](http://github.com/PromiseKit/Foundation) + +
NotificationCenter:
+    observe(once:object:).cancellize()
+
+NSObject
+    observe(_:keyPath:).cancellize()
+
+Process
+    launch(_:).cancellize()
+
+URLSession
+    dataTask(_:with:).cancellize()
+    uploadTask(_:with:from:).cancellize()
+    uploadTask(_:with:fromFile:).cancellize()
+    downloadTask(_:with:to:).cancellize()
+
+CancellablePromise
+    validate()
+
+ +[HomeKit](http://github.com/PromiseKit/HomeKit) + +
HMPromiseAccessoryBrowser
+    start(scanInterval:).cancellize()
+
+HMHomeManager
+    homes().cancellize()
+
+ +[MapKit](http://github.com/PromiseKit/MapKit) + +
MKDirections
+    calculate().cancellize()
+    calculateETA().cancellize()
+
+MKMapSnapshotter
+    start().cancellize()
+
+ +[StoreKit](http://github.com/PromiseKit/StoreKit) + +
SKProductsRequest
+    start(_:).cancellize()
+
+SKReceiptRefreshRequest
+    promise().cancellize()
+
+ +[SystemConfiguration](http://github.com/PromiseKit/SystemConfiguration) + +
SCNetworkReachability
+    promise().cancellize()
+
+ +[UIKit](http://github.com/PromiseKit/UIKit) + +
UIViewPropertyAnimator
+    startAnimation(_:).cancellize()
+
+ +## Choose Your Networking Library + +All the networking library extensions supported by PromiseKit are now simple to cancel! + +[Alamofire](http://github.com/PromiseKit/Alamofire-) + +```swift +// pod 'PromiseKit/Alamofire' +// # https://github.com/PromiseKit/Alamofire + +let context = firstly { + Alamofire + .request("http://example.com", method: .post, parameters: params) + .responseDecodable(Foo.self) +}.cancellize().done { foo in + //… +}.catch { error in + //… +}.cancelContext + +//… + +context.cancel() +``` + +And (of course) plain `URLSession` from [Foundation](http://github.com/PromiseKit/Foundation): + +```swift +// pod 'PromiseKit/Foundation' +// # https://github.com/PromiseKit/Foundation + +let context = firstly { + URLSession.shared.dataTask(.promise, with: try makeUrlRequest()) +}.cancellize().map { + try JSONDecoder().decode(Foo.self, with: $0.data) +}.done { foo in + //… +}.catch { error in + //… +}.cancelContext + +//… + +context.cancel() + +func makeUrlRequest() throws -> URLRequest { + var rq = URLRequest(url: url) + rq.httpMethod = "POST" + rq.addValue("application/json", forHTTPHeaderField: "Content-Type") + rq.addValue("application/json", forHTTPHeaderField: "Accept") + rq.httpBody = try JSONSerialization.jsonData(with: obj) + return rq +} +``` + +# Cancellability Goals + +* Provide a streamlined way to cancel a promise chain, which rejects all associated promises and cancels all associated tasks. For example: + +```swift +let promise = firstly { + login() +}.cancellize().then { creds in // Use the 'cancellize' function to initiate a cancellable promise chain + fetch(avatar: creds.user) +}.done { image in + self.imageView = image +}.catch(policy: .allErrors) { error in + if error.isCancelled { + // the chain has been cancelled! + } +} +//… +promise.cancel() +``` + +* Ensure that subsequent code blocks in a promise chain are _never_ called after the chain has been cancelled + +* Fully support concurrency, where all code is thread-safe. Cancellable promises and promise chains can safely and efficiently be cancelled from any thread at any time. + +* Provide cancellable support for all PromiseKit extensions whose native tasks can be cancelled (e.g. Alamofire, Bolts, CoreLocation, Foundation, HealthKit, HomeKit, MapKit, StoreKit, SystemConfiguration, UIKit) + +* Support cancellation for all PromiseKit primitives such as 'after', 'firstly', 'when', 'race' + +* Provide a simple way to make new types of cancellable promises + +* Ensure promise branches are properly cancelled. For example: + +```swift +import Alamofire +import PromiseKit + +func updateWeather(forCity searchName: String) { + refreshButton.startAnimating() + let context = firstly { + getForecast(forCity: searchName) + }.cancellize().done { response in + updateUI(forecast: response) + }.ensure { + refreshButton.stopAnimating() + }.catch { error in + // Cancellation errors are ignored by default + showAlert(error: error) + }.cancelContext + + //… + + /* **** Cancels EVERYTHING (except... the 'ensure' block always executes regardless) + Note: non-cancellable tasks cannot be interrupted. For example: if 'cancel()' is + called in the middle of 'updateUI()' then the chain will immediately be rejected, + however the 'updateUI' call will complete normally because it is not cancellable. + Its return value (if any) will be discarded. */ + context.cancel() +} + +func getForecast(forCity name: String) -> CancellablePromise { + return firstly { + Alamofire.request("https://autocomplete.weather.com/\(name)") + .responseDecodable(AutoCompleteCity.self) + }.cancellize().then { city in + Alamofire.request("https://forecast.weather.com/\(city.name)") + .responseDecodable(WeatherResponse.self).cancellize() + }.map { response in + format(response) + } +} +``` diff --git a/Documents/CommonPatterns.md b/Documents/CommonPatterns.md index 398affc60..8be09e415 100644 --- a/Documents/CommonPatterns.md +++ b/Documents/CommonPatterns.md @@ -212,22 +212,42 @@ one promise at a time if you need to. ```swift let fetches: [Promise] = makeFetches() -let timeout = after(seconds: 4) -race(when(fulfilled: fetches).asVoid(), timeout).then { +race(when(fulfilled: fetches).asVoid(), timeout(seconds: 4)).then { //… +}.catch(policy: .allErrors) { + // Rejects with 'PMKError.timedOut' if the timeout is exceeded } ``` `race` continues as soon as one of the promises it is watching finishes. +`timeout(seconds: TimeInterval)` returns a promise that throws +`PMKError.timedOut` when the time interval is exceeded. Note that `PMKError.timedOut` +is a cancellation error therefore the `.allErrors` catch policy must be specified +to handle this exception. + Make sure the promises you pass to `race` are all of the same type. The easiest way to ensure this is to use `asVoid()`. Note that if any component promise rejects, the `race` will reject, too. +When used with cancellable promises, all promises will be cancelled if either the timeout is +exceeded or if any promise rejects. + +```swift +let fetches: [Promise] = makeFetches() +let cancellableFetches: [CancellablePromise] = fetches.map { return $0.cancellize() } + +// All promises are automatically cancelled if any of them reject. +race(when(fulfilled: cancellableFetches).asVoid(), timeout(seconds: 4).cancellize()).then { + //… +}.catch(policy: .allErrors) { + // Rejects with 'PMKError.timedOut' if the timeout is exceeded. +} +``` -# Minimum Duration +## Minimum Duration Sometimes you need a task to take *at least* a certain amount of time. (For example, you want to show a progress spinner, but if it shows for less than 0.3 seconds, the UI @@ -245,61 +265,22 @@ firstly { } ``` -The code above works because we create the delay *before* we do work in `foo()`. By the +The code above works because we create the delay *before* we do work in `foo()`. By the time we get to waiting on that promise, either it will have already timed out or we will wait for whatever remains of the 0.3 seconds before continuing the chain. ## Cancellation -Promises don’t have a `cancel` function, but they do support cancellation through a -special error type that conforms to the `CancellableError` protocol. - -```swift -func foo() -> (Promise, cancel: () -> Void) { - let task = Task(…) - var cancelme = false - - let promise = Promise { seal in - task.completion = { value in - guard !cancelme else { return reject(PMKError.cancelled) } - seal.fulfill(value) - } - task.start() - } - - let cancel = { - cancelme = true - task.cancel() - } - - return (promise, cancel) -} -``` - -Promises don’t have a `cancel` function because you don’t want code outside of -your control to be able to cancel your operations--*unless*, of course, you explicitly -want to enable that behavior. In cases where you do want cancellation, the exact way -that it should work will vary depending on how the underlying task supports cancellation. -PromiseKit provides cancellation primitives but no concrete API. - -Cancelled chains do not call `catch` handlers by default. However you can -intercept cancellation if you like: - -```swift -foo.then { - //… -}.catch(policy: .allErrors) { - // cancelled errors are handled *as well* -} -``` +Starting with version 7, PromiseKit explicitly supports cancellation of promises and +promise chains. There is a new class called `CancellablePromise` that defines a `cancel` +method. Use the `cancellize` method on `Thenable` to obtain a `CancellablePromise` from a +`Promise` or `Guarantee`. -**Important**: Canceling a promise chain is *not* the same as canceling the underlying -asynchronous task. Promises are wrappers around asynchronicity, but they have no -control over the underlying tasks. If you need to cancel an underlying task, you -need to cancel the underlying task! +Invoking `cancel` will both reject the promise with `PMKError.cancelled` and cancel any +underlying asynchronous task(s). -> The library [CancellablePromiseKit](https://github.com/johannesd/CancellablePromiseKit) extends the concept of Promises to fully cover cancellable tasks. +For full details see [Cancelling Promises](Cancel.md). ## Retry / Polling diff --git a/Documents/README.md b/Documents/README.md index 8164ddfec..20da99cd0 100644 --- a/Documents/README.md +++ b/Documents/README.md @@ -4,6 +4,7 @@ * Handbook * [Getting Started](GettingStarted.md) * [Promises: Common Patterns](CommonPatterns.md) + * [Cancelling Promises](Cancel.md) * [Frequently Asked Questions](FAQ.md) * Manual * [Installation Guide](Installation.md) diff --git a/Documents/Troubleshooting.md b/Documents/Troubleshooting.md index 417fe17c2..218112124 100644 --- a/Documents/Troubleshooting.md +++ b/Documents/Troubleshooting.md @@ -163,6 +163,84 @@ An *inline* function like this is all you need. Here, the problem is that you forgot to mark the last line of the closure with an explicit `return`. It's required here because the closure is longer than one line. +### Cancellable promise embedded in the middle of a standard promise chain + +Error: ***Cannot convert value of type 'Promise<>' to closure result type 'Guarantee<>'***. Fixed by adding `cancellize` to `firstly { login() }`. + +```swift +/// 'login()' returns 'Promise' +/// 'fetch(avatar:)' returns 'CancellablePromise' + +let promise = firstly { + login() /// <-- ERROR: Cannot convert value of type 'Promise' to closure result type 'Guarantee' +}.then { creds in /// CHANGE TO: "}.cancellize().then { creds in" + fetch(avatar: creds.user) /// <-- ERROR: Cannot convert value of type 'CancellablePromise' to + /// closure result type 'Guarantee' +}.done { image in + self.imageView = image +}.catch(policy: .allErrors) { error in + if error.isCancelled { + // the chain has been cancelled! + } +} + +// … + +promise.cancel() +``` + +### The return type for a multi-line closure returning `CancellablePromise` is not explicitly stated + +The Swift compiler cannot (yet) determine the return type of a multi-line closure. + +The following example gives the unhelpful error: ***'()' is not convertible to 'UIImage'***. Many other strange errors can result from not explicitly declaring the return type of a multi-line closure. These kinds of errors are fixed by explicitly declaring the return type, which in the following example is a `CancellablePromise``. + +```swift +/// 'login()' returns 'Promise' +/// 'fetch(avatar:)' returns 'CancellablePromise' + +let promise = firstly { + login() +}.cancellize().then { creds in /// CHANGE TO: "}.cancellize().then { creds -> CancellablePromise in" + let f = fetch(avatar: creds.user) + return f +}.done { image in + self.imageView = image /// <-- ERROR: '()' is not convertible to 'UIImage' +}.catch(policy: .allErrors) { error in + if error.isCancelled { + // the chain has been cancelled! + } +} + +// … + +promise.cancel() +``` + +### Trying to cancel a standard promise chain + +Error: ***Value of type `PMKFinalizer` has no member `cancel`***. Fixed by using cancellable promises instead of standard promises. + +```swift +/// 'login()' returns 'Promise' +/// 'fetch(avatar:)' returns 'CancellablePromise' + +let promise = firstly { + login() +}.then { creds in /// CHANGE TO: "}.cancellize().then { creds in" + fetch(avatar: creds.user).promise /// CHANGE TO: fetch(avatar: creds.user) +}.done { image in + self.imageView = image +}.catch(policy: .allErrors) { error in + if error.isCancelled { + // the chain has been cancelled! + } +} + +// … + +promise.cancel() /// <-- ERROR: Value of type 'PMKFinalizer' has no member 'cancel' +``` ## You copied code off the Internet that doesn’t work diff --git a/Package.swift b/Package.swift index 4c5e54ce9..748cc1d70 100644 --- a/Package.swift +++ b/Package.swift @@ -18,6 +18,7 @@ pkg.swiftLanguageVersions = [ pkg.targets = [ .target(name: "PromiseKit", path: "Sources"), .testTarget(name: "Core", dependencies: ["PromiseKit"], path: "Tests/Core"), + .testTarget(name: "Cancel", dependencies: ["PromiseKit"], path: "Tests/Cancel"), .testTarget(name: "A+.swift", dependencies: ["PromiseKit"], path: "Tests/A+/Swift"), .testTarget(name: "A+.js", dependencies: ["PromiseKit"], path: "Tests/A+/JavaScript"), ] diff --git a/README.md b/README.md index 683b39f7f..0cde1f4e4 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,8 @@ PromiseKit 7 generalizes `DispatchQueue`s to a `Dispatcher` protocol. However, `DispatchQueue`s are `Dispatcher`-conformant, so existing code should not need to change. Please report any issues related to this transition. +PromiseKit 7 adds support for cancelling promises and promise chains. + # PromiseKit 6 [Release notes and migration guide][PMK6]. @@ -97,6 +99,7 @@ help me continue my work, I appreciate it 🙏🏻 * Handbook * [Getting Started](Documents/GettingStarted.md) * [Promises: Common Patterns](Documents/CommonPatterns.md) + * [Cancelling Promises](Documents/Cancel.md) * [Frequently Asked Questions](Documents/FAQ.md) * Manual * [Installation Guide](Documents/Installation.md) diff --git a/Sources/CancelContext.swift b/Sources/CancelContext.swift new file mode 100644 index 000000000..2e9ae677d --- /dev/null +++ b/Sources/CancelContext.swift @@ -0,0 +1,257 @@ +import Dispatch +import Foundation + +/** + Keeps track of all promises in a promise chain with pending or currently running tasks, and cancels them all when `cancel` is called. + */ +public class CancelContext: Hashable { + public static func == (lhs: CancelContext, rhs: CancelContext) -> Bool { + return lhs === rhs + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(ObjectIdentifier(self)) + } + + // Create a barrier queue that is used as a read/write lock for the CancelContext + // For reads: barrier.sync { } + // For writes: barrier.sync(flags: .barrier) { } + private let barrier = DispatchQueue(label: "org.promisekit.barrier.cancel", attributes: .concurrent) + + private var cancelItems = [CancelItem]() + private var cancelItemSet = Set() + + /** + Cancel all members of the promise chain and their associated asynchronous operations. + + - Parameter error: Specifies the cancellation error to use for the cancel operation, defaults to `PMKError.cancelled` + */ + public func cancel(with error: Error = PMKError.cancelled) { + self.cancel(with: error, visited: Set()) + } + + func cancel(with error: Error = PMKError.cancelled, visited: Set) { + var items: [CancelItem]! + barrier.sync(flags: .barrier) { + internalCancelledError = error + items = cancelItems + } + + for item in items { + item.cancel(with: error, visited: visited) + } + } + + /** + True if all members of the promise chain have been successfully cancelled, false otherwise. + */ + public var isCancelled: Bool { + var items: [CancelItem]! + barrier.sync { + items = cancelItems + } + + for item in items where !item.isCancelled { + return false + } + return true + } + + /** + True if `cancel` has been called on the CancelContext associated with this promise, false otherwise. `cancelAttempted` will be true if `cancel` is called on any promise in the chain. + */ + public var cancelAttempted: Bool { + return cancelledError != nil + } + + private var internalCancelledError: Error? + + /** + The cancellation error initialized when the promise is cancelled, or `nil` if not cancelled. + */ + public private(set) var cancelledError: Error? { + get { + var err: Error! + barrier.sync { + err = internalCancelledError + } + return err + } + + set { + barrier.sync(flags: .barrier) { + internalCancelledError = newValue + } + } + } + + func append(cancellable: Cancellable?, reject: ((Error) -> Void)?, thenable: Z) { + if cancellable == nil && reject == nil { + return + } + let item = CancelItem(cancellable: cancellable, reject: reject) + + var error: Error? + barrier.sync(flags: .barrier) { + error = internalCancelledError + cancelItems.append(item) + cancelItemSet.insert(item) + thenable.cancelItemList.append(item) + } + + if error != nil { + item.cancel(with: error!) + } + } + + func append(context childContext: CancelContext, thenable: Z) { + guard childContext !== self else { + return + } + let item = CancelItem(context: childContext) + + var error: Error? + barrier.sync(flags: .barrier) { + error = internalCancelledError + cancelItems.append(item) + cancelItemSet.insert(item) + thenable.cancelItemList.append(item) + } + + crossCancel(childContext: childContext, parentCancelledError: error) + } + + func append(context childContext: CancelContext, thenableCancelItemList: CancelItemList) { + guard childContext !== self else { + return + } + let item = CancelItem(context: childContext) + + var error: Error? + barrier.sync(flags: .barrier) { + error = internalCancelledError + cancelItems.append(item) + cancelItemSet.insert(item) + thenableCancelItemList.append(item) + } + + crossCancel(childContext: childContext, parentCancelledError: error) + } + + private func crossCancel(childContext: CancelContext, parentCancelledError: Error?) { + let parentError = parentCancelledError + let childError = childContext.cancelledError + + if parentError != nil { + if childError == nil { + childContext.cancel(with: parentError!) + } + } else if childError != nil { + if parentError == nil { + cancel(with: childError!) + } + } + } + + func recover() { + cancelledError = nil + } + + func removeItems(_ list: CancelItemList, clearList: Bool) -> Error? { + var error: Error? + barrier.sync(flags: .barrier) { + error = internalCancelledError + if error == nil && list.items.count != 0 { + var currentIndex = 1 + // The `list` parameter should match a block of items in the cancelItemList, remove them from the cancelItemList + // in one operation for efficiency + if cancelItemSet.remove(list.items[0]) != nil { + let removeIndex = cancelItems.firstIndex(of: list.items[0])! + while currentIndex < list.items.count { + let item = list.items[currentIndex] + if item != cancelItems[removeIndex + currentIndex] { + break + } + cancelItemSet.remove(item) + currentIndex += 1 + } + cancelItems.removeSubrange(removeIndex..<(removeIndex+currentIndex)) + } + + // Remove whatever falls outside of the block + while currentIndex < list.items.count { + let item = list.items[currentIndex] + if cancelItemSet.remove(item) != nil { + cancelItems.remove(at: cancelItems.firstIndex(of: item)!) + } + currentIndex += 1 + } + + if clearList { + list.removeAll() + } + } + } + return error + } +} + +/// Tracks the cancel items for a CancellablePromise. These items are removed from the associated CancelContext when the promise resolves. +public class CancelItemList { + fileprivate var items: [CancelItem] + + init() { + self.items = [] + } + + fileprivate func append(_ item: CancelItem) { + items.append(item) + } + + fileprivate func removeAll() { + items.removeAll() + } +} + +fileprivate class CancelItem: Hashable { + static func == (lhs: CancelItem, rhs: CancelItem) -> Bool { + return lhs === rhs + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(ObjectIdentifier(self)) + } + + let cancellable: Cancellable? + var reject: ((Error) -> Void)? + weak var context: CancelContext? + var cancelAttempted = false + + init(cancellable: Cancellable?, reject: ((Error) -> Void)?) { + self.cancellable = cancellable + self.reject = reject + } + + init(context: CancelContext) { + self.cancellable = nil + self.context = context + } + + func cancel(with error: Error, visited: Set? = nil) { + cancelAttempted = true + + cancellable?.cancel() + reject?(error) + + if var v = visited, let c = context { + if !v.contains(c) { + v.insert(c) + c.cancel(with: error, visited: v) + } + } + } + + var isCancelled: Bool { + return cancellable?.isCancelled ?? cancelAttempted + } +} diff --git a/Sources/CancellableCatchable.swift b/Sources/CancellableCatchable.swift new file mode 100644 index 000000000..9a53fd642 --- /dev/null +++ b/Sources/CancellableCatchable.swift @@ -0,0 +1,301 @@ +import Dispatch + +/// Provides `catch` and `recover` to your object that conforms to `CancellableThenable` +public protocol CancellableCatchMixin: CancellableThenable { + /// Type of the delegate `catchable` + associatedtype C: CatchMixin + + /// Delegate `catchable` for this CancellablePromise + var catchable: C { get } +} + +public extension CancellableCatchMixin { + /** + The provided closure executes when this cancellable promise rejects. + + Rejecting a promise cascades: rejecting all subsequent promises (unless + recover is invoked) thus you will typically place your catch at the end + of a chain. Often utility promises will not have a catch, instead + delegating the error handling to the caller. + + - Parameter on: The dispatcher that executes the provided closure. + - Parameter policy: The default policy does not execute your handler for cancellation errors. + - Parameter execute: The handler to execute if this promise is rejected. + - Returns: A promise finalizer. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + */ + @discardableResult + func `catch`(on: Dispatcher = conf.D.return, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) -> Void) -> CancellableFinalizer { + return CancellableFinalizer(self.catchable.catch(on: on, policy: policy, body), cancel: self.cancelContext) + } +} + +/** + Cancellable finalizer returned from `catch`. Use `finally` to specify a code block that executes when the promise chain resolves. + */ +public class CancellableFinalizer { + let pmkFinalizer: PMKFinalizer + + /// The CancelContext associated with this finalizer + public let cancelContext: CancelContext + + init(_ pmkFinalizer: PMKFinalizer, cancel: CancelContext) { + self.pmkFinalizer = pmkFinalizer + self.cancelContext = cancel + } + + /// `finally` is the same as `ensure`, but it is not chainable + @discardableResult + public func finally(on: Dispatcher = conf.D.return, _ body: @escaping () -> Void) -> CancelContext { + pmkFinalizer.finally(on: on, body) + return cancelContext + } + + /** + Cancel all members of the promise chain and their associated asynchronous operations. + + - Parameter error: Specifies the cancellation error to use for the cancel operation, defaults to `PMKError.cancelled` + */ + public func cancel(with error: Error = PMKError.cancelled) { + cancelContext.cancel(with: error) + } + + /** + True if all members of the promise chain have been successfully cancelled, false otherwise. + */ + public var isCancelled: Bool { + return cancelContext.isCancelled + } + + /** + True if `cancel` has been called on the CancelContext associated with this promise, false otherwise. `cancelAttempted` will be true if `cancel` is called on any promise in the chain. + */ + public var cancelAttempted: Bool { + return cancelContext.cancelAttempted + } + + /** + The cancellation error generated when the promise is cancelled, or `nil` if not cancelled. + */ + public var cancelledError: Error? { + return cancelContext.cancelledError + } +} + +public extension CancellableCatchMixin { + /** + The provided closure executes when this cancellable promise rejects. + + Unlike `catch`, `recover` continues the chain. + Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: + + let context = firstly { + CLLocationManager.requestLocation() + }.recover { error in + guard error == CLError.unknownLocation else { throw error } + return .value(CLLocation.chicago) + }.cancelContext + + //… + + context.cancel() + + - Parameter on: The dispatcher that executes the provided closure. + - Parameter body: The handler to execute if this promise is rejected. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + */ + func recover(on: Dispatcher = conf.D.map, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) throws -> V) -> CancellablePromise where V.U.T == C.T { + + let cancelItemList = CancelItemList() + + let cancelBody = { (error: Error) throws -> V.U in + _ = self.cancelContext.removeItems(self.cancelItemList, clearList: true) + let rval = try body(error) + if policy == .allErrors { + self.cancelContext.recover() + } + self.cancelContext.append(context: rval.cancelContext, thenableCancelItemList: cancelItemList) + return rval.thenable + } + + let promise = self.catchable.recover(on: on, policy: policy, cancelBody) + if thenable.result != nil && policy == .allErrors { + self.cancelContext.recover() + } + return CancellablePromise(promise: promise, context: self.cancelContext, cancelItemList: cancelItemList) + } + + /** + The provided closure executes when this cancellable promise rejects. + + Unlike `catch`, `recover` continues the chain. + Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: + + let context = firstly { + CLLocationManager.requestLocation() + }.cancellize().recover { error in + guard error == CLError.unknownLocation else { throw error } + return .value(CLLocation.chicago) + }.cancelContext + + //… + + context.cancel() + + - Parameter on: The dispatcher that executes the provided closure. + - Parameter body: The handler to execute if this promise is rejected. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + - Note: Methods with the `cancellable` prefix create a new CancellablePromise, and those without the `cancellable` prefix accept an existing CancellablePromise. + */ + func recover(on: Dispatcher = conf.D.map, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) throws -> V) -> CancellablePromise where V.T == C.T { + + let cancelBody = { (error: Error) throws -> V in + _ = self.cancelContext.removeItems(self.cancelItemList, clearList: true) + let rval = try body(error) + if policy == .allErrors { + self.cancelContext.recover() + } + return rval + } + + let promise = self.catchable.recover(on: on, policy: policy, cancelBody) + if thenable.result != nil && policy == .allErrors { + self.cancelContext.recover() + } + let cancellablePromise = CancellablePromise(promise: promise, context: self.cancelContext) + if let cancellable = promise.cancellable { + self.cancelContext.append(cancellable: cancellable, reject: promise.rejectIfCancelled, thenable: cancellablePromise) + } + return cancellablePromise + } + + /** + The provided closure executes when this cancellable promise resolves, whether it rejects or not. + + let context = firstly { + UIApplication.shared.networkActivityIndicatorVisible = true + //… returns a cancellable promise + }.done { + //… + }.ensure { + UIApplication.shared.networkActivityIndicatorVisible = false + }.catch { + //… + }.cancelContext + + //… + + context.cancel() + + - Parameter on: The dispatcher that executes the provided closure. + - Parameter body: The closure that executes when this promise resolves. + - Returns: A new promise, resolved with this promise’s resolution. + */ + func ensure(on: Dispatcher = conf.D.return, _ body: @escaping () -> Void) -> CancellablePromise { + let rp = CancellablePromise.pending() + rp.promise.cancelContext = self.cancelContext + self.catchable.pipe { result in + on.dispatch { + body() + switch result { + case .success(let value): + if let error = self.cancelContext.cancelledError { + rp.resolver.reject(error) + } else { + rp.resolver.fulfill(value) + } + case .failure(let error): + rp.resolver.reject(error) + } + } + } + return rp.promise + } + + /** + The provided closure executes when this cancellable promise resolves, whether it rejects or not. + The chain waits on the returned `CancellablePromise`. + + let context = firstly { + setup() // returns a cancellable promise + }.done { + //… + }.ensureThen { + teardown() // -> CancellablePromise + }.catch { + //… + }.cancelContext + + //… + + context.cancel() + + - Parameter on: The dispatcher that executes the provided closure. + - Parameter body: The closure that executes when this promise resolves. + - Returns: A new promise, resolved with this promise’s resolution. + */ + func ensureThen(on: Dispatcher = conf.D.return, _ body: @escaping () -> CancellablePromise) -> CancellablePromise { + let rp = CancellablePromise.pending() + rp.promise.cancelContext = cancelContext + self.catchable.pipe { result in + on.dispatch { + let rv = body() + rp.promise.appendCancelContext(from: rv) + + rv.done { + switch result { + case .success(let value): + if let error = self.cancelContext.cancelledError { + rp.resolver.reject(error) + } else { + rp.resolver.fulfill(value) + } + case .failure(let error): + rp.resolver.reject(error) + } + }.catch(policy: .allErrors) { + rp.resolver.reject($0) + } + } + } + return rp.promise + } + + /** + Consumes the Swift unused-result warning. + - Note: You should `catch`, but in situations where you know you don’t need a `catch`, `cauterize` makes your intentions clear. + */ + @discardableResult + func cauterize() -> CancellableFinalizer { + return self.catch(policy: .allErrors) { + Swift.print("PromiseKit:cauterized-error:", $0) + } + } +} + +public extension CancellableCatchMixin where C.T == Void { + /** + The provided closure executes when this cancellable promise rejects. + + This variant of `recover` ensures that no error is thrown from the handler and allows specifying a catch policy. + + - Parameter on: The dispatcher that executes the provided closure. + - Parameter body: The handler to execute if this promise is rejected. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + */ + func recover(on: Dispatcher = conf.D.map, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) throws -> Void) -> CancellablePromise { + let cancelBody = { (error: Error) throws -> Void in + _ = self.cancelContext.removeItems(self.cancelItemList, clearList: true) + try body(error) + if policy == .allErrors { + self.cancelContext.recover() + } + } + + let promise = self.catchable.recover(on: on, policy: policy, cancelBody) + if thenable.result != nil && policy == .allErrors { + self.cancelContext.recover() + } + return CancellablePromise(promise: promise, context: self.cancelContext) + } +} diff --git a/Sources/CancellablePromise.swift b/Sources/CancellablePromise.swift new file mode 100644 index 000000000..212d8777d --- /dev/null +++ b/Sources/CancellablePromise.swift @@ -0,0 +1,143 @@ +import class Foundation.Thread +import Dispatch + +/** + A `CancellablePromise` is a functional abstraction around a failable and cancellable asynchronous operation. + + At runtime the promise can become a member of a chain of promises, where the `cancelContext` is used to track and cancel (if desired) all promises in this chain. + + - See: `CancellableThenable` + */ +public class CancellablePromise: CancellableThenable, CancellableCatchMixin { + /// Delegate `promise` for this CancellablePromise + public let promise: Promise + + /// Type of the delegate `thenable` + public typealias U = Promise + + /// Delegate `thenable` for this CancellablePromise + public var thenable: U { + return promise + } + + /// Type of the delegate `catchable` + public typealias C = Promise + + /// Delegate `catchable` for this CancellablePromise + public var catchable: C { + return promise + } + + /// The CancelContext associated with this CancellablePromise + public var cancelContext: CancelContext + + /// Tracks the cancel items for this CancellablePromise. These items are removed from the associated CancelContext when the promise resolves. + public var cancelItemList: CancelItemList + + init(promise: Promise, context: CancelContext? = nil, cancelItemList: CancelItemList? = nil) { + self.promise = promise + self.cancelContext = context ?? CancelContext() + self.cancelItemList = cancelItemList ?? CancelItemList() + } + + /// Initialize a new rejected cancellable promise. + public convenience init(cancellable: Cancellable? = nil, error: Error) { + var reject: ((Error) -> Void)! + self.init(promise: Promise { seal in + reject = seal.reject + seal.reject(error) + }) + self.appendCancellable(cancellable, reject: reject) + } + + /// Initialize a new cancellable promise bound to the provided `Thenable`. + public convenience init(_ bridge: U, cancelContext: CancelContext? = nil) where U.T == T { + var promise: Promise! + let cancellable: Cancellable! + var reject: ((Error) -> Void)! + + if let p = bridge as? Promise { + cancellable = p.cancellable + if let r = p.rejectIfCancelled { + promise = p + reject = r + } + } else if let g = bridge as? Guarantee { + cancellable = g.cancellable + } else { + cancellable = nil + } + + if promise == nil { + // Wrapper promise + promise = Promise { seal in + reject = seal.reject + bridge.done(on: nil) { + seal.fulfill($0) + }.catch(policy: .allErrors) { + seal.reject($0) + } + } + } + + self.init(promise: promise, context: cancelContext) + self.appendCancellable(cancellable, reject: reject) + } + + /// Initialize a new cancellable promise that can be resolved with the provided `Resolver`. + public convenience init(cancellable: Cancellable? = nil, resolver body: (Resolver) throws -> Void) { + var reject: ((Error) -> Void)! + self.init(promise: Promise { seal in + reject = seal.reject + try body(seal) + }) + self.appendCancellable(cancellable, reject: reject) + } + + /// Initialize a new cancellable promise using the given Promise and its Resolver. + public convenience init(cancellable: Cancellable? = nil, promise: Promise, resolver: Resolver) { + self.init(promise: promise) + self.appendCancellable(cancellable, reject: resolver.reject) + } + + /// - Returns: a tuple of a new cancellable pending promise and its `Resolver`. + public class func pending() -> (promise: CancellablePromise, resolver: Resolver) { + let rp = Promise.pending() + return (promise: CancellablePromise(promise: rp.promise), resolver: rp.resolver) + } + + /// Internal function required for `Thenable` conformance. + /// - See: `Thenable.pipe` + public func pipe(to: @escaping (Result) -> Void) { + promise.pipe(to: to) + } + + /// - Returns: The current `Result` for this cancellable promise. + /// - See: `Thenable.result` + public var result: Result? { + return promise.result + } + + /** + Blocks this thread, so—you know—don’t call this on a serial thread that + any part of your chain may use. Like the main thread for example. + */ + public func wait() throws -> T { + return try promise.wait() + } +} + +#if swift(>=3.1) +extension CancellablePromise where T == Void { + /// Initializes a new cancellable promise fulfilled with `Void` + public convenience init() { + self.init(promise: Promise()) + } + + /// Initializes a new cancellable promise fulfilled with `Void` and with the given ` Cancellable` + public convenience init(cancellable: Cancellable) { + self.init() + self.appendCancellable(cancellable, reject: nil) + } +} +#endif diff --git a/Sources/CancellableTask.swift b/Sources/CancellableTask.swift new file mode 100644 index 000000000..28b57f5a4 --- /dev/null +++ b/Sources/CancellableTask.swift @@ -0,0 +1,12 @@ +import Dispatch + +/** + Use this protocol to define cancellable tasks for CancellablePromise. + */ +public protocol Cancellable { + /// Cancel the associated task + func cancel() + + /// `true` if the task was successfully cancelled, `false` otherwise + var isCancelled: Bool { get } +} diff --git a/Sources/CancellableThenable.swift b/Sources/CancellableThenable.swift new file mode 100644 index 000000000..646ada924 --- /dev/null +++ b/Sources/CancellableThenable.swift @@ -0,0 +1,520 @@ +import Dispatch + +/** + CancellableThenable represents an asynchronous operation that can be both chained and cancelled. When chained, all CancellableThenable members of the chain are cancelled when `cancel` is called on the associated CancelContext. + */ +public protocol CancellableThenable: class { + /// Type of the delegate `thenable` + associatedtype U: Thenable + + /// Delegate `thenable` for this `CancellableThenable` + var thenable: U { get } + + /// The `CancelContext` associated with this `CancellableThenable` + var cancelContext: CancelContext { get } + + /// Tracks the cancel items for this `CancellableThenable`. These items are removed from the associated `CancelContext` when the thenable resolves. + var cancelItemList: CancelItemList { get } +} + +public extension CancellableThenable { + /// Append the `task` and `reject` function for a cancellable task to the cancel context + func appendCancellable(_ cancellable: Cancellable?, reject: ((Error) -> Void)?) { + self.cancelContext.append(cancellable: cancellable, reject: reject, thenable: self) + } + + /// Append the cancel context associated with `from` to our cancel context. Typically `from` is a branch of our chain. + func appendCancelContext(from: Z) { + self.cancelContext.append(context: from.cancelContext, thenable: self) + } + + /** + Cancel all members of the promise chain and their associated asynchronous operations. + + - Parameter error: Specifies the cancellation error to use for the cancel operation, defaults to `PMKError.cancelled` + */ + func cancel(with error: Error = PMKError.cancelled) { + self.cancelContext.cancel(with: error) + } + + /** + True if all members of the promise chain have been successfully cancelled, false otherwise. + */ + var isCancelled: Bool { + return self.cancelContext.isCancelled + } + + /** + True if `cancel` has been called on the CancelContext associated with this promise, false otherwise. `cancelAttempted` will be true if `cancel` is called on any promise in the chain. + */ + var cancelAttempted: Bool { + return self.cancelContext.cancelAttempted + } + + /** + The cancellation error generated when the promise is cancelled, or `nil` if not cancelled. + */ + var cancelledError: Error? { + return self.cancelContext.cancelledError + } + + /** + The provided closure executes when this cancellable promise resolves. + + This allows chaining promises. The cancellable promise returned by the provided closure is resolved before the cancellable promise returned by this closure resolves. + + - Parameter on: The dispatcher that executes the provided closure. + - Parameter body: The closure that executes when this cancellable promise fulfills. It must return a cancellable promise. + - Returns: A new cancellable promise that resolves when the cancellable promise returned from the provided closure resolves. For example: + + let context = firstly { + URLSession.shared.dataTask(.promise, with: url1) + }.cancellize().then { response in + transform(data: response.data) // returns a CancellablePromise + }.done { transformation in + //… + }.cancelContext + + //… + + context.cancel() + */ + func then(on: Dispatcher = conf.D.map, _ body: @escaping (U.T) throws -> V) -> CancellablePromise { + + let cancelItemList = CancelItemList() + + let cancelBody = { (value: U.T) throws -> V.U in + if let error = self.cancelContext.removeItems(self.cancelItemList, clearList: true) { + throw error + } else { + let rv = try body(value) + self.cancelContext.append(context: rv.cancelContext, thenableCancelItemList: cancelItemList) + return rv.thenable + } + } + + let promise = self.thenable.then(on: on, cancelBody) + return CancellablePromise(promise: promise, context: self.cancelContext, cancelItemList: cancelItemList) + } + + /** + The provided closure executes when this cancellable promise resolves. + + This allows chaining promises. The promise returned by the provided closure is resolved before the cancellable promise returned by this closure resolves. + + - Parameter on: The dispatcher that executes the provided closure. + - Parameter body: The closure that executes when this promise fulfills. It must return a promise (not a cancellable promise). + - Returns: A new cancellable promise that resolves when the promise returned from the provided closure resolves. For example: + + let context = firstly { + URLSession.shared.dataTask(.promise, with: url1) + }.cancellize().then { response in + transform(data: response.data) // returns a Promise + }.done { transformation in + //… + }.cancelContext + + //… + + context.cancel() + */ + func then(on: Dispatcher = conf.D.map, _ body: @escaping (U.T) throws -> V) -> CancellablePromise { + let cancelBody = { (value: U.T) throws -> V in + if let error = self.cancelContext.removeItems(self.cancelItemList, clearList: true) { + throw error + } else { + return try body(value) + } + } + + let promise = self.thenable.then(on: on, cancelBody) + return CancellablePromise(promise, cancelContext: self.cancelContext) + } + + /** + The provided closure is executed when this cancellable promise is resolved. + + This is like `then` but it requires the closure to return a non-promise and non-cancellable-promise. + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter transform: The closure that is executed when this CancellablePromise is fulfilled. It must return a non-promise and non-cancellable-promise. + - Returns: A new cancellable promise that is resolved with the value returned from the provided closure. For example: + + let context = firstly { + URLSession.shared.dataTask(.promise, with: url1) + }.cancellize().map { response in + response.data.length + }.done { length in + //… + }.cancelContext + + //… + + context.cancel() + */ + func map(on: Dispatcher = conf.D.map, _ transform: @escaping (U.T) throws -> V) -> CancellablePromise { + let cancelTransform = { (value: U.T) throws -> V in + if let error = self.cancelContext.removeItems(self.cancelItemList, clearList: true) { + throw error + } else { + return try transform(value) + } + } + + let promise = self.thenable.map(on: on, cancelTransform) + return CancellablePromise(promise: promise, context: self.cancelContext) + } + + /** + The provided closure is executed when this cancellable promise is resolved. + + In your closure return an `Optional`, if you return `nil` the resulting cancellable promise is rejected with `PMKError.compactMap`, otherwise the cancellable promise is fulfilled with the unwrapped value. + + let context = firstly { + URLSession.shared.dataTask(.promise, with: url) + }.cancellize().compactMap { + try JSONSerialization.jsonObject(with: $0.data) as? [String: String] + }.done { dictionary in + //… + }.catch { + // either `PMKError.compactMap` or a `JSONError` + }.cancelContext + + //… + + context.cancel() + */ + func compactMap(on: Dispatcher = conf.D.map, _ transform: @escaping (U.T) throws -> V?) -> CancellablePromise { + let cancelTransform = { (value: U.T) throws -> V? in + if let error = self.cancelContext.removeItems(self.cancelItemList, clearList: true) { + throw error + } else { + return try transform(value) + } + } + + let promise = self.thenable.compactMap(on: on, cancelTransform) + return CancellablePromise(promise: promise, context: self.cancelContext) + } + + /** + The provided closure is executed when this cancellable promise is resolved. + + Equivalent to `map { x -> Void in`, but since we force the `Void` return Swift + is happier and gives you less hassle about your closure’s qualification. + + - Parameter on: The dispatcher that executes the provided closure. + - Parameter body: The closure that is executed when this promise is fulfilled. + - Returns: A new cancellable promise fulfilled as `Void`. + + let context = firstly { + URLSession.shared.dataTask(.promise, with: url) + }.cancellize().done { response in + print(response.data) + }.cancelContext + + //… + + context.cancel() + */ + func done(on: Dispatcher = conf.D.return, _ body: @escaping (U.T) throws -> Void) -> CancellablePromise { + let cancelBody = { (value: U.T) throws -> Void in + if let error = self.cancelContext.removeItems(self.cancelItemList, clearList: true) { + throw error + } else { + try body(value) + } + } + + let promise = self.thenable.done(on: on, cancelBody) + return CancellablePromise(promise: promise, context: self.cancelContext) + } + + /** + The provided closure is executed when this cancellable promise is resolved. + + This is like `done` but it returns the same value that the handler is fed. + `get` immutably accesses the fulfilled value; the returned CancellablePromise maintains that value. + + - Parameter on: The dispatcher that executes the provided closure. + - Parameter body: The closure that is executed when this promise is fulfilled. + - Returns: A new cancellable promise that is resolved with the value that the handler is fed. For example: + + let context = firstly { + cancellize(Promise.value(1)) + }.get { foo in + print(foo, " is 1") + }.done { foo in + print(foo, " is 1") + }.done { foo in + print(foo, " is Void") + }.cancelContext + + //… + + context.cancel() + */ + func get(on: Dispatcher = conf.D.return, _ body: @escaping (U.T) throws -> Void) -> CancellablePromise { + return map(on: on) { + try body($0) + return $0 + } + } + + /** + The provided closure is executed with cancellable promise result. + + This is like `get` but provides the Result of the CancellablePromise so you can inspect the value of the chain at this point without causing any side effects. + + - Parameter on: The dispatcher that executes the provided closure. + - Parameter body: The closure that is executed with Result of CancellablePromise. + - Returns: A new cancellable promise that is resolved with the result that the handler is fed. For example: + + promise.tap{ print($0) }.then{ /*…*/ } + */ + func tap(on: Dispatcher = conf.D.map, _ body: @escaping(Result) -> Void) -> CancellablePromise { + let rp = CancellablePromise.pending() + rp.promise.cancelContext = self.cancelContext + self.thenable.pipe { result in + on.dispatch { + if let error = self.cancelContext.removeItems(self.cancelItemList, clearList: true) { + rp.resolver.reject(error) + } else { + body(result) + rp.resolver.resolve(result) + } + } + } + return rp.promise + } + + /// - Returns: a new cancellable promise chained off this cancellable promise but with its value discarded. + func asVoid() -> CancellablePromise { + return map(on: nil) { _ in } + } +} + +public extension CancellableThenable { + /** + - Returns: The error with which this cancellable promise was rejected; `nil` if this promise is not rejected. + */ + var error: Error? { + return thenable.error + } + + /** + - Returns: `true` if the cancellable promise has not yet resolved. + */ + var isPending: Bool { + return thenable.isPending + } + + /** + - Returns: `true` if the cancellable promise has resolved. + */ + var isResolved: Bool { + return thenable.isResolved + } + + /** + - Returns: `true` if the cancellable promise was fulfilled. + */ + var isFulfilled: Bool { + return thenable.isFulfilled + } + + /** + - Returns: `true` if the cancellable promise was rejected. + */ + var isRejected: Bool { + return thenable.isRejected + } + + /** + - Returns: The value with which this cancellable promise was fulfilled or `nil` if this cancellable promise is pending or rejected. + */ + var value: U.T? { + return thenable.value + } +} + +public extension CancellableThenable where U.T: Sequence { + /** + `CancellablePromise<[U.T]>` => `U.T` -> `V` => `CancellablePromise<[V]>` + + firstly { + cancellize(Promise.value([1,2,3])) + }.mapValues { integer in + integer * 2 + }.done { + // $0 => [2,4,6] + } + */ + func mapValues(on: Dispatcher = conf.D.map, _ transform: @escaping(U.T.Iterator.Element) throws -> V) -> CancellablePromise<[V]> { + return map(on: on) { try $0.map(transform) } + } + + /** + `CancellablePromise<[U.T]>` => `U.T` -> `[V]` => `CancellablePromise<[V]>` + + firstly { + cancellize(Promise.value([1,2,3])) + }.flatMapValues { integer in + [integer, integer] + }.done { + // $0 => [1,1,2,2,3,3] + } + */ + func flatMapValues(on: Dispatcher = conf.D.map, _ transform: @escaping(U.T.Iterator.Element) throws -> V) -> CancellablePromise<[V.Iterator.Element]> { + return map(on: on) { (foo: U.T) in + try foo.flatMap { try transform($0) } + } + } + + /** + `CancellablePromise<[U.T]>` => `U.T` -> `V?` => `CancellablePromise<[V]>` + + firstly { + cancellize(Promise.value(["1","2","a","3"])) + }.compactMapValues { + Int($0) + }.done { + // $0 => [1,2,3] + } + */ + func compactMapValues(on: Dispatcher = conf.D.map, _ transform: @escaping(U.T.Iterator.Element) throws -> V?) -> CancellablePromise<[V]> { + return map(on: on) { foo -> [V] in + return try foo.compactMap(transform) + } + } + + /** + `CancellablePromise<[U.T]>` => `U.T` -> `CancellablePromise` => `CancellablePromise<[V]>` + + firstly { + cancellize(Promise.value([1,2,3])) + }.thenMap { integer in + cancellize(Promise.value(integer * 2)) + }.done { + // $0 => [2,4,6] + } + */ + func thenMap(on: Dispatcher = conf.D.map, _ transform: @escaping(U.T.Iterator.Element) throws -> V) -> CancellablePromise<[V.U.T]> { + return then(on: on) { + when(fulfilled: try $0.map(transform)) + } + } + + /** + `CancellablePromise<[U.T]>` => `U.T` -> `Promise` => `CancellablePromise<[V]>` + + firstly { + Promise.value([1,2,3]) + }.cancellize().thenMap { integer in + .value(integer * 2) + }.done { + // $0 => [2,4,6] + } + */ + func thenMap(on: Dispatcher = conf.D.map, _ transform: @escaping(U.T.Iterator.Element) throws -> V) -> CancellablePromise<[V.T]> { + return then(on: on) { + when(fulfilled: try $0.map(transform)) + } + } + + /** + `CancellablePromise<[T]>` => `T` -> `CancellablePromise<[U]>` => `CancellablePromise<[U]>` + + firstly { + cancellize(Promise.value([1,2,3])) + }.thenFlatMap { integer in + cancellize(Promise.value([integer, integer])) + }.done { + // $0 => [1,1,2,2,3,3] + } + */ + func thenFlatMap(on: Dispatcher = conf.D.map, _ transform: @escaping(U.T.Iterator.Element) throws -> V) -> CancellablePromise<[V.U.T.Iterator.Element]> where V.U.T: Sequence { + return then(on: on) { + when(fulfilled: try $0.map(transform)) + }.map(on: nil) { + $0.flatMap { $0 } + } + } + + /** + `CancellablePromise<[T]>` => `T` -> `Promise<[U]>` => `CancellablePromise<[U]>` + + firstly { + Promise.value([1,2,3]) + }.cancellize().thenFlatMap { integer in + .value([integer, integer]) + }.done { + // $0 => [1,1,2,2,3,3] + } + */ + func thenFlatMap(on: Dispatcher = conf.D.map, _ transform: @escaping(U.T.Iterator.Element) throws -> V) -> CancellablePromise<[V.T.Iterator.Element]> where V.T: Sequence { + return then(on: on) { + when(fulfilled: try $0.map(transform)) + }.map(on: nil) { + $0.flatMap { $0 } + } + } + + /** + `CancellablePromise<[T]>` => `T` -> Bool => `CancellablePromise<[U]>` + + firstly { + cancellize(Promise.value([1,2,3])) + }.filterValues { + $0 > 1 + }.done { + // $0 => [2,3] + } + */ + func filterValues(on: Dispatcher = conf.D.map, _ isIncluded: @escaping (U.T.Iterator.Element) -> Bool) -> CancellablePromise<[U.T.Iterator.Element]> { + return map(on: on) { + $0.filter(isIncluded) + } + } +} + +public extension CancellableThenable where U.T: Collection { + /// - Returns: a cancellable promise fulfilled with the first value of this `Collection` or, if empty, a promise rejected with PMKError.emptySequence. + var firstValue: CancellablePromise { + return map(on: nil) { aa in + if let a1 = aa.first { + return a1 + } else { + throw PMKError.emptySequence + } + } + } + + func firstValue(on: Dispatcher = conf.D.map, where test: @escaping (U.T.Iterator.Element) -> Bool) -> CancellablePromise { + return map(on: on) { + for x in $0 where test(x) { + return x + } + throw PMKError.emptySequence + } + } + + /// - Returns: a cancellable promise fulfilled with the last value of this `Collection` or, if empty, a promise rejected with PMKError.emptySequence. + var lastValue: CancellablePromise { + return map(on: nil) { aa in + if aa.isEmpty { + throw PMKError.emptySequence + } else { + let i = aa.index(aa.endIndex, offsetBy: -1) + return aa[i] + } + } + } +} + +public extension CancellableThenable where U.T: Sequence, U.T.Iterator.Element: Comparable { + /// - Returns: a cancellable promise fulfilled with the sorted values of this `Sequence`. + func sortedValues(on: Dispatcher = conf.D.map) -> CancellablePromise<[U.T.Iterator.Element]> { + return map(on: on) { $0.sorted() } + } +} diff --git a/Sources/Dispatcher.swift b/Sources/Dispatcher.swift index 835cb6fd4..0000e5e79 100644 --- a/Sources/Dispatcher.swift +++ b/Sources/Dispatcher.swift @@ -541,3 +541,481 @@ public extension CatchMixin where T == Void { return recover(on: dispatcher, policy: policy, body) } } + +//////////////////////////////////////////////////////////// Cancellation + +public extension CancellableThenable { + /** + The provided closure executes when this cancellable promise resolves. + + This allows chaining promises. The cancellable promise returned by the provided closure is resolved before the cancellable promise returned by this closure resolves. + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter body: The closure that executes when this cancellable promise fulfills. It must return a cancellable promise. + - Returns: A new cancellable promise that resolves when the promise returned from the provided closure resolves. For example: + + let context = firstly { + URLSession.shared.dataTask(.promise, with: url1) + }.cancellize().then { response in + transform(data: response.data) // returns a CancellablePromise + }.done { transformation in + //… + }.cancelContext + + //… + + context.cancel() + */ + func then(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping (U.T) throws -> V) -> CancellablePromise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return then(on: dispatcher, body) + } + + /** + The provided closure executes when this cancellable promise resolves. + + This allows chaining promises. The promise returned by the provided closure is resolved before the cancellable promise returned by this closure resolves. + + - Parameter on: The dispatcher that executes the provided closure. + - Parameter body: The closure that executes when this cancellable promise fulfills. It must return a promise (not a cancellable promise). + - Returns: A new cancellable promise that resolves when the promise returned from the provided closure resolves. For example: + + let context = firstly { + URLSession.shared.dataTask(.promise, with: url1) + }.cancellize().then { response in + transform(data: response.data) // returns a Promise + }.done { transformation in + //… + }.cancelContext + + //… + + context.cancel() + */ + func then(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping (U.T) throws -> V) -> CancellablePromise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return then(on: dispatcher, body) + } + + /** + The provided closure is executed when this cancellable promise is resolved. + + This is like `then` but it requires the closure to return a non-promise and non-cancellable-promise. + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter transform: The closure that is executed when this CancellablePromise is fulfilled. It must return a non-promise and non-cancellable-promise. + - Returns: A new cancellable promise that is resolved with the value returned from the provided closure. For example: + + let context = firstly { + URLSession.shared.dataTask(.promise, with: url1) + }.cancellize().map { response in + response.data.length + }.done { length in + //… + }.cancelContext + + //… + + context.cancel() + */ + func map(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping (U.T) throws -> V) -> CancellablePromise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return map(on: dispatcher, transform) + } + + /** + The provided closure is executed when this cancellable promise is resolved. + + In your closure return an `Optional`, if you return `nil` the resulting cancellable promise is rejected with `PMKError.compactMap`, otherwise the cancellable promise is fulfilled with the unwrapped value. + + let context = firstly { + URLSession.shared.dataTask(.promise, with: url) + }.cancellize().compactMap { + try JSONSerialization.jsonObject(with: $0.data) as? [String: String] + }.done { dictionary in + //… + }.catch { + // either `PMKError.compactMap` or a `JSONError` + }.cancelContext + + //… + + context.cancel() + */ + func compactMap(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping (U.T) throws -> V?) -> CancellablePromise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return compactMap(on: dispatcher, transform) + } + + /** + The provided closure is executed when this cancellable promise is resolved. + + Equivalent to `map { x -> Void in`, but since we force the `Void` return Swift + is happier and gives you less hassle about your closure’s qualification. + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter body: The closure that is executed when this promise is fulfilled. + - Returns: A new cancellable promise fulfilled as `Void`. + + let context = firstly { + URLSession.shared.dataTask(.promise, with: url) + }.cancellize().done { response in + print(response.data) + }.cancelContext + + //… + + context.cancel() + */ + func done(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping (U.T) throws -> Void) -> CancellablePromise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) + return done(on: dispatcher, body) + } + + /** + The provided closure is executed when this cancellable promise is resolved. + + This is like `done` but it returns the same value that the handler is fed. + `get` immutably accesses the fulfilled value; the returned CancellablePromise maintains that value. + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter body: The closure that is executed when this CancellablePromise is fulfilled. + - Returns: A new cancellable promise that is resolved with the value that the handler is fed. For example: + + let context = firstly { + cancellize(Promise.value(1)) + }.get { foo in + print(foo, " is 1") + }.done { foo in + print(foo, " is 1") + }.done { foo in + print(foo, " is Void") + }.cancelContext + + //… + + context.cancel() + */ + func get(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping (U.T) throws -> Void) -> CancellablePromise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) + return get(on: dispatcher, body) + } + + /** + The provided closure is executed with cancellable promise result. + + This is like `get` but provides the Result of the CancellablePromise so you can inspect the value of the chain at this point without causing any side effects. + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter body: The closure that is executed with Result of CancellablePromise. + - Returns: A new cancellable promise that is resolved with the result that the handler is fed. For example: + + promise.tap{ print($0) }.then{ /*…*/ } + */ + func tap(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Result) -> Void) -> CancellablePromise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return tap(on: dispatcher, body) + } +} + +public extension CancellableThenable where U.T: Sequence { + /** + `CancellablePromise<[U.T]>` => `U.T` -> `V` => `CancellablePromise<[V]>` + + firstly { + cancellize(Promise.value([1,2,3])) + }.mapValues { integer in + integer * 2 + }.done { + // $0 => [2,4,6] + } + */ + func mapValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(U.T.Iterator.Element) throws -> V) -> CancellablePromise<[V]> { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return mapValues(on: dispatcher, transform) + } + + /** + `CancellablePromise<[U.T]>` => `U.T` -> `[V]` => `CancellablePromise<[V]>` + + firstly { + cancellize(Promise.value([1,2,3])) + }.flatMapValues { integer in + [integer, integer] + }.done { + // $0 => [1,1,2,2,3,3] + } + */ + func flatMapValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(U.T.Iterator.Element) throws -> V) -> CancellablePromise<[V.Iterator.Element]> { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return flatMapValues(on: dispatcher, transform) + } + + /** + `CancellablePromise<[U.T]>` => `U.T` -> `V?` => `CancellablePromise<[V]>` + + firstly { + cancellize(Promise.value(["1","2","a","3"])) + }.compactMapValues { + Int($0) + }.done { + // $0 => [1,2,3] + } + */ + func compactMapValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(U.T.Iterator.Element) throws -> V?) -> CancellablePromise<[V]> { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return compactMapValues(on: dispatcher, transform) + } + + /** + `CancellablePromise<[U.T]>` => `U.T` -> `CancellablePromise` => `CancellablePromise<[V]>` + + firstly { + cancellize(Promise.value([1,2,3])) + }.thenMap { integer in + cancellize(Promise.value(integer * 2)) + }.done { + // $0 => [2,4,6] + } + */ + func thenMap(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(U.T.Iterator.Element) throws -> V) -> CancellablePromise<[V.U.T]> { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return thenMap(on: dispatcher, transform) + } + + /** + `CancellablePromise<[U.T]>` => `U.T` -> `Promise` => `CancellablePromise<[V]>` + + firstly { + Promise.value([1,2,3]) + }.cancellize().thenMap { integer in + .value(integer * 2) + }.done { + // $0 => [2,4,6] + } + */ + func thenMap(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(U.T.Iterator.Element) throws -> V) -> CancellablePromise<[V.T]> { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return thenMap(on: dispatcher, transform) + } + + /** + `CancellablePromise<[T]>` => `T` -> `CancellablePromise<[U]>` => `CancellablePromise<[U]>` + + firstly { + cancellize(Promise.value([1,2,3])) + }.thenFlatMap { integer in + cancellize(Promise.value([integer, integer])) + }.done { + // $0 => [1,1,2,2,3,3] + } + */ + func thenFlatMap(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(U.T.Iterator.Element) throws -> V) -> CancellablePromise<[V.U.T.Iterator.Element]> where V.U.T: Sequence { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return thenFlatMap(on: dispatcher, transform) + } + + /** + `CancellablePromise<[T]>` => `T` -> `Promise<[U]>` => `CancellablePromise<[U]>` + + firstly { + Promise.value([1,2,3]) + }.cancellize().thenFlatMap { integer in + .value([integer, integer]) + }.done { + // $0 => [1,1,2,2,3,3] + } + */ + func thenFlatMap(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(U.T.Iterator.Element) throws -> V) -> CancellablePromise<[V.T.Iterator.Element]> where V.T: Sequence { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return thenFlatMap(on: dispatcher, transform) + } + + /** + `CancellablePromise<[T]>` => `T` -> Bool => `CancellablePromise<[U]>` + + firstly { + cancellize(Promise.value([1,2,3])) + }.filterValues { + $0 > 1 + }.done { + // $0 => [2,3] + } + */ + func filterValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ isIncluded: @escaping (U.T.Iterator.Element) -> Bool) -> CancellablePromise<[U.T.Iterator.Element]> { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return filterValues(on: dispatcher, isIncluded) + } +} + +public extension CancellableThenable where U.T: Collection { + func firstValue(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, where test: @escaping (U.T.Iterator.Element) -> Bool) -> CancellablePromise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return firstValue(on: dispatcher, where: test) + } +} + +public extension CancellableThenable where U.T: Sequence, U.T.Iterator.Element: Comparable { + /// - Returns: a cancellable promise fulfilled with the sorted values of this `Sequence`. + func sortedValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil) -> CancellablePromise<[U.T.Iterator.Element]> { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return sortedValues(on: dispatcher) + } +} + +public extension CancellableCatchMixin { + /** + The provided closure executes when this cancellable promise rejects. + + Rejecting a promise cascades: rejecting all subsequent promises (unless + recover is invoked) thus you will typically place your catch at the end + of a chain. Often utility promises will not have a catch, instead + delegating the error handling to the caller. + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter policy: The default policy does not execute your handler for cancellation errors. + - Parameter body: The handler to execute if this promise is rejected. + - Returns: A promise finalizer. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + */ + @discardableResult + func `catch`(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) -> Void) -> CancellableFinalizer { + let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) + return `catch`(on: dispatcher, policy: policy, body) + } + + /** + The provided closure executes when this cancellable promise rejects. + + Unlike `catch`, `recover` continues the chain. + Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: + + let context = firstly { + CLLocationManager.requestLocation() + }.recover { error in + guard error == CLError.unknownLocation else { throw error } + return .value(CLLocation.chicago) + }.cancelContext + + //… + + context.cancel() + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter policy: The default policy does not execute your handler for cancellation errors. + - Parameter body: The handler to execute if this promise is rejected. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + */ + func recover(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) throws -> V) -> CancellablePromise where V.U.T == C.T { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return recover(on: dispatcher, policy: policy, body) + } + + /** + The provided closure executes when this cancellable promise rejects. + + Unlike `catch`, `recover` continues the chain. + Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: + + let context = firstly { + CLLocationManager.requestLocation() + }.cancellize().recover { error in + guard error == CLError.unknownLocation else { throw error } + return .value(CLLocation.chicago) + }.cancelContext + + //… + + context.cancel() + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter policy: The default policy does not execute your handler for cancellation errors. + - Parameter body: The handler to execute if this promise is rejected. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + - Note: Methods with the `cancellable` prefix create a new CancellablePromise, and those without the `cancellable` prefix accept an existing CancellablePromise. + */ + func recover(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) throws -> V) -> CancellablePromise where V.T == C.T { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return recover(on: dispatcher, body) + } + + /** + The provided closure executes when this cancellable promise resolves, whether it rejects or not. + + let context = firstly { + UIApplication.shared.networkActivityIndicatorVisible = true + //… returns a cancellable promise + }.done { + //… + }.ensure { + UIApplication.shared.networkActivityIndicatorVisible = false + }.catch { + //… + }.cancelContext + + //… + + context.cancel() + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter body: The closure that executes when this promise resolves. + - Returns: A new promise, resolved with this promise’s resolution. + */ + func ensure(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping () -> Void) -> CancellablePromise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) + return ensure(on: dispatcher, body) + } + + /** + The provided closure executes when this cancellable promise resolves, whether it rejects or not. + The chain waits on the returned `CancellablePromise`. + + let context = firstly { + setup() // returns a cancellable promise + }.done { + //… + }.ensureThen { + teardown() // -> CancellablePromise + }.catch { + //… + }.cancelContext + + //… + + context.cancel() + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter body: The closure that executes when this promise resolves. + - Returns: A new cancellable promise, resolved with this promise’s resolution. + */ + func ensureThen(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping () -> CancellablePromise) -> CancellablePromise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) + return ensureThen(on: dispatcher, body) + } +} + +public extension CancellableFinalizer { + /// `finally` is the same as `ensure`, but it is not chainable + @discardableResult + func finally(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping () -> Void) -> CancelContext { + let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) + return finally(on: dispatcher, body) + } +} + +public extension CancellableCatchMixin where C.T == Void { + /** + The provided closure executes when this cancellable promise rejects. + + This variant of `recover` ensures that no error is thrown from the handler and allows specifying a catch policy. + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter policy: The default policy does not execute your handler for cancellation errors. + - Parameter body: The handler to execute if this promise is rejected. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + */ + func recover(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) throws -> Void) -> CancellablePromise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return recover(on: dispatcher, policy: policy, body) + } +} diff --git a/Sources/Error.swift b/Sources/Error.swift index 7229e6f49..1b8238a69 100644 --- a/Sources/Error.swift +++ b/Sources/Error.swift @@ -18,7 +18,10 @@ public enum PMKError: Error { /// The operation was cancelled case cancelled - + + /// The operation timed out and was cancelled + case timedOut + /// `nil` was returned from `flatMap` @available(*, deprecated, message: "See: `compactMap`") case flatMap(Any, Any.Type) @@ -49,6 +52,8 @@ extension PMKError: CustomDebugStringConvertible { return "Bad input was provided to a PromiseKit function" case .cancelled: return "The asynchronous sequence was cancelled" + case .timedOut: + return "The asynchronous sequence timed out" case .emptySequence: return "The first or last element was requested for an empty sequence" } @@ -76,6 +81,8 @@ extension Error { throw self } catch PMKError.cancelled { return true + } catch PMKError.timedOut { + return true } catch let error as CancellableError { return error.isCancelled } catch URLError.cancelled { diff --git a/Sources/Guarantee.swift b/Sources/Guarantee.swift index d007f8214..a33e87d85 100644 --- a/Sources/Guarantee.swift +++ b/Sources/Guarantee.swift @@ -23,6 +23,12 @@ public final class Guarantee: Thenable { body(box.seal) } + /// Returns a pending `Guarantee` that can be resolved with the provided closure’s parameter. + public convenience init(cancellable: Cancellable, resolver body: (@escaping(T) -> Void) -> Void) { + self.init(resolver: body) + setCancellable(cancellable) + } + /// - See: `Thenable.pipe` public func pipe(to: @escaping(Result) -> Void) { pipe{ to(.success($0)) } @@ -55,10 +61,13 @@ public final class Guarantee: Thenable { } final private class Box: EmptyBox { + var cancelled = false deinit { switch inspect() { case .pending: - PromiseKit.conf.logHandler(.pendingGuaranteeDeallocated) + if !cancelled { + PromiseKit.conf.logHandler(.pendingGuaranteeDeallocated) + } case .resolved: break } @@ -73,6 +82,35 @@ public final class Guarantee: Thenable { public class func pending() -> (guarantee: Guarantee, resolve: (T) -> Void) { return { ($0, $0.box.seal) }(Guarantee(.pending)) } + + var cancellable: Cancellable? + + public func setCancellable(_ cancellable: Cancellable) { + if let gb = (box as? Guarantee.Box) { + self.cancellable = CancellableWrapper(box: gb, cancellable: cancellable) + } else { + self.cancellable = cancellable + } + } + + final private class CancellableWrapper: Cancellable { + let box: Guarantee.Box + let cancellable: Cancellable + + init(box: Guarantee.Box, cancellable: Cancellable) { + self.box = box + self.cancellable = cancellable + } + + func cancel() { + box.cancelled = true + cancellable.cancel() + } + + var isCancelled: Bool { + return cancellable.isCancelled + } + } } public extension Guarantee { diff --git a/Sources/Promise.swift b/Sources/Promise.swift index 792f2d4da..74e7d1db6 100644 --- a/Sources/Promise.swift +++ b/Sources/Promise.swift @@ -64,6 +64,19 @@ public final class Promise: Thenable, CatchMixin { } } + /// Initialize a new promise that can be resolved with the provided `Resolver`. + public init(cancellable: Cancellable, resolver body: (Resolver) throws -> Void) { + box = EmptyBox() + let resolver = Resolver(box) + self.cancellable = cancellable + self.rejectIfCancelled = resolver.reject + do { + try body(resolver) + } catch { + resolver.reject(error) + } + } + /// - Returns: a tuple of a new pending promise and its `Resolver`. public class func pending() -> (promise: Promise, resolver: Resolver) { return { ($0, Resolver($0.box)) }(Promise(.pending)) @@ -99,6 +112,14 @@ public final class Promise: Thenable, CatchMixin { init(_: PMKUnambiguousInitializer) { box = EmptyBox() } + + var cancellable: Cancellable? + var rejectIfCancelled: ((Error) -> Void)? + + public func setCancellable(_ cancellable: Cancellable?, reject: ((Error) -> Void)? = nil) { + self.cancellable = cancellable + rejectIfCancelled = reject + } } public extension Promise { diff --git a/Sources/Thenable.swift b/Sources/Thenable.swift index 055d956f9..98206116f 100644 --- a/Sources/Thenable.swift +++ b/Sources/Thenable.swift @@ -274,6 +274,18 @@ public extension Thenable { } } +public extension Thenable { + /** + Converts a Promise or Guarantee into a promise that can be cancelled. + - Parameter thenable: The Thenable (Promise or Guarantee) to be made cancellable. + - Returns: A CancellablePromise that is a cancellable variant of the given Promise or Guarantee. + */ + func cancellize(cancelContext: CancelContext? = nil) -> CancellablePromise { + return CancellablePromise(self, cancelContext: cancelContext) + } +} + + public extension Thenable where T: Sequence { /** `Promise<[T]>` => `T` -> `U` => `Promise<[U]>` diff --git a/Sources/after.swift b/Sources/after.swift index ca31911fc..eadb0aa6f 100644 --- a/Sources/after.swift +++ b/Sources/after.swift @@ -1,17 +1,25 @@ import struct Foundation.TimeInterval import Dispatch + +/// Extend DispatchWorkItem to be cancellable +extension DispatchWorkItem: Cancellable { } + /** after(seconds: 1.5).then { //… } - Returns: A guarantee that resolves after the specified duration. +- Note: cancelling this guarantee will cancel the underlying timer task +- SeeAlso: [Cancellation](http://promisekit.org/docs/) */ public func after(seconds: TimeInterval) -> Guarantee { let (rg, seal) = Guarantee.pending() let when = DispatchTime.now() + seconds - q.asyncAfter(deadline: when, execute: { seal(()) }) + let task = DispatchWorkItem { seal(()) } + rg.setCancellable(task) + q.asyncAfter(deadline: when, execute: task) return rg } @@ -21,11 +29,15 @@ public func after(seconds: TimeInterval) -> Guarantee { } - Returns: A guarantee that resolves after the specified duration. + - Note: cancelling this guarantee will cancel the underlying timer task + - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ public func after(_ interval: DispatchTimeInterval) -> Guarantee { let (rg, seal) = Guarantee.pending() let when = DispatchTime.now() + interval - q.asyncAfter(deadline: when, execute: { seal(()) }) + let task = DispatchWorkItem { seal(()) } + rg.setCancellable(task) + q.asyncAfter(deadline: when, execute: task) return rg } diff --git a/Sources/firstly.swift b/Sources/firstly.swift index a5b477da1..e5db90f50 100644 --- a/Sources/firstly.swift +++ b/Sources/firstly.swift @@ -37,3 +37,53 @@ public func firstly(execute body: () throws -> U) -> Promise { public func firstly(execute body: () -> Guarantee) -> Guarantee { return body() } + +//////////////////////////////////////////////////////////// Cancellation + +/** + `firstly` for cancellable promises. + + Compare: + + let context = URLSession.shared.dataTask(url: url1).cancellize().then { + URLSession.shared.dataTask(url: url2) + }.then { + URLSession.shared.dataTask(url: url3) + }.cancelContext + + // … + + context.cancel() + + With: + + let context = firstly { + URLSession.shared.dataTask(url: url1) + }.cancellize().then { + URLSession.shared.dataTask(url: url2) + }.then { + URLSession.shared.dataTask(url: url3) + }.cancelContext + + // … + + context.cancel() + + - Note: the block you pass excecutes immediately on the current thread/queue. + - See: firstly(execute: () -> Thenable) +*/ +public func firstly(execute body: () throws -> V) -> CancellablePromise { + do { + let rv = try body() + let rp: CancellablePromise + if let promise = rv as? CancellablePromise { + rp = promise + } else { + rp = CancellablePromise(rv.thenable) + } + rp.appendCancelContext(from: rv) + return rp + } catch { + return CancellablePromise(error: error) + } +} diff --git a/Sources/hang.swift b/Sources/hang.swift index f5b45f5de..80892e327 100644 --- a/Sources/hang.swift +++ b/Sources/hang.swift @@ -53,3 +53,14 @@ public func hang(_ promise: Promise) throws -> T { return value } } + +//////////////////////////////////////////////////////////// Cancellation + +/** + Runs the active run-loop until the provided promise resolves. + + Simply calls `hang` directly on the delegate promise, so the behavior is exactly the same with Promise and CancellablePromise. + */ +public func hang(_ promise: CancellablePromise) throws -> T { + return try hang(promise.promise) +} diff --git a/Sources/race.swift b/Sources/race.swift index 2b817de26..7b55a0178 100644 --- a/Sources/race.swift +++ b/Sources/race.swift @@ -1,3 +1,5 @@ +import struct Foundation.TimeInterval + @inline(__always) private func _race(_ thenables: [U]) -> Promise { let rp = Promise(.pending) @@ -55,3 +57,88 @@ public func race(_ guarantees: Guarantee...) -> Guarantee { } return rg } + +//////////////////////////////////////////////////////////// Cancellation + +/** + Resolves with the first resolving cancellable promise from a set of cancellable promises. Calling + `cancel` on the race promise cancels all pending promises. All promises will be cancelled if any + promise rejects. + + let racePromise = race(promise1, promise2, promise3).then { winner in + //… + } + + //… + + racePromise.cancel() + + - Returns: A new promise that resolves when the first promise in the provided promises resolves. + - Warning: If any of the provided promises reject, the returned promise is rejected. + - Warning: aborts if the array is empty. +*/ +public func race(_ thenables: V...) -> CancellablePromise { + return race(thenables) +} + +/** + Resolves with the first resolving promise from a set of promises. Calling `cancel` on the race + promise cancels all pending promises. All promises will be cancelled if any promise rejects. + + let racePromise = race(promise1, promise2, promise3).then { winner in + //… + } + + //… + + racePromise.cancel() + + - Returns: A new promise that resolves when the first promise in the provided promises resolves. + - Warning: If any of the provided promises reject, the returned promise is rejected. + - Remark: Returns promise rejected with PMKError.badInput if empty array provided +*/ +public func race(_ thenables: [V]) -> CancellablePromise { + guard !thenables.isEmpty else { + return CancellablePromise(error: PMKError.badInput) + } + + let cancelThenables: (Result) -> Void = { result in + if case .failure = result { + for t in thenables { + if !t.cancelAttempted { + t.cancel() + } + } + } + } + + let promise = CancellablePromise(race(asThenables(thenables))) + for t in thenables { + t.thenable.pipe(to: cancelThenables) + promise.appendCancelContext(from: t) + } + return promise +} + +/** + Returns a promise that can be used to set a timeout for `race`. + + let promise1, promise2: Promise + race(promise1, promise2, timeout(seconds: 1.0)).done { winner in + //… + }.catch(policy: .allErrors) { + // Rejects with `PMKError.timedOut` if the timeout is exceeded before either `promise1` or + // `promise2` succeeds. + } + + When used with cancellable promises, all promises will be cancelled if the timeout is + exceeded or any promise rejects: + + let promise1, promise2: CancellablePromise + race(promise1, promise2, cancellize(timeout(seconds: 1.0))).done { winner in + //… + } + */ +public func timeout(seconds: TimeInterval) -> Promise { + return after(seconds: seconds).done { throw PMKError.timedOut } +} diff --git a/Sources/when.swift b/Sources/when.swift index ed654e0c5..6ab4f6b53 100644 --- a/Sources/when.swift +++ b/Sources/when.swift @@ -106,7 +106,7 @@ public func when Promise { - // ... + // … } let urls: [URL] = /*…*/ @@ -120,7 +120,7 @@ public func when...) -> Guarantee { public func when(guarantees: [Guarantee]) -> Guarantee { return when(fulfilled: guarantees).recover{ _ in }.asVoid() } + +//////////////////////////////////////////////////////////// Cancellation + +/** + Wait for all cancellable promises in a set to fulfill. + + For example: + + let p = when(fulfilled: promise1, promise2).then { results in + //… + }.catch { error in + switch error { + case URLError.notConnectedToInternet: + //… + case CLError.denied: + //… + } + } + + //… + + p.cancel() + + - Note: If *any* of the provided promises reject, the returned promise is immediately rejected with that error. + - Warning: In the event of rejection the other promises will continue to resolve and, as per any other promise, will either fulfill or reject. This is the right pattern for `getter` style asynchronous tasks, but often for `setter` tasks (eg. storing data on a server), you most likely will need to wait on all tasks and then act based on which have succeeded and which have failed, in such situations use `when(resolved:)`. + - Parameter promises: The promises upon which to wait before the returned promise resolves. + - Returns: A new promise that resolves when all the provided promises fulfill or one of the provided promises rejects. + - Note: `when` provides `NSProgress`. + - SeeAlso: `when(resolved:)` +*/ +public func when(fulfilled thenables: V...) -> CancellablePromise<[V.U.T]> { + let rp = CancellablePromise(when(fulfilled: asThenables(thenables))) + for t in thenables { + rp.appendCancelContext(from: t) + } + return rp +} + +public func when(fulfilled thenables: [V]) -> CancellablePromise<[V.U.T]> { + let rp = CancellablePromise(when(fulfilled: asThenables(thenables))) + for t in thenables { + rp.appendCancelContext(from: t) + } + return rp +} + +/// Wait for all cancellable promises in a set to fulfill. +public func when(fulfilled promises: V...) -> CancellablePromise where V.U.T == Void { + let rp = CancellablePromise(when(fulfilled: asThenables(promises))) + for p in promises { + rp.appendCancelContext(from: p) + } + return rp +} + +/// Wait for all cancellable promises in a set to fulfill. +public func when(fulfilled promises: [V]) -> CancellablePromise where V.U.T == Void { + let rp = CancellablePromise(when(fulfilled: asThenables(promises))) + for p in promises { + rp.appendCancelContext(from: p) + } + return rp +} + +/** + Wait for all cancellable promises in a set to fulfill. + + - Note: by convention the cancellable 'when' functions should not have a 'cancellable' prefix, however the prefix is necessary due to a compiler bug exemplified by the following: + + ```` + This works fine: + 1 func hi(_: String...) { } + 2 func hi(_: String, _: String) { } + 3 hi("hi", "there") + + This does not compile: + 1 func hi(_: String...) { } + 2 func hi(_: String, _: String) { } + 3 func hi(_: Int...) { } + 4 func hi(_: Int, _: Int) { } + 5 + 6 hi("hi", "there") // Ambiguous use of 'hi' (lines 1 & 2 are candidates) + 7 hi(1, 2) // Ambiguous use of 'hi' (lines 3 & 4 are candidates) + ```` + + - SeeAlso: `when(fulfilled:,_:)` +*/ +public func cancellableWhen(fulfilled pu: U, _ pv: V) -> CancellablePromise<(U.U.T, V.U.T)> { + return when(fulfilled: [pu.asVoid(), pv.asVoid()]).map(on: nil) { (pu.value!, pv.value!) } +} + +/// Wait for all cancellable promises in a set to fulfill. +/// - SeeAlso: `when(fulfilled:,_:)` +public func cancellableWhen(fulfilled pu: U, _ pv: V, _ pw: W) -> CancellablePromise<(U.U.T, V.U.T, W.U.T)> { + return when(fulfilled: [pu.asVoid(), pv.asVoid(), pw.asVoid()]).map(on: nil) { (pu.value!, pv.value!, pw.value!) } +} + +/// Wait for all cancellable promises in a set to fulfill. +/// - SeeAlso: `when(fulfilled:,_:)` +public func cancellableWhen(fulfilled pu: U, _ pv: V, _ pw: W, _ px: X) -> CancellablePromise<(U.U.T, V.U.T, W.U.T, X.U.T)> { + return when(fulfilled: [pu.asVoid(), pv.asVoid(), pw.asVoid(), px.asVoid()]).map(on: nil) { (pu.value!, pv.value!, pw.value!, px.value!) } +} + +/// Wait for all cancellable promises in a set to fulfill. +/// - SeeAlso: `when(fulfilled:,_:)` +public func cancellableWhen(fulfilled pu: U, _ pv: V, _ pw: W, _ px: X, _ py: Y) -> CancellablePromise<(U.U.T, V.U.T, W.U.T, X.U.T, Y.U.T)> { + return when(fulfilled: [pu.asVoid(), pv.asVoid(), pw.asVoid(), px.asVoid(), py.asVoid()]).map(on: nil) { (pu.value!, pv.value!, pw.value!, px.value!, py.value!) } +} + +/** + Generate cancellable promises at a limited rate and wait for all to fulfill. Call `cancel` on the returned promise to cancel all currently pending promises. + + For example: + + func downloadFile(url: URL) -> CancellablePromise { + // … + } + + let urls: [URL] = /*…*/ + let urlGenerator = urls.makeIterator() + + let generator = AnyIterator> { + guard url = urlGenerator.next() else { + return nil + } + return downloadFile(url) + } + + let promise = when(generator, concurrently: 3).done { datas in + // … + } + + // … + + promise.cancel() + + + No more than three downloads will occur simultaneously. + + - Note: The generator is called *serially* on a *background* queue. + - Warning: Refer to the warnings on `when(fulfilled:)` + - Parameter promiseGenerator: Generator of promises. + - Parameter cancel: Optional cancel context, overrides the default context. + - Returns: A new promise that resolves when all the provided promises fulfill or one of the provided promises rejects. + - SeeAlso: `when(resolved:)` + */ +public func when(fulfilled promiseIterator: It, concurrently: Int) -> CancellablePromise<[It.Element.U.T]> where It.Element: CancellableThenable { + guard concurrently > 0 else { + return CancellablePromise(error: PMKError.badInput) + } + + var pi = promiseIterator + var generatedPromises: [CancellablePromise] = [] + var rootPromise: CancellablePromise<[It.Element.U.T]>! + + let generator = AnyIterator> { + guard let promise = pi.next() as? CancellablePromise else { + return nil + } + if let root = rootPromise { + root.appendCancelContext(from: promise) + } else { + generatedPromises.append(promise) + } + return promise.promise + } + + rootPromise = CancellablePromise(when(fulfilled: generator, concurrently: concurrently)) + for p in generatedPromises { + rootPromise.appendCancelContext(from: p) + } + return rootPromise +} + +/** + Waits on all provided cancellable promises. + + `when(fulfilled:)` rejects as soon as one of the provided promises rejects. `when(resolved:)` waits on all provided promises and *never* rejects. When cancelled, all promises will attempt to be cancelled and those that are successfully cancelled will have a result of + PMKError.cancelled. + + let p = when(resolved: promise1, promise2, promise3, cancel: context).then { results in + for result in results where case .fulfilled(let value) { + //… + } + }.catch { error in + // invalid! Never rejects + } + + //… + + p.cancel() + + - Returns: A new promise that resolves once all the provided promises resolve. The array is ordered the same as the input, ie. the result order is *not* resolution order. + - Note: Any promises that error are implicitly consumed. + - Remark: Doesn't take CancellableThenable due to protocol associatedtype paradox +*/ +public func when(resolved promises: CancellablePromise...) -> CancellablePromise<[Result]> { + return when(resolved: promises) +} + +/// Waits on all provided cancellable promises. +/// - SeeAlso: `when(resolved:)` +public func when(resolved promises: [CancellablePromise]) -> CancellablePromise<[Result]> { + let rp = CancellablePromise(when(resolved: asPromises(promises))) + for p in promises { + rp.appendCancelContext(from: p) + } + return rp +} + +func asThenables(_ cancellableThenables: [V]) -> [V.U] { + var thenables: [V.U] = [] + for ct in cancellableThenables { + thenables.append(ct.thenable) + } + return thenables +} + +func asPromises(_ cancellablePromises: [CancellablePromise]) -> [Promise] { + var promises = [Promise]() + for cp in cancellablePromises { + promises.append(cp.promise) + } + return promises +} diff --git a/Tests/Cancel/AfterTests.swift b/Tests/Cancel/AfterTests.swift new file mode 100644 index 000000000..27460ea9b --- /dev/null +++ b/Tests/Cancel/AfterTests.swift @@ -0,0 +1,121 @@ +import Foundation +import XCTest +import PromiseKit + +extension XCTestExpectation { + open func fulfill(error: Error) { + fulfill() + } +} + +class AfterTests: XCTestCase { + func fail() { XCTFail() } + + func testZero() { + let ex2 = expectation(description: "") + let cc2 = after(seconds: 0).cancellize().done(fail).catch(policy: .allErrors, ex2.fulfill) + cc2.cancel() + waitForExpectations(timeout: 2, handler: nil) + + let ex3 = expectation(description: "") + let cc3 = after(.seconds(0)).cancellize().done(fail).catch(policy: .allErrors, ex3.fulfill) + cc3.cancel() + waitForExpectations(timeout: 2, handler: nil) + } + + func testNegative() { + let ex2 = expectation(description: "") + let cc2 = after(seconds: -1).cancellize().done(fail).catch(policy: .allErrors, ex2.fulfill) + cc2.cancel() + waitForExpectations(timeout: 2, handler: nil) + + let ex3 = expectation(description: "") + let cc3 = after(.seconds(-1)).cancellize().done(fail).catch(policy: .allErrors, ex3.fulfill) + cc3.cancel() + waitForExpectations(timeout: 2, handler: nil) + } + + func testPositive() { + let ex2 = expectation(description: "") + let cc2 = after(seconds: 1).cancellize().done(fail).catch(policy: .allErrors, ex2.fulfill) + cc2.cancel() + waitForExpectations(timeout: 2, handler: nil) + + let ex3 = expectation(description: "") + let cc3 = after(.seconds(1)).cancellize().done(fail).catch(policy: .allErrors, ex3.fulfill) + cc3.cancel() + waitForExpectations(timeout: 2, handler: nil) + } + + func testCancellableAfter() { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + + // Test the normal 'after' function + let exComplete = expectation(description: "after completes") + let afterPromise = after(seconds: 0) + afterPromise.done { + exComplete.fulfill() + }.catch { error in + XCTFail("afterPromise failed with error: \(error)") + } + + let exCancelComplete = expectation(description: "after completes") + + // Test cancellable `after` to ensure it is fulfilled if not cancelled + let cancelIgnoreAfterPromise = after(seconds: 0).cancellize() + cancelIgnoreAfterPromise.done { + exCancelComplete.fulfill() + }.catch(policy: .allErrors) { error in + XCTFail("cancelIgnoreAfterPromise failed with error: \(error)") + } + + // Test cancellable `after` to ensure it is cancelled + let cancellableAfterPromise = after(seconds: 0).cancellize() + cancellableAfterPromise.done { + XCTFail("cancellableAfter not cancelled") + }.catch(policy: .allErrorsExceptCancellation) { error in + XCTFail("cancellableAfterPromise failed with error: \(error)") + }.cancel() + + // Test cancellable `after` to ensure it is cancelled and throws a `CancellableError` + let exCancel = expectation(description: "after cancels") + let cancellableAfterPromiseWithError = after(seconds: 0).cancellize() + cancellableAfterPromiseWithError.done { + XCTFail("cancellableAfterWithError not cancelled") + }.catch(policy: .allErrors) { error in + error.isCancelled ? exCancel.fulfill() : XCTFail("unexpected error \(error)") + }.cancel() + + wait(for: [exComplete, exCancelComplete, exCancel], timeout: 1) + } + + func testCancelForPromise_Done() { + let exComplete = expectation(description: "done is cancelled") + + let promise = CancellablePromise { seal in + seal.fulfill(()) + } + promise.done { _ in + XCTFail("done not cancelled") + }.catch(policy: .allErrors) { error in + error.isCancelled ? exComplete.fulfill() : XCTFail("error: \(error)") + } + + promise.cancel() + + wait(for: [exComplete], timeout: 1) + } + + func testCancelForGuarantee_Done() { + let exComplete = expectation(description: "done is cancelled") + + after(seconds: 0).cancellize().done { _ in + XCTFail("done not cancelled") + }.catch(policy: .allErrors) { error in + error.isCancelled ? exComplete.fulfill() : XCTFail("error: \(error)") + }.cancel() + + wait(for: [exComplete], timeout: 1) + } +} diff --git a/Tests/Cancel/CancelChain.swift b/Tests/Cancel/CancelChain.swift new file mode 100644 index 000000000..e2345637d --- /dev/null +++ b/Tests/Cancel/CancelChain.swift @@ -0,0 +1,246 @@ +import XCTest +import PromiseKit + +class CancelChain: XCTestCase { + // Using a distinct type for each promise so we can tell which promise is which when using trace messages inside Thenable + struct A { } + struct B { } + struct C { } + struct D { } + struct E { } + + struct Chain { + let pA: CancellablePromise + let pB: CancellablePromise + let pC: CancellablePromise + let pD: CancellablePromise + let pE: CancellablePromise + } + + func trace(_ message: String) { + // print(message) + } + + func cancelChainPromises() -> Chain { + let pA = CancellablePromise { seal in + self.trace("A IN") + after(seconds: 0.05).cancellize().done { + self.trace("A FULFILL") + seal.fulfill(A()) + }.catch(policy: .allErrors) { + self.trace("A ERR") + seal.reject($0) + } + } + + let pB = CancellablePromise { seal in + self.trace("B IN") + after(seconds: 0.1).cancellize().done { + self.trace("B FULFILL") + seal.fulfill(B()) + }.catch(policy: .allErrors) { + self.trace("B ERR") + seal.reject($0) + } + } + + let pC = CancellablePromise { seal in + self.trace("C IN") + after(seconds: 0.15).cancellize().done { + self.trace("C FULFILL") + seal.fulfill(C()) + }.catch(policy: .allErrors) { + self.trace("C ERR") + seal.reject($0) + } + } + + let pD = CancellablePromise { seal in + self.trace("D IN") + after(seconds: 0.2).cancellize().done { + self.trace("D FULFILL") + seal.fulfill(D()) + }.catch(policy: .allErrors) { + self.trace("D ERR") + seal.reject($0) + } + } + + let pE = CancellablePromise { seal in + self.trace("E IN") + after(seconds: 0.25).cancellize().done { + self.trace("E FULFILL") + seal.fulfill(E()) + }.catch(policy: .allErrors) { + self.trace("E ERR") + seal.reject($0) + } + } + + return Chain(pA: pA, pB: pB, pC: pC, pD: pD, pE: pE) + } + + struct exABCDE { + let a: XCTestExpectation? + let b: XCTestExpectation? + let c: XCTestExpectation? + let d: XCTestExpectation? + let e: XCTestExpectation? + let cancelled: XCTestExpectation? + + let cancelA: Bool + let cancelB: Bool + let cancelC: Bool + let cancelD: Bool + let cancelE: Bool + } + + func cancelChainSetup(ex: exABCDE) { + { + let c = cancelChainPromises() + + c.pA.then { (_: A) -> CancellablePromise in + self.trace("pA.then") + return firstly { () -> CancellablePromise in + self.trace("pB.firstly") + return c.pB + }.then { (_: B) -> CancellablePromise in + self.trace("pB.then") + return firstly { () -> CancellablePromise in + self.trace("pC.firstly") + if ex.cancelB { + self.trace("CANCEL") + c.pA.cancel() + } + ex.b?.fulfill() ?? XCTFail("pB.then") + return c.pC + }.then { (_: C) -> CancellablePromise in + ex.c?.fulfill() ?? XCTFail("pC.then") + if ex.cancelC { + self.trace("CANCEL") + c.pA.cancel() + } + self.trace("pC.then") + return c.pD + } + }.then { (_: D) -> CancellablePromise in + ex.d?.fulfill() ?? XCTFail("pD.done") + if ex.cancelD { + self.trace("CANCEL") + c.pA.cancel() + } + return c.pA // Intentional reuse of pA -- causes a loop that CancelContext must detect + } + }.then { (_: A) -> CancellablePromise in + self.trace("pA.then") + ex.a?.fulfill() ?? XCTFail("pA completed") + if ex.cancelA { + self.trace("CANCEL") + c.pA.cancel() + } + return c.pE + }.done { _ in + ex.e?.fulfill() ?? XCTFail("pE completed") + if ex.cancelE { + self.trace("CANCEL") + c.pA.cancel() + } + self.trace("pE.done") + }.catch(policy: .allErrors) { + self.trace("Error: \($0)") + $0.isCancelled ? ex.cancelled?.fulfill() : XCTFail("Error: \($0)") + } + + self.trace("SETUP COMPLETE") + +#if swift(>=4.1) + let expectations = [ex.a, ex.b, ex.c, ex.d, ex.e, ex.cancelled].compactMap { $0 } +#else + let expectations = [ex.a, ex.b, ex.c, ex.d, ex.e, ex.cancelled].flatMap { $0 } +#endif + wait(for: expectations, timeout: 1) + + XCTAssert(c.pA.cancelContext.cancelAttempted) + XCTAssert(ex.a == nil || isFulfilled(c.pB) || c.pB.cancelContext.cancelAttempted) + XCTAssert(ex.b == nil || isFulfilled(c.pC) || c.pC.cancelContext.cancelAttempted) + XCTAssert(ex.c == nil || isFulfilled(c.pD) || c.pD.cancelContext.cancelAttempted) + XCTAssert(ex.d == nil || isFulfilled(c.pE) || c.pE.cancelContext.cancelAttempted) + }() + + self.trace("DONE") + + return + } + + func isFulfilled(_ p: CancellablePromise) -> Bool { + if let result = p.promise.result { + if case .success = result { + return true + } else { + return false + } + } else { + return false + } + } + + func testCancelChainPB() { + let ex = exABCDE(a: nil, + b: expectation(description: "pB completed"), + c: nil, + d: nil, + e: nil, + cancelled: expectation(description: "cancelled"), + cancelA: false, + cancelB: true, + cancelC: false, + cancelD: false, + cancelE: false) + cancelChainSetup(ex: ex) + } + + func testCancelChainPC() { + let ex = exABCDE(a: nil, + b: expectation(description: "pB completed"), + c: expectation(description: "pC completed"), + d: nil, + e: nil, + cancelled: expectation(description: "cancelled"), + cancelA: false, + cancelB: false, + cancelC: true, + cancelD: false, + cancelE: false) + cancelChainSetup(ex: ex) + } + + func testCancelChainPAD() { + let ex = exABCDE(a: expectation(description: "pA completed"), + b: expectation(description: "pB completed"), + c: expectation(description: "pC completed"), + d: expectation(description: "pD completed"), + e: nil, + cancelled: expectation(description: "cancelled"), + cancelA: true, + cancelB: false, + cancelC: false, + cancelD: false, + cancelE: false) + cancelChainSetup(ex: ex) + } + + func testCancelChainSuccess() { + let ex = exABCDE(a: expectation(description: "pA completed"), + b: expectation(description: "pB completed"), + c: expectation(description: "pC completed"), + d: expectation(description: "pD completed"), + e: expectation(description: "pE completed"), + cancelled: nil, + cancelA: false, + cancelB: false, + cancelC: false, + cancelD: false, + cancelE: true) + cancelChainSetup(ex: ex) + } +} diff --git a/Tests/Cancel/CancellableErrorTests.swift b/Tests/Cancel/CancellableErrorTests.swift new file mode 100644 index 000000000..612925b74 --- /dev/null +++ b/Tests/Cancel/CancellableErrorTests.swift @@ -0,0 +1,136 @@ +import Foundation +import PromiseKit +import XCTest + +class CancellationTests: XCTestCase { + func testCancellation() { + let ex1 = expectation(description: "") + + let p = after(seconds: 0).cancellize().done { _ in + XCTFail() + } + p.catch { _ in + XCTFail() + } + p.catch(policy: .allErrors) { + XCTAssertTrue($0.isCancelled) + ex1.fulfill() + } + + p.cancel(with: LocalError.cancel) + + waitForExpectations(timeout: 1) + } + + func testThrowCancellableErrorThatIsNotCancelled() { + let expect = expectation(description: "") + + let cc = after(seconds: 0).cancellize().done { + XCTFail() + }.catch { + XCTAssertFalse($0.isCancelled) + expect.fulfill() + } + + cc.cancel(with: LocalError.notCancel) + + waitForExpectations(timeout: 1) + } + + func testRecoverWithCancellation() { + let ex1 = expectation(description: "") + let ex2 = expectation(description: "") + + let p = after(seconds: 0).cancellize().done { _ in + XCTFail() + }.recover(policy: .allErrors) { err -> CancellablePromise in + ex1.fulfill() + XCTAssertTrue(err.isCancelled) + throw err + }.done { _ in + XCTFail() + } + p.catch { _ in + XCTFail() + } + p.catch(policy: .allErrors) { + XCTAssertTrue($0.isCancelled) + ex2.fulfill() + } + + p.cancel(with: CocoaError.cancelled) + + waitForExpectations(timeout: 1) + } + + func testFoundationBridging1() { + let ex = expectation(description: "") + + let p = after(seconds: 0).cancellize().done { _ in + XCTFail() + } + p.catch { _ in + XCTFail() + } + p.catch(policy: .allErrors) { + XCTAssertTrue($0.isCancelled) + ex.fulfill() + } + + p.cancel(with: CocoaError.cancelled) + + waitForExpectations(timeout: 1) + } + + func testFoundationBridging2() { + let ex = expectation(description: "") + + let p = CancellablePromise().done { + XCTFail() + } + p.catch { _ in + XCTFail() + } + p.catch(policy: .allErrors) { + XCTAssertTrue($0.isCancelled) + ex.fulfill() + } + + p.cancel(with: URLError.cancelled) + + waitForExpectations(timeout: 1) + } + +#if swift(>=3.2) + func testIsCancelled() { + XCTAssertTrue(PMKError.cancelled.isCancelled) + XCTAssertTrue(URLError.cancelled.isCancelled) + XCTAssertTrue(CocoaError.cancelled.isCancelled) + XCTAssertFalse(CocoaError(_nsError: NSError(domain: NSCocoaErrorDomain, code: CocoaError.Code.coderInvalidValue.rawValue)).isCancelled) + } +#endif +} + +private enum LocalError: CancellableError { + case notCancel + case cancel + + var isCancelled: Bool { + switch self { + case .notCancel: return false + case .cancel: return true + } + } +} + +private extension URLError { + static var cancelled: URLError { + return .init(_nsError: NSError(domain: NSURLErrorDomain, code: URLError.Code.cancelled.rawValue)) + } +} + +private extension CocoaError { + static var cancelled: CocoaError { + return .init(_nsError: NSError(domain: NSCocoaErrorDomain, code: CocoaError.Code.userCancelled.rawValue)) + } +} diff --git a/Tests/Cancel/CancellablePromiseTests.swift b/Tests/Cancel/CancellablePromiseTests.swift new file mode 100644 index 000000000..7485a3c50 --- /dev/null +++ b/Tests/Cancel/CancellablePromiseTests.swift @@ -0,0 +1,205 @@ +import Foundation +import PromiseKit +import XCTest + +class CancellablePromiseTests: XCTestCase { + func login() -> Promise { + return Promise.value(1) + } + + func fetch(avatar: Int) -> CancellablePromise { + return Promise.value(avatar + 2).cancellize() + } + + func testCancellablePromiseEmbeddedInStandardPromiseChain() { + let ex = expectation(description: "") + var imageView: Int? + let promise = firstly { /// <-- ERROR: Ambiguous reference to member 'firstly(execute:)' + /* The 'cancellize' method initiates a cancellable promise chain by + returning a 'CancellablePromise'. */ + login().cancellize() /// CHANGE TO: "login().cancellize()" + }.then { creds in + self.fetch(avatar: creds) + }.done { image in + imageView = image + XCTAssert(imageView == 3) + XCTFail() + }.catch(policy: .allErrors) { error in + if error.isCancelled { + // the chain has been cancelled! + ex.fulfill() + } else { + XCTFail() + } + } + + // … + + promise.cancel() + + waitForExpectations(timeout: 1) + } + + func testReturnTypeForAMultiLineClosureIsNotExplicitlyStated() { + let ex = expectation(description: "") + var imageView: Int? + firstly { + login() + }.cancellize().then { creds -> CancellablePromise in + let f = self.fetch(avatar: creds) + return f + }.done { image in + imageView = image + XCTAssert(imageView == 3) + ex.fulfill() + }.catch(policy: .allErrors) { error in + XCTFail() + } + + waitForExpectations(timeout: 1) + } + + func testTryingToCancelAStandardPromiseChain() { + let ex = expectation(description: "") + var imageView: Int? + let promise = firstly { + login() + }.cancellize().then { creds in + self.fetch(avatar: creds) + }.done { image in + imageView = image + XCTAssert(imageView == 3) + XCTFail() + }.catch(policy: .allErrors) { error in + if error.isCancelled { + // the chain has been cancelled! + ex.fulfill() + } else { + XCTFail() + } + } + + // … + + promise.cancel() /// <-- ERROR: Value of type 'PMKFinalizer' has no member 'cancel' + + waitForExpectations(timeout: 1) + } + + func testCancel() { + let ex = expectation(description: "") + let p = CancellablePromise.pending() + p.promise.then { (val: Int) -> Promise in + Promise.value("hi") + }.done { _ in + XCTFail() + ex.fulfill() + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + } + p.resolver.fulfill(3) + p.promise.cancel() + + wait(for: [ex], timeout: 1) + } + + func testFirstly() { + let ex = expectation(description: "") + firstly { + Promise.value(3) + }.cancellize().then { (_: Int) -> Promise in + XCTFail() + return Promise.value("hi") + }.done { _ in + XCTFail() + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + }.cancel() + + wait(for: [ex], timeout: 1) + } + + func testFirstlyWithPromise() { + let ex = expectation(description: "") + firstly { + return Promise.value(3) + }.cancellize().then { (_: Int) -> Promise in + XCTFail() + return Promise.value("hi") + }.done { _ in + XCTFail() + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail("\($0)") + }.cancel() + + wait(for: [ex], timeout: 1) + } + + func testThenMapSuccess() { + let ex = expectation(description: "") + firstly { + Promise.value([1,2,3]) + }.cancellize().thenMap { (integer: Int) -> Promise in + return Promise.value(integer * 2) + }.done { _ in + ex.fulfill() + // $0 => [2,4,6] + }.catch(policy: .allErrors) { _ in + XCTFail() + } + waitForExpectations(timeout: 1) + } + + func testThenMapCancel() { + let ex = expectation(description: "") + firstly { + Promise.value([1,2,3]) + }.cancellize().thenMap { (integer: Int) -> Promise in + XCTFail() + return Promise.value(integer * 2) + }.done { _ in + XCTFail() + // $0 => [2,4,6] + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + }.cancel() + waitForExpectations(timeout: 1) + } + + func testChain() { + let ex = expectation(description: "") + firstly { + Promise.value(1) + }.cancellize().then { (integer: Int) -> Promise in + XCTFail() + return Promise.value(integer * 2) + }.done { _ in + // $0 => [2,4,6] + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + }.cancel() + waitForExpectations(timeout: 1) + } + + func testBridge() { + let ex1 = expectation(description: "") + let ex2 = expectation(description: "") + + let (promise, seal) = Promise.pending() + DispatchQueue.global(qos: .default).asyncAfter(deadline: DispatchTime.now() + 0.2) { seal.fulfill(()) } + + CancellablePromise(promise).done { _ in + XCTFail() + }.catch(policy: .allErrors) { + $0.isCancelled ? ex2.fulfill() : XCTFail() + }.cancel() + + promise.done { _ in + ex1.fulfill() + }.catch(policy: .allErrors) { _ in + XCTFail() + } + + waitForExpectations(timeout: 1) + } +} diff --git a/Tests/Cancel/CatchableTests.swift b/Tests/Cancel/CatchableTests.swift new file mode 100644 index 000000000..fc4520986 --- /dev/null +++ b/Tests/Cancel/CatchableTests.swift @@ -0,0 +1,395 @@ +import PromiseKit +import Dispatch +import XCTest + +class CatchableTests: XCTestCase { + + func testFinally() { + let finallyQueue = DispatchQueue(label: "\(#file):\(#line)", attributes: .concurrent) + + func helper(error: Error, on queue: DispatchQueue = .main, flags: DispatchWorkItemFlags? = nil) { + let ex = (expectation(description: ""), expectation(description: "")) + var x = 0 + let p = after(seconds: 0.01).cancellize().catch(policy: .allErrors) { _ in + XCTAssertEqual(x, 0) + x += 1 + ex.0.fulfill() + }.finally(on: queue, flags: flags) { + if let flags = flags, flags.contains(.barrier) { + dispatchPrecondition(condition: .onQueueAsBarrier(queue)) + } else { + dispatchPrecondition(condition: .onQueue(queue)) + } + XCTAssertEqual(x, 1) + x += 1 + ex.1.fulfill() + } + + p.cancel(with: error) + + wait(for: [ex.0, ex.1], timeout: 10) + } + + helper(error: Error.dummy) + helper(error: Error.cancelled) + helper(error: Error.dummy, on: finallyQueue) + helper(error: Error.dummy, on: finallyQueue, flags: .barrier) + } + + func testCauterize() { + let ex = expectation(description: "") + let p = after(seconds: 0.01).cancellize() + + // cannot test specifically that this outputs to console, + // but code-coverage will note that the line is run + p.cauterize() + + p.catch { _ in + ex.fulfill() + } + + p.cancel(with: Error.dummy) + + wait(for: [ex], timeout: 1) + } +} + +/// `Promise.recover` +extension CatchableTests { + func test__void_specialized_full_recover() { + + func helper(policy: CatchPolicy, error: Swift.Error, line: UInt = #line) { + let ex = expectation(description: "error caught") + CancellablePromise(error: error).recover { _ in }.done { _ in XCTFail() }.catch(policy: .allErrors, ex.fulfill).cancel() + wait(for: [ex], timeout: 1) + } + + helper(policy: .allErrorsExceptCancellation, error: Error.dummy) + helper(policy: .allErrors, error: Error.dummy) + helper(policy: .allErrorsExceptCancellation, error: Error.cancelled) + helper(policy: .allErrors, error: Error.cancelled) + + let ex2 = expectation(description: "cancel caught") + let d2 = CancellablePromise(error: Error.cancelled).recover(policy: .allErrors) { _ in }.done(ex2.fulfill) + d2.cancel() + wait(for: [ex2], timeout: 1) + } + + func test__void_specialized_full_recover__fulfilled_path() { + let ex = expectation(description: "") + CancellablePromise().recover { _ in + XCTFail() + }.done { _ in + XCTFail() + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + }.cancel() + wait(for: [ex], timeout: 1) + + let ex2 = expectation(description: "") + let promise = CancellablePromise() + promise.cancel() + promise.recover(policy: .allErrors) { _ in }.done(ex2.fulfill).catch(policy: .allErrors) { _ in XCTFail() } + wait(for: [ex2], timeout: 1) + } + + func test__void_specialized_conditional_recover() { + func helperDone(policy: CatchPolicy, error: Swift.Error, line: UInt = #line) { + let ex = expectation(description: "") + var x = 0 + let promise = CancellablePromise(error: error).recover(policy: policy) { (err: Swift.Error) throws -> Void in + guard x < 1 else { throw err } + x += 1 + }.done(ex.fulfill).catch(policy: .allErrors) { _ in + XCTFail() + } + promise.cancel() + wait(for: [ex], timeout: 1) + } + + func helperCatch(policy: CatchPolicy, error: Swift.Error, line: UInt = #line) { + let ex = expectation(description: "") + var x = 0 + let promise = CancellablePromise(error: error).recover(policy: policy) { (err: Swift.Error) throws -> Void in + guard x < 1 else { throw err } + x += 1 + }.done { _ in + XCTFail() + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + } + promise.cancel() + wait(for: [ex], timeout: 1) + } + + for error in [Error.dummy as Swift.Error, Error.cancelled] { + helperDone(policy: .allErrors, error: error) + } + helperCatch(policy: .allErrorsExceptCancellation, error: Error.dummy) + } + + func test__void_specialized_conditional_recover__no_recover() { + + func helper(policy: CatchPolicy, error: Error, line: UInt = #line) { + let ex = expectation(description: "") + CancellablePromise(error: error).recover(policy: .allErrorsExceptCancellation) { err in + throw err + }.catch(policy: .allErrors) { _ in + ex.fulfill() + }.cancel() + wait(for: [ex], timeout: 1) + } + + for error in [Error.dummy, Error.cancelled] { + helper(policy: .allErrors, error: error) + } + helper(policy: .allErrorsExceptCancellation, error: Error.dummy) + } + + func test__void_specialized_conditional_recover__ignores_cancellation_but_fed_cancellation() { + let ex = expectation(description: "") + CancellablePromise(error: Error.cancelled).recover(policy: .allErrorsExceptCancellation) { _ in + XCTFail() + }.catch(policy: .allErrors) { + XCTAssertEqual(Error.cancelled, $0 as? Error) + ex.fulfill() + }.cancel() + wait(for: [ex], timeout: 1) + } + + func test__void_specialized_conditional_recover__fulfilled_path() { + let ex = expectation(description: "") + let p = CancellablePromise().recover { _ in + XCTFail() + }.catch { _ in + XCTFail() // this `catch` to ensure we are calling the `recover` variant we think we are + }.finally { + ex.fulfill() + } + p.cancel() + wait(for: [ex], timeout: 1) + } +} + +/// `Promise.recover` +extension CatchableTests { + func test__full_recover() { + func helper(error: Swift.Error) { + let ex = expectation(description: "") + CancellablePromise(error: error).recover { _ in + return Promise.value(2).cancellize() + }.done { _ in + XCTFail() + }.catch(policy: .allErrors, ex.fulfill).cancel() + wait(for: [ex], timeout: 1) + } + + helper(error: Error.dummy) + helper(error: Error.cancelled) + } + + func test__full_recover__fulfilled_path() { + let ex = expectation(description: "") + Promise.value(1).cancellize().recover { _ -> CancellablePromise in + XCTFail() + return Promise.value(2).cancellize() + }.done { _ in + XCTFail() + }.catch(policy: .allErrors) { error in + error.isCancelled ? ex.fulfill() : XCTFail() + }.cancel() + wait(for: [ex], timeout: 1) + } + + func test__conditional_recover() { + func helper(policy: CatchPolicy, error: Swift.Error, line: UInt = #line) { + let ex = expectation(description: "\(policy) \(error) \(line)") + var x = 0 + CancellablePromise(error: error).recover(policy: policy) { (err: Swift.Error) throws -> CancellablePromise in + guard x < 1 else { + throw err + } + x += 1 + return Promise.value(x).cancellize() + }.done { _ in + ex.fulfill() + }.catch(policy: .allErrors) { error in + if policy == .allErrorsExceptCancellation { + error.isCancelled ? ex.fulfill() : XCTFail() + } else { + XCTFail() + } + }.cancel() + wait(for: [ex], timeout: 1) + } + + for error in [Error.dummy as Swift.Error, Error.cancelled] { + helper(policy: .allErrors, error: error) + } + helper(policy: .allErrorsExceptCancellation, error: Error.dummy) + } + + func test__conditional_recover__no_recover() { + + func helper(policy: CatchPolicy, error: Error, line: UInt = #line) { + let ex = expectation(description: "\(policy) \(error) \(line)") + CancellablePromise(error: error).recover(policy: policy) { err -> CancellablePromise in + throw err + }.catch(policy: .allErrors) { + if !(($0 as? PMKError)?.isCancelled ?? false) { + XCTAssertEqual(error, $0 as? Error) + } + ex.fulfill() + }.cancel() + wait(for: [ex], timeout: 1) + } + + for error in [Error.dummy, Error.cancelled] { + helper(policy: .allErrors, error: error) + } + helper(policy: .allErrorsExceptCancellation, error: Error.dummy) + } + + func test__conditional_recover__ignores_cancellation_but_fed_cancellation() { + let ex = expectation(description: "") + CancellablePromise(error: Error.cancelled).recover(policy: .allErrorsExceptCancellation) { _ -> CancellablePromise in + XCTFail() + return Promise.value(1).cancellize() + }.catch(policy: .allErrors) { + XCTAssertEqual(Error.cancelled, $0 as? Error) + ex.fulfill() + }.cancel() + wait(for: [ex], timeout: 1) + } + + func test__conditional_recover__fulfilled_path() { + let ex = expectation(description: "") + Promise.value(1).cancellize().recover { err -> CancellablePromise in + XCTFail() + throw err + }.done { + XCTFail() + XCTAssertEqual($0, 1) + }.catch(policy: .allErrors) { error in + error.isCancelled ? ex.fulfill() : XCTFail() + }.cancel() + wait(for: [ex], timeout: 1) + } + + func test__cancellable_conditional_recover__fulfilled_path() { + let ex = expectation(description: "") + Promise.value(1).cancellize().recover { err -> Promise in + XCTFail() + throw err + }.done { + XCTFail() + XCTAssertEqual($0, 1) + }.catch(policy: .allErrors) { error in + error.isCancelled ? ex.fulfill() : XCTFail() + }.cancel() + wait(for: [ex], timeout: 1) + } + + func testEnsureThen_Error() { + let ex = expectation(description: "") + + let p = Promise.value(1).cancellize().done { + XCTAssertEqual($0, 1) + throw Error.dummy + }.ensureThen { + return after(seconds: 0.01).cancellize() + }.catch(policy: .allErrors) { + XCTAssert(($0 as? PMKError)?.isCancelled ?? false) + }.finally { + ex.fulfill() + } + p.cancel() + + wait(for: [ex], timeout: 1) + } + + func testEnsureThen_Value() { + let ex = expectation(description: "") + + Promise.value(1).cancellize().ensureThen { + after(seconds: 0.01).cancellize() + }.done { _ in + XCTFail() + }.catch(policy: .allErrors) { + if !$0.isCancelled { + XCTFail() + } + }.finally { + ex.fulfill() + }.cancel() + + wait(for: [ex], timeout: 1) + } + + func testEnsureThen_Value_NotCancelled() { + let ex = expectation(description: "") + + Promise.value(1).cancellize().ensureThen { + after(seconds: 0.01).cancellize() + }.done { + XCTAssertEqual($0, 1) + }.catch(policy: .allErrors) { _ in + XCTFail() + }.finally { + ex.fulfill() + } + + wait(for: [ex], timeout: 1) + } + + func testCancellableFinalizerHelpers() { + let ex = expectation(description: "") + + let f = Promise.value(1).cancellize().done { _ in + XCTFail() + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + } + f.cancel() + + XCTAssertEqual(f.isCancelled, true) + XCTAssertEqual(f.cancelAttempted, true) + XCTAssert(f.cancelledError?.isCancelled ?? false) + + wait(for: [ex], timeout: 1) + } + + func testCancellableRecoverFromError() { + let ex = expectation(description: "") + + let p = Promise(error: PMKError.emptySequence).cancellize().recover(policy: .allErrors) { _ in + Promise.value(1) + }.done { + XCTAssertEqual($0, 1) + ex.fulfill() + } + let f = p.catch(policy: .allErrors) { _ in + XCTFail() + } + + XCTAssertEqual(f.isCancelled, false) + XCTAssertEqual(f.cancelAttempted, false) + XCTAssert(f.cancelledError == nil) + XCTAssert(p.cancelledError == nil) + + wait(for: [ex], timeout: 1) + + XCTAssertEqual(p.isPending, false) + XCTAssertEqual(p.isResolved, true) + XCTAssertEqual(p.isFulfilled, true) + } +} + +private enum Error: CancellableError { + case dummy + case cancelled + + var isCancelled: Bool { + return self == Error.cancelled + } +} diff --git a/Tests/Cancel/DefaultDispatchQueueTests.swift b/Tests/Cancel/DefaultDispatchQueueTests.swift new file mode 100644 index 000000000..2cc3c79ee --- /dev/null +++ b/Tests/Cancel/DefaultDispatchQueueTests.swift @@ -0,0 +1,74 @@ +import class Foundation.Thread +import PromiseKit +import Dispatch +import XCTest + +private enum Error: Swift.Error { case dummy } + +class CancellableDefaultDispatchQueueTest: XCTestCase { + + let myQueue = DispatchQueue(label: "myQueue") + + override func setUp() { + // can actually only set the default queue once + // - See: PMKSetDefaultDispatchQueue + conf.Q = (myQueue, myQueue) + } + + override func tearDown() { + conf.Q = (.main, .main) + } + + func testOverrodeDefaultThenQueue() { + let ex = expectation(description: "resolving") + + let p = Promise.value(1).cancellize() + p.cancel() + p.then { _ -> CancellablePromise in + XCTFail() + XCTAssertFalse(Thread.isMainThread) + return CancellablePromise() + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + XCTAssertFalse(Thread.isMainThread) + } + + XCTAssertTrue(Thread.isMainThread) + + waitForExpectations(timeout: 1) + } + + func testOverrodeDefaultCatchQueue() { + let ex = expectation(description: "resolving") + + let p = CancellablePromise(error: Error.dummy) + p.cancel() + p.catch { _ in + ex.fulfill() + XCTAssertFalse(Thread.isMainThread) + } + + XCTAssertTrue(Thread.isMainThread) + + waitForExpectations(timeout: 1) + } + + func testOverrodeDefaultAlwaysQueue() { + let ex = expectation(description: "resolving") + let ex2 = expectation(description: "catching") + + let p = Promise.value(1).cancellize() + p.cancel() + p.ensure { + ex.fulfill() + XCTAssertFalse(Thread.isMainThread) + }.catch(policy: .allErrors) { + $0.isCancelled ? ex2.fulfill() : XCTFail() + XCTAssertFalse(Thread.isMainThread) + } + + XCTAssertTrue(Thread.isMainThread) + + waitForExpectations(timeout: 1) + } +} diff --git a/Tests/Cancel/DispatcherTests.swift b/Tests/Cancel/DispatcherTests.swift new file mode 100644 index 000000000..0274212cb --- /dev/null +++ b/Tests/Cancel/DispatcherTests.swift @@ -0,0 +1,205 @@ +import Dispatch +import PromiseKit +import XCTest + +fileprivate let queueIDKey = DispatchSpecificKey() + +class RecordingDispatcher: Dispatcher { + + static var queueIndex = 1 + + var dispatchCount = 0 + let queue: DispatchQueue + + init() { + queue = DispatchQueue(label: "org.promisekit.testqueue \(RecordingDispatcher.queueIndex)") + RecordingDispatcher.queueIndex += 1 + } + + func dispatch(_ body: @escaping () -> Void) { + dispatchCount += 1 + queue.async(execute: body) + } + +} + +class DispatcherTests: XCTestCase { + + var dispatcher = RecordingDispatcher() + + override func setUp() { + dispatcher = RecordingDispatcher() + } + + func testDispatcherWithThrow() { + let ex = expectation(description: "Dispatcher with throw") + CancellablePromise { seal in + seal.fulfill(42) + }.map(on: dispatcher) { _ in + throw PMKError.badInput + }.catch(on: dispatcher) { _ in + ex.fulfill() + } + waitForExpectations(timeout: 1) + XCTAssertEqual(self.dispatcher.dispatchCount, 2) + } + + func testDispatchQueueSelection() { + + let ex = expectation(description: "DispatchQueue compatibility") + + let oldConf = PromiseKit.conf.D + PromiseKit.conf.D = (map: dispatcher, return: dispatcher) + + let background = DispatchQueue.global(qos: .background) + background.setSpecific(key: queueIDKey, value: 100) + DispatchQueue.main.setSpecific(key: queueIDKey, value: 102) + dispatcher.queue.setSpecific(key: queueIDKey, value: 103) + + Promise.value(42).cancellize().map(on: .global(qos: .background), flags: .barrier) { (x: Int) -> Int in + let queueID = DispatchQueue.getSpecific(key: queueIDKey) + XCTAssertNotNil(queueID) + XCTAssertEqual(queueID!, 100) + return x + 10 + }.get(on: .global(qos: .background), flags: .barrier) { _ in + }.tap(on: .global(qos: .background), flags: .barrier) { _ in + }.then(on: .main, flags: []) { (x: Int) -> CancellablePromise in + XCTAssertEqual(x, 52) + let queueID = DispatchQueue.getSpecific(key: queueIDKey) + XCTAssertNotNil(queueID) + XCTAssertEqual(queueID!, 102) + return Promise.value(50).cancellize() + }.map(on: nil) { (x: Int) -> Int in + let queueID = DispatchQueue.getSpecific(key: queueIDKey) + XCTAssertNotNil(queueID) + XCTAssertEqual(queueID!, 102) + return x + 10 + }.map { (x: Int) -> Int in + XCTAssertEqual(x, 60) + let queueID = DispatchQueue.getSpecific(key: queueIDKey) + XCTAssertNotNil(queueID) + XCTAssertEqual(queueID!, 103) + return x + 10 + }.done(on: background) { + XCTAssertEqual($0, 70) + let queueID = DispatchQueue.getSpecific(key: queueIDKey) + XCTAssertNotNil(queueID) + XCTAssertEqual(queueID!, 100) + ex.fulfill() + }.cauterize() + + waitForExpectations(timeout: 1) + PromiseKit.conf.D = oldConf + + } + + func testMapValues() { + let ex1 = expectation(description: "DispatchQueue MapValues compatibility") + Promise.value([42, 52]).cancellize().then(on: .global(qos: .background), flags: .barrier) { v -> Promise<[Int]> in + Promise.value(v) + }.compactMap(on: .global(qos: .background), flags: .barrier) { + $0 + }.mapValues(on: .global(qos: .background), flags: .barrier) { + $0 + 10 + }.flatMapValues(on: .global(qos: .background), flags: .barrier) { + [$0 + 10] + }.compactMapValues(on: .global(qos: .background), flags: .barrier) { + $0 + 10 + }.thenMap(on: .global(qos: .background), flags: .barrier) { v -> CancellablePromise in + Promise.value(v + 10).cancellize() + }.thenMap(on: .global(qos: .background), flags: .barrier) { v -> Promise in + Promise.value(v + 10) + }.thenFlatMap(on: .global(qos: .background), flags: .barrier) { + Promise.value([$0 + 10]).cancellize() + }.thenFlatMap(on: .global(qos: .background), flags: .barrier) { v -> Promise<[Int]> in + Promise.value([v + 10]) + }.filterValues(on: .global(qos: .background), flags: .barrier) { _ in + true + }.sortedValues(on: .global(qos: .background), flags: .barrier).firstValue(on: .global(qos: .background), flags: .barrier) { _ in + true + }.done(on: .global(qos: .background), flags: .barrier) { + XCTAssertEqual($0, 112) + ex1.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier) { _ in + XCTFail() + } + + let ex2 = expectation(description: "DispatchQueue firstValue property") + Promise.value([42, 52]).cancellize().firstValue.done(on: .global(qos: .background), flags: .barrier) { + XCTAssertEqual($0, 42) + ex2.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + XCTFail() + } + + let ex3 = expectation(description: "DispatchQueue lastValue property") + Promise.value([42, 52]).cancellize().lastValue.done(on: .global(qos: .background), flags: .barrier) { + XCTAssertEqual($0, 52) + ex3.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + XCTFail() + } + + waitForExpectations(timeout: 1) + } + + func testRecover() { + let ex1 = expectation(description: "DispatchQueue CatchMixin compatibility") + Promise.value(42).cancellize().recover(on: .global(qos: .background), flags: .barrier) { _ in + Promise.value(42) + }.ensure(on: .global(qos: .background), flags: .barrier) { + }.ensureThen(on: .global(qos: .background), flags: .barrier) { + Promise.value(42).asVoid().cancellize() + }.recover(on: .global(qos: .background), flags: .barrier) { _ in + Promise.value(42).cancellize() + }.recover(on: .global(qos: .background), flags: .barrier) { _ in + Promise.value(42) + }.done(on: .global(qos: .background), flags: .barrier) { + XCTAssertEqual($0, 42) + ex1.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier) { _ in + XCTFail() + } + + let ex2 = expectation(description: "DispatchQueue CatchMixin Void recover") + firstly { + Promise.value(42).asVoid() + }.cancellize().recover(on: .global(qos: .background), flags: .barrier) { _ in + }.done { + ex2.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier) { _ in + XCTFail() + } + + waitForExpectations(timeout: 1) + } + + @available(macOS 10.10, iOS 2.0, tvOS 10.0, watchOS 2.0, *) + func testDispatcherExtensionReturnsGuarantee() { + let ex = expectation(description: "Dispatcher.promise") + dispatcher.dispatch() { () -> Int in + XCTAssertFalse(Thread.isMainThread) + return 1 + }.cancellize().done { one in + XCTAssertEqual(one, 1) + ex.fulfill() + }.catch { _ in + XCTFail() + } + waitForExpectations(timeout: 1) + } + + @available(macOS 10.10, iOS 2.0, tvOS 10.0, watchOS 2.0, *) + func testDispatcherExtensionCanThrowInBody() { + let ex = expectation(description: "Dispatcher.promise") + dispatcher.dispatch() { () -> Int in + throw PMKError.badInput + }.cancellize().done { _ in + XCTFail() + }.catch { _ in + ex.fulfill() + } + waitForExpectations(timeout: 1) + } + +} diff --git a/Tests/Cancel/ErrorTests.swift b/Tests/Cancel/ErrorTests.swift new file mode 100644 index 000000000..b97c7c8ab --- /dev/null +++ b/Tests/Cancel/ErrorTests.swift @@ -0,0 +1,12 @@ +import PromiseKit +import XCTest + +class CancellableErrorTests: XCTestCase { + func testCustomStringConvertible() { + XCTAssertNotNil(PMKError.cancelled.errorDescription) + } + + func testCustomDebugStringConvertible() { + XCTAssertFalse(PMKError.cancelled.debugDescription.isEmpty) + } +} diff --git a/Tests/Cancel/GuaranteeTests.swift b/Tests/Cancel/GuaranteeTests.swift new file mode 100644 index 000000000..51f3a9f9f --- /dev/null +++ b/Tests/Cancel/GuaranteeTests.swift @@ -0,0 +1,119 @@ +import PromiseKit +import XCTest + +class GuaranteeTests: XCTestCase { + func testInit() { + let ex = expectation(description: "") + Guarantee { seal in + seal(1) + }.cancellize().done { + XCTFail() + XCTAssertEqual(1, $0) + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + }.cancel() + wait(for: [ex], timeout: 1) + } + + func testWait() { + let ex = expectation(description: "") + do { + let p = after(.milliseconds(100)).cancellize().map(on: nil) { 1 } + p.cancel() + let value = try p.wait() + XCTAssertEqual(value, 1) + } catch { + error.isCancelled ? ex.fulfill() : XCTFail() + } + wait(for: [ex], timeout: 1) + } + + func testThenMap() { + let ex = expectation(description: "") + + Guarantee.value([1, 2, 3]).cancellize().thenMap { Guarantee.value($0 * 2).cancellize() } + .done { values in + XCTAssertEqual([], values) + XCTFail() + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + }.cancel() + + wait(for: [ex], timeout: 1) + } + + func testCancellable() { +#if swift(>=4.0) + var resolver: ((()) -> Void)! +#else + var resolver: ((Void) -> Void)! +#endif + + let task = DispatchWorkItem { +#if swift(>=4.0) + resolver(()) +#else + resolver() +#endif + } + + q.asyncAfter(deadline: DispatchTime.now() + 0.5, execute: task) + + let g = Guarantee(cancellable: task) { seal in + resolver = seal + } + + let ex = expectation(description: "") + firstly { + CancellablePromise(g) + }.done { + XCTFail() + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail("\($0)") + } .cancel() + + wait(for: [ex], timeout: 1) + } + + func testSetCancellable() { +#if swift(>=4.0) + var resolver: ((()) -> Void)! +#else + var resolver: ((Void) -> Void)! +#endif + + let task = DispatchWorkItem { +#if swift(>=4.0) + resolver(()) +#else + resolver() +#endif + } + + q.asyncAfter(deadline: DispatchTime.now() + 0.5, execute: task) + + let g = Guarantee { seal in + resolver = seal + } + g.setCancellable(task) + + let ex = expectation(description: "") + firstly { + g + }.cancellize().done { + XCTFail() + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail("\($0)") + } .cancel() + + wait(for: [ex], timeout: 1) + } +} + +private var q: DispatchQueue { + if #available(macOS 10.10, iOS 8.0, tvOS 9.0, watchOS 2.0, *) { + return DispatchQueue.global(qos: .default) + } else { + return DispatchQueue.global(priority: .default) + } +} diff --git a/Tests/Cancel/HangTests.swift b/Tests/Cancel/HangTests.swift new file mode 100644 index 000000000..5c5524399 --- /dev/null +++ b/Tests/Cancel/HangTests.swift @@ -0,0 +1,48 @@ +import PromiseKit +import XCTest + +class HangTests: XCTestCase { + func test() { + let ex = expectation(description: "block executed") + do { + let p = after(seconds: 0.02).cancellize().then { _ -> Promise in + XCTFail() + return Promise.value(1) + } + p.cancel() + let value = try hang(p) + XCTFail() + XCTAssertEqual(value, 1) + } catch { + error.isCancelled ? ex.fulfill() : XCTFail("Unexpected error") + } + waitForExpectations(timeout: 0) + } + + enum Error: Swift.Error { + case test + } + + func testError() { + var value = 0 + do { + let p = after(seconds: 0.02).cancellize().done { + XCTFail() + value = 1 + throw Error.test + } + p.cancel() + _ = try hang(p) + XCTFail() + XCTAssertEqual(value, 1) + } catch Error.test { + XCTFail() + } catch { + if !error.isCancelled { + XCTFail("Unexpected error (expected PMKError.cancelled)") + } + return + } + XCTFail("Expected error but no error was thrown") + } +} diff --git a/Tests/Cancel/PromiseTests.swift b/Tests/Cancel/PromiseTests.swift new file mode 100644 index 000000000..be8dba451 --- /dev/null +++ b/Tests/Cancel/PromiseTests.swift @@ -0,0 +1,286 @@ +import PromiseKit +import Dispatch +import XCTest + +class PromiseTests: XCTestCase { + func testIsPending() { + XCTAssertTrue(CancellablePromise.pending().promise.promise.isPending) + XCTAssertFalse(CancellablePromise().promise.isPending) + XCTAssertFalse(CancellablePromise(error: Error.dummy).promise.isPending) + } + + func testIsResolved() { + XCTAssertFalse(CancellablePromise.pending().promise.promise.isResolved) + XCTAssertTrue(CancellablePromise().promise.isResolved) + XCTAssertTrue(CancellablePromise(error: Error.dummy).promise.isResolved) + } + + func testIsFulfilled() { + XCTAssertFalse(CancellablePromise.pending().promise.promise.isFulfilled) + XCTAssertTrue(CancellablePromise().promise.isFulfilled) + XCTAssertFalse(CancellablePromise(error: Error.dummy).promise.isFulfilled) + } + + func testIsRejected() { + XCTAssertFalse(CancellablePromise.pending().promise.promise.isRejected) + XCTAssertTrue(CancellablePromise(error: Error.dummy).promise.isRejected) + XCTAssertFalse(CancellablePromise().promise.isRejected) + } + + @available(macOS 10.10, iOS 2.0, tvOS 10.0, watchOS 2.0, *) + func testDispatchQueueAsyncExtensionReturnsPromise() { + let ex = expectation(description: "") + + DispatchQueue.global().async(.promise) { () -> Int in + usleep(100000) + XCTAssertFalse(Thread.isMainThread) + return 1 + }.cancellize().done { one in + XCTFail() + XCTAssertEqual(one, 1) + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail("Error: \($0)") + }.cancel() + + waitForExpectations(timeout: 1) + } + + @available(macOS 10.10, iOS 2.0, tvOS 10.0, watchOS 2.0, *) + func testDispatchQueueAsyncExtensionCanThrowInBody() { + let ex = expectation(description: "") + + DispatchQueue.global().async(.promise) { () -> Int in + throw Error.dummy + }.cancellize().done { _ in + XCTFail() + }.catch(policy: .allErrors) { _ in + ex.fulfill() + }.cancel() + + waitForExpectations(timeout: 1) + } + + func testCustomStringConvertible() { + XCTAssertEqual(CancellablePromise.pending().promise.promise.debugDescription, "Promise.pending(handlers: 0)") + XCTAssertEqual(CancellablePromise().promise.debugDescription, "Promise<()>.success(())") + XCTAssertEqual(CancellablePromise(error: Error.dummy).promise.debugDescription, "Promise.failure(Error.dummy)") + + XCTAssertEqual("\(CancellablePromise.pending().promise.promise)", "Promise(…Int)") + XCTAssertEqual("\(Promise.value(3).cancellize().promise)", "Promise(3)") + XCTAssertEqual("\(CancellablePromise(error: Error.dummy).promise)", "Promise(dummy)") + } + + func testCannotFulfillWithError() { + + // sadly this test proves the opposite :( + // left here so maybe one day we can prevent instantiation of `CancellablePromise` + + _ = CancellablePromise { seal in + seal.fulfill(Error.dummy) + } + + _ = CancellablePromise.pending() + + _ = Promise.value(Error.dummy).cancellize() + + _ = CancellablePromise().map { Error.dummy } + } + +#if swift(>=3.1) + func testCanMakeVoidPromise() { + _ = CancellablePromise() + _ = Guarantee() + } +#endif + + enum Error: Swift.Error { + case dummy + } + + func testThrowInInitializer() { + let p = CancellablePromise { _ in + throw Error.dummy + } + p.cancel() + XCTAssertTrue(p.promise.isRejected) + guard let err = p.promise.error, case Error.dummy = err else { return XCTFail() } + } + + func testThrowInFirstly() { + let ex = expectation(description: "") + + firstly { () -> CancellablePromise in + throw Error.dummy + }.catch { + XCTAssertEqual($0 as? Error, Error.dummy) + ex.fulfill() + }.cancel() + + wait(for: [ex], timeout: 1) + } + + func testWait() throws { + let p = after(.milliseconds(100)).cancellize().then(on: nil){ Promise.value(1) } + p.cancel() + do { + _ = try p.wait() + XCTFail() + } catch { + XCTAssert(error.isCancelled) + } + + do { + let p = after(.milliseconds(100)).cancellize().map(on: nil){ throw Error.dummy } + p.cancel() + try p.wait() + XCTFail() + } catch { + XCTAssert(error.isCancelled) + } + } + + func testPipeForResolved() { + let ex = expectation(description: "") + Promise.value(1).cancellize().done { + XCTFail() + XCTAssertEqual(1, $0) + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail("\($0)") + }.cancel() + wait(for: [ex], timeout: 1) + } + + func testCancellable() { + var resolver: Resolver! + + let task = DispatchWorkItem { +#if swift(>=4.0) + resolver.fulfill(()) +#else + resolver.fulfill() +#endif + } + + q.asyncAfter(deadline: DispatchTime.now() + 0.5, execute: task) + + let p = Promise(cancellable: task) { seal in + resolver = seal + } + + let ex = expectation(description: "") + firstly { + CancellablePromise(p) + }.done { + XCTFail() + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail("\($0)") + }.cancel() + + wait(for: [ex], timeout: 1) + } + + func testSetCancellable() { + var resolver: Resolver! + + let task = DispatchWorkItem { +#if swift(>=4.0) + resolver.fulfill(()) +#else + resolver.fulfill() +#endif + } + + q.asyncAfter(deadline: DispatchTime.now() + 0.5, execute: task) + + var reject: ((Swift.Error) -> Void)? + let p = Promise(cancellable: task) { seal in + resolver = seal + reject = seal.reject + } + p.setCancellable(task, reject: reject) + + let ex = expectation(description: "") + firstly { + p + }.cancellize().done { + XCTFail() + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail("\($0)") + }.cancel() + + wait(for: [ex], timeout: 1) + } + + func testInitCancellable() { + var resolver: Resolver! + + let task = DispatchWorkItem { +#if swift(>=4.0) + resolver.fulfill(()) +#else + resolver.fulfill() +#endif + } + + q.asyncAfter(deadline: DispatchTime.now() + 0.5, execute: task) + + let p = Promise { seal in + resolver = seal + } + + let ex = expectation(description: "") + firstly { + CancellablePromise(cancellable: task, promise: p, resolver: resolver) + }.done { + XCTFail() + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail("\($0)") + }.cancel() + + wait(for: [ex], timeout: 1) + } + + func testInitVoidCancellable() { + let task = DispatchWorkItem { } + q.asyncAfter(deadline: DispatchTime.now() + 0.5, execute: task) + + let ex = expectation(description: "") + firstly { + CancellablePromise(cancellable: task) + }.done { + XCTFail() + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail("\($0)") + }.cancel() + + wait(for: [ex], timeout: 1) + } + + func testBodyThrowsError() { + let task = DispatchWorkItem { } + q.asyncAfter(deadline: DispatchTime.now() + 0.5, execute: task) + + let p = Promise(cancellable: task) { seal in + throw PMKError.badInput + } + + let ex = expectation(description: "") + firstly { + CancellablePromise(p) + }.done { + XCTFail() + }.catch(policy: .allErrors) { + $0.isCancelled ? XCTFail("\($0)") : ex.fulfill() + }.cancel() + + wait(for: [ex], timeout: 1) + } +} + +private var q: DispatchQueue { + if #available(macOS 10.10, iOS 8.0, tvOS 9.0, watchOS 2.0, *) { + return DispatchQueue.global(qos: .default) + } else { + return DispatchQueue.global(priority: .default) + } +} diff --git a/Tests/Cancel/RaceTests.swift b/Tests/Cancel/RaceTests.swift new file mode 100644 index 000000000..d2ebadfbc --- /dev/null +++ b/Tests/Cancel/RaceTests.swift @@ -0,0 +1,107 @@ +import XCTest +import PromiseKit + +class RaceTests: XCTestCase { + func test1() { + let ex = expectation(description: "") + let after1 = after(.milliseconds(10)).cancellize() + let after2 = after(seconds: 1).cancellize() + race(after1.then{ Promise.value(1) }, after2.map { 2 }).done { index in + XCTFail() + XCTAssertEqual(index, 1) + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + }.cancel() + XCTAssert(after1.isCancelled) + XCTAssert(after2.isCancelled) + XCTAssert(after1.cancelAttempted) + XCTAssert(after2.cancelAttempted) + waitForExpectations(timeout: 1, handler: nil) + } + + func test2() { + let ex = expectation(description: "") + let after1 = after(seconds: 1).cancellize().map { 1 } + let after2 = after(.milliseconds(10)).cancellize().map { 2 } + race(after1, after2).done { index in + XCTFail() + XCTAssertEqual(index, 2) + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + }.cancel() + XCTAssert(after1.isCancelled) + XCTAssert(after2.isCancelled) + XCTAssert(after1.cancelAttempted) + XCTAssert(after2.cancelAttempted) + waitForExpectations(timeout: 1, handler: nil) + } + + func test1Array() { + let ex = expectation(description: "") + let promises = [after(.milliseconds(10)).cancellize().map { 1 }, after(seconds: 1).cancellize().map { 2 }] + race(promises).done { index in + XCTAssertEqual(index, 1) + ex.fulfill() + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + }.cancel() + for p in promises { + XCTAssert(p.cancelAttempted) + XCTAssert(p.isCancelled) + } + waitForExpectations(timeout: 1, handler: nil) + } + + func test2Array() { + let ex = expectation(description: "") + let after1 = after(seconds: 1).cancellize().map { 1 } + let after2 = after(.milliseconds(10)).cancellize().map { 2 } + race(after1, after2).done { index in + XCTFail() + XCTAssertEqual(index, 2) + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + }.cancel() + XCTAssert(after1.isCancelled) + XCTAssert(after2.isCancelled) + XCTAssert(after1.cancelAttempted) + XCTAssert(after2.cancelAttempted) + waitForExpectations(timeout: 1, handler: nil) + } + + func testEmptyArray() { + let ex = expectation(description: "") + let empty = [CancellablePromise]() + race(empty).catch { + guard case PMKError.badInput = $0 else { return XCTFail() } + ex.fulfill() + } + wait(for: [ex], timeout: 1) + } + + func testReject() { + let ex = expectation(description: "") + race(CancellablePromise(error: PMKError.timedOut), after(.milliseconds(10)).map{ 2 }.cancellize()).done { index in + XCTFail() + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + } + waitForExpectations(timeout: 1, handler: nil) + } + + func testCancelInner() { + let ex = expectation(description: "") + + let after1 = after(.milliseconds(10)).cancellize() + let after2 = after(seconds: 1).cancellize() + let r = race(after1.then{ Promise.value(1).cancellize() }, after2.map { 2 }) + + r.done { index in + XCTFail() + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + } + after1.cancel() + waitForExpectations(timeout: 1, handler: nil) + } +} diff --git a/Tests/Cancel/RegressionTests.swift b/Tests/Cancel/RegressionTests.swift new file mode 100644 index 000000000..baa7c58d4 --- /dev/null +++ b/Tests/Cancel/RegressionTests.swift @@ -0,0 +1,57 @@ +import PromiseKit +import XCTest + +class RegressionTests: XCTestCase { + func testReturningPreviousPromiseWorks() { + + // regression test because we were doing this wrong + // in our A+ tests implementation for spec: 2.3.1 + + do { + let ex = expectation(description: "") + let promise1 = CancellablePromise() + promise1.cancel() + let promise2 = promise1.then(on: nil) { promise1 } + promise2.catch(on: nil, policy: .allErrors) { + ex.fulfill() + if !$0.isCancelled { + XCTFail() + } + } + wait(for: [ex], timeout: 1) + } + + do { + let ex = expectation(description: "") + let promise1 = CancellablePromise() + promise1.cancel() + let promise2 = promise1.then(on: nil) { () -> CancellablePromise in + XCTFail() + return promise1 + } + promise2.catch(on: nil, policy: .allErrors) { + ex.fulfill() + if !$0.isCancelled { + XCTFail() + } + } + wait(for: [ex], timeout: 1) + } + + do { + let ex = expectation(description: "") + enum Error: Swift.Error { case dummy } + + let promise1 = CancellablePromise(error: Error.dummy) + promise1.cancel() + let promise2 = promise1.recover(on: nil) { _ in promise1 } + promise2.catch(on: nil, policy: .allErrors) { err in + if case PMKError.returnedSelf = err { + XCTFail() + } + ex.fulfill() + } + wait(for: [ex], timeout: 1) + } + } +} diff --git a/Tests/Cancel/ResolverTests.swift b/Tests/Cancel/ResolverTests.swift new file mode 100644 index 000000000..98124ffa6 --- /dev/null +++ b/Tests/Cancel/ResolverTests.swift @@ -0,0 +1,340 @@ +import PromiseKit +import XCTest + +class WrapTests: XCTestCase { + fileprivate class KittenFetcher { + let value: Int? + let error: Error? + + init(value: Int?, error: Error?) { + self.value = value + self.error = error + } + + func fetchWithCompletionBlock(block: @escaping(Int?, Error?) -> Void) { + after(.milliseconds(20)).done { + block(self.value, self.error) + } + } + + func fetchWithCompletionBlock2(block: @escaping(Error?, Int?) -> Void) { + after(.milliseconds(20)).done { + block(self.error, self.value) + } + } + + func fetchWithCompletionBlock3(block: @escaping(Int, Error?) -> Void) { + after(.milliseconds(20)).done { + block(self.value ?? -99, self.error) + } + } + + func fetchWithCompletionBlock4(block: @escaping(Error?) -> Void) { + after(.milliseconds(20)).done { + block(self.error) + } + } + } + + fileprivate class CancellableKittenFetcher: Cancellable { + func cancel() { + finalizer?.cancel() + } + + var isCancelled: Bool { + return finalizer?.isCancelled ?? false + } + + let value: Int? + let error: Swift.Error? + var finalizer: CancellableFinalizer? + + init(value: Int?, error: Swift.Error?) { + self.value = value + self.error = error + } + + func fetchWithCompletionBlock(block: @escaping(Int?, Swift.Error?) -> Void) { + finalizer = after(.milliseconds(20)).cancellize().done {_ in + block(self.value, self.error) + }.catch(policy: .allErrors) { + block(nil, $0) + } + } + + func fetchWithCompletionBlock2(block: @escaping(Swift.Error?, Int?) -> Void) { + finalizer = after(.milliseconds(20)).cancellize().done { + block(self.error, self.value) + }.catch(policy: .allErrors) { + block($0, nil) + } + } + + func fetchWithCompletionBlock3(block: @escaping(Int, Swift.Error?) -> Void) { + finalizer = after(.milliseconds(20)).cancellize().done { + block(self.value ?? -99, self.error) + }.catch(policy: .allErrors) { + block(-99, $0) + } + } + + func fetchWithCompletionBlock4(block: @escaping(Swift.Error?) -> Void) { + finalizer = after(.milliseconds(20)).cancellize().done { + block(self.error) + }.catch(policy: .allErrors) { + block($0) + } + } + } + + func testSuccess() { + let ex = expectation(description: "") + let kittenFetcher = KittenFetcher(value: 2, error: nil) + CancellablePromise { seal in + kittenFetcher.fetchWithCompletionBlock(block: seal.resolve) + }.done { + XCTFail() + XCTAssertEqual($0, 2) + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + }.cancel() + waitForExpectations(timeout: 1) + } + + func testError() { + let ex = expectation(description: "") + + let kittenFetcher = KittenFetcher(value: nil, error: Error.test) + CancellablePromise { seal in + kittenFetcher.fetchWithCompletionBlock(block: seal.resolve) + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + }.cancel() + + waitForExpectations(timeout: 1) + } + + func testErrorNoDelay() { + let ex = expectation(description: "") + + CancellablePromise { seal in + seal.resolve(nil, Error.test) + }.catch(policy: .allErrors) { error in + defer { ex.fulfill() } + guard case Error.test = error else { + return XCTFail() + } + }.cancel() + + waitForExpectations(timeout: 1) + } + + func testErrorCancellableKitten() { + let ex = expectation(description: "") + + let kittenFetcher = CancellableKittenFetcher(value: nil, error: Error.test) + CancellablePromise(cancellable: kittenFetcher) { seal in + kittenFetcher.fetchWithCompletionBlock(block: seal.resolve) + }.catch(policy: .allErrors) { error in + error.isCancelled ? ex.fulfill() : XCTFail() + }.cancel() + + waitForExpectations(timeout: 1) + } + + func testInvalidCallingConvention() { + let ex = expectation(description: "") + + let kittenFetcher = KittenFetcher(value: nil, error: nil) + CancellablePromise { seal in + kittenFetcher.fetchWithCompletionBlock(block: seal.resolve) + }.catch(policy: .allErrors) { error in + error.isCancelled ? ex.fulfill() : XCTFail() + }.cancel() + + waitForExpectations(timeout: 1) + } + + func testInvalidCallingConventionCancellableKitten() { + let ex = expectation(description: "") + + let kittenFetcher = CancellableKittenFetcher(value: nil, error: nil) + CancellablePromise(cancellable: kittenFetcher) { seal in + kittenFetcher.fetchWithCompletionBlock(block: seal.resolve) + }.catch(policy: .allErrors) { (error: Swift.Error) -> Void in + error.isCancelled ? ex.fulfill() : XCTFail() + }.cancel() + + waitForExpectations(timeout: 1) + } + + func testInvertedCallingConvention() { + let ex = expectation(description: "") + let kittenFetcher = KittenFetcher(value: 2, error: nil) + CancellablePromise { seal in + kittenFetcher.fetchWithCompletionBlock2(block: seal.resolve) + }.done { + XCTFail() + XCTAssertEqual($0, 2) + }.catch(policy: .allErrors) { error in + error.isCancelled ? ex.fulfill() : XCTFail() + }.cancel() + + waitForExpectations(timeout: 1) + } + + func testInvertedCallingConventionCancellableKitten() { + let ex = expectation(description: "") + let kittenFetcher = CancellableKittenFetcher(value: 2, error: nil) + CancellablePromise(cancellable: kittenFetcher) { seal in + kittenFetcher.fetchWithCompletionBlock2(block: seal.resolve) + }.done { + XCTFail() + XCTAssertEqual($0, 2) + }.catch(policy: .allErrors) { error in + error.isCancelled ? ex.fulfill() : XCTFail() + }.cancel() + + waitForExpectations(timeout: 1) + } + + func testNonOptionalFirstParameter() { + let ex1 = expectation(description: "") + let kf1 = KittenFetcher(value: 2, error: nil) + CancellablePromise { seal in + kf1.fetchWithCompletionBlock3(block: seal.resolve) + }.done { + XCTFail() + XCTAssertEqual($0, 2) + }.catch(policy: .allErrors) { error in + error.isCancelled ? ex1.fulfill() : XCTFail() + }.cancel() + + let ex2 = expectation(description: "") + let kf2 = KittenFetcher(value: -100, error: Error.test) + CancellablePromise { seal in + kf2.fetchWithCompletionBlock3(block: seal.resolve) + }.catch(policy: .allErrors) { error in + error.isCancelled ? ex2.fulfill() : XCTFail() + }.cancel() + + wait(for: [ex1, ex2] ,timeout: 1) + } + + func testNonOptionalFirstParameterCancellableKitten() { + let ex1 = expectation(description: "") + let kf1 = CancellableKittenFetcher(value: 2, error: nil) + CancellablePromise(cancellable: kf1) { seal in + kf1.fetchWithCompletionBlock3(block: seal.resolve) + }.done { + XCTAssertEqual($0, 2) + ex1.fulfill() + }.catch(policy: .allErrors) { error in + error.isCancelled ? ex1.fulfill() : XCTFail() + }.cancel() + + let ex2 = expectation(description: "") + let kf2 = CancellableKittenFetcher(value: -100, error: Error.test) + CancellablePromise(cancellable: kf2) { seal in + kf2.fetchWithCompletionBlock3(block: seal.resolve) + }.catch(policy: .allErrors) { error in + error.isCancelled ? ex2.fulfill() : XCTFail() + }.cancel() + + wait(for: [ex1, ex2] ,timeout: 1) + } + +#if swift(>=3.1) + func testVoidCompletionValue() { + let ex1 = expectation(description: "") + let kf1 = KittenFetcher(value: nil, error: nil) + CancellablePromise { seal in + kf1.fetchWithCompletionBlock4(block: seal.resolve) + }.done(ex1.fulfill).catch(policy: .allErrors) { error in + error.isCancelled ? ex1.fulfill() : XCTFail() + }.cancel() + + let ex2 = expectation(description: "") + let kf2 = KittenFetcher(value: nil, error: Error.test) + CancellablePromise { seal in + kf2.fetchWithCompletionBlock4(block: seal.resolve) + }.catch(policy: .allErrors) { error in + error.isCancelled ? ex2.fulfill() : XCTFail() + }.cancel() + + wait(for: [ex1, ex2], timeout: 1) + } + + func testVoidCompletionValueCancellableKitten() { + let ex1 = expectation(description: "") + let kf1 = CancellableKittenFetcher(value: nil, error: nil) + CancellablePromise(cancellable: kf1) { seal in + kf1.fetchWithCompletionBlock4(block: seal.resolve) + }.done(ex1.fulfill).catch(policy: .allErrors) { error in + error.isCancelled ? ex1.fulfill() : XCTFail() + }.cancel() + + let ex2 = expectation(description: "") + let kf2 = CancellableKittenFetcher(value: nil, error: Error.test) + CancellablePromise(cancellable: kf2) { seal in + kf2.fetchWithCompletionBlock4(block: seal.resolve) + }.catch(policy: .allErrors) { error in + error.isCancelled ? ex2.fulfill() : XCTFail() + }.cancel() + + wait(for: [ex1, ex2], timeout: 1) + } +#endif + + func testIsFulfilled() { + let p1 = Promise.value(()).cancellize() + p1.cancel() + + var success1 = false + if let r1 = p1.result, case .success = r1 { + success1 = true + } + XCTAssertTrue(success1) + XCTAssertTrue(p1.isCancelled) + + let p2 = CancellablePromise(error: Error.test) + p2.cancel() + var success2 = true + if let r2 = p2.result { + if case .success = r2 { + success2 = true + } else { + success2 = false + } + } + XCTAssertFalse(success2) + XCTAssertTrue(p2.isCancelled) + } + + func testPendingPromiseDeallocated() { + + // NOTE this doesn't seem to register the `deinit` as covered :( + // BUT putting a breakpoint in the deinit CLEARLY shows it getting covered… + + class Foo { + let p = CancellablePromise.pending() + var ex: XCTestExpectation! + + deinit { + after(.milliseconds(100)).done(ex.fulfill) + } + } + + let ex = expectation(description: "") + do { + // for code coverage report for `Resolver.deinit` warning + let foo = Foo() + foo.ex = ex + } + wait(for: [ex], timeout: 1) + } +} + +private enum Error: Swift.Error { + case test +} diff --git a/Tests/Cancel/StressTests.swift b/Tests/Cancel/StressTests.swift new file mode 100644 index 000000000..c34a7cb8f --- /dev/null +++ b/Tests/Cancel/StressTests.swift @@ -0,0 +1,173 @@ +@testable import PromiseKit +import Dispatch +import XCTest + +class StressTests: XCTestCase { + func testThenDataRace() { + let e1 = expectation(description: "") + let e2 = expectation(description: "") + var errorCounter = 0 + + //will crash if then doesn't protect handlers + stressDataRace(expectation: e1, iterations: 1000, stressFunction: { promise in + promise.done { s in + XCTFail() + XCTAssertEqual("ok", s) + return + }.catch(policy: .allErrors) { + if !$0.isCancelled { + XCTFail() + } + errorCounter += 1 + if errorCounter == 1000 { + e2.fulfill() + } + }.cancel() + }, fulfill: { "ok" }) + + waitForExpectations(timeout: 10, handler: nil) + } + + @available(macOS 10.10, iOS 2.0, tvOS 10.0, watchOS 2.0, *) + func testThensAreSequentialForLongTime() { + var values = [Int]() + let ex = expectation(description: "") + var promise = DispatchQueue.global().async(.promise){ 0 }.cancellize() + let N = 1000 + for x in 1.. CancellablePromise in + values.append(y) + XCTFail() + return DispatchQueue.global().async(.promise) { x }.cancellize() + } + } + promise.done { x in + values.append(x) + XCTFail() + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + }.cancel() + + waitForExpectations(timeout: 10, handler: nil) + } + + func testZalgoDataRace() { + let e1 = expectation(description: "") + let e2 = expectation(description: "") + var errorCounter = 0 + + //will crash if zalgo doesn't protect handlers + stressDataRace(expectation: e1, iterations: 1000, stressFunction: { promise in + promise.done(on: nil) { s in + XCTAssertEqual("ok", s) + }.catch(policy: .allErrors) { + if !$0.isCancelled { + XCTFail() + } + errorCounter += 1 + if errorCounter == 1000 { + e2.fulfill() + } + }.cancel() + }, fulfill: { + return "ok" + }) + + waitForExpectations(timeout: 10, handler: nil) + } + + class StressTask: Cancellable { + init() { + isCancelled = true + } + + func cancel() { + } + + var isCancelled: Bool + } + + func testCancelContextConcurrentReadWrite() { + let e1 = expectation(description: "") + let context = CancelContext() + func consume(error: Swift.Error?) { } + + stressRace(expectation: e1, iterations: 1000, stressFactor: 1000, stressFunction: { + consume(error: context.cancelledError) + }, fulfillFunction: { + context.cancel() + }) + + waitForExpectations(timeout: 10, handler: nil) + } + + func testCancelContextConcurrentAppend() { + let e1 = expectation(description: "") + let context = CancelContext() + let promise = CancellablePromise() + let task = StressTask() + + stressRace(expectation: e1, iterations: 1000, stressFactor: 100, stressFunction: { + context.append(cancellable: task, reject: nil, thenable: promise) + }, fulfillFunction: { + context.append(cancellable: task, reject: nil, thenable: promise) + }) + + waitForExpectations(timeout: 10, handler: nil) + } + + func testCancelContextConcurrentCancel() { + let e1 = expectation(description: "") + let context = CancelContext() + let promise = CancellablePromise() + let task = StressTask() + + stressRace(expectation: e1, iterations: 500, stressFactor: 10, stressFunction: { + context.append(cancellable: task, reject: nil, thenable: promise) + }, fulfillFunction: { + context.cancel() + }) + + waitForExpectations(timeout: 10, handler: nil) + } +} + +private enum Error: Swift.Error { + case Dummy +} + +private func stressDataRace(expectation e1: XCTestExpectation, iterations: Int = 1000, stressFactor: Int = 10, stressFunction: @escaping (CancellablePromise) -> Void, fulfill f: @escaping () -> T) { + let group = DispatchGroup() + let queue = DispatchQueue(label: "the.domain.of.Zalgo", attributes: .concurrent) + + for _ in 0...pending() + + DispatchQueue.concurrentPerform(iterations: stressFactor) { _ in + stressFunction(promise) + } + + queue.async(group: group) { + seal.fulfill(f()) + } + } + + group.notify(queue: queue, execute: e1.fulfill) +} + +private func stressRace(expectation e1: XCTestExpectation, iterations: Int = 10000, stressFactor: Int = 1000, stressFunction: @escaping () -> Void, fulfillFunction: @escaping () -> Void) { + let group = DispatchGroup() + let queue = DispatchQueue(label: "the.domain.of.Zalgo", attributes: .concurrent) + + for _ in 0.. Int in + promise.cancel() + throw E.dummy + }.catch(policy: .allErrors) { + if case E.dummy = $0 {} else { + XCTFail() + } + ex.fulfill() + } + wait(for: [ex], timeout: 1) + } + + func testRejectedPromiseCompactMap() { + + enum E: Error { case dummy } + + let ex = expectation(description: "") + CancellablePromise(error: E.dummy).compactMap { + XCTFail() + }.catch(policy: .allErrors) { + if case E.dummy = $0 {} else { + XCTFail() + } + ex.fulfill() + }.cancel() + wait(for: [ex], timeout: 1) + } + + func testPMKErrorCompactMap() { + let ex = expectation(description: "") + Promise.value("a").cancellize().compactMap { + Int($0) + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + }.cancel() + wait(for: [ex], timeout: 1) + } + + func testCompactMapValues() { + let ex = expectation(description: "") + let promise = Promise.value(["1","2","a","4"]).cancellize() + promise.compactMapValues { + Int($0) + }.done { + promise.cancel() + XCTAssertEqual([1,2,4], $0) + ex.fulfill() + }.catch(policy: .allErrors) { _ in + XCTFail() + } + wait(for: [ex], timeout: 1) + } + + func testThenMap() { + let ex = expectation(description: "") + let promise = Promise.value([1,2,3,4]).cancellize() + promise.thenMap { (x: Int) -> Promise in + promise.cancel() + return Promise.value(x) // Intentionally use `Promise` rather than `CancellablePromise` + }.done { _ in + XCTFail() + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + } + wait(for: [ex], timeout: 1) + } + + func testThenFlatMap() { + let ex = expectation(description: "") + Promise.value([1,2,3,4]).cancellize().thenFlatMap { (x: Int) -> CancellablePromise<[Int]> in + XCTFail() + return Promise.value([x, x]).cancellize() + }.done { + XCTFail() + XCTAssertEqual([1,1,2,2,3,3,4,4], $0) + ex.fulfill() + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + }.cancel() + wait(for: [ex], timeout: 1) + } + + func testLastValueForEmpty() { + XCTAssertTrue(Promise.value([]).cancellize().lastValue.isRejected) + } + + func testFirstValueForEmpty() { + XCTAssertTrue(Promise.value([]).cancellize().firstValue.isRejected) + } + + func testThenOffRejected() { + // surprisingly missing in our CI, mainly due to + // extensive use of `done` in A+ tests since PMK 5 + + let ex = expectation(description: "") + CancellablePromise(error: PMKError.badInput).then { x -> Promise in + XCTFail() + return .value(x) + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + }.cancel() + wait(for: [ex], timeout: 1) + } + + func testBarrier() { + let ex = expectation(description: "") + let q = DispatchQueue(label: "\(#file):\(#line)", attributes: .concurrent) + Promise.value(1).cancellize().done(on: q, flags: .barrier) { + XCTAssertEqual($0, 1) + dispatchPrecondition(condition: .onQueueAsBarrier(q)) + ex.fulfill() + }.catch { _ in + XCTFail() + } + wait(for: [ex], timeout: 10) + } + + func testDispatchFlagsSyntax() { + let ex = expectation(description: "") + let q = DispatchQueue(label: "\(#file):\(#line)", attributes: .concurrent) + Promise.value(1).cancellize().done(on: q, flags: [.barrier, .inheritQoS]) { + XCTAssertEqual($0, 1) + dispatchPrecondition(condition: .onQueueAsBarrier(q)) + ex.fulfill() + }.catch { _ in + XCTFail() + } + wait(for: [ex], timeout: 10) + } +} diff --git a/Tests/Cancel/TimeoutTests.swift b/Tests/Cancel/TimeoutTests.swift new file mode 100644 index 000000000..a47f4d01d --- /dev/null +++ b/Tests/Cancel/TimeoutTests.swift @@ -0,0 +1,125 @@ +import PromiseKit +import XCTest + +class TimeoutTests: XCTestCase { + func testTimeout() { + let ex = expectation(description: "") + + race(after(seconds: 0.5).cancellize(), timeout(seconds: 0.01).cancellize()).done { + // race(cancellize(after(seconds: 0.5)), timeout(seconds: 0.01)).done { + XCTFail() + }.catch(policy: .allErrors) { + do { + throw $0 + } catch PMKError.timedOut { + ex.fulfill() + } catch { + XCTFail() + } + } + waitForExpectations(timeout: 1) + } + + func testReset() { + let ex = expectation(description: "") + let p = after(seconds: 0.5).cancellize() + race(p, timeout(seconds: 2.0).cancellize(), timeout(seconds: 0.01).cancellize()).done { + XCTFail() + }.catch(policy: .allErrors) { err in + do { + throw err + } catch PMKError.timedOut { + _ = (err as? PMKError).debugDescription + ex.fulfill() + } catch { + XCTFail() + } + } + waitForExpectations(timeout: 1) + XCTAssert(p.isCancelled) + } + + func testDoubleTimeout() { + let ex = expectation(description: "") + let p = after(seconds: 0.5).cancellize() + race(p, timeout(seconds: 0.01).cancellize(), timeout(seconds: 0.01).cancellize()).done { + XCTFail() + }.catch(policy: .allErrors) { + do { + throw $0 + } catch PMKError.timedOut { + ex.fulfill() + } catch { + XCTFail() + } + } + waitForExpectations(timeout: 1) + XCTAssert(p.isCancelled) + } + + func testNoTimeout() { + let ex = expectation(description: "") + race(after(seconds: 0.01).cancellize(), timeout(seconds: 0.5).cancellize()).then { _ -> Promise in + ex.fulfill() + return Promise.value(1) + }.catch(policy: .allErrors) { _ in + XCTFail() + } + waitForExpectations(timeout: 1) + } + + func testCancelBeforeTimeout() { + let ex = expectation(description: "") + let p = after(seconds: 0.5).cancellize() + race(p, timeout(seconds: 2).cancellize()).then { _ -> Promise in + XCTFail() + return Promise.value(1) + }.catch(policy: .allErrors) { + do { + throw $0 + } catch PMKError.cancelled { + ex.fulfill() + } catch { + XCTFail() + } + } + p.cancel() + waitForExpectations(timeout: 1) + } + + func testCancelRaceBeforeTimeout() { + let ex = expectation(description: "") + let ctxt = race(after(seconds: 0.5).cancellize(), timeout(seconds: 2).cancellize()).then { _ -> Promise in + XCTFail() + return Promise.value(1) + }.catch(policy: .allErrors) { + do { + throw $0 + } catch PMKError.cancelled { + ex.fulfill() + } catch { + XCTFail() + } + }.cancelContext + ctxt.cancel() + waitForExpectations(timeout: 1) + } + + func testMixTypes() { + let ex = expectation(description: "") + let promise1, promise2: CancellablePromise + promise1 = Promise.value("string").cancellize().asVoid() + promise2 = Promise.value(22).cancellize().asVoid() + race(promise1, promise2, + Promise.value("string").cancellize().asVoid(), + Promise.value(22).cancellize().asVoid(), + timeout(seconds: 2).cancellize()).then { thisone -> Promise in + print("\(thisone)") + ex.fulfill() + return Promise.value(1) + }.catch(policy: .allErrors) { _ in + XCTFail() + } + waitForExpectations(timeout: 1) + } +} diff --git a/Tests/Cancel/Utilities.swift b/Tests/Cancel/Utilities.swift new file mode 100644 index 000000000..87a408d4a --- /dev/null +++ b/Tests/Cancel/Utilities.swift @@ -0,0 +1,40 @@ +import PromiseKit + +// Workaround for error with missing libswiftContacts.dylib, this import causes the +// library to be included as needed +#if os(iOS) || os(watchOS) || os(OSX) +import class Contacts.CNPostalAddress +#endif + +extension Promise { + func silenceWarning() {} +} + +extension CancellablePromise { + func silenceWarning() {} +} + +#if os(Linux) +import func CoreFoundation._CFIsMainThread + +extension Thread { + // `isMainThread` is not implemented yet in swift-corelibs-foundation. + static var isMainThread: Bool { + return _CFIsMainThread() + } +} + +import XCTest + +extension XCTestCase { + func wait(for: [XCTestExpectation], timeout: TimeInterval, file: StaticString = #file, line: UInt = #line) { + waitForExpectations(timeout: timeout, file: file, line: Int(line)) + } +} + +extension XCTestExpectation { + func fulfill() { + fulfill(#file, line: #line) + } +} +#endif diff --git a/Tests/Cancel/ValueTests.swift b/Tests/Cancel/ValueTests.swift new file mode 100644 index 000000000..8692a1a58 --- /dev/null +++ b/Tests/Cancel/ValueTests.swift @@ -0,0 +1,139 @@ +import XCTest +import PromiseKit + +class ValueTests: XCTestCase { + func testValueContext() { + let exComplete = expectation(description: "after completes") + Promise.value("hi").cancellize().done { _ in + XCTFail("value not cancelled") + }.catch(policy: .allErrors) { error in + error.isCancelled ? exComplete.fulfill() : XCTFail("error: \(error)") + }.cancel() + + wait(for: [exComplete], timeout: 1) + } + + func testValueDone() { + let exComplete = expectation(description: "after completes") + Promise.value("hi").cancellize().done { _ in + XCTFail("value not cancelled") + }.catch(policy: .allErrors) { error in + error.isCancelled ? exComplete.fulfill() : XCTFail("error: \(error)") + }.cancel() + + wait(for: [exComplete], timeout: 1) + } + + func testValueThen() { + let exComplete = expectation(description: "after completes") + + Promise.value("hi").cancellize().then { (_: String) -> Promise in + XCTFail("value not cancelled") + return Promise.value("bye") + }.done { _ in + XCTFail("value not cancelled") + }.catch(policy: .allErrors) { error in + error.isCancelled ? exComplete.fulfill() : XCTFail("error: \(error)") + }.cancel() + + wait(for: [exComplete], timeout: 1) + } + + func testFirstlyValueDone() { + let exComplete = expectation(description: "after completes") + + firstly { + Promise.value("hi") + }.cancellize().done { _ in + XCTFail("value not cancelled") + }.catch(policy: .allErrors) { error in + error.isCancelled ? exComplete.fulfill() : XCTFail("error: \(error)") + }.cancel() + + wait(for: [exComplete], timeout: 1) + } + + func testFirstlyThenValueDone() { + let exComplete = expectation(description: "after completes") + + firstly { + Promise.value("hi").cancellize() + }.then { (_: String) -> CancellablePromise in + XCTFail("'hi' not cancelled") + return Promise.value("there").cancellize() + }.done { _ in + XCTFail("'there' not cancelled") + }.catch(policy: .allErrors) { error in + error.isCancelled ? exComplete.fulfill() : XCTFail("error: \(error)") + }.cancel() + + wait(for: [exComplete], timeout: 1) + } + + func testFirstlyValueDifferentContextDone() { + let exComplete = expectation(description: "after completes") + + let p = firstly { + return Promise.value("hi") + }.cancellize().done { _ in + XCTFail("value not cancelled") + }.catch(policy: .allErrors) { error in + error.isCancelled ? exComplete.fulfill() : XCTFail("error: \(error)") + } + p.cancel() + + wait(for: [exComplete], timeout: 1) + } + + func testFirstlyValueDoneDifferentContext() { + let exComplete = expectation(description: "after completes") + + firstly { + Promise.value("hi") + }.cancellize().done { _ in + XCTFail("value not cancelled") + }.catch(policy: .allErrors) { error in + error.isCancelled ? exComplete.fulfill() : XCTFail("error: \(error)") + }.cancel() + + wait(for: [exComplete], timeout: 1) + } + + func testCancelForPromise_Then() { + let exComplete = expectation(description: "after completes") + + let promise = CancellablePromise { seal in + usleep(100000) + seal.fulfill(()) + } + promise.then { () throws -> Promise in + XCTFail("then not cancelled") + return Promise.value("x") + }.done { _ in + XCTFail("done not cancelled") + }.catch(policy: .allErrors) { error in + error.isCancelled ? exComplete.fulfill() : XCTFail("error: \(error)") + }.cancel() + + wait(for: [exComplete], timeout: 1) + } + + func testCancelForPromise_ThenDone() { + let exComplete = expectation(description: "done is cancelled") + + let promise = CancellablePromise { seal in + usleep(100000) + seal.fulfill(()) + } + promise.then { _ -> CancellablePromise in + XCTFail("then not cancelled") + return Promise.value("x").cancellize() + }.done { _ in + XCTFail("done not cancelled") + }.catch(policy: .allErrors) { error in + error.isCancelled ? exComplete.fulfill() : XCTFail("error: \(error)") + }.cancel() + + wait(for: [exComplete], timeout: 1) + } +} diff --git a/Tests/Cancel/WhenConcurrentTests.swift b/Tests/Cancel/WhenConcurrentTests.swift new file mode 100644 index 000000000..d2e9879ea --- /dev/null +++ b/Tests/Cancel/WhenConcurrentTests.swift @@ -0,0 +1,275 @@ +import XCTest +import PromiseKit + +class WhenConcurrentTestCase_Swift: XCTestCase { + + func testWhenSucceed() { + let e = expectation(description: "") + + var numbers = (0..<42).makeIterator() + let squareNumbers = numbers.map { $0 * $0 } + + let generator = AnyIterator> { + guard let number = numbers.next() else { + return nil + } + + return after(.milliseconds(10)).cancellize().map { + return number * number + } + } + + when(fulfilled: generator, concurrently: 5).done { numbers in + if numbers == squareNumbers { + e.fulfill() + } + }.silenceWarning() + + waitForExpectations(timeout: 3, handler: nil) + } + + func testWhenCancel() { + let e = expectation(description: "") + + var numbers = (0..<42).makeIterator() + let squareNumbers = numbers.map { $0 * $0 } + + let generator = AnyIterator> { + guard let number = numbers.next() else { + return nil + } + + return after(.milliseconds(10)).cancellize().map { + XCTFail() + return number * number + } + } + + when(fulfilled: generator, concurrently: 5).done { numbers in + XCTFail() + if numbers == squareNumbers { + e.fulfill() + } + }.catch(policy: .allErrors) { + $0.isCancelled ? e.fulfill() : XCTFail() + }.cancel() + + waitForExpectations(timeout: 3, handler: nil) + } + + func testWhenEmptyGeneratorSucceed() { + let e = expectation(description: "") + + let generator = AnyIterator> { + return nil + } + + when(fulfilled: generator, concurrently: 5).done { numbers in + if numbers.count == 0 { + e.fulfill() + } + }.silenceWarning() + + waitForExpectations(timeout: 1, handler: nil) + } + + func testWhenEmptyGeneratorCancel() { + let e = expectation(description: "") + + let generator = AnyIterator> { + return nil + } + + when(fulfilled: generator, concurrently: 5).done { numbers in + if numbers.count == 0 { + e.fulfill() + } + }.catch(policy: .allErrors) { + $0.isCancelled ? e.fulfill() : XCTFail() + }.cancel() + + waitForExpectations(timeout: 1, handler: nil) + } + + func testWhenGeneratorErrorSucceed() { + enum LocalError: Error { + case Unknown + case DivisionByZero + } + + let expectedErrorIndex = 42 + let expectedError = LocalError.DivisionByZero + + let e = expectation(description: "") + + var numbers = (-expectedErrorIndex..> { + guard let number = numbers.next() else { + return nil + } + + return after(.milliseconds(10)).cancellize().then { _ -> Promise in + if number != 0 { + return Promise(error: expectedError) + } else { + return Promise.value(100500 / number) + } + } + } + + when(fulfilled: generator, concurrently: 3).catch { error in + guard let error = error as? LocalError else { + return + } + guard case .DivisionByZero = error else { + return + } + e.fulfill() + } + + waitForExpectations(timeout: 3, handler: nil) + } + + func testWhenGeneratorErrorCancel() { + enum LocalError: Error { + case Unknown + case DivisionByZero + } + + let expectedErrorIndex = 42 + let expectedError = LocalError.DivisionByZero + + let e = expectation(description: "") + + var numbers = (-expectedErrorIndex..> { + guard let number = numbers.next() else { + return nil + } + + return after(.milliseconds(10)).cancellize().then { _ -> CancellablePromise in + if number != 0 { + return CancellablePromise(error: expectedError) + } else { + return Promise.value(100500 / number).cancellize() + } + } + } + + when(fulfilled: generator, concurrently: 3).catch(policy: .allErrors) { error in + error.isCancelled ? e.fulfill() : XCTFail() + }.cancel() + + waitForExpectations(timeout: 3, handler: nil) + } + + func testWhenConcurrencySucceed() { + let expectedConcurrently = 4 + var currentConcurrently = 0 + var maxConcurrently = 0 + + let e = expectation(description: "") + + var numbers = (0..<42).makeIterator() + + let generator = AnyIterator> { + currentConcurrently += 1 + maxConcurrently = max(maxConcurrently, currentConcurrently) + + guard let number = numbers.next() else { + return nil + } + + return after(.milliseconds(10)).cancellize().then(on: .main) { _ -> Promise in + currentConcurrently -= 1 + return Promise.value(number * number) + } + } + + when(fulfilled: generator, concurrently: expectedConcurrently).done { _ in + XCTAssertEqual(expectedConcurrently, maxConcurrently) + e.fulfill() + }.silenceWarning() + + waitForExpectations(timeout: 3) + } + + func testWhenConcurrencyCancel() { + let expectedConcurrently = 4 + var currentConcurrently = 0 + var maxConcurrently = 0 + + let e = expectation(description: "") + + var numbers = (0..<42).makeIterator() + + let generator = AnyIterator> { + currentConcurrently += 1 + maxConcurrently = max(maxConcurrently, currentConcurrently) + + guard let number = numbers.next() else { + return nil + } + + return after(.milliseconds(10)).cancellize().then(on: .main) { _ -> Promise in + currentConcurrently -= 1 + return Promise.value(number * number) + } + } + + when(fulfilled: generator, concurrently: expectedConcurrently).done { _ in + XCTFail() + XCTAssertEqual(expectedConcurrently, maxConcurrently) + e.fulfill() + }.catch(policy: .allErrors) { + $0.isCancelled ? e.fulfill() : XCTFail() + }.cancel() + + waitForExpectations(timeout: 3) + } + + func testWhenConcurrencyLessThanZero() { + let generator = AnyIterator> { XCTFail(); return nil } + + let p1 = when(fulfilled: generator, concurrently: 0) + let p2 = when(fulfilled: generator, concurrently: -1) + p1.cancel() + p2.cancel() + + guard let e1 = p1.error else { return XCTFail() } + guard let e2 = p2.error else { return XCTFail() } + guard case PMKError.badInput = e1 else { return XCTFail() } + guard case PMKError.badInput = e2 else { return XCTFail() } + } + + func testStopsDequeueingOnceRejected() { + let ex = expectation(description: "") + enum Error: Swift.Error { case dummy } + + var x: UInt = 0 + let generator = AnyIterator> { + x += 1 + switch x { + case 0: + fatalError() + case 1: + return CancellablePromise() + case 2: + return CancellablePromise(error: Error.dummy) + case _: + XCTFail() + return nil + } + } + + when(fulfilled: generator, concurrently: 1).done { + XCTFail("\($0)") + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + }.cancel() + + waitForExpectations(timeout: 3) + } +} diff --git a/Tests/Cancel/WhenResolvedTests.swift b/Tests/Cancel/WhenResolvedTests.swift new file mode 100644 index 000000000..c3859777f --- /dev/null +++ b/Tests/Cancel/WhenResolvedTests.swift @@ -0,0 +1,69 @@ +// Created by Austin Feight on 3/19/16. +// Copyright © 2016 Max Howell. All rights reserved. + +import PromiseKit +import XCTest + +class JoinTests: XCTestCase { + func testImmediates() { + let successPromise = CancellablePromise() + + var joinFinished = false + when(resolved: successPromise).done(on: nil) { _ in joinFinished = true }.cancel() + XCTAssert(joinFinished, "Join immediately finishes on fulfilled promise") + + let promise2 = Promise.value(2) + let promise3: CancellablePromise = Promise.value(3).cancellize() + let promise4 = Promise.value(4) + var join2Finished = false + when(resolved: CancellablePromise(promise2), promise3, CancellablePromise(promise4)).done(on: nil) { _ in join2Finished = true }.cancel() + XCTAssert(join2Finished, "Join immediately finishes on fulfilled promises") + } + + func testFulfilledAfterAllResolve() { + let (promise1, seal1) = CancellablePromise.pending() + let (promise2, seal2) = Promise.pending() + let (promise3, seal3) = CancellablePromise.pending() + + var finished = false + let promise = when(resolved: promise1, CancellablePromise(promise2), promise3).done(on: nil) { _ in finished = true } + XCTAssertFalse(finished, "Not all promises have resolved") + + seal1.fulfill(()) + XCTAssertFalse(finished, "Not all promises have resolved") + + seal2.fulfill(()) + XCTAssertFalse(finished, "Not all promises have resolved") + + seal3.fulfill(()) + promise.cancel() + XCTAssert(finished, "All promises have resolved") + } + + func testCancelledAfterAllResolve() { + let (promise1, seal1) = CancellablePromise.pending() + let (promise2, seal2) = Promise.pending() + let (promise3, seal3) = CancellablePromise.pending() + + var cancelled = false + let ex = expectation(description: "") + let cp2 = CancellablePromise(promise2) + when(resolved: promise1, cp2, promise3).done(on: nil) { _ in + XCTFail() + }.catch(policy: .allErrors) { + cancelled = $0.isCancelled + cancelled ? ex.fulfill() : XCTFail() + }.cancel() + + seal1.fulfill(()) + seal2.fulfill(()) + seal3.fulfill(()) + + waitForExpectations(timeout: 1) + + XCTAssert(cancelled, "Cancel error caught") + XCTAssert(promise1.isCancelled, "Promise 1 cancelled") + XCTAssert(cp2.isCancelled, "Promise 2 cancelled") + XCTAssert(promise3.isCancelled, "Promise 3 cancelled") + } +} diff --git a/Tests/Cancel/WhenTests.swift b/Tests/Cancel/WhenTests.swift new file mode 100644 index 000000000..5edeab944 --- /dev/null +++ b/Tests/Cancel/WhenTests.swift @@ -0,0 +1,348 @@ +import PromiseKit +import Dispatch +import XCTest + +class WhenTests: XCTestCase { + + func testEmpty() { + let e1 = expectation(description: "") + let promises: [CancellablePromise] = [] + when(fulfilled: promises).done { _ in + XCTFail() + }.catch(policy: .allErrors) { + $0.isCancelled ? e1.fulfill() : XCTFail() + }.cancel() + + let e2 = expectation(description: "") + when(resolved: promises).done { _ in + XCTFail() + e2.fulfill() + }.catch(policy: .allErrors) { + $0.isCancelled ? e2.fulfill() : XCTFail() + }.cancel() + + wait(for: [e1, e2], timeout: 1) + } + + func testInt() { + let e1 = expectation(description: "") + let p1 = Promise.value(1).cancellize() + let p2 = Promise.value(2).cancellize() + let p3 = Promise.value(3).cancellize() + let p4 = Promise.value(4).cancellize() + + when(fulfilled: [p1, p2, p3, p4]).done { _ in + XCTFail() + }.catch(policy: .allErrors) { + $0.isCancelled ? e1.fulfill() : XCTFail() + }.cancel() + waitForExpectations(timeout: 1, handler: nil) + } + + func testIntAlt() { + let e1 = expectation(description: "") + let p1 = Promise.value(1).cancellize() + let p2 = Promise.value(2).cancellize() + let p3 = Promise.value(3).cancellize() + let p4 = Promise.value(4).cancellize() + + when(fulfilled: p1, p2, p3, p4).done { _ in + XCTFail() + }.catch(policy: .allErrors) { + $0.isCancelled ? e1.fulfill() : XCTFail() + }.cancel() + waitForExpectations(timeout: 1, handler: nil) + } + + func testDoubleTupleSucceed() { + let e1 = expectation(description: "") + let p1 = Promise.value(1).cancellize() + let p2 = Promise.value("abc").cancellize() + cancellableWhen(fulfilled: p1, p2).done{ x, y in + XCTAssertEqual(x, 1) + XCTAssertEqual(y, "abc") + e1.fulfill() + }.silenceWarning() + waitForExpectations(timeout: 1, handler: nil) + } + + func testDoubleTupleCancel() { + let e1 = expectation(description: "") + let p1 = Promise.value(1).cancellize() + let p2 = Promise.value("abc").cancellize() + cancellableWhen(fulfilled: p1, p2).done{ _, _ in + XCTFail() + }.catch(policy: .allErrors) { + $0.isCancelled ? e1.fulfill() : XCTFail() + }.cancel() + waitForExpectations(timeout: 1, handler: nil) + } + + func testTripleTuple() { + let e1 = expectation(description: "") + let p1 = Promise.value(1).cancellize() + let p2 = Promise.value("abc").cancellize() + let p3 = Promise.value(1.0).cancellize() + cancellableWhen(fulfilled: p1, p2, p3).done { _, _, _ in + XCTFail() + }.catch(policy: .allErrors) { + $0.isCancelled ? e1.fulfill() : XCTFail() + }.cancel() + waitForExpectations(timeout: 1, handler: nil) + } + + func testQuadrupleTuple() { + let e1 = expectation(description: "") + let p1 = Promise.value(1).cancellize() + let p2 = Promise.value("abc").cancellize() + let p3 = Promise.value(1.0).cancellize() + let p4 = Promise.value(true).cancellize() + cancellableWhen(fulfilled: p1, p2, p3, p4).done { _, _, _, _ in + XCTFail() + }.catch(policy: .allErrors) { + $0.isCancelled ? e1.fulfill() : XCTFail() + }.cancel() + waitForExpectations(timeout: 1, handler: nil) + } + + func testQuintupleTuple() { + let e1 = expectation(description: "") + let p1 = Promise.value(1).cancellize() + let p2 = Promise.value("abc").cancellize() + let p3 = Promise.value(1.0).cancellize() + let p4 = Promise.value(true).cancellize() + let p5 = Promise.value("a" as Character).cancellize() + cancellableWhen(fulfilled: p1, p2, p3, p4, p5).done { _, _, _, _, _ in + XCTFail() + }.catch(policy: .allErrors) { + $0.isCancelled ? e1.fulfill() : XCTFail() + }.cancel() + waitForExpectations(timeout: 1, handler: nil) + } + + func testVoid() { + let e1 = expectation(description: "") + let p1 = Promise.value(1).cancellize().done { _ in } + let p2 = Promise.value(2).cancellize().done { _ in } + let p3 = Promise.value(3).cancellize().done { _ in } + let p4 = Promise.value(4).cancellize().done { _ in } + + when(fulfilled: p1, p2, p3, p4).done { + XCTFail() + }.catch(policy: .allErrors) { + $0.isCancelled ? e1.fulfill() : XCTFail() + }.cancel() + + waitForExpectations(timeout: 1, handler: nil) + } + + func testRejected() { + enum Error: Swift.Error { case dummy } + + let e1 = expectation(description: "") + let p1 = after(.milliseconds(100)).cancellize().map{ true } + let p2: CancellablePromise = after(.milliseconds(200)).cancellize().map{ throw Error.dummy } + let p3 = Promise.value(false).cancellize() + + cancellableWhen(fulfilled: p1, p2, p3).catch(policy: .allErrors) { + $0.isCancelled ? e1.fulfill() : XCTFail() + }.cancel() + + waitForExpectations(timeout: 1, handler: nil) + } + + func testProgress() { + let ex = expectation(description: "") + + XCTAssertNil(Progress.current()) + + let p1 = after(.milliseconds(10)).cancellize() + let p2 = after(.milliseconds(20)).cancellize() + let p3 = after(.milliseconds(30)).cancellize() + let p4 = after(.milliseconds(40)).cancellize() + + let progress = Progress(totalUnitCount: 1) + progress.becomeCurrent(withPendingUnitCount: 1) + + when(fulfilled: p1, p2, p3, p4).done { _ in + XCTAssertEqual(progress.completedUnitCount, 1) + ex.fulfill() + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + }.cancel() + + progress.resignCurrent() + + waitForExpectations(timeout: 1, handler: nil) + } + + func testProgressDoesNotExceed100PercentSucceed() { + let ex1 = expectation(description: "") + let ex2 = expectation(description: "") + + XCTAssertNil(Progress.current()) + + let p1 = after(.milliseconds(10)).cancellize() + let p2 = after(.milliseconds(20)).cancellize().done { throw NSError(domain: "a", code: 1, userInfo: nil) } + let p3 = after(.milliseconds(30)).cancellize() + let p4 = after(.milliseconds(40)).cancellize() + + let progress = Progress(totalUnitCount: 1) + progress.becomeCurrent(withPendingUnitCount: 1) + + let promise = when(fulfilled: p1, p2, p3, p4) + + progress.resignCurrent() + + promise.catch { _ in + ex2.fulfill() + } + + var x = 0 + func finally() { + x += 1 + if x == 4 { + XCTAssertLessThanOrEqual(1, progress.fractionCompleted) + XCTAssertEqual(progress.completedUnitCount, 1) + ex1.fulfill() + } + } + + let q = DispatchQueue.main + p1.done(on: q, finally).silenceWarning() + p2.ensure(on: q, finally).silenceWarning() + p3.done(on: q, finally).silenceWarning() + p4.done(on: q, finally).silenceWarning() + + waitForExpectations(timeout: 1, handler: nil) + } + + func testProgressDoesNotExceed100PercentCancel() { + let ex1 = expectation(description: "") + let ex2 = expectation(description: "") + let ex3 = expectation(description: "") + + XCTAssertNil(Progress.current()) + + let p1 = after(.milliseconds(10)).cancellize() + let p2 = after(.milliseconds(20)).cancellize().done { throw NSError(domain: "a", code: 1, userInfo: nil) } + let p3 = after(.milliseconds(30)).cancellize() + let p4 = after(.milliseconds(40)).cancellize() + + let progress = Progress(totalUnitCount: 1) + progress.becomeCurrent(withPendingUnitCount: 1) + + let promise = when(fulfilled: p1, p2, p3, p4) + + progress.resignCurrent() + + promise.catch(policy: .allErrors) { + $0.isCancelled ? ex2.fulfill() : XCTFail() + } + + promise.cancel() + + func finally() { + XCTFail() + } + + func finallyEnsure() { + ex3.fulfill() + } + + var x = 0 + func catchall(err: Error) { + XCTAssert(err.isCancelled) + x += 1 + if x == 4 { + XCTAssertLessThanOrEqual(1, progress.fractionCompleted) + XCTAssertEqual(progress.completedUnitCount, 1) + ex1.fulfill() + } + } + + let q = DispatchQueue.main + p1.done(on: q, finally).catch(policy: .allErrors, catchall) + p2.ensure(on: q, finallyEnsure).catch(policy: .allErrors, catchall) + p3.done(on: q, finally).catch(policy: .allErrors, catchall) + p4.done(on: q, finally).catch(policy: .allErrors, catchall) + + waitForExpectations(timeout: 1, handler: nil) + } + + func testUnhandledErrorHandlerDoesNotFire() { + enum Error: Swift.Error { + case test + } + + let ex = expectation(description: "") + let p1 = CancellablePromise(error: Error.test) + let p2 = after(.milliseconds(100)).cancellize() + cancellableWhen(fulfilled: p1, p2).done{ _ in XCTFail() }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + }.cancel() + + waitForExpectations(timeout: 1, handler: nil) + } + + func testUnhandledErrorHandlerDoesNotFireForStragglers() { + enum Error: Swift.Error { + case test + case straggler + } + + let ex1 = expectation(description: "") + let ex2 = expectation(description: "") + let ex3 = expectation(description: "") + + let p1 = CancellablePromise(error: Error.test) + let p2 = after(.milliseconds(100)).cancellize().done { throw Error.straggler } + let p3 = after(.milliseconds(200)).cancellize().done { throw Error.straggler } + + cancellableWhen(fulfilled: p1, p2, p3).catch(policy: .allErrors) { + $0.isCancelled ? ex1.fulfill() : XCTFail() + }.cancel() + + p2.ensure { after(.milliseconds(100)).done(ex2.fulfill) }.silenceWarning() + p3.ensure { after(.milliseconds(100)).done(ex3.fulfill) }.silenceWarning() + + waitForExpectations(timeout: 1, handler: nil) + } + + func testAllSealedRejectedFirstOneRejects() { + enum Error: Swift.Error { + case test1 + case test2 + case test3 + } + + let ex = expectation(description: "") + let p1 = CancellablePromise(error: Error.test1) + let p2 = CancellablePromise(error: Error.test2) + let p3 = CancellablePromise(error: Error.test3) + + when(fulfilled: p1, p2, p3).catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + }.cancel() + + waitForExpectations(timeout: 1) + } + + func testGuaranteeWhen() { + let ex1 = expectation(description: "") + when(resolved: Guarantee().cancellize(), Guarantee().cancellize()).done { _ in + XCTFail() + }.catch(policy: .allErrors) { + $0.isCancelled ? ex1.fulfill() : XCTFail() + }.cancel() + + let ex2 = expectation(description: "") + when(resolved: [Guarantee().cancellize(), Guarantee().cancellize()]).done { _ in + XCTFail() + }.catch(policy: .allErrors) { + $0.isCancelled ? ex2.fulfill() : XCTFail() + }.cancel() + + wait(for: [ex1, ex2], timeout: 10) + } +} diff --git a/Tests/Cancel/XCTestManifests.swift b/Tests/Cancel/XCTestManifests.swift new file mode 100644 index 000000000..cf90f8da5 --- /dev/null +++ b/Tests/Cancel/XCTestManifests.swift @@ -0,0 +1,380 @@ +#if !canImport(ObjectiveC) +import XCTest + +extension AfterTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__AfterTests = [ + ("testCancelForGuarantee_Done", testCancelForGuarantee_Done), + ("testCancelForPromise_Done", testCancelForPromise_Done), + ("testCancellableAfter", testCancellableAfter), + ("testNegative", testNegative), + ("testPositive", testPositive), + ("testZero", testZero), + ] +} + +extension CancelChain { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__CancelChain = [ + ("testCancelChainPAD", testCancelChainPAD), + ("testCancelChainPB", testCancelChainPB), + ("testCancelChainPC", testCancelChainPC), + ("testCancelChainSuccess", testCancelChainSuccess), + ] +} + +extension CancellableDefaultDispatchQueueTest { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__CancellableDefaultDispatchQueueTest = [ + ("testOverrodeDefaultAlwaysQueue", testOverrodeDefaultAlwaysQueue), + ("testOverrodeDefaultCatchQueue", testOverrodeDefaultCatchQueue), + ("testOverrodeDefaultThenQueue", testOverrodeDefaultThenQueue), + ] +} + +extension CancellableErrorTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__CancellableErrorTests = [ + ("testCustomDebugStringConvertible", testCustomDebugStringConvertible), + ("testCustomStringConvertible", testCustomStringConvertible), + ] +} + +extension CancellablePromiseTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__CancellablePromiseTests = [ + ("testBridge", testBridge), + ("testCancel", testCancel), + ("testCancellablePromiseEmbeddedInStandardPromiseChain", testCancellablePromiseEmbeddedInStandardPromiseChain), + ("testChain", testChain), + ("testFirstly", testFirstly), + ("testFirstlyWithPromise", testFirstlyWithPromise), + ("testReturnTypeForAMultiLineClosureIsNotExplicitlyStated", testReturnTypeForAMultiLineClosureIsNotExplicitlyStated), + ("testThenMapCancel", testThenMapCancel), + ("testThenMapSuccess", testThenMapSuccess), + ("testTryingToCancelAStandardPromiseChain", testTryingToCancelAStandardPromiseChain), + ] +} + +extension CancellationTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__CancellationTests = [ + ("testCancellation", testCancellation), + ("testFoundationBridging1", testFoundationBridging1), + ("testFoundationBridging2", testFoundationBridging2), + ("testIsCancelled", testIsCancelled), + ("testRecoverWithCancellation", testRecoverWithCancellation), + ("testThrowCancellableErrorThatIsNotCancelled", testThrowCancellableErrorThatIsNotCancelled), + ] +} + +extension CatchableTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__CatchableTests = [ + ("test__cancellable_conditional_recover__fulfilled_path", test__cancellable_conditional_recover__fulfilled_path), + ("test__conditional_recover", test__conditional_recover), + ("test__conditional_recover__fulfilled_path", test__conditional_recover__fulfilled_path), + ("test__conditional_recover__ignores_cancellation_but_fed_cancellation", test__conditional_recover__ignores_cancellation_but_fed_cancellation), + ("test__conditional_recover__no_recover", test__conditional_recover__no_recover), + ("test__full_recover", test__full_recover), + ("test__full_recover__fulfilled_path", test__full_recover__fulfilled_path), + ("test__void_specialized_conditional_recover", test__void_specialized_conditional_recover), + ("test__void_specialized_conditional_recover__fulfilled_path", test__void_specialized_conditional_recover__fulfilled_path), + ("test__void_specialized_conditional_recover__ignores_cancellation_but_fed_cancellation", test__void_specialized_conditional_recover__ignores_cancellation_but_fed_cancellation), + ("test__void_specialized_conditional_recover__no_recover", test__void_specialized_conditional_recover__no_recover), + ("test__void_specialized_full_recover", test__void_specialized_full_recover), + ("test__void_specialized_full_recover__fulfilled_path", test__void_specialized_full_recover__fulfilled_path), + ("testCancellableFinalizerHelpers", testCancellableFinalizerHelpers), + ("testCancellableRecoverFromError", testCancellableRecoverFromError), + ("testCauterize", testCauterize), + ("testEnsureThen_Error", testEnsureThen_Error), + ("testEnsureThen_Value", testEnsureThen_Value), + ("testEnsureThen_Value_NotCancelled", testEnsureThen_Value_NotCancelled), + ("testFinally", testFinally), + ] +} + +extension DispatcherTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__DispatcherTests = [ + ("testDispatcherExtensionCanThrowInBody", testDispatcherExtensionCanThrowInBody), + ("testDispatcherExtensionReturnsGuarantee", testDispatcherExtensionReturnsGuarantee), + ("testDispatcherWithThrow", testDispatcherWithThrow), + ("testDispatchQueueSelection", testDispatchQueueSelection), + ("testMapValues", testMapValues), + ("testRecover", testRecover), + ] +} + +extension GuaranteeTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__GuaranteeTests = [ + ("testCancellable", testCancellable), + ("testInit", testInit), + ("testSetCancellable", testSetCancellable), + ("testThenMap", testThenMap), + ("testWait", testWait), + ] +} + +extension HangTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__HangTests = [ + ("test", test), + ("testError", testError), + ] +} + +extension JoinTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__JoinTests = [ + ("testCancelledAfterAllResolve", testCancelledAfterAllResolve), + ("testFulfilledAfterAllResolve", testFulfilledAfterAllResolve), + ("testImmediates", testImmediates), + ] +} + +extension PromiseTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__PromiseTests = [ + ("testBodyThrowsError", testBodyThrowsError), + ("testCancellable", testCancellable), + ("testCanMakeVoidPromise", testCanMakeVoidPromise), + ("testCannotFulfillWithError", testCannotFulfillWithError), + ("testCustomStringConvertible", testCustomStringConvertible), + ("testDispatchQueueAsyncExtensionCanThrowInBody", testDispatchQueueAsyncExtensionCanThrowInBody), + ("testDispatchQueueAsyncExtensionReturnsPromise", testDispatchQueueAsyncExtensionReturnsPromise), + ("testInitCancellable", testInitCancellable), + ("testInitVoidCancellable", testInitVoidCancellable), + ("testIsFulfilled", testIsFulfilled), + ("testIsPending", testIsPending), + ("testIsRejected", testIsRejected), + ("testIsResolved", testIsResolved), + ("testPipeForResolved", testPipeForResolved), + ("testSetCancellable", testSetCancellable), + ("testThrowInFirstly", testThrowInFirstly), + ("testThrowInInitializer", testThrowInInitializer), + ("testWait", testWait), + ] +} + +extension RaceTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__RaceTests = [ + ("test1", test1), + ("test1Array", test1Array), + ("test2", test2), + ("test2Array", test2Array), + ("testCancelInner", testCancelInner), + ("testEmptyArray", testEmptyArray), + ("testReject", testReject), + ] +} + +extension RegressionTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__RegressionTests = [ + ("testReturningPreviousPromiseWorks", testReturningPreviousPromiseWorks), + ] +} + +extension StressTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__StressTests = [ + ("testCancelContextConcurrentAppend", testCancelContextConcurrentAppend), + ("testCancelContextConcurrentCancel", testCancelContextConcurrentCancel), + ("testCancelContextConcurrentReadWrite", testCancelContextConcurrentReadWrite), + ("testThenDataRace", testThenDataRace), + ("testThensAreSequentialForLongTime", testThensAreSequentialForLongTime), + ("testZalgoDataRace", testZalgoDataRace), + ] +} + +extension ThenableTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__ThenableTests = [ + ("testBarrier", testBarrier), + ("testCompactMap", testCompactMap), + ("testCompactMapThrows", testCompactMapThrows), + ("testCompactMapValues", testCompactMapValues), + ("testDispatchFlagsSyntax", testDispatchFlagsSyntax), + ("testFirstValueForEmpty", testFirstValueForEmpty), + ("testGet", testGet), + ("testLastValueForEmpty", testLastValueForEmpty), + ("testPMKErrorCompactMap", testPMKErrorCompactMap), + ("testRejectedPromiseCompactMap", testRejectedPromiseCompactMap), + ("testThenFlatMap", testThenFlatMap), + ("testThenMap", testThenMap), + ("testThenOffRejected", testThenOffRejected), + ] +} + +extension TimeoutTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__TimeoutTests = [ + ("testCancelBeforeTimeout", testCancelBeforeTimeout), + ("testCancelRaceBeforeTimeout", testCancelRaceBeforeTimeout), + ("testDoubleTimeout", testDoubleTimeout), + ("testMixTypes", testMixTypes), + ("testNoTimeout", testNoTimeout), + ("testReset", testReset), + ("testTimeout", testTimeout), + ] +} + +extension ValueTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__ValueTests = [ + ("testCancelForPromise_Then", testCancelForPromise_Then), + ("testCancelForPromise_ThenDone", testCancelForPromise_ThenDone), + ("testFirstlyThenValueDone", testFirstlyThenValueDone), + ("testFirstlyValueDifferentContextDone", testFirstlyValueDifferentContextDone), + ("testFirstlyValueDone", testFirstlyValueDone), + ("testFirstlyValueDoneDifferentContext", testFirstlyValueDoneDifferentContext), + ("testValueContext", testValueContext), + ("testValueDone", testValueDone), + ("testValueThen", testValueThen), + ] +} + +extension WhenConcurrentTestCase_Swift { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__WhenConcurrentTestCase_Swift = [ + ("testStopsDequeueingOnceRejected", testStopsDequeueingOnceRejected), + ("testWhenCancel", testWhenCancel), + ("testWhenConcurrencyCancel", testWhenConcurrencyCancel), + ("testWhenConcurrencyLessThanZero", testWhenConcurrencyLessThanZero), + ("testWhenConcurrencySucceed", testWhenConcurrencySucceed), + ("testWhenEmptyGeneratorCancel", testWhenEmptyGeneratorCancel), + ("testWhenEmptyGeneratorSucceed", testWhenEmptyGeneratorSucceed), + ("testWhenGeneratorErrorCancel", testWhenGeneratorErrorCancel), + ("testWhenGeneratorErrorSucceed", testWhenGeneratorErrorSucceed), + ("testWhenSucceed", testWhenSucceed), + ] +} + +extension WhenTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__WhenTests = [ + ("testAllSealedRejectedFirstOneRejects", testAllSealedRejectedFirstOneRejects), + ("testDoubleTupleCancel", testDoubleTupleCancel), + ("testDoubleTupleSucceed", testDoubleTupleSucceed), + ("testEmpty", testEmpty), + ("testGuaranteeWhen", testGuaranteeWhen), + ("testInt", testInt), + ("testIntAlt", testIntAlt), + ("testProgress", testProgress), + ("testProgressDoesNotExceed100PercentCancel", testProgressDoesNotExceed100PercentCancel), + ("testProgressDoesNotExceed100PercentSucceed", testProgressDoesNotExceed100PercentSucceed), + ("testQuadrupleTuple", testQuadrupleTuple), + ("testQuintupleTuple", testQuintupleTuple), + ("testRejected", testRejected), + ("testTripleTuple", testTripleTuple), + ("testUnhandledErrorHandlerDoesNotFire", testUnhandledErrorHandlerDoesNotFire), + ("testUnhandledErrorHandlerDoesNotFireForStragglers", testUnhandledErrorHandlerDoesNotFireForStragglers), + ("testVoid", testVoid), + ] +} + +extension WrapTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__WrapTests = [ + ("testError", testError), + ("testErrorCancellableKitten", testErrorCancellableKitten), + ("testErrorNoDelay", testErrorNoDelay), + ("testInvalidCallingConvention", testInvalidCallingConvention), + ("testInvalidCallingConventionCancellableKitten", testInvalidCallingConventionCancellableKitten), + ("testInvertedCallingConvention", testInvertedCallingConvention), + ("testInvertedCallingConventionCancellableKitten", testInvertedCallingConventionCancellableKitten), + ("testIsFulfilled", testIsFulfilled), + ("testNonOptionalFirstParameter", testNonOptionalFirstParameter), + ("testNonOptionalFirstParameterCancellableKitten", testNonOptionalFirstParameterCancellableKitten), + ("testPendingPromiseDeallocated", testPendingPromiseDeallocated), + ("testSuccess", testSuccess), + ("testVoidCompletionValue", testVoidCompletionValue), + ("testVoidCompletionValueCancellableKitten", testVoidCompletionValueCancellableKitten), + ] +} + +extension ZalgoTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__ZalgoTests = [ + ("test1", test1), + ("test2", test2), + ("test3Cancel", test3Cancel), + ("test3Succeed", test3Succeed), + ("test4", test4), + ] +} + +public func __allTests() -> [XCTestCaseEntry] { + return [ + testCase(AfterTests.__allTests__AfterTests), + testCase(CancelChain.__allTests__CancelChain), + testCase(CancellableDefaultDispatchQueueTest.__allTests__CancellableDefaultDispatchQueueTest), + testCase(CancellableErrorTests.__allTests__CancellableErrorTests), + testCase(CancellablePromiseTests.__allTests__CancellablePromiseTests), + testCase(CancellationTests.__allTests__CancellationTests), + testCase(CatchableTests.__allTests__CatchableTests), + testCase(DispatcherTests.__allTests__DispatcherTests), + testCase(GuaranteeTests.__allTests__GuaranteeTests), + testCase(HangTests.__allTests__HangTests), + testCase(JoinTests.__allTests__JoinTests), + testCase(PromiseTests.__allTests__PromiseTests), + testCase(RaceTests.__allTests__RaceTests), + testCase(RegressionTests.__allTests__RegressionTests), + testCase(StressTests.__allTests__StressTests), + testCase(ThenableTests.__allTests__ThenableTests), + testCase(TimeoutTests.__allTests__TimeoutTests), + testCase(ValueTests.__allTests__ValueTests), + testCase(WhenConcurrentTestCase_Swift.__allTests__WhenConcurrentTestCase_Swift), + testCase(WhenTests.__allTests__WhenTests), + testCase(WrapTests.__allTests__WrapTests), + testCase(ZalgoTests.__allTests__ZalgoTests), + ] +} +#endif diff --git a/Tests/Cancel/ZalgoTests.swift b/Tests/Cancel/ZalgoTests.swift new file mode 100644 index 000000000..11fe3ca9b --- /dev/null +++ b/Tests/Cancel/ZalgoTests.swift @@ -0,0 +1,89 @@ +import XCTest +import PromiseKit + +class ZalgoTests: XCTestCase { + func test1() { + var resolved = false + Promise.value(1).cancellize().done(on: nil) { _ in + resolved = true + }.catch(policy: .allErrors) { _ in + resolved = false + }.cancel() + XCTAssertTrue(resolved) + } + + func test2() { + let p1 = Promise.value(1).cancellize().map(on: nil) { _ in + return 2 + } + p1.cancel() + XCTAssertEqual(p1.value!, 2) + + var x = 0 + + let ex = expectation(description: "") + let (p2, seal) = CancellablePromise.pending() + p2.cancel() + p2.done(on: nil) { _ in + x = 1 + }.catch(policy: .allErrors) { _ in + x = 2 + ex.fulfill() + } + XCTAssertEqual(x, 0) + + seal.fulfill(1) + waitForExpectations(timeout: 1) + XCTAssertEqual(x, 2) + } + + // returning a pending promise from its own zalgo’d then handler doesn’t hang + func test3Succeed() { + let ex = (expectation(description: ""), expectation(description: "")) + + var p1: CancellablePromise! + p1 = after(.milliseconds(100)).cancellize().then(on: nil) { _ -> CancellablePromise in + p1.cancel() + ex.0.fulfill() + return p1 + } + + p1.catch(policy: .allErrors) { err in + defer{ ex.1.fulfill() } + guard case PMKError.returnedSelf = err else { return XCTFail() } + } + + waitForExpectations(timeout: 1) + } + + // returning a pending promise from its own zalgo’d then handler doesn’t hang + func test3Cancel() { + let ex = expectation(description: "") + + var p1: CancellablePromise! + p1 = after(.milliseconds(100)).cancellize().then(on: nil) { _ -> CancellablePromise in + XCTFail() + return p1 + } + p1.cancel() + + p1.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + } + + waitForExpectations(timeout: 1) + } + + // return a sealed promise from its own zalgo’d then handler doesn’t hang + func test4() { + let ex = expectation(description: "") + let p1 = Promise.value(1).cancellize() + p1.then(on: nil) { _ -> CancellablePromise in + ex.fulfill() + return p1 + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + }.cancel() + waitForExpectations(timeout: 1) + } +} diff --git a/Tests/Core/DispatcherTests.swift b/Tests/Core/DispatcherTests.swift index c5551afa2..b4288660b 100644 --- a/Tests/Core/DispatcherTests.swift +++ b/Tests/Core/DispatcherTests.swift @@ -94,6 +94,8 @@ class DispatcherTests: XCTestCase { XCTAssertNotNil(queueID) XCTAssertEqual(queueID!, 100) return x + 10 + }.get(on: .global(qos: .background), flags: .barrier) { _ in + }.tap(on: .global(qos: .background), flags: .barrier) { _ in }.then(on: .main, flags: []) { (x: Int) -> Promise in XCTAssertEqual(x, 52) let queueID = DispatchQueue.getSpecific(key: queueIDKey) @@ -124,6 +126,81 @@ class DispatcherTests: XCTestCase { } + func testMapValues() { + let ex1 = expectation(description: "DispatchQueue MapValues compatibility") + Promise.value([42, 52]).mapValues(on: .global(qos: .background), flags: .barrier) { + $0 + 10 + }.compactMap(on: .global(qos: .background), flags: .barrier) { + $0 + }.flatMapValues(on: .global(qos: .background), flags: .barrier) { + [$0 + 10] + }.compactMapValues(on: .global(qos: .background), flags: .barrier) { + $0 + 10 + }.thenMap(on: .global(qos: .background), flags: .barrier) { + Promise.value($0 + 10) + }.thenFlatMap(on: .global(qos: .background), flags: .barrier) { + Promise.value([$0 + 10]) + }.filterValues(on: .global(qos: .background), flags: .barrier) { _ in + true + }.sortedValues(on: .global(qos: .background), flags: .barrier).firstValue(on: .global(qos: .background), flags: .barrier) { _ in + true + }.done(on: .global(qos: .background), flags: .barrier) { + XCTAssertEqual($0, 92) + ex1.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier) { _ in + XCTFail() + } + + let ex2 = expectation(description: "DispatchQueue firstValue property") + Promise.value([42, 52]).firstValue.done(on: .global(qos: .background), flags: .barrier) { + XCTAssertEqual($0, 42) + ex2.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + XCTFail() + } + + let ex3 = expectation(description: "DispatchQueue lastValue property") + Promise.value([42, 52]).lastValue.done(on: .global(qos: .background), flags: .barrier) { + XCTAssertEqual($0, 52) + ex3.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + XCTFail() + } + + waitForExpectations(timeout: 1) + } + + func testRecover() { + let ex1 = expectation(description: "DispatchQueue CatchMixin compatibility") + Promise.value(42).recover(on: .global(qos: .background), flags: .barrier) { _ in + return Promise.value(42) + }.ensure(on: .global(qos: .background), flags: .barrier) { + }.ensureThen(on: .global(qos: .background), flags: .barrier) { + return after(seconds: 0.0) + }.recover(on: .global(qos: .background), flags: .barrier) { _ in + return Promise.value(42) + }.done(on: .global(qos: .background), flags: .barrier) { + XCTAssertEqual($0, 42) + ex1.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier) { _ in + XCTFail() + } + + let ex2 = expectation(description: "DispatchQueue CatchMixin Void recover") + firstly { + Promise.value(42).asVoid() + }.recover(on: .global(qos: .background), flags: .barrier) { _ in + throw PMKError.emptySequence + }.recover(on: .global(qos: .background), flags: .barrier) { _ in + }.done { + ex2.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier) { _ in + XCTFail() + } + + waitForExpectations(timeout: 1) + } + @available(macOS 10.10, iOS 2.0, tvOS 10.0, watchOS 2.0, *) func testDispatcherExtensionReturnsGuarantee() { let ex = expectation(description: "Dispatcher.promise") diff --git a/Tests/Core/RaceTests.swift b/Tests/Core/RaceTests.swift index c3676a11e..b0af141f9 100644 --- a/Tests/Core/RaceTests.swift +++ b/Tests/Core/RaceTests.swift @@ -48,4 +48,14 @@ class RaceTests: XCTestCase { } wait(for: [ex], timeout: 10) } + + func testReject() { + let ex = expectation(description: "") + race(Promise(error: PMKError.timedOut), after(.milliseconds(10)).map{ 2 }).done { index in + XCTFail() + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + } + waitForExpectations(timeout: 1, handler: nil) + } } diff --git a/Tests/Core/XCTestManifests.swift b/Tests/Core/XCTestManifests.swift index 085fafd03..6fce488bb 100644 --- a/Tests/Core/XCTestManifests.swift +++ b/Tests/Core/XCTestManifests.swift @@ -69,7 +69,9 @@ extension DispatcherTests { ("testDispatcherExtensionReturnsGuarantee", testDispatcherExtensionReturnsGuarantee), ("testDispatcherWithThrow", testDispatcherWithThrow), ("testDispatchQueueSelection", testDispatchQueueSelection), + ("testMapValues", testMapValues), ("testPMKDefaultIdentity", testPMKDefaultIdentity), + ("testRecover", testRecover), ] } @@ -175,6 +177,7 @@ extension RaceTests { ("test2", test2), ("test2Array", test2Array), ("testEmptyArray", testEmptyArray), + ("testReject", testReject), ] } diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index 3ccd10feb..ba82cd3fd 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -2,11 +2,13 @@ import XCTest import A__js import A__swift +import Cancel import Core var tests = [XCTestCaseEntry]() tests += A__js.__allTests() tests += A__swift.__allTests() +tests += Cancel.__allTests() tests += Core.__allTests() XCTMain(tests) From ed6a108e56fd62ec12731514efcf0409112d6823 Mon Sep 17 00:00:00 2001 From: Garth Snyder Date: Sat, 16 Mar 2019 18:00:25 -0700 Subject: [PATCH 19/81] Access control rationalization for dispatchers --- Sources/ConcurrencyLimitedDispatcher.swift | 4 ++-- Sources/Dispatcher.swift | 4 ++-- Sources/RateLimitedDispatcher.swift | 4 ++-- Sources/RateLimitedDispatcherBase.swift | 18 +++++++++--------- Sources/StrictRateLimitedDispatcher.swift | 2 +- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Sources/ConcurrencyLimitedDispatcher.swift b/Sources/ConcurrencyLimitedDispatcher.swift index 2a745c59f..b4e7da74d 100644 --- a/Sources/ConcurrencyLimitedDispatcher.swift +++ b/Sources/ConcurrencyLimitedDispatcher.swift @@ -3,12 +3,12 @@ import Foundation /// A PromiseKit Dispatcher that allows no more than X simultaneous /// executions at once. -class ConcurrencyLimitedDispatcher: Dispatcher { +public class ConcurrencyLimitedDispatcher: Dispatcher { let queue: Dispatcher let serializer: DispatchQueue = DispatchQueue(label: "CLD serializer") - private let semaphore: DispatchSemaphore + let semaphore: DispatchSemaphore /// A `PromiseKit` `Dispatcher` that allows no more than X simultaneous /// executions at once. diff --git a/Sources/Dispatcher.swift b/Sources/Dispatcher.swift index 835cb6fd4..0a731225b 100644 --- a/Sources/Dispatcher.swift +++ b/Sources/Dispatcher.swift @@ -27,7 +27,7 @@ public struct DispatchQueueDispatcher: Dispatcher { let qos: DispatchQoS? let flags: DispatchWorkItemFlags? - init(queue: DispatchQueue, group: DispatchGroup? = nil, qos: DispatchQoS? = nil, flags: DispatchWorkItemFlags? = nil) { + public init(queue: DispatchQueue, group: DispatchGroup? = nil, qos: DispatchQoS? = nil, flags: DispatchWorkItemFlags? = nil) { self.queue = queue self.group = group self.qos = qos @@ -40,7 +40,7 @@ public struct DispatchQueueDispatcher: Dispatcher { } // Avoid having to hard-code any particular defaults for qos or flags -public extension DispatchQueue { +internal extension DispatchQueue { final func asyncD(group: DispatchGroup? = nil, qos: DispatchQoS? = nil, flags: DispatchWorkItemFlags? = nil, execute body: @escaping () -> Void) { switch (qos, flags) { case (nil, nil): diff --git a/Sources/RateLimitedDispatcher.swift b/Sources/RateLimitedDispatcher.swift index addc55524..f74525b74 100644 --- a/Sources/RateLimitedDispatcher.swift +++ b/Sources/RateLimitedDispatcher.swift @@ -39,7 +39,7 @@ public class RateLimitedDispatcher: RateLimitedDispatcherBase { /// - perInterval: The length of the reference interval, in seconds. /// - queue: The DispatchQueue or Dispatcher on which to perform executions. May be serial or concurrent. - override init(maxDispatches: Int, perInterval interval: TimeInterval, queue: Dispatcher = DispatchQueue.global()) { + override public init(maxDispatches: Int, perInterval interval: TimeInterval, queue: Dispatcher = DispatchQueue.global()) { latestAccrual = DispatchTime.now() super.init(maxDispatches: maxDispatches, perInterval: interval, queue: queue) tokensInBucket = Double(maxDispatches) @@ -97,7 +97,7 @@ public class RateLimitedDispatcher: RateLimitedDispatcherBase { } -internal extension DispatchTime { +extension DispatchTime { static func -(a: DispatchTime, b: DispatchTime) -> TimeInterval { let delta = a.uptimeNanoseconds - b.uptimeNanoseconds return TimeInterval(delta) / 1_000_000_000 diff --git a/Sources/RateLimitedDispatcherBase.swift b/Sources/RateLimitedDispatcherBase.swift index 042b5559c..c43b80cc4 100644 --- a/Sources/RateLimitedDispatcherBase.swift +++ b/Sources/RateLimitedDispatcherBase.swift @@ -6,13 +6,13 @@ public class RateLimitedDispatcherBase: Dispatcher { let interval: TimeInterval let queue: Dispatcher - internal let serializer = DispatchQueue(label: "RLD serializer") + let serializer = DispatchQueue(label: "RLD serializer") - internal var nDispatched = 0 - internal var undispatched = Queue<() -> Void>() + var nDispatched = 0 + var undispatched = Queue<() -> Void>() - internal var cleanupNonce: Int64 = 0 - internal var cleanupWorkItem: DispatchWorkItem? { willSet { cleanupWorkItem?.cancel() }} + var cleanupNonce: Int64 = 0 + var cleanupWorkItem: DispatchWorkItem? { willSet { cleanupWorkItem?.cancel() }} public init(maxDispatches: Int, perInterval interval: TimeInterval, queue: Dispatcher = DispatchQueue.global()) { self.maxDispatches = maxDispatches @@ -27,11 +27,11 @@ public class RateLimitedDispatcherBase: Dispatcher { } } - internal func dispatchFromQueue() { + func dispatchFromQueue() { fatalError("Subclass responsibility") } - internal func recordActualStart() { + func recordActualStart() { nDispatched -= 1 dispatchFromQueue() if nDispatched == 0 && undispatched.isEmpty { @@ -39,14 +39,14 @@ public class RateLimitedDispatcherBase: Dispatcher { } } - internal func scheduleCleanup() { + func scheduleCleanup() { cleanupWorkItem = DispatchWorkItem { [ weak self, nonce = self.cleanupNonce ] in self?.cleanup(nonce) } serializer.asyncAfter(deadline: DispatchTime.now() + interval, execute: cleanupWorkItem!) } - internal func cleanup(_ nonce: Int64) { + func cleanup(_ nonce: Int64) { // Calls to cleanup() have to go through the serializer queue, so by by the time // we get here, more activity may have occurred. Ergo, verify nonce. guard nonce == cleanupNonce else { return } diff --git a/Sources/StrictRateLimitedDispatcher.swift b/Sources/StrictRateLimitedDispatcher.swift index 87a175c4f..841ad99ab 100644 --- a/Sources/StrictRateLimitedDispatcher.swift +++ b/Sources/StrictRateLimitedDispatcher.swift @@ -37,7 +37,7 @@ public class StrictRateLimitedDispatcher: RateLimitedDispatcherBase { /// - perInterval: The length of the reference interval, in seconds. /// - queue: The DispatchQueue or Dispatcher on which to perform executions. May be serial or concurrent. - override init(maxDispatches: Int, perInterval interval: TimeInterval, queue: Dispatcher = DispatchQueue.global()) { + override public init(maxDispatches: Int, perInterval interval: TimeInterval, queue: Dispatcher = DispatchQueue.global()) { startTimeHistory = Queue(maxDepth: maxDispatches) immediateDispatchesAvailable = maxDispatches super.init(maxDispatches: maxDispatches, perInterval: interval, queue: queue) From c68af9f0214d4c4104a160060e7459e707d57362 Mon Sep 17 00:00:00 2001 From: Garth Snyder Date: Sat, 16 Mar 2019 21:47:43 -0700 Subject: [PATCH 20/81] Add wrappers for DispatchQueue compatibility to Dispatchers --- Sources/ConcurrencyLimitedDispatcher.swift | 4 ++ Sources/RateLimitedDispatcher.swift | 4 ++ Sources/StrictRateLimitedDispatcher.swift | 4 ++ Tests/Core/DispatcherTypeTests.swift | 45 ++++++++++++++-------- 4 files changed, 42 insertions(+), 15 deletions(-) diff --git a/Sources/ConcurrencyLimitedDispatcher.swift b/Sources/ConcurrencyLimitedDispatcher.swift index b4e7da74d..47525de64 100644 --- a/Sources/ConcurrencyLimitedDispatcher.swift +++ b/Sources/ConcurrencyLimitedDispatcher.swift @@ -23,6 +23,10 @@ public class ConcurrencyLimitedDispatcher: Dispatcher { semaphore = DispatchSemaphore(value: limit) } + public convenience init(limit: Int, queue: DispatchQueue) { + self.init(limit: limit, queue: queue as Dispatcher) + } + public func dispatch(_ body: @escaping () -> Void) { serializer.async { self.semaphore.wait() diff --git a/Sources/RateLimitedDispatcher.swift b/Sources/RateLimitedDispatcher.swift index f74525b74..3a8ddca41 100644 --- a/Sources/RateLimitedDispatcher.swift +++ b/Sources/RateLimitedDispatcher.swift @@ -45,6 +45,10 @@ public class RateLimitedDispatcher: RateLimitedDispatcherBase { tokensInBucket = Double(maxDispatches) } + public convenience init(maxDispatches: Int, perInterval interval: TimeInterval, queue: DispatchQueue) { + self.init(maxDispatches: maxDispatches, perInterval: interval, queue: queue as Dispatcher) + } + override func dispatchFromQueue() { guard undispatched.count > 0 else { return } diff --git a/Sources/StrictRateLimitedDispatcher.swift b/Sources/StrictRateLimitedDispatcher.swift index 841ad99ab..5d2644407 100644 --- a/Sources/StrictRateLimitedDispatcher.swift +++ b/Sources/StrictRateLimitedDispatcher.swift @@ -43,6 +43,10 @@ public class StrictRateLimitedDispatcher: RateLimitedDispatcherBase { super.init(maxDispatches: maxDispatches, perInterval: interval, queue: queue) } + public convenience init(maxDispatches: Int, perInterval interval: TimeInterval, queue: DispatchQueue) { + self.init(maxDispatches: maxDispatches, perInterval: interval, queue: queue as Dispatcher) + } + override func dispatchFromQueue() { cleanupNonce += 1 diff --git a/Tests/Core/DispatcherTypeTests.swift b/Tests/Core/DispatcherTypeTests.swift index dcf2b5e2f..d60e3bc7f 100644 --- a/Tests/Core/DispatcherTypeTests.swift +++ b/Tests/Core/DispatcherTypeTests.swift @@ -2,7 +2,7 @@ import Dispatch @testable import PromiseKit import XCTest -class DispatcherTestBase: XCTestCase { +class DispatcherTypeTests: XCTestCase { struct ScenarioParameters { let hiatusLikelihoods: [Double] @@ -10,7 +10,7 @@ class DispatcherTestBase: XCTestCase { let intervals: [Double] let dispatches: [Int] } - + let standardParams = ScenarioParameters( hiatusLikelihoods: [ 0.3 ], noDelayLikelihoods: [ 0.75 ], @@ -144,10 +144,6 @@ class DispatcherTestBase: XCTestCase { } return most } - -} - -class RateLimitTests: DispatcherTestBase { func testRateLimitedDispatcher() { for scenario in scenarios { @@ -166,10 +162,6 @@ class RateLimitTests: DispatcherTestBase { } } -} - -class StrictRateLimitTests: DispatcherTestBase { - func testStrictRateLimitedDispatcher() { for scenario in scenarios { printScenarioDetails(scenario) @@ -190,10 +182,6 @@ class StrictRateLimitTests: DispatcherTestBase { } } -} - -class ConcurrencyLimitTests: DispatcherTestBase { - func testConcurrencyLimitedDispatcher() { for scenario in scenarios { @@ -233,8 +221,35 @@ class ConcurrencyLimitTests: DispatcherTestBase { } } -} + // These aren't really "tests" per se; they just exercise all the various init types + // to verify that none of them produce ambiguity warnings or recurse indefinitely, + // and that DispatchQueue members are accessible. + func testRateLimitedDispatcherInit() { + XCTAssertNotNil(RateLimitedDispatcher(maxDispatches: 1, perInterval: 1)) + XCTAssertNotNil(RateLimitedDispatcher(maxDispatches: 1, perInterval: 1, queue: DispatchQueue.main)) + XCTAssertNotNil(RateLimitedDispatcher(maxDispatches: 1, perInterval: 1, queue: CurrentThreadDispatcher())) + XCTAssertNotNil(RateLimitedDispatcher(maxDispatches: 1, perInterval: 1, queue: .main)) + XCTAssertNotNil(RateLimitedDispatcher(maxDispatches: 1, perInterval: 1, queue: .global(qos: .background))) + } + + func testStrictRateLimitedDispatcherInit() { + XCTAssertNotNil(StrictRateLimitedDispatcher(maxDispatches: 1, perInterval: 1)) + XCTAssertNotNil(StrictRateLimitedDispatcher(maxDispatches: 1, perInterval: 1, queue: DispatchQueue.main)) + XCTAssertNotNil(StrictRateLimitedDispatcher(maxDispatches: 1, perInterval: 1, queue: CurrentThreadDispatcher())) + XCTAssertNotNil(StrictRateLimitedDispatcher(maxDispatches: 1, perInterval: 1, queue: .main)) + XCTAssertNotNil(StrictRateLimitedDispatcher(maxDispatches: 1, perInterval: 1, queue: .global(qos: .background))) + } + + func testConcurrencyLimitedDispatcherInit() { + XCTAssertNotNil(ConcurrencyLimitedDispatcher(limit: 1)) + XCTAssertNotNil(ConcurrencyLimitedDispatcher(limit: 1, queue: DispatchQueue.main)) + XCTAssertNotNil(ConcurrencyLimitedDispatcher(limit: 1, queue: CurrentThreadDispatcher())) + XCTAssertNotNil(ConcurrencyLimitedDispatcher(limit: 1, queue: .main)) + XCTAssertNotNil(ConcurrencyLimitedDispatcher(limit: 1, queue: .global(qos: .background))) + } + +} // Reproducible, seedable RNG From 5e3fe21423c8c47b4e11fc34190825fed7683bd0 Mon Sep 17 00:00:00 2001 From: Garth Snyder Date: Sat, 16 Mar 2019 22:11:29 -0700 Subject: [PATCH 21/81] Update test manifests --- Tests/Core/XCTestManifests.swift | 45 +++++++++++--------------------- 1 file changed, 15 insertions(+), 30 deletions(-) diff --git a/Tests/Core/XCTestManifests.swift b/Tests/Core/XCTestManifests.swift index 085fafd03..1c9a11149 100644 --- a/Tests/Core/XCTestManifests.swift +++ b/Tests/Core/XCTestManifests.swift @@ -50,15 +50,6 @@ extension CatchableTests { ] } -extension ConcurrencyLimitTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__ConcurrencyLimitTests = [ - ("testConcurrencyLimitedDispatcher", testConcurrencyLimitedDispatcher), - ] -} - extension DispatcherTests { // DO NOT MODIFY: This is autogenerated, use: // `swift test --generate-linuxmain` @@ -73,6 +64,20 @@ extension DispatcherTests { ] } +extension DispatcherTypeTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__DispatcherTypeTests = [ + ("testConcurrencyLimitedDispatcher", testConcurrencyLimitedDispatcher), + ("testConcurrencyLimitedDispatcherInit", testConcurrencyLimitedDispatcherInit), + ("testRateLimitedDispatcher", testRateLimitedDispatcher), + ("testRateLimitedDispatcherInit", testRateLimitedDispatcherInit), + ("testStrictRateLimitedDispatcher", testStrictRateLimitedDispatcher), + ("testStrictRateLimitedDispatcherInit", testStrictRateLimitedDispatcherInit), + ] +} + extension GuaranteeTests { // DO NOT MODIFY: This is autogenerated, use: // `swift test --generate-linuxmain` @@ -178,15 +183,6 @@ extension RaceTests { ] } -extension RateLimitTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__RateLimitTests = [ - ("testRateLimitedDispatcher", testRateLimitedDispatcher), - ] -} - extension RegressionTests { // DO NOT MODIFY: This is autogenerated, use: // `swift test --generate-linuxmain` @@ -207,15 +203,6 @@ extension StressTests { ] } -extension StrictRateLimitTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__StrictRateLimitTests = [ - ("testStrictRateLimitedDispatcher", testStrictRateLimitedDispatcher), - ] -} - extension ThenableTests { // DO NOT MODIFY: This is autogenerated, use: // `swift test --generate-linuxmain` @@ -307,8 +294,8 @@ public func __allTests() -> [XCTestCaseEntry] { testCase(AfterTests.__allTests__AfterTests), testCase(CancellationTests.__allTests__CancellationTests), testCase(CatchableTests.__allTests__CatchableTests), - testCase(ConcurrencyLimitTests.__allTests__ConcurrencyLimitTests), testCase(DispatcherTests.__allTests__DispatcherTests), + testCase(DispatcherTypeTests.__allTests__DispatcherTypeTests), testCase(GuaranteeTests.__allTests__GuaranteeTests), testCase(HangTests.__allTests__HangTests), testCase(JoinTests.__allTests__JoinTests), @@ -317,10 +304,8 @@ public func __allTests() -> [XCTestCaseEntry] { testCase(PMKErrorTests.__allTests__PMKErrorTests), testCase(PromiseTests.__allTests__PromiseTests), testCase(RaceTests.__allTests__RaceTests), - testCase(RateLimitTests.__allTests__RateLimitTests), testCase(RegressionTests.__allTests__RegressionTests), testCase(StressTests.__allTests__StressTests), - testCase(StrictRateLimitTests.__allTests__StrictRateLimitTests), testCase(ThenableTests.__allTests__ThenableTests), testCase(WhenConcurrentTestCase_Swift.__allTests__WhenConcurrentTestCase_Swift), testCase(WhenTests.__allTests__WhenTests), From 4ff28fad6cb0d5e8e7de365002403f2dd966ef81 Mon Sep 17 00:00:00 2001 From: Garth Snyder Date: Sun, 17 Mar 2019 21:14:36 -0700 Subject: [PATCH 22/81] Moved dispatcher implementations to subdirectory --- Sources/{ => Dispatchers}/ConcurrencyLimitedDispatcher.swift | 0 Sources/{ => Dispatchers}/CoreDataDispatcher.swift | 0 Sources/{ => Dispatchers}/Queue.swift | 0 Sources/{ => Dispatchers}/RateLimitedDispatcher.swift | 0 Sources/{ => Dispatchers}/RateLimitedDispatcherBase.swift | 0 Sources/{ => Dispatchers}/StrictRateLimitedDispatcher.swift | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename Sources/{ => Dispatchers}/ConcurrencyLimitedDispatcher.swift (100%) rename Sources/{ => Dispatchers}/CoreDataDispatcher.swift (100%) rename Sources/{ => Dispatchers}/Queue.swift (100%) rename Sources/{ => Dispatchers}/RateLimitedDispatcher.swift (100%) rename Sources/{ => Dispatchers}/RateLimitedDispatcherBase.swift (100%) rename Sources/{ => Dispatchers}/StrictRateLimitedDispatcher.swift (100%) diff --git a/Sources/ConcurrencyLimitedDispatcher.swift b/Sources/Dispatchers/ConcurrencyLimitedDispatcher.swift similarity index 100% rename from Sources/ConcurrencyLimitedDispatcher.swift rename to Sources/Dispatchers/ConcurrencyLimitedDispatcher.swift diff --git a/Sources/CoreDataDispatcher.swift b/Sources/Dispatchers/CoreDataDispatcher.swift similarity index 100% rename from Sources/CoreDataDispatcher.swift rename to Sources/Dispatchers/CoreDataDispatcher.swift diff --git a/Sources/Queue.swift b/Sources/Dispatchers/Queue.swift similarity index 100% rename from Sources/Queue.swift rename to Sources/Dispatchers/Queue.swift diff --git a/Sources/RateLimitedDispatcher.swift b/Sources/Dispatchers/RateLimitedDispatcher.swift similarity index 100% rename from Sources/RateLimitedDispatcher.swift rename to Sources/Dispatchers/RateLimitedDispatcher.swift diff --git a/Sources/RateLimitedDispatcherBase.swift b/Sources/Dispatchers/RateLimitedDispatcherBase.swift similarity index 100% rename from Sources/RateLimitedDispatcherBase.swift rename to Sources/Dispatchers/RateLimitedDispatcherBase.swift diff --git a/Sources/StrictRateLimitedDispatcher.swift b/Sources/Dispatchers/StrictRateLimitedDispatcher.swift similarity index 100% rename from Sources/StrictRateLimitedDispatcher.swift rename to Sources/Dispatchers/StrictRateLimitedDispatcher.swift From b06d7e59ed15125b1c3c95f96fa644cf52ebbe2d Mon Sep 17 00:00:00 2001 From: Nathan Hosselton Date: Mon, 19 Nov 2018 19:04:53 -0500 Subject: [PATCH 23/81] =?UTF-8?q?Provide=20`catchOnly(=E2=80=A6)`=20for=20?= =?UTF-8?q?specific=20error=20handling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/Catchable.swift | 64 ++++++++++++++++ Tests/Core/CatchableTests.swift | 132 ++++++++++++++++++++++++++++++++ 2 files changed, 196 insertions(+) diff --git a/Sources/Catchable.swift b/Sources/Catchable.swift index a0af9e61e..28135f1ca 100644 --- a/Sources/Catchable.swift +++ b/Sources/Catchable.swift @@ -39,6 +39,70 @@ public extension CatchMixin { } return finalizer } + + /** + The provided closure executes when this promise rejects with the specific error passed in, mimicing Swift's do-catch error pattern. As such, a final `catch` is still required at the end of your chain. + + Rejecting a promise cascades: rejecting all subsequent promises (unless + recover is invoked) thus you will typically place your catch at the end + of a chain. Often utility promises will not have a catch, instead + delegating the error handling to the caller. + + - Parameter only: The specific error to be caught and handled. + - Parameter on: The queue to which the provided closure dispatches. + - Parameter execute: The handler to execute if this promise is rejected. + - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. + - SeeAlso: [Cancellation](http://promisekit.org/docs/) + */ + func catchOnly(_ only: Error, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Error) -> Void) -> Promise where Error: Equatable { + let rp = Promise(.pending) + pipe { + switch $0 { + case .rejected(let error as Error) where error == only: + on.async(flags: flags) { + body(error) + rp.box.seal(.fulfilled(())) + } + case .rejected(let error): + rp.box.seal(.rejected(error)) + case .fulfilled: + rp.box.seal(.fulfilled(())) + } + } + return rp + } + + /** + The provided closure executes when this promise rejects with an error of the type passed in, mimicing Swift's do-catch error pattern. As such, a final `catch` is still required at the end of your chain. + + Rejecting a promise cascades: rejecting all subsequent promises (unless + recover is invoked) thus you will typically place your catch at the end + of a chain. Often utility promises will not have a catch, instead + delegating the error handling to the caller. + + - Parameter only: The error type to be caught and handled. + - Parameter on: The queue to which the provided closure dispatches. + - Parameter execute: The handler to execute if this promise is rejected. + - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. + - SeeAlso: [Cancellation](http://promisekit.org/docs/) + */ + func catchOnly(_ only: Error.Type, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Error) -> Void) -> Promise { + let rp = Promise(.pending) + pipe { + switch $0 { + case .rejected(let error as Error): + on.async(flags: flags) { + body(error) + rp.box.seal(.fulfilled(())) + } + case .rejected(let error): + rp.box.seal(.rejected(error)) + case .fulfilled: + rp.box.seal(.fulfilled(())) + } + } + return rp + } } public class PMKFinalizer { diff --git a/Tests/Core/CatchableTests.swift b/Tests/Core/CatchableTests.swift index d94b2b6f5..b6f89fb23 100644 --- a/Tests/Core/CatchableTests.swift +++ b/Tests/Core/CatchableTests.swift @@ -257,6 +257,138 @@ extension CatchableTests { } } +/// `Promise.catchOnly` +extension CatchableTests { + func testCatchOnly() { + let x = expectation(description: #file + #function) + + Promise.value(1).then { _ -> Promise in + throw Error.dummy + }.catchOnly(Error.dummy) { _ in + x.fulfill() + }.silenceWarning() + + wait(for: [x], timeout: 5) + } + + func testCatchOnly_PatternMatch_1() { + let x = expectation(description: "Pattern match only Error.dummy") + + Promise.value(1).then { _ -> Promise in + throw Error.dummy + }.catchOnly(Error.dummy) { _ in + x.fulfill() + }.catchOnly(Error.cancelled) { + XCTFail("\($0) error was caught") + x.fulfill() + }.silenceWarning() + + wait(for: [x], timeout: 5) + } + + func testCatchOnly_PatternMatch_2() { + let x = expectation(description: "Pattern match only Error.dummy") + + Promise.value(1).then { _ -> Promise in + throw Error.dummy + }.catchOnly(Error.cancelled) { + XCTFail("\($0) error was caught") + x.fulfill() + }.catchOnly(Error.dummy) { _ in + x.fulfill() + }.silenceWarning() + + wait(for: [x], timeout: 5) + } + + func testCatchOnly_BaseCatchIsNotCalledAfterCatchOnlyExecutes() { + let x = expectation(description: #file + #function) + + Promise.value(1).then { _ -> Promise in + throw Error.dummy + }.catchOnly(Error.dummy) { _ in + x.fulfill() + }.catch { _ in + XCTFail() + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testCatchOnly_BaseCatchIsCalledWhenCatchOnlyDoesNotExecute() { + let x = expectation(description: #file + #function) + + Promise.value(1).then { _ -> Promise in + throw Error.dummy + }.catchOnly(Error.cancelled) { _ in + XCTFail() + x.fulfill() + }.catch { _ in + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testCatchOnly_Type() { + let x = expectation(description: #file + #function) + + Promise.value(1).then { _ -> Promise in + throw Error.dummy + }.catchOnly(Error.self) { _ in + x.fulfill() + }.silenceWarning() + + wait(for: [x], timeout: 5) + } + + func testCatchOnly_Type_PatternMatch_1() { + let x = expectation(description: "Pattern match only Error.Type") + + Promise.value(1).then { _ -> Promise in + throw Error.dummy + }.catchOnly(Error.self) { _ in + x.fulfill() + }.catchOnly(Error.dummy) { + XCTFail("\($0) error was caught") + x.fulfill() + }.silenceWarning() + + wait(for: [x], timeout: 5) + } + + func testCatchOnly_Type_PatternMatch_2() { + let x = expectation(description: "Pattern match only Error.dummy") + + Promise.value(1).then { _ -> Promise in + throw Error.dummy + }.catchOnly(Error.dummy) { _ in + x.fulfill() + }.catchOnly(Error.self) { + XCTFail("\($0) error was caught") + x.fulfill() + }.silenceWarning() + + wait(for: [x], timeout: 5) + } + + func testCatchOnly_Type_BaseCatchIsNotCalledAfterCatchOnlyExecutes() { + let x = expectation(description: #file + #function) + + Promise.value(1).then { _ -> Promise in + throw Error.dummy + }.catchOnly(Error.self) { _ in + x.fulfill() + }.catch { _ in + XCTFail() + x.fulfill() + } + + wait(for: [x], timeout: 5) + } +} + private enum Error: CancellableError { case dummy case cancelled From 087f066492c0485160b7907ad598627f8bcd6e7c Mon Sep 17 00:00:00 2001 From: Doug Stein Date: Sat, 23 Mar 2019 01:14:46 -0700 Subject: [PATCH 24/81] 'Cancel' for PromiseKit: fix for #1026, plus cleanup * Fix "Typechecking timeout compiling tests in v7 #1026" by specifying the closure return type in DispatcherTests * Remove unnecessary "#if swift" checks in the cancellable code and tests --- Sources/CancellablePromise.swift | 2 -- Tests/Cancel/CancelChain.swift | 4 ---- Tests/Cancel/CancellableErrorTests.swift | 2 -- Tests/Cancel/DispatcherTests.swift | 4 ++-- Tests/Cancel/GuaranteeTests.swift | 16 ---------------- Tests/Cancel/PromiseTests.swift | 14 -------------- Tests/Cancel/ResolverTests.swift | 2 -- 7 files changed, 2 insertions(+), 42 deletions(-) diff --git a/Sources/CancellablePromise.swift b/Sources/CancellablePromise.swift index 212d8777d..195a0a740 100644 --- a/Sources/CancellablePromise.swift +++ b/Sources/CancellablePromise.swift @@ -127,7 +127,6 @@ public class CancellablePromise: CancellableThenable, CancellableCatchMixin { } } -#if swift(>=3.1) extension CancellablePromise where T == Void { /// Initializes a new cancellable promise fulfilled with `Void` public convenience init() { @@ -140,4 +139,3 @@ extension CancellablePromise where T == Void { self.appendCancellable(cancellable, reject: nil) } } -#endif diff --git a/Tests/Cancel/CancelChain.swift b/Tests/Cancel/CancelChain.swift index e2345637d..167d5f179 100644 --- a/Tests/Cancel/CancelChain.swift +++ b/Tests/Cancel/CancelChain.swift @@ -153,11 +153,7 @@ class CancelChain: XCTestCase { self.trace("SETUP COMPLETE") -#if swift(>=4.1) let expectations = [ex.a, ex.b, ex.c, ex.d, ex.e, ex.cancelled].compactMap { $0 } -#else - let expectations = [ex.a, ex.b, ex.c, ex.d, ex.e, ex.cancelled].flatMap { $0 } -#endif wait(for: expectations, timeout: 1) XCTAssert(c.pA.cancelContext.cancelAttempted) diff --git a/Tests/Cancel/CancellableErrorTests.swift b/Tests/Cancel/CancellableErrorTests.swift index 612925b74..66d3f5fc8 100644 --- a/Tests/Cancel/CancellableErrorTests.swift +++ b/Tests/Cancel/CancellableErrorTests.swift @@ -101,14 +101,12 @@ class CancellationTests: XCTestCase { waitForExpectations(timeout: 1) } -#if swift(>=3.2) func testIsCancelled() { XCTAssertTrue(PMKError.cancelled.isCancelled) XCTAssertTrue(URLError.cancelled.isCancelled) XCTAssertTrue(CocoaError.cancelled.isCancelled) XCTAssertFalse(CocoaError(_nsError: NSError(domain: NSCocoaErrorDomain, code: CocoaError.Code.coderInvalidValue.rawValue)).isCancelled) } -#endif } private enum LocalError: CancellableError { diff --git a/Tests/Cancel/DispatcherTests.swift b/Tests/Cancel/DispatcherTests.swift index 0274212cb..828739190 100644 --- a/Tests/Cancel/DispatcherTests.swift +++ b/Tests/Cancel/DispatcherTests.swift @@ -109,8 +109,8 @@ class DispatcherTests: XCTestCase { Promise.value(v + 10).cancellize() }.thenMap(on: .global(qos: .background), flags: .barrier) { v -> Promise in Promise.value(v + 10) - }.thenFlatMap(on: .global(qos: .background), flags: .barrier) { - Promise.value([$0 + 10]).cancellize() + }.thenFlatMap(on: .global(qos: .background), flags: .barrier) { v -> CancellablePromise<[Int]> in + Promise.value([v + 10]).cancellize() }.thenFlatMap(on: .global(qos: .background), flags: .barrier) { v -> Promise<[Int]> in Promise.value([v + 10]) }.filterValues(on: .global(qos: .background), flags: .barrier) { _ in diff --git a/Tests/Cancel/GuaranteeTests.swift b/Tests/Cancel/GuaranteeTests.swift index 51f3a9f9f..7a81595ad 100644 --- a/Tests/Cancel/GuaranteeTests.swift +++ b/Tests/Cancel/GuaranteeTests.swift @@ -43,18 +43,10 @@ class GuaranteeTests: XCTestCase { } func testCancellable() { -#if swift(>=4.0) var resolver: ((()) -> Void)! -#else - var resolver: ((Void) -> Void)! -#endif let task = DispatchWorkItem { -#if swift(>=4.0) resolver(()) -#else - resolver() -#endif } q.asyncAfter(deadline: DispatchTime.now() + 0.5, execute: task) @@ -76,18 +68,10 @@ class GuaranteeTests: XCTestCase { } func testSetCancellable() { -#if swift(>=4.0) var resolver: ((()) -> Void)! -#else - var resolver: ((Void) -> Void)! -#endif let task = DispatchWorkItem { -#if swift(>=4.0) resolver(()) -#else - resolver() -#endif } q.asyncAfter(deadline: DispatchTime.now() + 0.5, execute: task) diff --git a/Tests/Cancel/PromiseTests.swift b/Tests/Cancel/PromiseTests.swift index be8dba451..0195e700d 100644 --- a/Tests/Cancel/PromiseTests.swift +++ b/Tests/Cancel/PromiseTests.swift @@ -86,12 +86,10 @@ class PromiseTests: XCTestCase { _ = CancellablePromise().map { Error.dummy } } -#if swift(>=3.1) func testCanMakeVoidPromise() { _ = CancellablePromise() _ = Guarantee() } -#endif enum Error: Swift.Error { case dummy @@ -154,11 +152,7 @@ class PromiseTests: XCTestCase { var resolver: Resolver! let task = DispatchWorkItem { -#if swift(>=4.0) resolver.fulfill(()) -#else - resolver.fulfill() -#endif } q.asyncAfter(deadline: DispatchTime.now() + 0.5, execute: task) @@ -183,11 +177,7 @@ class PromiseTests: XCTestCase { var resolver: Resolver! let task = DispatchWorkItem { -#if swift(>=4.0) resolver.fulfill(()) -#else - resolver.fulfill() -#endif } q.asyncAfter(deadline: DispatchTime.now() + 0.5, execute: task) @@ -215,11 +205,7 @@ class PromiseTests: XCTestCase { var resolver: Resolver! let task = DispatchWorkItem { -#if swift(>=4.0) resolver.fulfill(()) -#else - resolver.fulfill() -#endif } q.asyncAfter(deadline: DispatchTime.now() + 0.5, execute: task) diff --git a/Tests/Cancel/ResolverTests.swift b/Tests/Cancel/ResolverTests.swift index 98124ffa6..fc1cc02f0 100644 --- a/Tests/Cancel/ResolverTests.swift +++ b/Tests/Cancel/ResolverTests.swift @@ -244,7 +244,6 @@ class WrapTests: XCTestCase { wait(for: [ex1, ex2] ,timeout: 1) } -#if swift(>=3.1) func testVoidCompletionValue() { let ex1 = expectation(description: "") let kf1 = KittenFetcher(value: nil, error: nil) @@ -284,7 +283,6 @@ class WrapTests: XCTestCase { wait(for: [ex1, ex2], timeout: 1) } -#endif func testIsFulfilled() { let p1 = Promise.value(()).cancellize() From 1495d68f77bbd16e2f341ebf4a12de9d3009077b Mon Sep 17 00:00:00 2001 From: Nathan Hosselton Date: Sun, 25 Nov 2018 22:07:34 +0000 Subject: [PATCH 25/81] Obfuscate catchOnly's promise behind a finalizer --- Sources/Catchable.swift | 92 ++++++++++++++++++++++++++++++++------ Tests/Core/Utilities.swift | 4 ++ 2 files changed, 82 insertions(+), 14 deletions(-) diff --git a/Sources/Catchable.swift b/Sources/Catchable.swift index 28135f1ca..b17b75c02 100644 --- a/Sources/Catchable.swift +++ b/Sources/Catchable.swift @@ -41,7 +41,7 @@ public extension CatchMixin { } /** - The provided closure executes when this promise rejects with the specific error passed in, mimicing Swift's do-catch error pattern. As such, a final `catch` is still required at the end of your chain. + The provided closure executes when this promise rejects with the specific error passed in. A final `catch` is still required at the end of your chain. Rejecting a promise cascades: rejecting all subsequent promises (unless recover is invoked) thus you will typically place your catch at the end @@ -54,26 +54,26 @@ public extension CatchMixin { - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func catchOnly(_ only: Error, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Error) -> Void) -> Promise where Error: Equatable { - let rp = Promise(.pending) + func catchOnly(_ only: Error, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Error) -> Void) -> PMKCascadingFinalizer where Error: Equatable { + let finalizer = PMKCascadingFinalizer() pipe { switch $0 { case .rejected(let error as Error) where error == only: on.async(flags: flags) { body(error) - rp.box.seal(.fulfilled(())) + finalizer.pending.resolver.fulfill(()) } case .rejected(let error): - rp.box.seal(.rejected(error)) + finalizer.pending.resolver.reject(error) case .fulfilled: - rp.box.seal(.fulfilled(())) + finalizer.pending.resolver.fulfill(()) } } - return rp + return finalizer } /** - The provided closure executes when this promise rejects with an error of the type passed in, mimicing Swift's do-catch error pattern. As such, a final `catch` is still required at the end of your chain. + The provided closure executes when this promise rejects with an error of the type passed in. A final `catch` is still required at the end of your chain. Rejecting a promise cascades: rejecting all subsequent promises (unless recover is invoked) thus you will typically place your catch at the end @@ -86,22 +86,22 @@ public extension CatchMixin { - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func catchOnly(_ only: Error.Type, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Error) -> Void) -> Promise { - let rp = Promise(.pending) + func catchOnly(_ only: Error.Type, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Error) -> Void) -> PMKCascadingFinalizer { + let finalizer = PMKCascadingFinalizer() pipe { switch $0 { case .rejected(let error as Error): on.async(flags: flags) { body(error) - rp.box.seal(.fulfilled(())) + finalizer.pending.resolver.fulfill(()) } case .rejected(let error): - rp.box.seal(.rejected(error)) + finalizer.pending.resolver.reject(error) case .fulfilled: - rp.box.seal(.fulfilled(())) + finalizer.pending.resolver.fulfill(()) } } - return rp + return finalizer } } @@ -116,6 +116,70 @@ public class PMKFinalizer { } } +public class PMKCascadingFinalizer { + let pending = Promise.pending() + + /** + The provided closure executes when this promise rejects. + + Rejecting a promise cascades: rejecting all subsequent promises (unless + recover is invoked) thus you will typically place your catch at the end + of a chain. Often utility promises will not have a catch, instead + delegating the error handling to the caller. + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter policy: The default policy does not execute your handler for cancellation errors. + - Parameter execute: The handler to execute if this promise is rejected. + - Returns: A promise finalizer. + - SeeAlso: [Cancellation](http://promisekit.org/docs/) + */ + @discardableResult + public func `catch`(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) -> Void) -> PMKFinalizer { + return pending.promise.catch(on: on, flags: flags, policy: policy) { + body($0) + } + } + + /** + The provided closure executes when this promise rejects with the specific error passed in. A final `catch` is still required at the end of your chain. + + Rejecting a promise cascades: rejecting all subsequent promises (unless + recover is invoked) thus you will typically place your catch at the end + of a chain. Often utility promises will not have a catch, instead + delegating the error handling to the caller. + + - Parameter only: The specific error to be caught and handled. + - Parameter on: The queue to which the provided closure dispatches. + - Parameter execute: The handler to execute if this promise is rejected. + - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. + - SeeAlso: [Cancellation](http://promisekit.org/docs/) + */ + public func catchOnly(_ only: Error, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Error) -> Void) -> PMKCascadingFinalizer where Error: Equatable { + return pending.promise.catchOnly(only, on: on, flags: flags) { + body($0) + } + } + + /** + The provided closure executes when this promise rejects with an error of the type passed in. A final `catch` is still required at the end of your chain. + + Rejecting a promise cascades: rejecting all subsequent promises (unless + recover is invoked) thus you will typically place your catch at the end + of a chain. Often utility promises will not have a catch, instead + delegating the error handling to the caller. + + - Parameter only: The error type to be caught and handled. + - Parameter on: The queue to which the provided closure dispatches. + - Parameter execute: The handler to execute if this promise is rejected. + - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. + - SeeAlso: [Cancellation](http://promisekit.org/docs/) + */ + public func catchOnly(_ only: Error.Type, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Error) -> Void) -> PMKCascadingFinalizer { + return pending.promise.catchOnly(only, on: on, flags: flags) { + body($0) + } + } +} public extension CatchMixin { diff --git a/Tests/Core/Utilities.swift b/Tests/Core/Utilities.swift index 6b4c5de68..2ab532aa8 100644 --- a/Tests/Core/Utilities.swift +++ b/Tests/Core/Utilities.swift @@ -4,6 +4,10 @@ extension Promise { func silenceWarning() {} } +extension PMKCascadingFinalizer { + func silenceWarning() {} +} + #if os(Linux) import func CoreFoundation._CFIsMainThread From bd0a5513929081c38a099a9fca56332016a6456a Mon Sep 17 00:00:00 2001 From: Nathan Hosselton Date: Sun, 25 Nov 2018 20:06:40 -0500 Subject: [PATCH 26/81] Remove error param from catchOnly(object) handler Rationale: Since this variant takes a specific error object, it does not need checked against in handler and is therefore likely to be explicitly ignored (`_ in`). Thus removing the object removes the need for this. --- Sources/Catchable.swift | 8 ++++---- Tests/Core/CatchableTests.swift | 22 +++++++++++----------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Sources/Catchable.swift b/Sources/Catchable.swift index b17b75c02..7eb77a2e5 100644 --- a/Sources/Catchable.swift +++ b/Sources/Catchable.swift @@ -54,13 +54,13 @@ public extension CatchMixin { - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func catchOnly(_ only: Error, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Error) -> Void) -> PMKCascadingFinalizer where Error: Equatable { + func catchOnly(_ only: Error, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping() -> Void) -> PMKCascadingFinalizer where Error: Equatable { let finalizer = PMKCascadingFinalizer() pipe { switch $0 { case .rejected(let error as Error) where error == only: on.async(flags: flags) { - body(error) + body() finalizer.pending.resolver.fulfill(()) } case .rejected(let error): @@ -154,9 +154,9 @@ public class PMKCascadingFinalizer { - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - public func catchOnly(_ only: Error, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Error) -> Void) -> PMKCascadingFinalizer where Error: Equatable { + public func catchOnly(_ only: Error, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping() -> Void) -> PMKCascadingFinalizer where Error: Equatable { return pending.promise.catchOnly(only, on: on, flags: flags) { - body($0) + body() } } diff --git a/Tests/Core/CatchableTests.swift b/Tests/Core/CatchableTests.swift index b6f89fb23..1a226c548 100644 --- a/Tests/Core/CatchableTests.swift +++ b/Tests/Core/CatchableTests.swift @@ -264,7 +264,7 @@ extension CatchableTests { Promise.value(1).then { _ -> Promise in throw Error.dummy - }.catchOnly(Error.dummy) { _ in + }.catchOnly(Error.dummy) { x.fulfill() }.silenceWarning() @@ -276,10 +276,10 @@ extension CatchableTests { Promise.value(1).then { _ -> Promise in throw Error.dummy - }.catchOnly(Error.dummy) { _ in + }.catchOnly(Error.dummy) { x.fulfill() }.catchOnly(Error.cancelled) { - XCTFail("\($0) error was caught") + XCTFail() x.fulfill() }.silenceWarning() @@ -292,9 +292,9 @@ extension CatchableTests { Promise.value(1).then { _ -> Promise in throw Error.dummy }.catchOnly(Error.cancelled) { - XCTFail("\($0) error was caught") + XCTFail() x.fulfill() - }.catchOnly(Error.dummy) { _ in + }.catchOnly(Error.dummy) { x.fulfill() }.silenceWarning() @@ -306,7 +306,7 @@ extension CatchableTests { Promise.value(1).then { _ -> Promise in throw Error.dummy - }.catchOnly(Error.dummy) { _ in + }.catchOnly(Error.dummy) { x.fulfill() }.catch { _ in XCTFail() @@ -321,7 +321,7 @@ extension CatchableTests { Promise.value(1).then { _ -> Promise in throw Error.dummy - }.catchOnly(Error.cancelled) { _ in + }.catchOnly(Error.cancelled) { XCTFail() x.fulfill() }.catch { _ in @@ -351,7 +351,7 @@ extension CatchableTests { }.catchOnly(Error.self) { _ in x.fulfill() }.catchOnly(Error.dummy) { - XCTFail("\($0) error was caught") + XCTFail() x.fulfill() }.silenceWarning() @@ -363,10 +363,10 @@ extension CatchableTests { Promise.value(1).then { _ -> Promise in throw Error.dummy - }.catchOnly(Error.dummy) { _ in + }.catchOnly(Error.dummy) { x.fulfill() - }.catchOnly(Error.self) { - XCTFail("\($0) error was caught") + }.catchOnly(Error.self) { _ in + XCTFail() x.fulfill() }.silenceWarning() From eddafa8e8eaebfea82a5ce03f40219bb3781d55a Mon Sep 17 00:00:00 2001 From: Nathan Hosselton Date: Mon, 26 Nov 2018 01:58:06 +0000 Subject: [PATCH 27/81] Allow specifying a CatchPolicy in catchOnly(Type) Because for this error type accepting variant of catchOnly it makes sense to want to not catch specific cancellable cases in the type. --- Sources/Catchable.swift | 11 ++++++----- Tests/Core/CatchableTests.swift | 26 ++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/Sources/Catchable.swift b/Sources/Catchable.swift index 7eb77a2e5..ee73e7d61 100644 --- a/Sources/Catchable.swift +++ b/Sources/Catchable.swift @@ -83,14 +83,16 @@ public extension CatchMixin { - Parameter only: The error type to be caught and handled. - Parameter on: The queue to which the provided closure dispatches. - Parameter execute: The handler to execute if this promise is rejected. - - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func catchOnly(_ only: Error.Type, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Error) -> Void) -> PMKCascadingFinalizer { + func catchOnly(_ only: Error.Type, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) -> Void) -> PMKCascadingFinalizer { let finalizer = PMKCascadingFinalizer() pipe { switch $0 { case .rejected(let error as Error): + guard policy == .allErrors || !error.isCancelled else { + return finalizer.pending.resolver.reject(error) + } on.async(flags: flags) { body(error) finalizer.pending.resolver.fulfill(()) @@ -171,11 +173,10 @@ public class PMKCascadingFinalizer { - Parameter only: The error type to be caught and handled. - Parameter on: The queue to which the provided closure dispatches. - Parameter execute: The handler to execute if this promise is rejected. - - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - public func catchOnly(_ only: Error.Type, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Error) -> Void) -> PMKCascadingFinalizer { - return pending.promise.catchOnly(only, on: on, flags: flags) { + public func catchOnly(_ only: Error.Type, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) -> Void) -> PMKCascadingFinalizer { + return pending.promise.catchOnly(only, on: on, flags: flags, policy: policy) { body($0) } } diff --git a/Tests/Core/CatchableTests.swift b/Tests/Core/CatchableTests.swift index 1a226c548..a003cef28 100644 --- a/Tests/Core/CatchableTests.swift +++ b/Tests/Core/CatchableTests.swift @@ -387,6 +387,32 @@ extension CatchableTests { wait(for: [x], timeout: 5) } + + func testCatchOnly_Type_Cancellation_Ignore() { + let x = expectation(description: #file + #function) + + Promise(error: Error.cancelled).catchOnly(Error.self) { _ in + XCTFail() + x.fulfill() + }.catch(policy: .allErrors) { _ in + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testCatchOnly_Type_Cancellation_Handle() { + let x = expectation(description: #file + #function) + + Promise(error: Error.cancelled).catchOnly(Error.self, policy: .allErrors) { _ in + x.fulfill() + }.catch { _ in + XCTFail() + x.fulfill() + } + + wait(for: [x], timeout: 5) + } } private enum Error: CancellableError { From dd83de89fc42b106d3d1097778519712bcd0354b Mon Sep 17 00:00:00 2001 From: Nathan Hosselton Date: Sun, 25 Nov 2018 21:10:20 -0500 Subject: [PATCH 28/81] Rename generic parameter `Error` to `E` --- Sources/Catchable.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Sources/Catchable.swift b/Sources/Catchable.swift index ee73e7d61..b99e7fab0 100644 --- a/Sources/Catchable.swift +++ b/Sources/Catchable.swift @@ -54,11 +54,11 @@ public extension CatchMixin { - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func catchOnly(_ only: Error, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping() -> Void) -> PMKCascadingFinalizer where Error: Equatable { + func catchOnly(_ only: E, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping() -> Void) -> PMKCascadingFinalizer where E: Equatable { let finalizer = PMKCascadingFinalizer() pipe { switch $0 { - case .rejected(let error as Error) where error == only: + case .rejected(let error as E) where error == only: on.async(flags: flags) { body() finalizer.pending.resolver.fulfill(()) @@ -85,11 +85,11 @@ public extension CatchMixin { - Parameter execute: The handler to execute if this promise is rejected. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func catchOnly(_ only: Error.Type, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) -> Void) -> PMKCascadingFinalizer { + func catchOnly(_ only: E.Type, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) -> Void) -> PMKCascadingFinalizer { let finalizer = PMKCascadingFinalizer() pipe { switch $0 { - case .rejected(let error as Error): + case .rejected(let error as E): guard policy == .allErrors || !error.isCancelled else { return finalizer.pending.resolver.reject(error) } @@ -156,7 +156,7 @@ public class PMKCascadingFinalizer { - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - public func catchOnly(_ only: Error, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping() -> Void) -> PMKCascadingFinalizer where Error: Equatable { + public func catchOnly(_ only: E, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping() -> Void) -> PMKCascadingFinalizer where E: Equatable { return pending.promise.catchOnly(only, on: on, flags: flags) { body() } @@ -175,7 +175,7 @@ public class PMKCascadingFinalizer { - Parameter execute: The handler to execute if this promise is rejected. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - public func catchOnly(_ only: Error.Type, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) -> Void) -> PMKCascadingFinalizer { + public func catchOnly(_ only: E.Type, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) -> Void) -> PMKCascadingFinalizer { return pending.promise.catchOnly(only, on: on, flags: flags, policy: policy) { body($0) } From d91425dec3c208bfdb5cd4fb52353ffa6c5869d7 Mon Sep 17 00:00:00 2001 From: Nathan Hosselton Date: Sun, 25 Nov 2018 22:30:43 -0500 Subject: [PATCH 29/81] Provide `recoverOnly` for specific error recovery --- Sources/Catchable.swift | 87 +++++++++++++++++ Tests/Core/CatchableTests.swift | 161 ++++++++++++++++++++++++++++++++ 2 files changed, 248 insertions(+) diff --git a/Sources/Catchable.swift b/Sources/Catchable.swift index b99e7fab0..b447bd3df 100644 --- a/Sources/Catchable.swift +++ b/Sources/Catchable.swift @@ -226,6 +226,93 @@ public extension CatchMixin { return rp } + /** + The provided closure executes when this promise rejects with the specific error passed in. + + Unlike `catch`, `recover` continues the chain. + Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: + + firstly { + CLLocationManager.requestLocation() + }.recoverOnly(CLError.unknownLocation) { + return .value(CLLocation.chicago) + } + + - Parameter only: The specific error to be recovered. + - Parameter on: The queue to which the provided closure dispatches. + - Parameter body: The handler to execute if this promise is rejected with the provided error. + - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. + - SeeAlso: [Cancellation](http://promisekit.org/docs/) + */ + func recoverOnly(_ only: E, on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping() -> U) -> Promise where U.T == T, E: Equatable { + let rp = Promise(.pending) + pipe { + switch $0 { + case .fulfilled(let value): + rp.box.seal(.fulfilled(value)) + case .rejected(let error as E) where error == only: + on.async(flags: flags) { + do { + let rv = body() + guard rv !== rp else { throw PMKError.returnedSelf } + rv.pipe(to: rp.box.seal) + } catch { + rp.box.seal(.rejected(error)) + } + } + case .rejected(let error): + rp.box.seal(.rejected(error)) + } + } + return rp + } + + /** + The provided closure executes when this promise rejects with an error of the type passed in. + + Unlike `catch`, `recover` continues the chain. + Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: + + firstly { + API.fetchData() + }.recoverOnly(FetchError.self) { error in + guard case .missingImage(let partialData) = error else { throw error } + //… + return .value(dataWithDefaultImage) + } + + - Parameter only: The error type to be recovered. + - Parameter on: The queue to which the provided closure dispatches. + - Parameter body: The handler to execute if this promise is rejected with the provided error type. + - SeeAlso: [Cancellation](http://promisekit.org/docs/) + */ + func recoverOnly(_ only: E.Type, on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> U) -> Promise where U.T == T { + let rp = Promise(.pending) + pipe { + switch $0 { + case .fulfilled(let value): + rp.box.seal(.fulfilled(value)) + case .rejected(let error as E): + if policy == .allErrors || !error.isCancelled { + on.async(flags: flags) { + do { + let rv = try body(error) + guard rv !== rp else { throw PMKError.returnedSelf } + rv.pipe(to: rp.box.seal) + } catch { + rp.box.seal(.rejected(error)) + } + } + } else { + rp.box.seal(.rejected(error)) + } + case .rejected(let error): + rp.box.seal(.rejected(error)) + } + } + return rp + } + /** The provided closure executes when this promise rejects. This variant of `recover` requires the handler to return a Guarantee, thus it returns a Guarantee itself and your closure cannot `throw`. diff --git a/Tests/Core/CatchableTests.swift b/Tests/Core/CatchableTests.swift index a003cef28..6db00ef24 100644 --- a/Tests/Core/CatchableTests.swift +++ b/Tests/Core/CatchableTests.swift @@ -415,6 +415,167 @@ extension CatchableTests { } } +/// `Promise.recoverOnly` +extension CatchableTests { + func testRecoverOnly_Object() { + let x = expectation(description: #file + #function) + + Promise(error: Error.dummy).recoverOnly(Error.dummy) { + return Promise.value(1) + }.done { _ in + x.fulfill() + }.catch { _ in + XCTFail() + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_Object_Ignored() { + let x = expectation(description: #file + #function) + + Promise.value(1).recoverOnly(Error.dummy) { + return Promise(error: Error.dummy) + }.done { _ in + x.fulfill() + }.catch { _ in + XCTFail() + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_Object_PatternMatch() { + let x = expectation(description: #file + #function) + + Promise(error: Error.cancelled).recoverOnly(Error.dummy) { + return Promise.value(1) + }.done { _ in + XCTFail() + x.fulfill() + }.catch(policy: .allErrors) { _ in + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_Type() { + let x = expectation(description: #file + #function) + + Promise(error: Error.dummy).recoverOnly(Error.self) { _ in + return Promise.value(1) + }.done { _ in + x.fulfill() + }.catch { _ in + XCTFail() + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_Type_Ignored() { + let x = expectation(description: #file + #function) + + Promise.value(1).recoverOnly(Error.self) { _ in + return Promise(error: Error.dummy) + }.done { _ in + x.fulfill() + }.catch { _ in + XCTFail() + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_Type_PatternMatch() { + let x = expectation(description: #file + #function) + + enum Foo: Swift.Error {} + + Promise(error: Error.dummy).recoverOnly(Foo.self) { _ in + return Promise.value(1) + }.done { _ in + XCTFail() + x.fulfill() + }.catch { _ in + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_Type_Cancellation_Ignore() { + let x = expectation(description: #file + #function) + + Promise(error: Error.cancelled).recoverOnly(Error.self) { _ in + return Promise.value(1) + }.done { _ in + XCTFail() + x.fulfill() + }.catch(policy: .allErrors) { _ in + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_Type_Cancellation_Handle() { + let x = expectation(description: #file + #function) + + Promise(error: Error.cancelled).recoverOnly(Error.self, policy: .allErrors) { _ in + return Promise.value(1) + }.done { _ in + x.fulfill() + }.catch { _ in + XCTFail() + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_Chaining() { + let x = expectation(description: #file + #function) + + enum Foo: Swift.Error { case bar } + + Promise(error: Error.dummy).recoverOnly(Foo.self) { _ in + return Promise(error: Foo.bar) + }.recoverOnly(Error.dummy) { + return Promise.value(1) + }.done { _ in + x.fulfill() + }.catch { _ in + XCTFail() + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_BaseRecoverIsNotCalledAfterRecoverOnlyExecutes() { + let x = expectation(description: #file + #function) + + Promise(error: Error.dummy).recoverOnly(Error.dummy) { + return Promise.value(1) + }.recover { _ in + return Promise(error: Error.dummy) + }.done { _ in + x.fulfill() + }.catch { _ in + XCTFail() + x.fulfill() + } + + wait(for: [x], timeout: 5) + } +} + private enum Error: CancellableError { case dummy case cancelled From b286d7bd3655050873c9bfeb6c9b6ce27dded30f Mon Sep 17 00:00:00 2001 From: Nathan Hosselton Date: Sun, 25 Nov 2018 22:32:07 -0500 Subject: [PATCH 30/81] Documentation tweaks and test simplification --- Sources/Catchable.swift | 16 +++++++-------- Tests/Core/CatchableTests.swift | 36 +++++++++------------------------ 2 files changed, 17 insertions(+), 35 deletions(-) diff --git a/Sources/Catchable.swift b/Sources/Catchable.swift index b447bd3df..5c09f63e8 100644 --- a/Sources/Catchable.swift +++ b/Sources/Catchable.swift @@ -41,7 +41,7 @@ public extension CatchMixin { } /** - The provided closure executes when this promise rejects with the specific error passed in. A final `catch` is still required at the end of your chain. + The provided closure executes when this promise rejects with the specific error passed in. A final `catch` is still required at the end of the chain. Rejecting a promise cascades: rejecting all subsequent promises (unless recover is invoked) thus you will typically place your catch at the end @@ -50,7 +50,7 @@ public extension CatchMixin { - Parameter only: The specific error to be caught and handled. - Parameter on: The queue to which the provided closure dispatches. - - Parameter execute: The handler to execute if this promise is rejected. + - Parameter execute: The handler to execute if this promise is rejected with the provided error. - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ @@ -73,7 +73,7 @@ public extension CatchMixin { } /** - The provided closure executes when this promise rejects with an error of the type passed in. A final `catch` is still required at the end of your chain. + The provided closure executes when this promise rejects with an error of the type passed in. A final `catch` is still required at the end of the chain. Rejecting a promise cascades: rejecting all subsequent promises (unless recover is invoked) thus you will typically place your catch at the end @@ -82,7 +82,7 @@ public extension CatchMixin { - Parameter only: The error type to be caught and handled. - Parameter on: The queue to which the provided closure dispatches. - - Parameter execute: The handler to execute if this promise is rejected. + - Parameter execute: The handler to execute if this promise is rejected with the provided error type. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ func catchOnly(_ only: E.Type, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) -> Void) -> PMKCascadingFinalizer { @@ -143,7 +143,7 @@ public class PMKCascadingFinalizer { } /** - The provided closure executes when this promise rejects with the specific error passed in. A final `catch` is still required at the end of your chain. + The provided closure executes when this promise rejects with the specific error passed in. A final `catch` is still required at the end of the chain. Rejecting a promise cascades: rejecting all subsequent promises (unless recover is invoked) thus you will typically place your catch at the end @@ -152,7 +152,7 @@ public class PMKCascadingFinalizer { - Parameter only: The specific error to be caught and handled. - Parameter on: The queue to which the provided closure dispatches. - - Parameter execute: The handler to execute if this promise is rejected. + - Parameter execute: The handler to execute if this promise is rejected with the provided error. - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ @@ -163,7 +163,7 @@ public class PMKCascadingFinalizer { } /** - The provided closure executes when this promise rejects with an error of the type passed in. A final `catch` is still required at the end of your chain. + The provided closure executes when this promise rejects with an error of the type passed in. A final `catch` is still required at the end of the chain. Rejecting a promise cascades: rejecting all subsequent promises (unless recover is invoked) thus you will typically place your catch at the end @@ -172,7 +172,7 @@ public class PMKCascadingFinalizer { - Parameter only: The error type to be caught and handled. - Parameter on: The queue to which the provided closure dispatches. - - Parameter execute: The handler to execute if this promise is rejected. + - Parameter execute: The handler to execute if this promise is rejected with the provided error type. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ public func catchOnly(_ only: E.Type, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) -> Void) -> PMKCascadingFinalizer { diff --git a/Tests/Core/CatchableTests.swift b/Tests/Core/CatchableTests.swift index 6db00ef24..04cefe1e2 100644 --- a/Tests/Core/CatchableTests.swift +++ b/Tests/Core/CatchableTests.swift @@ -262,9 +262,7 @@ extension CatchableTests { func testCatchOnly() { let x = expectation(description: #file + #function) - Promise.value(1).then { _ -> Promise in - throw Error.dummy - }.catchOnly(Error.dummy) { + Promise(error: Error.dummy).catchOnly(Error.dummy) { x.fulfill() }.silenceWarning() @@ -274,9 +272,7 @@ extension CatchableTests { func testCatchOnly_PatternMatch_1() { let x = expectation(description: "Pattern match only Error.dummy") - Promise.value(1).then { _ -> Promise in - throw Error.dummy - }.catchOnly(Error.dummy) { + Promise(error: Error.dummy).catchOnly(Error.dummy) { x.fulfill() }.catchOnly(Error.cancelled) { XCTFail() @@ -289,9 +285,7 @@ extension CatchableTests { func testCatchOnly_PatternMatch_2() { let x = expectation(description: "Pattern match only Error.dummy") - Promise.value(1).then { _ -> Promise in - throw Error.dummy - }.catchOnly(Error.cancelled) { + Promise(error: Error.dummy).catchOnly(Error.cancelled) { XCTFail() x.fulfill() }.catchOnly(Error.dummy) { @@ -304,9 +298,7 @@ extension CatchableTests { func testCatchOnly_BaseCatchIsNotCalledAfterCatchOnlyExecutes() { let x = expectation(description: #file + #function) - Promise.value(1).then { _ -> Promise in - throw Error.dummy - }.catchOnly(Error.dummy) { + Promise(error: Error.dummy).catchOnly(Error.dummy) { x.fulfill() }.catch { _ in XCTFail() @@ -319,9 +311,7 @@ extension CatchableTests { func testCatchOnly_BaseCatchIsCalledWhenCatchOnlyDoesNotExecute() { let x = expectation(description: #file + #function) - Promise.value(1).then { _ -> Promise in - throw Error.dummy - }.catchOnly(Error.cancelled) { + Promise(error: Error.dummy).catchOnly(Error.cancelled) { XCTFail() x.fulfill() }.catch { _ in @@ -334,9 +324,7 @@ extension CatchableTests { func testCatchOnly_Type() { let x = expectation(description: #file + #function) - Promise.value(1).then { _ -> Promise in - throw Error.dummy - }.catchOnly(Error.self) { _ in + Promise(error: Error.dummy).catchOnly(Error.self) { _ in x.fulfill() }.silenceWarning() @@ -346,9 +334,7 @@ extension CatchableTests { func testCatchOnly_Type_PatternMatch_1() { let x = expectation(description: "Pattern match only Error.Type") - Promise.value(1).then { _ -> Promise in - throw Error.dummy - }.catchOnly(Error.self) { _ in + Promise(error: Error.dummy).catchOnly(Error.self) { _ in x.fulfill() }.catchOnly(Error.dummy) { XCTFail() @@ -361,9 +347,7 @@ extension CatchableTests { func testCatchOnly_Type_PatternMatch_2() { let x = expectation(description: "Pattern match only Error.dummy") - Promise.value(1).then { _ -> Promise in - throw Error.dummy - }.catchOnly(Error.dummy) { + Promise(error: Error.dummy).catchOnly(Error.dummy) { x.fulfill() }.catchOnly(Error.self) { _ in XCTFail() @@ -376,9 +360,7 @@ extension CatchableTests { func testCatchOnly_Type_BaseCatchIsNotCalledAfterCatchOnlyExecutes() { let x = expectation(description: #file + #function) - Promise.value(1).then { _ -> Promise in - throw Error.dummy - }.catchOnly(Error.self) { _ in + Promise(error: Error.dummy).catchOnly(Error.self) { _ in x.fulfill() }.catch { _ in XCTFail() From 2078574b145295a4f8e9870e4dd8882eabdc2fb4 Mon Sep 17 00:00:00 2001 From: Nathan Hosselton Date: Sun, 25 Nov 2018 22:50:33 -0500 Subject: [PATCH 31/81] test coverage ++ --- Tests/Core/CatchableTests.swift | 60 +++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/Tests/Core/CatchableTests.swift b/Tests/Core/CatchableTests.swift index 04cefe1e2..f664b0cdd 100644 --- a/Tests/Core/CatchableTests.swift +++ b/Tests/Core/CatchableTests.swift @@ -331,6 +331,21 @@ extension CatchableTests { wait(for: [x], timeout: 5) } + func testCatchOnly_Type_Ignored() { + let x = expectation(description: #file + #function) + + enum Foo: Swift.Error {} + + Promise(error: Error.dummy).catchOnly(Foo.self) { _ in + XCTFail() + x.fulfill() + }.catch { _ in + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + func testCatchOnly_Type_PatternMatch_1() { let x = expectation(description: "Pattern match only Error.Type") @@ -395,6 +410,21 @@ extension CatchableTests { wait(for: [x], timeout: 5) } + + func testCatchOnly_Mixed() { + let x = expectation(description: #file + #function) + + enum Foo: Swift.Error { case bar } + + Promise(error: Foo.bar).catchOnly(Error.dummy) { + XCTFail() + x.fulfill() + }.catchOnly(Foo.self) { _ in + x.fulfill() + }.silenceWarning() + + wait(for: [x], timeout: 5) + } } /// `Promise.recoverOnly` @@ -556,6 +586,36 @@ extension CatchableTests { wait(for: [x], timeout: 5) } + + func testRecoverOnly_Object_DoesNotReturnSelf() { + let x = expectation(description: #file + #function) + var promise: Promise! + promise = Promise(error: Error.dummy).recoverOnly(Error.dummy) { () -> Promise in + return promise + } + promise.catch { err in + if case PMKError.returnedSelf = err { + x.fulfill() + } + } + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_Type_DoesNotReturnSelf() { + let x = expectation(description: #file + #function) + var promise: Promise! + promise = Promise(error: Error.dummy).recoverOnly(Error.self) { _ -> Promise in + return promise + } + promise.catch { err in + if case PMKError.returnedSelf = err { + x.fulfill() + } + } + + wait(for: [x], timeout: 5) + } } private enum Error: CancellableError { From 9e4b9be0d59f83864e9e5d6e3659ade762ec8f3a Mon Sep 17 00:00:00 2001 From: Nathan Hosselton Date: Mon, 26 Nov 2018 21:17:10 -0500 Subject: [PATCH 32/81] Provide `Promise.recoverOnly` variants --- Sources/Catchable.swift | 81 +++++++++++++++++++++ Tests/Core/CatchableTests.swift | 121 ++++++++++++++++++++++++++++++++ 2 files changed, 202 insertions(+) diff --git a/Sources/Catchable.swift b/Sources/Catchable.swift index 5c09f63e8..ad2013190 100644 --- a/Sources/Catchable.swift +++ b/Sources/Catchable.swift @@ -468,4 +468,85 @@ public extension CatchMixin where T == Void { } return rg } + + /** + The provided closure executes when this promise rejects with the specific error passed in. + + Unlike `catch`, `recover` continues the chain. + Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: + + firstly { + CLLocationManager.requestLocation() + }.recoverOnly(CLError.unknownLocation) { + return .value(CLLocation.chicago) + } + + - Parameter only: The specific error to be recovered. + - Parameter on: The queue to which the provided closure dispatches. + - Parameter body: The handler to execute if this promise is rejected with the provided error. + - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. + - SeeAlso: [Cancellation](http://promisekit.org/docs/) + */ + func recoverOnly(_ only: E, on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping() -> Void) -> Promise where E: Equatable { + let rp = Promise(.pending) + pipe { + switch $0 { + case .fulfilled: + rp.box.seal(.fulfilled()) + case .rejected(let error as E) where error == only: + on.async(flags: flags) { + body() + rp.box.seal(.fulfilled()) + } + case .rejected(let error): + rp.box.seal(.rejected(error)) + } + } + return rp + } + + /** + The provided closure executes when this promise rejects with an error of the type passed in. + + Unlike `catch`, `recover` continues the chain. + Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: + + firstly { + API.fetchData() + }.recoverOnly(FetchError.self) { error in + guard case .missingImage(let partialData) = error else { throw error } + //… + return .value(dataWithDefaultImage) + } + + - Parameter only: The error type to be recovered. + - Parameter on: The queue to which the provided closure dispatches. + - Parameter body: The handler to execute if this promise is rejected with the provided error type. + - SeeAlso: [Cancellation](http://promisekit.org/docs/) + */ + func recoverOnly(_ only: E.Type, on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> Void) -> Promise { + let rp = Promise(.pending) + pipe { + switch $0 { + case .fulfilled: + rp.box.seal(.fulfilled()) + case .rejected(let error as E): + if policy == .allErrors || !error.isCancelled { + on.async(flags: flags) { + do { + try body(error) + rp.box.seal(.fulfilled()) + } catch { + rp.box.seal(.rejected(error)) + } + } + } else { + rp.box.seal(.rejected(error)) + } + case .rejected(let error): + rp.box.seal(.rejected(error)) + } + } + return rp + } } diff --git a/Tests/Core/CatchableTests.swift b/Tests/Core/CatchableTests.swift index f664b0cdd..dc6f149f3 100644 --- a/Tests/Core/CatchableTests.swift +++ b/Tests/Core/CatchableTests.swift @@ -618,6 +618,127 @@ extension CatchableTests { } } +/// `Promise.recoverOnly` +extension CatchableTests { + func testRecoverOnly_Object_Void() { + let x = expectation(description: #file + #function) + + Promise(error: Error.dummy).recoverOnly(Error.dummy) { + return () + }.done { + x.fulfill() + }.catch { _ in + XCTFail() + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_Object_Void_Fufilled() { + let x = expectation(description: #file + #function) + + Promise.value(()).recoverOnly(Error.dummy) { + XCTFail() + x.fulfill() + }.done { + x.fulfill() + }.silenceWarning() + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_Object_Void_Ignored() { + let x = expectation(description: #file + #function) + + enum Foo: Swift.Error { case bar } + + Promise(error: Error.dummy).recoverOnly(Foo.bar) { + XCTFail() + x.fulfill() + }.done { + XCTFail() + x.fulfill() + }.catch { _ in + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_Type_Void() { + let x = expectation(description: #file + #function) + + Promise(error: Error.dummy).recoverOnly(Error.self) { _ in }.done { + x.fulfill() + }.catch { _ in + XCTFail() + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_Type_Void_Fufilled() { + let x = expectation(description: #file + #function) + + Promise.value(()).recoverOnly(Error.self) { _ in + XCTFail() + x.fulfill() + }.done { + x.fulfill() + }.silenceWarning() + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_Type_Void_Ignored() { + let x = expectation(description: #file + #function) + + enum Foo: Swift.Error { case bar } + + Promise(error: Error.dummy).recoverOnly(Foo.self) { _ in + XCTFail() + x.fulfill() + }.done { + XCTFail() + x.fulfill() + }.catch { _ in + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_Type_Void_Rethrow() { + let x = expectation(description: #file + #function) + + Promise(error: Error.dummy).recoverOnly(Error.self) { _ in + throw Error.dummy + }.done { + XCTFail() + x.fulfill() + }.catch { _ in + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_Type_Void_Cancellation_Ignore() { + let x = expectation(description: #file + #function) + + Promise(error: Error.cancelled).recoverOnly(Error.self) { _ in }.done { + XCTFail() + x.fulfill() + }.catch(policy: .allErrors) { _ in + x.fulfill() + } + + wait(for: [x], timeout: 5) + } +} + private enum Error: CancellableError { case dummy case cancelled From a09eb08b3291959c0744ae0cc1762475645d035c Mon Sep 17 00:00:00 2001 From: Nathan Hosselton Date: Tue, 27 Nov 2018 02:25:47 +0000 Subject: [PATCH 33/81] Remove examples from docs on void recoverOnly --- Sources/Catchable.swift | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/Sources/Catchable.swift b/Sources/Catchable.swift index ad2013190..566fe6049 100644 --- a/Sources/Catchable.swift +++ b/Sources/Catchable.swift @@ -473,13 +473,7 @@ public extension CatchMixin where T == Void { The provided closure executes when this promise rejects with the specific error passed in. Unlike `catch`, `recover` continues the chain. - Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: - - firstly { - CLLocationManager.requestLocation() - }.recoverOnly(CLError.unknownLocation) { - return .value(CLLocation.chicago) - } + Use `recover` in circumstances where recovering the chain from certain errors is a possibility. - Parameter only: The specific error to be recovered. - Parameter on: The queue to which the provided closure dispatches. @@ -509,15 +503,7 @@ public extension CatchMixin where T == Void { The provided closure executes when this promise rejects with an error of the type passed in. Unlike `catch`, `recover` continues the chain. - Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: - - firstly { - API.fetchData() - }.recoverOnly(FetchError.self) { error in - guard case .missingImage(let partialData) = error else { throw error } - //… - return .value(dataWithDefaultImage) - } + Use `recover` in circumstances where recovering the chain from certain errors is a possibility. - Parameter only: The error type to be recovered. - Parameter on: The queue to which the provided closure dispatches. From 4a8c2487d91a5f0295b3be6a8d92da0c34730633 Mon Sep 17 00:00:00 2001 From: Nathan Hosselton Date: Mon, 26 Nov 2018 21:38:19 -0500 Subject: [PATCH 34/81] Forgot to pass () --- Sources/Catchable.swift | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Sources/Catchable.swift b/Sources/Catchable.swift index 566fe6049..4f0834798 100644 --- a/Sources/Catchable.swift +++ b/Sources/Catchable.swift @@ -486,11 +486,11 @@ public extension CatchMixin where T == Void { pipe { switch $0 { case .fulfilled: - rp.box.seal(.fulfilled()) + rp.box.seal(.fulfilled(())) case .rejected(let error as E) where error == only: on.async(flags: flags) { body() - rp.box.seal(.fulfilled()) + rp.box.seal(.fulfilled(())) } case .rejected(let error): rp.box.seal(.rejected(error)) @@ -515,13 +515,12 @@ public extension CatchMixin where T == Void { pipe { switch $0 { case .fulfilled: - rp.box.seal(.fulfilled()) + rp.box.seal(.fulfilled(())) case .rejected(let error as E): if policy == .allErrors || !error.isCancelled { on.async(flags: flags) { do { - try body(error) - rp.box.seal(.fulfilled()) + rp.box.seal(.fulfilled(try body(error))) } catch { rp.box.seal(.rejected(error)) } From 78818516c5435ce5bf8603bff077d967789f64ac Mon Sep 17 00:00:00 2001 From: Nathan Hosselton Date: Mon, 7 Jan 2019 15:19:57 -0500 Subject: [PATCH 35/81] Rename catchOnly -> catch, recoverOnly -> recover --- Sources/Catchable.swift | 24 +++++----- Tests/Core/CatchableTests.swift | 84 ++++++++++++++++----------------- 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/Sources/Catchable.swift b/Sources/Catchable.swift index 4f0834798..2ac45ee83 100644 --- a/Sources/Catchable.swift +++ b/Sources/Catchable.swift @@ -54,7 +54,7 @@ public extension CatchMixin { - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func catchOnly(_ only: E, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping() -> Void) -> PMKCascadingFinalizer where E: Equatable { + func `catch`(_ only: E, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping() -> Void) -> PMKCascadingFinalizer where E: Equatable { let finalizer = PMKCascadingFinalizer() pipe { switch $0 { @@ -85,7 +85,7 @@ public extension CatchMixin { - Parameter execute: The handler to execute if this promise is rejected with the provided error type. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func catchOnly(_ only: E.Type, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) -> Void) -> PMKCascadingFinalizer { + func `catch`(_ only: E.Type, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) -> Void) -> PMKCascadingFinalizer { let finalizer = PMKCascadingFinalizer() pipe { switch $0 { @@ -156,8 +156,8 @@ public class PMKCascadingFinalizer { - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - public func catchOnly(_ only: E, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping() -> Void) -> PMKCascadingFinalizer where E: Equatable { - return pending.promise.catchOnly(only, on: on, flags: flags) { + public func `catch`(_ only: E, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping() -> Void) -> PMKCascadingFinalizer where E: Equatable { + return pending.promise.catch(only, on: on, flags: flags) { body() } } @@ -175,8 +175,8 @@ public class PMKCascadingFinalizer { - Parameter execute: The handler to execute if this promise is rejected with the provided error type. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - public func catchOnly(_ only: E.Type, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) -> Void) -> PMKCascadingFinalizer { - return pending.promise.catchOnly(only, on: on, flags: flags, policy: policy) { + public func `catch`(_ only: E.Type, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) -> Void) -> PMKCascadingFinalizer { + return pending.promise.catch(only, on: on, flags: flags, policy: policy) { body($0) } } @@ -234,7 +234,7 @@ public extension CatchMixin { firstly { CLLocationManager.requestLocation() - }.recoverOnly(CLError.unknownLocation) { + }.recover(CLError.unknownLocation) { return .value(CLLocation.chicago) } @@ -244,7 +244,7 @@ public extension CatchMixin { - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func recoverOnly(_ only: E, on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping() -> U) -> Promise where U.T == T, E: Equatable { + func recover(_ only: E, on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping() -> U) -> Promise where U.T == T, E: Equatable { let rp = Promise(.pending) pipe { switch $0 { @@ -275,7 +275,7 @@ public extension CatchMixin { firstly { API.fetchData() - }.recoverOnly(FetchError.self) { error in + }.recover(FetchError.self) { error in guard case .missingImage(let partialData) = error else { throw error } //… return .value(dataWithDefaultImage) @@ -286,7 +286,7 @@ public extension CatchMixin { - Parameter body: The handler to execute if this promise is rejected with the provided error type. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func recoverOnly(_ only: E.Type, on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> U) -> Promise where U.T == T { + func recover(_ only: E.Type, on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> U) -> Promise where U.T == T { let rp = Promise(.pending) pipe { switch $0 { @@ -481,7 +481,7 @@ public extension CatchMixin where T == Void { - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func recoverOnly(_ only: E, on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping() -> Void) -> Promise where E: Equatable { + func recover(_ only: E, on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping() -> Void) -> Promise where E: Equatable { let rp = Promise(.pending) pipe { switch $0 { @@ -510,7 +510,7 @@ public extension CatchMixin where T == Void { - Parameter body: The handler to execute if this promise is rejected with the provided error type. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func recoverOnly(_ only: E.Type, on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> Void) -> Promise { + func recover(_ only: E.Type, on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> Void) -> Promise { let rp = Promise(.pending) pipe { switch $0 { diff --git a/Tests/Core/CatchableTests.swift b/Tests/Core/CatchableTests.swift index dc6f149f3..9a07f434c 100644 --- a/Tests/Core/CatchableTests.swift +++ b/Tests/Core/CatchableTests.swift @@ -257,12 +257,12 @@ extension CatchableTests { } } -/// `Promise.catchOnly` +/// `Promise.catch(_ only:)` extension CatchableTests { func testCatchOnly() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).catchOnly(Error.dummy) { + Promise(error: Error.dummy).catch(Error.dummy) { x.fulfill() }.silenceWarning() @@ -272,9 +272,9 @@ extension CatchableTests { func testCatchOnly_PatternMatch_1() { let x = expectation(description: "Pattern match only Error.dummy") - Promise(error: Error.dummy).catchOnly(Error.dummy) { + Promise(error: Error.dummy).catch(Error.dummy) { x.fulfill() - }.catchOnly(Error.cancelled) { + }.catch(Error.cancelled) { XCTFail() x.fulfill() }.silenceWarning() @@ -285,10 +285,10 @@ extension CatchableTests { func testCatchOnly_PatternMatch_2() { let x = expectation(description: "Pattern match only Error.dummy") - Promise(error: Error.dummy).catchOnly(Error.cancelled) { + Promise(error: Error.dummy).catch(Error.cancelled) { XCTFail() x.fulfill() - }.catchOnly(Error.dummy) { + }.catch(Error.dummy) { x.fulfill() }.silenceWarning() @@ -298,7 +298,7 @@ extension CatchableTests { func testCatchOnly_BaseCatchIsNotCalledAfterCatchOnlyExecutes() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).catchOnly(Error.dummy) { + Promise(error: Error.dummy).catch(Error.dummy) { x.fulfill() }.catch { _ in XCTFail() @@ -311,7 +311,7 @@ extension CatchableTests { func testCatchOnly_BaseCatchIsCalledWhenCatchOnlyDoesNotExecute() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).catchOnly(Error.cancelled) { + Promise(error: Error.dummy).catch(Error.cancelled) { XCTFail() x.fulfill() }.catch { _ in @@ -324,7 +324,7 @@ extension CatchableTests { func testCatchOnly_Type() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).catchOnly(Error.self) { _ in + Promise(error: Error.dummy).catch(Error.self) { _ in x.fulfill() }.silenceWarning() @@ -336,7 +336,7 @@ extension CatchableTests { enum Foo: Swift.Error {} - Promise(error: Error.dummy).catchOnly(Foo.self) { _ in + Promise(error: Error.dummy).catch(Foo.self) { _ in XCTFail() x.fulfill() }.catch { _ in @@ -349,9 +349,9 @@ extension CatchableTests { func testCatchOnly_Type_PatternMatch_1() { let x = expectation(description: "Pattern match only Error.Type") - Promise(error: Error.dummy).catchOnly(Error.self) { _ in + Promise(error: Error.dummy).catch(Error.self) { _ in x.fulfill() - }.catchOnly(Error.dummy) { + }.catch(Error.dummy) { XCTFail() x.fulfill() }.silenceWarning() @@ -362,9 +362,9 @@ extension CatchableTests { func testCatchOnly_Type_PatternMatch_2() { let x = expectation(description: "Pattern match only Error.dummy") - Promise(error: Error.dummy).catchOnly(Error.dummy) { + Promise(error: Error.dummy).catch(Error.dummy) { x.fulfill() - }.catchOnly(Error.self) { _ in + }.catch(Error.self) { _ in XCTFail() x.fulfill() }.silenceWarning() @@ -375,7 +375,7 @@ extension CatchableTests { func testCatchOnly_Type_BaseCatchIsNotCalledAfterCatchOnlyExecutes() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).catchOnly(Error.self) { _ in + Promise(error: Error.dummy).catch(Error.self) { _ in x.fulfill() }.catch { _ in XCTFail() @@ -388,7 +388,7 @@ extension CatchableTests { func testCatchOnly_Type_Cancellation_Ignore() { let x = expectation(description: #file + #function) - Promise(error: Error.cancelled).catchOnly(Error.self) { _ in + Promise(error: Error.cancelled).catch(Error.self) { _ in XCTFail() x.fulfill() }.catch(policy: .allErrors) { _ in @@ -401,7 +401,7 @@ extension CatchableTests { func testCatchOnly_Type_Cancellation_Handle() { let x = expectation(description: #file + #function) - Promise(error: Error.cancelled).catchOnly(Error.self, policy: .allErrors) { _ in + Promise(error: Error.cancelled).catch(Error.self, policy: .allErrors) { _ in x.fulfill() }.catch { _ in XCTFail() @@ -416,10 +416,10 @@ extension CatchableTests { enum Foo: Swift.Error { case bar } - Promise(error: Foo.bar).catchOnly(Error.dummy) { + Promise(error: Foo.bar).catch(Error.dummy) { XCTFail() x.fulfill() - }.catchOnly(Foo.self) { _ in + }.catch(Foo.self) { _ in x.fulfill() }.silenceWarning() @@ -427,12 +427,12 @@ extension CatchableTests { } } -/// `Promise.recoverOnly` +/// `Promise.recover(_ only:)` extension CatchableTests { func testRecoverOnly_Object() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).recoverOnly(Error.dummy) { + Promise(error: Error.dummy).recover(Error.dummy) { return Promise.value(1) }.done { _ in x.fulfill() @@ -447,7 +447,7 @@ extension CatchableTests { func testRecoverOnly_Object_Ignored() { let x = expectation(description: #file + #function) - Promise.value(1).recoverOnly(Error.dummy) { + Promise.value(1).recover(Error.dummy) { return Promise(error: Error.dummy) }.done { _ in x.fulfill() @@ -462,7 +462,7 @@ extension CatchableTests { func testRecoverOnly_Object_PatternMatch() { let x = expectation(description: #file + #function) - Promise(error: Error.cancelled).recoverOnly(Error.dummy) { + Promise(error: Error.cancelled).recover(Error.dummy) { return Promise.value(1) }.done { _ in XCTFail() @@ -477,7 +477,7 @@ extension CatchableTests { func testRecoverOnly_Type() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).recoverOnly(Error.self) { _ in + Promise(error: Error.dummy).recover(Error.self) { _ in return Promise.value(1) }.done { _ in x.fulfill() @@ -492,7 +492,7 @@ extension CatchableTests { func testRecoverOnly_Type_Ignored() { let x = expectation(description: #file + #function) - Promise.value(1).recoverOnly(Error.self) { _ in + Promise.value(1).recover(Error.self) { _ in return Promise(error: Error.dummy) }.done { _ in x.fulfill() @@ -509,7 +509,7 @@ extension CatchableTests { enum Foo: Swift.Error {} - Promise(error: Error.dummy).recoverOnly(Foo.self) { _ in + Promise(error: Error.dummy).recover(Foo.self) { _ in return Promise.value(1) }.done { _ in XCTFail() @@ -524,7 +524,7 @@ extension CatchableTests { func testRecoverOnly_Type_Cancellation_Ignore() { let x = expectation(description: #file + #function) - Promise(error: Error.cancelled).recoverOnly(Error.self) { _ in + Promise(error: Error.cancelled).recover(Error.self) { _ in return Promise.value(1) }.done { _ in XCTFail() @@ -539,7 +539,7 @@ extension CatchableTests { func testRecoverOnly_Type_Cancellation_Handle() { let x = expectation(description: #file + #function) - Promise(error: Error.cancelled).recoverOnly(Error.self, policy: .allErrors) { _ in + Promise(error: Error.cancelled).recover(Error.self, policy: .allErrors) { _ in return Promise.value(1) }.done { _ in x.fulfill() @@ -556,9 +556,9 @@ extension CatchableTests { enum Foo: Swift.Error { case bar } - Promise(error: Error.dummy).recoverOnly(Foo.self) { _ in + Promise(error: Error.dummy).recover(Foo.self) { _ in return Promise(error: Foo.bar) - }.recoverOnly(Error.dummy) { + }.recover(Error.dummy) { return Promise.value(1) }.done { _ in x.fulfill() @@ -573,7 +573,7 @@ extension CatchableTests { func testRecoverOnly_BaseRecoverIsNotCalledAfterRecoverOnlyExecutes() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).recoverOnly(Error.dummy) { + Promise(error: Error.dummy).recover(Error.dummy) { return Promise.value(1) }.recover { _ in return Promise(error: Error.dummy) @@ -590,7 +590,7 @@ extension CatchableTests { func testRecoverOnly_Object_DoesNotReturnSelf() { let x = expectation(description: #file + #function) var promise: Promise! - promise = Promise(error: Error.dummy).recoverOnly(Error.dummy) { () -> Promise in + promise = Promise(error: Error.dummy).recover(Error.dummy) { () -> Promise in return promise } promise.catch { err in @@ -605,7 +605,7 @@ extension CatchableTests { func testRecoverOnly_Type_DoesNotReturnSelf() { let x = expectation(description: #file + #function) var promise: Promise! - promise = Promise(error: Error.dummy).recoverOnly(Error.self) { _ -> Promise in + promise = Promise(error: Error.dummy).recover(Error.self) { _ -> Promise in return promise } promise.catch { err in @@ -618,12 +618,12 @@ extension CatchableTests { } } -/// `Promise.recoverOnly` +/// `Promise.recover(_ only:)` extension CatchableTests { func testRecoverOnly_Object_Void() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).recoverOnly(Error.dummy) { + Promise(error: Error.dummy).recover(Error.dummy) { return () }.done { x.fulfill() @@ -638,7 +638,7 @@ extension CatchableTests { func testRecoverOnly_Object_Void_Fufilled() { let x = expectation(description: #file + #function) - Promise.value(()).recoverOnly(Error.dummy) { + Promise.value(()).recover(Error.dummy) { XCTFail() x.fulfill() }.done { @@ -653,7 +653,7 @@ extension CatchableTests { enum Foo: Swift.Error { case bar } - Promise(error: Error.dummy).recoverOnly(Foo.bar) { + Promise(error: Error.dummy).recover(Foo.bar) { XCTFail() x.fulfill() }.done { @@ -669,7 +669,7 @@ extension CatchableTests { func testRecoverOnly_Type_Void() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).recoverOnly(Error.self) { _ in }.done { + Promise(error: Error.dummy).recover(Error.self) { _ in }.done { x.fulfill() }.catch { _ in XCTFail() @@ -682,7 +682,7 @@ extension CatchableTests { func testRecoverOnly_Type_Void_Fufilled() { let x = expectation(description: #file + #function) - Promise.value(()).recoverOnly(Error.self) { _ in + Promise.value(()).recover(Error.self) { _ in XCTFail() x.fulfill() }.done { @@ -697,7 +697,7 @@ extension CatchableTests { enum Foo: Swift.Error { case bar } - Promise(error: Error.dummy).recoverOnly(Foo.self) { _ in + Promise(error: Error.dummy).recover(Foo.self) { _ in XCTFail() x.fulfill() }.done { @@ -713,7 +713,7 @@ extension CatchableTests { func testRecoverOnly_Type_Void_Rethrow() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).recoverOnly(Error.self) { _ in + Promise(error: Error.dummy).recover(Error.self) { _ in throw Error.dummy }.done { XCTFail() @@ -728,7 +728,7 @@ extension CatchableTests { func testRecoverOnly_Type_Void_Cancellation_Ignore() { let x = expectation(description: #file + #function) - Promise(error: Error.cancelled).recoverOnly(Error.self) { _ in }.done { + Promise(error: Error.cancelled).recover(Error.self) { _ in }.done { XCTFail() x.fulfill() }.catch(policy: .allErrors) { _ in From 4692135d97f7eca34fea49aa77f7338cb1f50a84 Mon Sep 17 00:00:00 2001 From: Nathan Hosselton Date: Sat, 23 Mar 2019 21:04:53 +0000 Subject: [PATCH 36/81] Update for v7 and regenerate Core/XCTestManifests - remove `flags:` function params - DispatchQueue? -> Dispatcher for `on:` params - conf.Q -> conf.D for default `on:` values - update Result case usage to `success` `failure` - on.async(flags:) -> on.dispatch --- Sources/Catchable.swift | 102 +++++++++++++++---------------- Tests/Core/XCTestManifests.swift | 33 ++++++++++ 2 files changed, 84 insertions(+), 51 deletions(-) diff --git a/Sources/Catchable.swift b/Sources/Catchable.swift index 2ac45ee83..8c1b21828 100644 --- a/Sources/Catchable.swift +++ b/Sources/Catchable.swift @@ -54,18 +54,18 @@ public extension CatchMixin { - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func `catch`(_ only: E, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping() -> Void) -> PMKCascadingFinalizer where E: Equatable { + func `catch`(_ only: E, on: Dispatcher = conf.D.return, _ body: @escaping() -> Void) -> PMKCascadingFinalizer where E: Equatable { let finalizer = PMKCascadingFinalizer() pipe { switch $0 { - case .rejected(let error as E) where error == only: - on.async(flags: flags) { + case .failure(let error as E) where error == only: + on.dispatch { body() finalizer.pending.resolver.fulfill(()) } - case .rejected(let error): + case .failure(let error): finalizer.pending.resolver.reject(error) - case .fulfilled: + case .success: finalizer.pending.resolver.fulfill(()) } } @@ -85,21 +85,21 @@ public extension CatchMixin { - Parameter execute: The handler to execute if this promise is rejected with the provided error type. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func `catch`(_ only: E.Type, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) -> Void) -> PMKCascadingFinalizer { + func `catch`(_ only: E.Type, on: Dispatcher = conf.D.return, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) -> Void) -> PMKCascadingFinalizer { let finalizer = PMKCascadingFinalizer() pipe { switch $0 { - case .rejected(let error as E): + case .failure(let error as E): guard policy == .allErrors || !error.isCancelled else { return finalizer.pending.resolver.reject(error) } - on.async(flags: flags) { + on.dispatch { body(error) finalizer.pending.resolver.fulfill(()) } - case .rejected(let error): + case .failure(let error): finalizer.pending.resolver.reject(error) - case .fulfilled: + case .success: finalizer.pending.resolver.fulfill(()) } } @@ -136,8 +136,8 @@ public class PMKCascadingFinalizer { - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ @discardableResult - public func `catch`(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) -> Void) -> PMKFinalizer { - return pending.promise.catch(on: on, flags: flags, policy: policy) { + public func `catch`(on: Dispatcher = conf.D.return, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) -> Void) -> PMKFinalizer { + return pending.promise.catch(on: on, policy: policy) { body($0) } } @@ -156,8 +156,8 @@ public class PMKCascadingFinalizer { - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - public func `catch`(_ only: E, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping() -> Void) -> PMKCascadingFinalizer where E: Equatable { - return pending.promise.catch(only, on: on, flags: flags) { + public func `catch`(_ only: E, on: Dispatcher = conf.D.return, _ body: @escaping() -> Void) -> PMKCascadingFinalizer where E: Equatable { + return pending.promise.catch(only, on: on) { body() } } @@ -175,8 +175,8 @@ public class PMKCascadingFinalizer { - Parameter execute: The handler to execute if this promise is rejected with the provided error type. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - public func `catch`(_ only: E.Type, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) -> Void) -> PMKCascadingFinalizer { - return pending.promise.catch(only, on: on, flags: flags, policy: policy) { + public func `catch`(_ only: E.Type, on: Dispatcher = conf.D.return, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) -> Void) -> PMKCascadingFinalizer { + return pending.promise.catch(only, on: on, policy: policy) { body($0) } } @@ -244,24 +244,24 @@ public extension CatchMixin { - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func recover(_ only: E, on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping() -> U) -> Promise where U.T == T, E: Equatable { + func recover(_ only: E, on: Dispatcher = conf.D.map, _ body: @escaping() -> U) -> Promise where U.T == T, E: Equatable { let rp = Promise(.pending) pipe { switch $0 { - case .fulfilled(let value): - rp.box.seal(.fulfilled(value)) - case .rejected(let error as E) where error == only: - on.async(flags: flags) { + case .success(let value): + rp.box.seal(.success(value)) + case .failure(let error as E) where error == only: + on.dispatch { do { let rv = body() guard rv !== rp else { throw PMKError.returnedSelf } rv.pipe(to: rp.box.seal) } catch { - rp.box.seal(.rejected(error)) + rp.box.seal(.failure(error)) } } - case .rejected(let error): - rp.box.seal(.rejected(error)) + case .failure(let error): + rp.box.seal(.failure(error)) } } return rp @@ -286,28 +286,28 @@ public extension CatchMixin { - Parameter body: The handler to execute if this promise is rejected with the provided error type. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func recover(_ only: E.Type, on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> U) -> Promise where U.T == T { + func recover(_ only: E.Type, on: Dispatcher = conf.D.map, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> U) -> Promise where U.T == T { let rp = Promise(.pending) pipe { switch $0 { - case .fulfilled(let value): - rp.box.seal(.fulfilled(value)) - case .rejected(let error as E): + case .success(let value): + rp.box.seal(.success(value)) + case .failure(let error as E): if policy == .allErrors || !error.isCancelled { - on.async(flags: flags) { + on.dispatch { do { let rv = try body(error) guard rv !== rp else { throw PMKError.returnedSelf } rv.pipe(to: rp.box.seal) } catch { - rp.box.seal(.rejected(error)) + rp.box.seal(.failure(error)) } } } else { - rp.box.seal(.rejected(error)) + rp.box.seal(.failure(error)) } - case .rejected(let error): - rp.box.seal(.rejected(error)) + case .failure(let error): + rp.box.seal(.failure(error)) } } return rp @@ -481,19 +481,19 @@ public extension CatchMixin where T == Void { - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func recover(_ only: E, on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping() -> Void) -> Promise where E: Equatable { + func recover(_ only: E, on: Dispatcher = conf.D.map, _ body: @escaping() -> Void) -> Promise where E: Equatable { let rp = Promise(.pending) pipe { switch $0 { - case .fulfilled: - rp.box.seal(.fulfilled(())) - case .rejected(let error as E) where error == only: - on.async(flags: flags) { + case .success: + rp.box.seal(.success(())) + case .failure(let error as E) where error == only: + on.dispatch { body() - rp.box.seal(.fulfilled(())) + rp.box.seal(.success(())) } - case .rejected(let error): - rp.box.seal(.rejected(error)) + case .failure(let error): + rp.box.seal(.failure(error)) } } return rp @@ -510,26 +510,26 @@ public extension CatchMixin where T == Void { - Parameter body: The handler to execute if this promise is rejected with the provided error type. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func recover(_ only: E.Type, on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> Void) -> Promise { + func recover(_ only: E.Type, on: Dispatcher = conf.D.map, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> Void) -> Promise { let rp = Promise(.pending) pipe { switch $0 { - case .fulfilled: - rp.box.seal(.fulfilled(())) - case .rejected(let error as E): + case .success: + rp.box.seal(.success(())) + case .failure(let error as E): if policy == .allErrors || !error.isCancelled { - on.async(flags: flags) { + on.dispatch { do { - rp.box.seal(.fulfilled(try body(error))) + rp.box.seal(.success(try body(error))) } catch { - rp.box.seal(.rejected(error)) + rp.box.seal(.failure(error)) } } } else { - rp.box.seal(.rejected(error)) + rp.box.seal(.failure(error)) } - case .rejected(let error): - rp.box.seal(.rejected(error)) + case .failure(let error): + rp.box.seal(.failure(error)) } } return rp diff --git a/Tests/Core/XCTestManifests.swift b/Tests/Core/XCTestManifests.swift index f96dc728b..ff22bfaa9 100644 --- a/Tests/Core/XCTestManifests.swift +++ b/Tests/Core/XCTestManifests.swift @@ -43,10 +43,43 @@ extension CatchableTests { ("test__void_specialized_conditional_recover__no_recover", test__void_specialized_conditional_recover__no_recover), ("test__void_specialized_full_recover", test__void_specialized_full_recover), ("test__void_specialized_full_recover__fulfilled_path", test__void_specialized_full_recover__fulfilled_path), + ("testCatchOnly", testCatchOnly), + ("testCatchOnly_BaseCatchIsCalledWhenCatchOnlyDoesNotExecute", testCatchOnly_BaseCatchIsCalledWhenCatchOnlyDoesNotExecute), + ("testCatchOnly_BaseCatchIsNotCalledAfterCatchOnlyExecutes", testCatchOnly_BaseCatchIsNotCalledAfterCatchOnlyExecutes), + ("testCatchOnly_Mixed", testCatchOnly_Mixed), + ("testCatchOnly_PatternMatch_1", testCatchOnly_PatternMatch_1), + ("testCatchOnly_PatternMatch_2", testCatchOnly_PatternMatch_2), + ("testCatchOnly_Type", testCatchOnly_Type), + ("testCatchOnly_Type_BaseCatchIsNotCalledAfterCatchOnlyExecutes", testCatchOnly_Type_BaseCatchIsNotCalledAfterCatchOnlyExecutes), + ("testCatchOnly_Type_Cancellation_Handle", testCatchOnly_Type_Cancellation_Handle), + ("testCatchOnly_Type_Cancellation_Ignore", testCatchOnly_Type_Cancellation_Ignore), + ("testCatchOnly_Type_Ignored", testCatchOnly_Type_Ignored), + ("testCatchOnly_Type_PatternMatch_1", testCatchOnly_Type_PatternMatch_1), + ("testCatchOnly_Type_PatternMatch_2", testCatchOnly_Type_PatternMatch_2), ("testCauterize", testCauterize), ("testEnsureThen_Error", testEnsureThen_Error), ("testEnsureThen_Value", testEnsureThen_Value), ("testFinally", testFinally), + ("testRecoverOnly_BaseRecoverIsNotCalledAfterRecoverOnlyExecutes", testRecoverOnly_BaseRecoverIsNotCalledAfterRecoverOnlyExecutes), + ("testRecoverOnly_Chaining", testRecoverOnly_Chaining), + ("testRecoverOnly_Object", testRecoverOnly_Object), + ("testRecoverOnly_Object_DoesNotReturnSelf", testRecoverOnly_Object_DoesNotReturnSelf), + ("testRecoverOnly_Object_Ignored", testRecoverOnly_Object_Ignored), + ("testRecoverOnly_Object_PatternMatch", testRecoverOnly_Object_PatternMatch), + ("testRecoverOnly_Object_Void", testRecoverOnly_Object_Void), + ("testRecoverOnly_Object_Void_Fufilled", testRecoverOnly_Object_Void_Fufilled), + ("testRecoverOnly_Object_Void_Ignored", testRecoverOnly_Object_Void_Ignored), + ("testRecoverOnly_Type", testRecoverOnly_Type), + ("testRecoverOnly_Type_Cancellation_Handle", testRecoverOnly_Type_Cancellation_Handle), + ("testRecoverOnly_Type_Cancellation_Ignore", testRecoverOnly_Type_Cancellation_Ignore), + ("testRecoverOnly_Type_DoesNotReturnSelf", testRecoverOnly_Type_DoesNotReturnSelf), + ("testRecoverOnly_Type_Ignored", testRecoverOnly_Type_Ignored), + ("testRecoverOnly_Type_PatternMatch", testRecoverOnly_Type_PatternMatch), + ("testRecoverOnly_Type_Void", testRecoverOnly_Type_Void), + ("testRecoverOnly_Type_Void_Cancellation_Ignore", testRecoverOnly_Type_Void_Cancellation_Ignore), + ("testRecoverOnly_Type_Void_Fufilled", testRecoverOnly_Type_Void_Fufilled), + ("testRecoverOnly_Type_Void_Ignored", testRecoverOnly_Type_Void_Ignored), + ("testRecoverOnly_Type_Void_Rethrow", testRecoverOnly_Type_Void_Rethrow), ] } From c7b2bd16570406c8664d62d3cf7c44fc5b16cb44 Mon Sep 17 00:00:00 2001 From: Max Howell Date: Sun, 24 Mar 2019 13:59:06 +0000 Subject: [PATCH 37/81] [ci skip] Fix deploy --- .travis.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4ec9e78d9..1dbda4920 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,9 +18,6 @@ stages: - name: deploy if: branch =~ ^\d+\.\d+\.\d+$ -os: osx -osx_image: xcode10.2 -language: swift xcode_project: PromiseKit.xcodeproj xcode_scheme: PromiseKit-Package xcode_destination: 'platform=macOS' @@ -85,6 +82,7 @@ jobs: - name: Generate Documentation git.depth: false install: gem install jazzy + before_script: swift package generate-xcodeproj script: | set -exo pipefail jazzy --config .github/jazzy.yml \ From f1885f3d4f72c41c013363f4eb62b78d04af9203 Mon Sep 17 00:00:00 2001 From: Doug Stein Date: Mon, 25 Mar 2019 16:29:40 -0700 Subject: [PATCH 38/81] 'Cancel' for PromiseKit: add cancellable variants for #1024, plus doc update * Add cancellable variants and cancellable dispatcher stubs for all new methods introduced by #1024 * Update Troubleshooting doc with solution for compilation timeouts, encountered primarily with cancellable promises --- .github/spelling-skip-words | 1 + Documents/Troubleshooting.md | 47 +++ Sources/CancellableCatchable.swift | 351 ++++++++++++++++++-- Sources/Dispatcher.swift | 226 ++++++++++++- Tests/Cancel/CatchableTests.swift | 497 +++++++++++++++++++++++++++++ Tests/Cancel/DispatcherTests.swift | 298 ++++++++++++++++- Tests/Cancel/XCTestManifests.swift | 38 +++ Tests/Core/DispatcherTests.swift | 2 +- 8 files changed, 1424 insertions(+), 36 deletions(-) diff --git a/.github/spelling-skip-words b/.github/spelling-skip-words index f0f87c808..57c7f9b52 100644 --- a/.github/spelling-skip-words +++ b/.github/spelling-skip-words @@ -127,3 +127,4 @@ VoidPending PromiseURLSessionresumewait unusedResult discardableResultcatchreturncauterize +PMKFinalizercancelthenMapthenFlatMapthendonePromise diff --git a/Documents/Troubleshooting.md b/Documents/Troubleshooting.md index 218112124..b986190cf 100644 --- a/Documents/Troubleshooting.md +++ b/Documents/Troubleshooting.md @@ -242,6 +242,53 @@ let promise = firstly { promise.cancel() /// <-- ERROR: Value of type 'PMKFinalizer' has no member 'cancel' ``` +### Compilation for long chains is very slow or times out, especially with cancellable promises + +If a cancellable promise chain has more than a few (> 3) calls to `thenMap` and +`thenFlatMap` or has an extensive number of calls in the chain (> 6), you +may need to specify the return types for all closures in the chain. Standard +(non-cancellable) promise chains typically only see this problem if they are +extremely long (> 15 calls). + +And if compilation with promises is generally sluggish, the time may be greatly +improved by specifying return types for all closures. + +For example: + +```swift +/// Compilation timeout error: +Promise.value([42, 52]).cancellize().then { + Promise.value($0) +}.then { + Promise.value($0) +}.thenMap { + Promise.value($0 + 10).cancellize() +}.thenMap { + Promise.value($0 + 10) +}.thenFlatMap { + Promise.value([$0 + 10]).cancellize() +}.thenFlatMap { /// <-- Error: The compiler is unable to type-check this expression + /// in reasonable time; try breaking up the expression + /// into distinct sub-expressions + Promise.value([$0 + 10]) +} + +/// Compiles very quickly: +Promise.value([42, 52]).cancellize().then { v -> Promise<[Int]> in + Promise.value(v) +}.then { v -> Promise<[Int]> in + Promise.value(v) +}.thenMap { v -> CancellablePromise in + Promise.value(v + 10).cancellize() +}.thenMap { v -> Promise in + Promise.value(v + 10) +}.thenFlatMap { v -> CancellablePromise<[Int]> in + Promise.value([v + 10]).cancellize() +}.thenFlatMap { v -> Promise<[Int]> in + Promise.value([v + 10]) +} +``` + ## You copied code off the Internet that doesn’t work Swift has changed a lot over the years and so PromiseKit has had to change to keep diff --git a/Sources/CancellableCatchable.swift b/Sources/CancellableCatchable.swift index 9a53fd642..fe6775f31 100644 --- a/Sources/CancellableCatchable.swift +++ b/Sources/CancellableCatchable.swift @@ -28,29 +28,53 @@ public extension CancellableCatchMixin { func `catch`(on: Dispatcher = conf.D.return, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) -> Void) -> CancellableFinalizer { return CancellableFinalizer(self.catchable.catch(on: on, policy: policy, body), cancel: self.cancelContext) } -} -/** - Cancellable finalizer returned from `catch`. Use `finally` to specify a code block that executes when the promise chain resolves. - */ -public class CancellableFinalizer { - let pmkFinalizer: PMKFinalizer + /** + The provided closure executes when this cancellable promise rejects with the specific error passed in. + A final `catch` is still required at the end of the chain. + + Rejecting a promise cascades: rejecting all subsequent promises (unless + recover is invoked) thus you will typically place your catch at the end + of a chain. Often utility promises will not have a catch, instead + delegating the error handling to the caller. + + - Parameter only: The specific error to be caught and handled. + - Parameter on: The queue to which the provided closure dispatches. + - Parameter execute: The handler to execute if this promise is rejected with the provided error. + - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + */ + func `catch`(_ only: E, on: Dispatcher = conf.D.return, _ body: @escaping() -> Void) -> CancellableCascadingFinalizer where E: Equatable { + return CancellableCascadingFinalizer(self.catchable.catch(only, on: on, body), cancel: self.cancelContext) + } + /** + The provided closure executes when this cancellable promise rejects with an error of the type passed in. + A final `catch` is still required at the end of the chain. + + Rejecting a promise cascades: rejecting all subsequent promises (unless + recover is invoked) thus you will typically place your catch at the end + of a chain. Often utility promises will not have a catch, instead + delegating the error handling to the caller. + + - Parameter only: The error type to be caught and handled. + - Parameter on: The queue to which the provided closure dispatches. + - Parameter execute: The handler to execute if this promise is rejected with the provided error type. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + */ + func `catch`(_ only: E.Type, on: Dispatcher = conf.D.return, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) -> Void) -> CancellableCascadingFinalizer { + return CancellableCascadingFinalizer(self.catchable.catch(only, on: on, policy: policy, body), cancel: self.cancelContext) + } +} + +public class CancelContextFinalizer { /// The CancelContext associated with this finalizer public let cancelContext: CancelContext - init(_ pmkFinalizer: PMKFinalizer, cancel: CancelContext) { - self.pmkFinalizer = pmkFinalizer + init(cancel: CancelContext) { self.cancelContext = cancel } - /// `finally` is the same as `ensure`, but it is not chainable - @discardableResult - public func finally(on: Dispatcher = conf.D.return, _ body: @escaping () -> Void) -> CancelContext { - pmkFinalizer.finally(on: on, body) - return cancelContext - } - /** Cancel all members of the promise chain and their associated asynchronous operations. @@ -82,6 +106,88 @@ public class CancellableFinalizer { } } +/** + Cancellable finalizer returned from `catch`. Use `finally` to specify a code block that executes when the promise chain resolves. + */ +public class CancellableFinalizer: CancelContextFinalizer { + let pmkFinalizer: PMKFinalizer + + init(_ pmkFinalizer: PMKFinalizer, cancel: CancelContext) { + self.pmkFinalizer = pmkFinalizer + super.init(cancel: cancel) + } + + /// `finally` is the same as `ensure`, but it is not chainable + @discardableResult + public func finally(on: Dispatcher = conf.D.return, _ body: @escaping () -> Void) -> CancelContext { + pmkFinalizer.finally(on: on, body) + return cancelContext + } +} + +public class CancellableCascadingFinalizer: CancelContextFinalizer { + let pmkCascadingFinalizer: PMKCascadingFinalizer + + init(_ pmkCascadingFinalizer: PMKCascadingFinalizer, cancel: CancelContext) { + self.pmkCascadingFinalizer = pmkCascadingFinalizer + super.init(cancel: cancel) + } + + /** + The provided closure executes when this promise rejects. + + Rejecting a promise cascades: rejecting all subsequent promises (unless + recover is invoked) thus you will typically place your catch at the end + of a chain. Often utility promises will not have a catch, instead + delegating the error handling to the caller. + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter policy: The default policy does not execute your handler for cancellation errors. + - Parameter execute: The handler to execute if this promise is rejected. + - Returns: A promise finalizer. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + */ + @discardableResult + public func `catch`(on: Dispatcher = conf.D.return, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) -> Void) -> CancellableFinalizer { + return CancellableFinalizer(pmkCascadingFinalizer.catch(on: on, policy: policy, body), cancel: cancelContext) + } + + /** + The provided closure executes when this promise rejects with the specific error passed in. A final `catch` is still required at the end of the chain. + + Rejecting a promise cascades: rejecting all subsequent promises (unless + recover is invoked) thus you will typically place your catch at the end + of a chain. Often utility promises will not have a catch, instead + delegating the error handling to the caller. + + - Parameter only: The specific error to be caught and handled. + - Parameter on: The queue to which the provided closure dispatches. + - Parameter execute: The handler to execute if this promise is rejected with the provided error. + - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + */ + public func `catch`(_ only: E, on: Dispatcher = conf.D.return, _ body: @escaping() -> Void) -> CancellableCascadingFinalizer where E: Equatable { + return CancellableCascadingFinalizer(pmkCascadingFinalizer.catch(only, on: on, body), cancel: cancelContext) + } + + /** + The provided closure executes when this promise rejects with an error of the type passed in. A final `catch` is still required at the end of the chain. + + Rejecting a promise cascades: rejecting all subsequent promises (unless + recover is invoked) thus you will typically place your catch at the end + of a chain. Often utility promises will not have a catch, instead + delegating the error handling to the caller. + + - Parameter only: The error type to be caught and handled. + - Parameter on: The queue to which the provided closure dispatches. + - Parameter execute: The handler to execute if this promise is rejected with the provided error type. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + */ + public func `catch`(_ only: E.Type, on: Dispatcher = conf.D.return, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) -> Void) -> CancellableCascadingFinalizer { + return CancellableCascadingFinalizer(pmkCascadingFinalizer.catch(only, on: on, policy: policy, body), cancel: cancelContext) + } +} + public extension CancellableCatchMixin { /** The provided closure executes when this cancellable promise rejects. @@ -105,13 +211,12 @@ public extension CancellableCatchMixin { - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) */ func recover(on: Dispatcher = conf.D.map, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) throws -> V) -> CancellablePromise where V.U.T == C.T { - let cancelItemList = CancelItemList() let cancelBody = { (error: Error) throws -> V.U in _ = self.cancelContext.removeItems(self.cancelItemList, clearList: true) let rval = try body(error) - if policy == .allErrors { + if policy == .allErrors || error.isCancelled { self.cancelContext.recover() } self.cancelContext.append(context: rval.cancelContext, thenableCancelItemList: cancelItemList) @@ -135,7 +240,7 @@ public extension CancellableCatchMixin { CLLocationManager.requestLocation() }.cancellize().recover { error in guard error == CLError.unknownLocation else { throw error } - return .value(CLLocation.chicago) + return Promise.value(CLLocation.chicago) }.cancelContext //… @@ -145,14 +250,12 @@ public extension CancellableCatchMixin { - Parameter on: The dispatcher that executes the provided closure. - Parameter body: The handler to execute if this promise is rejected. - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) - - Note: Methods with the `cancellable` prefix create a new CancellablePromise, and those without the `cancellable` prefix accept an existing CancellablePromise. */ func recover(on: Dispatcher = conf.D.map, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) throws -> V) -> CancellablePromise where V.T == C.T { - let cancelBody = { (error: Error) throws -> V in _ = self.cancelContext.removeItems(self.cancelItemList, clearList: true) let rval = try body(error) - if policy == .allErrors { + if policy == .allErrors || error.isCancelled { self.cancelContext.recover() } return rval @@ -169,6 +272,159 @@ public extension CancellableCatchMixin { return cancellablePromise } + /** + The provided closure executes when this cancellable promise rejects with the specific error passed in. + + Unlike `catch`, `recover` continues the chain. + Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: + + firstly { + CLLocationManager.requestLocation() + }.recover(CLError.unknownLocation) { + return .value(CLLocation.chicago) + } + + - Parameter only: The specific error to be recovered. + - Parameter on: The queue to which the provided closure dispatches. + - Parameter body: The handler to execute if this promise is rejected with the provided error. + - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + */ + func recover(_ only: E, on: Dispatcher = conf.D.map, _ body: @escaping() -> V) -> CancellablePromise where V.U.T == C.T, E: Equatable { + let cancelItemList = CancelItemList() + + let cancelBody = { () -> V.U in + _ = self.cancelContext.removeItems(self.cancelItemList, clearList: true) + let rval = body() + if only.isCancelled { + self.cancelContext.recover() + } + self.cancelContext.append(context: rval.cancelContext, thenableCancelItemList: cancelItemList) + return rval.thenable + } + + let promise = self.catchable.recover(only, on: on, cancelBody) + if thenable.result != nil && only.isCancelled { + self.cancelContext.recover() + } + return CancellablePromise(promise: promise, context: self.cancelContext, cancelItemList: cancelItemList) + } + + /** + The provided closure executes when this cancellable promise rejects with the specific error passed in. + + Unlike `catch`, `recover` continues the chain. + Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: + + firstly { + CLLocationManager.requestLocation() + }.recover(CLError.unknownLocation) { + return Promise.value(CLLocation.chicago) + } + + - Parameter only: The specific error to be recovered. + - Parameter on: The queue to which the provided closure dispatches. + - Parameter body: The handler to execute if this promise is rejected with the provided error. + - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + */ + func recover(_ only: E, on: Dispatcher = conf.D.map, _ body: @escaping() -> V) -> CancellablePromise where V.T == C.T, E: Equatable { + let cancelBody = { () -> V in + _ = self.cancelContext.removeItems(self.cancelItemList, clearList: true) + let rval = body() + if only.isCancelled { + self.cancelContext.recover() + } + return rval + } + + let promise = self.catchable.recover(only, on: on, cancelBody) + let cancellablePromise = CancellablePromise(promise: promise, context: self.cancelContext) + if let cancellable = promise.cancellable { + self.cancelContext.append(cancellable: cancellable, reject: promise.rejectIfCancelled, thenable: cancellablePromise) + } + return cancellablePromise + } + + /** + The provided closure executes when this cancellable promise rejects with an error of the type passed in. + + Unlike `catch`, `recover` continues the chain. + Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: + + firstly { + API.fetchData() + }.recover(FetchError.self) { error in + guard case .missingImage(let partialData) = error else { throw error } + //… + return .value(dataWithDefaultImage) + } + + - Parameter only: The error type to be recovered. + - Parameter on: The queue to which the provided closure dispatches. + - Parameter body: The handler to execute if this promise is rejected with the provided error type. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + */ + func recover(_ only: E.Type, on: Dispatcher = conf.D.map, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> V) -> CancellablePromise where V.U.T == C.T { + let cancelItemList = CancelItemList() + + let cancelBody = { (error: E) throws -> V.U in + _ = self.cancelContext.removeItems(self.cancelItemList, clearList: true) + let rval = try body(error) + if policy == .allErrors || error.isCancelled { + self.cancelContext.recover() + } + self.cancelContext.append(context: rval.cancelContext, thenableCancelItemList: cancelItemList) + return rval.thenable + } + + let promise = self.catchable.recover(only, on: on, policy: policy, cancelBody) + if thenable.result != nil && policy == .allErrors { + self.cancelContext.recover() + } + return CancellablePromise(promise: promise, context: self.cancelContext, cancelItemList: cancelItemList) + } + + /** + The provided closure executes when this cancellable promise rejects with an error of the type passed in. + + Unlike `catch`, `recover` continues the chain. + Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: + + firstly { + API.fetchData() + }.recover(FetchError.self) { error in + guard case .missingImage(let partialData) = error else { throw error } + //… + return Promise.value(dataWithDefaultImage) + } + + - Parameter only: The error type to be recovered. + - Parameter on: The queue to which the provided closure dispatches. + - Parameter body: The handler to execute if this promise is rejected with the provided error type. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + */ + func recover(_ only: E.Type, on: Dispatcher = conf.D.map, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> V) -> CancellablePromise where V.T == C.T { + let cancelBody = { (error: E) throws -> V in + _ = self.cancelContext.removeItems(self.cancelItemList, clearList: true) + let rval = try body(error) + if policy == .allErrors { + self.cancelContext.recover() + } + return rval + } + + let promise = self.catchable.recover(only, on: on, policy: policy, cancelBody) + if thenable.result != nil && policy == .allErrors { + self.cancelContext.recover() + } + let cancellablePromise = CancellablePromise(promise: promise, context: self.cancelContext) + if let cancellable = promise.cancellable { + self.cancelContext.append(cancellable: cancellable, reject: promise.rejectIfCancelled, thenable: cancellablePromise) + } + return cancellablePromise + } + /** The provided closure executes when this cancellable promise resolves, whether it rejects or not. @@ -298,4 +554,57 @@ public extension CancellableCatchMixin where C.T == Void { } return CancellablePromise(promise: promise, context: self.cancelContext) } + + + /** + The provided closure executes when this cancellable promise rejects with the specific error passed in. + + Unlike `catch`, `recover` continues the chain. + Use `recover` in circumstances where recovering the chain from certain errors is a possibility. + + - Parameter only: The specific error to be recovered. + - Parameter on: The queue to which the provided closure dispatches. + - Parameter body: The handler to execute if this promise is rejected with the provided error. + - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + */ + func recover(_ only: E, on: Dispatcher = conf.D.map, _ body: @escaping() -> Void) -> CancellablePromise where E: Equatable { + let cancelBody = { () -> Void in + _ = self.cancelContext.removeItems(self.cancelItemList, clearList: true) + body() + } + + let promise = self.catchable.recover(only, on: on, cancelBody) + if thenable.result != nil && only.isCancelled { + self.cancelContext.recover() + } + return CancellablePromise(promise: promise, context: self.cancelContext) + } + + /** + The provided closure executes when this cancellable promise rejects with an error of the type passed in. + + Unlike `catch`, `recover` continues the chain. + Use `recover` in circumstances where recovering the chain from certain errors is a possibility. + + - Parameter only: The error type to be recovered. + - Parameter on: The queue to which the provided closure dispatches. + - Parameter body: The handler to execute if this promise is rejected with the provided error type. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + */ + func recover(_ only: E.Type, on: Dispatcher = conf.D.map, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> Void) -> CancellablePromise { + let cancelBody = { (error: E) throws -> Void in + _ = self.cancelContext.removeItems(self.cancelItemList, clearList: true) + try body(error) + if policy == .allErrors || error.isCancelled { + self.cancelContext.recover() + } + } + + let promise = self.catchable.recover(only, on: on, policy: policy, cancelBody) + if thenable.result != nil && policy == .allErrors { + self.cancelContext.recover() + } + return CancellablePromise(promise: promise, context: self.cancelContext) + } } diff --git a/Sources/Dispatcher.swift b/Sources/Dispatcher.swift index 66c465b38..bc38d8200 100644 --- a/Sources/Dispatcher.swift +++ b/Sources/Dispatcher.swift @@ -884,6 +884,43 @@ public extension CancellableCatchMixin { return `catch`(on: dispatcher, policy: policy, body) } + /** + The provided closure executes when this cancellable promise rejects with the specific error passed in. A final `catch` is still required at the end of the chain. + + Rejecting a promise cascades: rejecting all subsequent promises (unless + recover is invoked) thus you will typically place your catch at the end + of a chain. Often utility promises will not have a catch, instead + delegating the error handling to the caller. + + - Parameter only: The specific error to be caught and handled. + - Parameter on: The queue to which the provided closure dispatches. + - Parameter execute: The handler to execute if this promise is rejected with the provided error. + - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + */ + func `catch`(_ only: E, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping() -> Void) -> CancellableCascadingFinalizer where E: Equatable { + let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) + return `catch`(only, on: dispatcher, body) + } + + /** + The provided closure executes when this promise rejects with an error of the type passed in. A final `catch` is still required at the end of the chain. + + Rejecting a promise cascades: rejecting all subsequent promises (unless + recover is invoked) thus you will typically place your catch at the end + of a chain. Often utility promises will not have a catch, instead + delegating the error handling to the caller. + + - Parameter only: The error type to be caught and handled. + - Parameter on: The queue to which the provided closure dispatches. + - Parameter execute: The handler to execute if this promise is rejected with the provided error type. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + */ + func `catch`(_ only: E.Type, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) -> Void) -> CancellableCascadingFinalizer { + let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) + return `catch`(only, on: dispatcher, policy: policy, body) + } + /** The provided closure executes when this cancellable promise rejects. @@ -932,11 +969,104 @@ public extension CancellableCatchMixin { - Parameter policy: The default policy does not execute your handler for cancellation errors. - Parameter body: The handler to execute if this promise is rejected. - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) - - Note: Methods with the `cancellable` prefix create a new CancellablePromise, and those without the `cancellable` prefix accept an existing CancellablePromise. */ func recover(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) throws -> V) -> CancellablePromise where V.T == C.T { let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return recover(on: dispatcher, body) + return recover(on: dispatcher, policy: policy ,body) + } + + /** + The provided closure executes when this cancellable promise rejects with the specific error passed in. + + Unlike `catch`, `recover` continues the chain. + Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: + + firstly { + CLLocationManager.requestLocation() + }.recover(CLError.unknownLocation) { + return .value(CLLocation.chicago) + } + + - Parameter only: The specific error to be recovered. + - Parameter on: The queue to which the provided closure dispatches. + - Parameter body: The handler to execute if this promise is rejected with the provided error. + - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + */ + func recover(_ only: E, on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping() -> V) -> CancellablePromise where V.U.T == C.T, E: Equatable { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return recover(only, on: dispatcher, body) + } + + /** + The provided closure executes when this cancellable promise rejects with the specific error passed in. + + Unlike `catch`, `recover` continues the chain. + Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: + + firstly { + CLLocationManager.requestLocation() + }.recover(CLError.unknownLocation) { + return Promise.value(CLLocation.chicago) + } + + - Parameter only: The specific error to be recovered. + - Parameter on: The queue to which the provided closure dispatches. + - Parameter body: The handler to execute if this promise is rejected with the provided error. + - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + */ + func recover(_ only: E, on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping() -> V) -> CancellablePromise where V.T == C.T, E: Equatable { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return recover(only, on: dispatcher, body) + } + + /** + The provided closure executes when this cancellable promise rejects with an error of the type passed in. + + Unlike `catch`, `recover` continues the chain. + Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: + + firstly { + API.fetchData() + }.recover(FetchError.self) { error in + guard case .missingImage(let partialData) = error else { throw error } + //… + return .value(dataWithDefaultImage) + } + + - Parameter only: The error type to be recovered. + - Parameter on: The queue to which the provided closure dispatches. + - Parameter body: The handler to execute if this promise is rejected with the provided error type. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + */ + func recover(_ only: E.Type, on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> V) -> CancellablePromise where V.U.T == C.T { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return recover(only, on: dispatcher, policy: policy, body) + } + + /** + The provided closure executes when this cancellable promise rejects with an error of the type passed in. + + Unlike `catch`, `recover` continues the chain. + Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: + + firstly { + API.fetchData() + }.recover(FetchError.self) { error in + guard case .missingImage(let partialData) = error else { throw error } + //… + return Promise.value(dataWithDefaultImage) + } + + - Parameter only: The error type to be recovered. + - Parameter on: The queue to which the provided closure dispatches. + - Parameter body: The handler to execute if this promise is rejected with the provided error type. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + */ + func recover(_ only: E.Type, on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> V) -> CancellablePromise where V.T == C.T { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return recover(only, on: dispatcher, policy: policy, body) } /** @@ -1003,6 +1133,65 @@ public extension CancellableFinalizer { } } +public extension CancellableCascadingFinalizer { + /** + The provided closure executes when this promise rejects. + + Rejecting a promise cascades: rejecting all subsequent promises (unless + recover is invoked) thus you will typically place your catch at the end + of a chain. Often utility promises will not have a catch, instead + delegating the error handling to the caller. + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter policy: The default policy does not execute your handler for cancellation errors. + - Parameter execute: The handler to execute if this promise is rejected. + - Returns: A promise finalizer. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + */ + @discardableResult + func `catch`(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) -> Void) -> CancellableFinalizer { + let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) + return `catch`(on: dispatcher, policy: policy, body) + } + + /** + The provided closure executes when this promise rejects with the specific error passed in. A final `catch` is still required at the end of the chain. + + Rejecting a promise cascades: rejecting all subsequent promises (unless + recover is invoked) thus you will typically place your catch at the end + of a chain. Often utility promises will not have a catch, instead + delegating the error handling to the caller. + + - Parameter only: The specific error to be caught and handled. + - Parameter on: The queue to which the provided closure dispatches. + - Parameter execute: The handler to execute if this promise is rejected with the provided error. + - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + */ + func `catch`(_ only: E, on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping() -> Void) -> CancellableCascadingFinalizer where E: Equatable { + let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) + return `catch`(only, on: dispatcher, body) + } + + /** + The provided closure executes when this promise rejects with an error of the type passed in. A final `catch` is still required at the end of the chain. + + Rejecting a promise cascades: rejecting all subsequent promises (unless + recover is invoked) thus you will typically place your catch at the end + of a chain. Often utility promises will not have a catch, instead + delegating the error handling to the caller. + + - Parameter only: The error type to be caught and handled. + - Parameter on: The queue to which the provided closure dispatches. + - Parameter execute: The handler to execute if this promise is rejected with the provided error type. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + */ + func `catch`(_ only: E.Type, on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) -> Void) -> CancellableCascadingFinalizer { + let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) + return `catch`(only, on: dispatcher, policy: policy, body) + } +} + public extension CancellableCatchMixin where C.T == Void { /** The provided closure executes when this cancellable promise rejects. @@ -1018,4 +1207,37 @@ public extension CancellableCatchMixin where C.T == Void { let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) return recover(on: dispatcher, policy: policy, body) } + + /** + The provided closure executes when this cancellable promise rejects with the specific error passed in. + + Unlike `catch`, `recover` continues the chain. + Use `recover` in circumstances where recovering the chain from certain errors is a possibility. + + - Parameter only: The specific error to be recovered. + - Parameter on: The queue to which the provided closure dispatches. + - Parameter body: The handler to execute if this promise is rejected with the provided error. + - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + */ + func recover(_ only: E, on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping() -> Void) -> CancellablePromise where E: Equatable { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return recover(only, on: dispatcher, body) + } + + /** + The provided closure executes when this cancellable promise rejects with an error of the type passed in. + + Unlike `catch`, `recover` continues the chain. + Use `recover` in circumstances where recovering the chain from certain errors is a possibility. + + - Parameter only: The error type to be recovered. + - Parameter on: The queue to which the provided closure dispatches. + - Parameter body: The handler to execute if this promise is rejected with the provided error type. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + */ + func recover(_ only: E.Type, on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> Void) -> CancellablePromise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return recover(only, on: dispatcher, policy: policy, body) + } } diff --git a/Tests/Cancel/CatchableTests.swift b/Tests/Cancel/CatchableTests.swift index fc4520986..878515d2e 100644 --- a/Tests/Cancel/CatchableTests.swift +++ b/Tests/Cancel/CatchableTests.swift @@ -385,6 +385,503 @@ extension CatchableTests { } } +/// `Promise.catch(_ only:)` +extension CatchableTests { + func testCatchOnly() { + let x = expectation(description: #file + #function) + + Promise(error: Error.dummy).cancellize().catch(Error.dummy) { + x.fulfill() + }.catch(policy: .allErrors) { + $0.isCancelled ? x.fulfill() : XCTFail() + }.cancel() + + wait(for: [x], timeout: 5) + } + + func testCatchOnly_PatternMatch_1() { + let x = expectation(description: "Pattern match only Error.dummy") + + Promise(error: Error.dummy).cancellize().catch(Error.dummy) { + x.fulfill() + }.catch(Error.cancelled) { + XCTFail() + x.fulfill() + }.catch(policy: .allErrors) { + $0.isCancelled ? x.fulfill() : XCTFail() + }.cancel() + + wait(for: [x], timeout: 5) + } + + func testCatchOnly_PatternMatch_2() { + let x = expectation(description: "Pattern match only Error.dummy") + + Promise(error: Error.dummy).cancellize().catch(Error.cancelled) { + XCTFail() + x.fulfill() + }.catch(Error.dummy) { + x.fulfill() + }.catch(policy: .allErrors) { + $0.isCancelled ? x.fulfill() : XCTFail() + }.cancel() + + wait(for: [x], timeout: 5) + } + + func testCatchOnly_BaseCatchIsNotCalledAfterCatchOnlyExecutes() { + let x = expectation(description: #file + #function) + + Promise(error: Error.dummy).cancellize().catch(Error.dummy) { + x.fulfill() + }.catch { _ in + XCTFail() + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testCatchOnly_BaseCatchIsCalledWhenCatchOnlyDoesNotExecute() { + let x = expectation(description: #file + #function) + + Promise(error: Error.dummy).cancellize().catch(Error.cancelled) { + XCTFail() + x.fulfill() + }.catch { _ in + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testCatchOnly_Type() { + let x = expectation(description: #file + #function) + + Promise(error: Error.dummy).cancellize().catch(Error.self) { _ in + x.fulfill() + }.catch(policy: .allErrors) { + $0.isCancelled ? x.fulfill() : XCTFail() + }.cancel() + + wait(for: [x], timeout: 5) + } + + func testCatchOnly_Type_Ignored() { + let x = expectation(description: #file + #function) + + enum Foo: Swift.Error {} + + Promise(error: Error.dummy).cancellize().catch(Foo.self) { _ in + XCTFail() + x.fulfill() + }.catch { _ in + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testCatchOnly_Type_PatternMatch_1() { + let x = expectation(description: "Pattern match only Error.Type") + + Promise(error: Error.dummy).cancellize().catch(Error.self) { _ in + x.fulfill() + }.catch(Error.dummy) { + XCTFail() + x.fulfill() + }.catch(policy: .allErrors) { + $0.isCancelled ? x.fulfill() : XCTFail() + }.cancel() + + wait(for: [x], timeout: 5) + } + + func testCatchOnly_Type_PatternMatch_2() { + let x = expectation(description: "Pattern match only Error.dummy") + + Promise(error: Error.dummy).cancellize().catch(Error.dummy) { + x.fulfill() + }.catch(Error.self) { _ in + XCTFail() + x.fulfill() + }.catch(policy: .allErrors) { + $0.isCancelled ? x.fulfill() : XCTFail() + }.cancel() + + wait(for: [x], timeout: 5) + } + + func testCatchOnly_Type_BaseCatchIsNotCalledAfterCatchOnlyExecutes() { + let x = expectation(description: #file + #function) + + Promise(error: Error.dummy).cancellize().catch(Error.self) { _ in + x.fulfill() + }.catch { _ in + XCTFail() + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testCatchOnly_Type_Cancellation_Ignore() { + let x = expectation(description: #file + #function) + + Promise(error: Error.cancelled).cancellize().catch(Error.self) { _ in + XCTFail() + x.fulfill() + }.catch(policy: .allErrors) { _ in + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testCatchOnly_Type_Cancellation_Handle() { + let x = expectation(description: #file + #function) + + Promise(error: Error.cancelled).cancellize().catch(Error.self, policy: .allErrors) { _ in + x.fulfill() + }.catch { _ in + XCTFail() + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testCatchOnly_Mixed() { + let x = expectation(description: #file + #function) + + enum Foo: Swift.Error { case bar } + + Promise(error: Foo.bar).cancellize().catch(Error.dummy) { + XCTFail() + x.fulfill() + }.catch(Foo.self) { _ in + x.fulfill() + }.catch(policy: .allErrors) { + $0.isCancelled ? x.fulfill() : XCTFail() + }.cancel() + + wait(for: [x], timeout: 5) + } +} + +/// `Promise.recover(_ only:)` +extension CatchableTests { + func testRecoverOnly_Object() { + let x = expectation(description: #file + #function) + + Promise(error: Error.dummy).cancellize().recover(Error.dummy) { + return Promise.value(1) + }.done { _ in + x.fulfill() + }.catch { _ in + XCTFail() + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_Object_Ignored() { + let x = expectation(description: #file + #function) + + Promise.value(1).cancellize().recover(Error.dummy) { + return Promise(error: Error.dummy) + }.done { _ in + x.fulfill() + }.catch(policy: .allErrors) { + $0.isCancelled ? x.fulfill() : XCTFail() + }.cancel() + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_Object_PatternMatch() { + let x = expectation(description: #file + #function) + + Promise(error: Error.cancelled).cancellize().recover(Error.dummy) { + return Promise.value(1) + }.done { _ in + XCTFail() + x.fulfill() + }.catch(policy: .allErrors) { _ in + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_Type() { + let x = expectation(description: #file + #function) + + Promise(error: Error.dummy).cancellize().recover(Error.self) { _ in + return Promise.value(1) + }.done { _ in + x.fulfill() + }.catch(policy: .allErrors) { + $0.isCancelled ? x.fulfill() : XCTFail() + }.cancel() + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_Type_Ignored() { + let x = expectation(description: #file + #function) + + Promise.value(1).cancellize().recover(Error.self) { _ in + return Promise(error: Error.dummy) + }.done { _ in + x.fulfill() + }.catch { _ in + XCTFail() + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_Type_PatternMatch() { + let x = expectation(description: #file + #function) + + enum Foo: Swift.Error {} + + Promise(error: Error.dummy).cancellize().recover(Foo.self) { _ in + return Promise.value(1) + }.done { _ in + XCTFail() + x.fulfill() + }.catch { _ in + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_Type_Cancellation_Ignore() { + let x = expectation(description: #file + #function) + + Promise(error: Error.cancelled).cancellize().recover(Error.self) { _ in + return Promise.value(1) + }.done { _ in + XCTFail() + x.fulfill() + }.catch(policy: .allErrors) { _ in + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_Type_Cancellation_Handle() { + let x = expectation(description: #file + #function) + + Promise(error: Error.cancelled).cancellize().recover(Error.self, policy: .allErrors) { _ in + return Promise.value(1) + }.done { _ in + x.fulfill() + }.catch { _ in + XCTFail() + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_Chaining() { + let x = expectation(description: #file + #function) + + enum Foo: Swift.Error { case bar } + + Promise(error: Error.dummy).cancellize().recover(Foo.self) { _ in + return Promise(error: Foo.bar) + }.recover(Error.dummy) { + return Promise.value(1) + }.done { _ in + x.fulfill() + }.catch { _ in + XCTFail() + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_BaseRecoverIsNotCalledAfterRecoverOnlyExecutes() { + let x = expectation(description: #file + #function) + + Promise(error: Error.dummy).cancellize().recover(Error.dummy) { + return Promise.value(1) + }.recover { _ in + return Promise(error: Error.dummy) + }.done { _ in + x.fulfill() + }.catch { _ in + XCTFail() + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_Object_DoesNotReturnSelf() { + let x = expectation(description: #file + #function) + var promise: CancellablePromise! + promise = Promise(error: Error.dummy).cancellize().recover(Error.dummy) { () -> CancellablePromise in + return promise + } + promise.catch { err in + if case PMKError.returnedSelf = err { + x.fulfill() + } + } + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_Type_DoesNotReturnSelf() { + let x = expectation(description: #file + #function) + var promise: CancellablePromise! + promise = Promise(error: Error.dummy).cancellize().recover(Error.self) { _ -> CancellablePromise in + return promise + } + promise.catch(policy: .allErrors) { err in + err.isCancelled ? x.fulfill() : XCTFail() + } + promise.cancel() + + wait(for: [x], timeout: 5) + } +} + +/// `Promise.recover(_ only:)` +extension CatchableTests { + func testRecoverOnly_Object_Void() { + let x = expectation(description: #file + #function) + + Promise(error: Error.dummy).cancellize().recover(Error.dummy) { + return () + }.done { + x.fulfill() + }.catch { _ in + XCTFail() + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_Object_Void_Fufilled() { + let x = expectation(description: #file + #function) + + Promise.value(()).cancellize().recover(Error.dummy) { + XCTFail() + x.fulfill() + }.done { + x.fulfill() + }.catch(policy: .allErrors) { + $0.isCancelled ? x.fulfill() : XCTFail() + }.cancel() + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_Object_Void_Ignored() { + let x = expectation(description: #file + #function) + + enum Foo: Swift.Error { case bar } + + Promise(error: Error.dummy).cancellize().recover(Foo.bar) { + XCTFail() + x.fulfill() + }.done { + XCTFail() + x.fulfill() + }.catch { _ in + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_Type_Void() { + let x = expectation(description: #file + #function) + + Promise(error: Error.dummy).cancellize().recover(Error.self) { _ in }.done { + x.fulfill() + }.catch { _ in + XCTFail() + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_Type_Void_Fufilled() { + let x = expectation(description: #file + #function) + + Promise.value(()).cancellize().recover(Error.self) { _ in + XCTFail() + x.fulfill() + }.done { + x.fulfill() + }.catch(policy: .allErrors) { + $0.isCancelled ? x.fulfill() : XCTFail() + }.cancel() + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_Type_Void_Ignored() { + let x = expectation(description: #file + #function) + + enum Foo: Swift.Error { case bar } + + Promise(error: Error.dummy).cancellize().recover(Foo.self) { _ in + XCTFail() + x.fulfill() + }.done { + XCTFail() + x.fulfill() + }.catch { _ in + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_Type_Void_Rethrow() { + let x = expectation(description: #file + #function) + + Promise(error: Error.dummy).cancellize().recover(Error.self) { _ in + throw Error.dummy + }.done { + XCTFail() + x.fulfill() + }.catch { _ in + x.fulfill() + } + + wait(for: [x], timeout: 5) + } + + func testRecoverOnly_Type_Void_Cancellation_Ignore() { + let x = expectation(description: #file + #function) + + Promise(error: Error.cancelled).cancellize().recover(Error.self) { _ in }.done { + XCTFail() + x.fulfill() + }.catch(policy: .allErrors) { _ in + x.fulfill() + } + + wait(for: [x], timeout: 5) + } +} + private enum Error: CancellableError { case dummy case cancelled diff --git a/Tests/Cancel/DispatcherTests.swift b/Tests/Cancel/DispatcherTests.swift index 828739190..763528bac 100644 --- a/Tests/Cancel/DispatcherTests.swift +++ b/Tests/Cancel/DispatcherTests.swift @@ -140,39 +140,304 @@ class DispatcherTests: XCTestCase { XCTFail() } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testRecover() { - let ex1 = expectation(description: "DispatchQueue CatchMixin compatibility") - Promise.value(42).cancellize().recover(on: .global(qos: .background), flags: .barrier) { _ in - Promise.value(42) + let ex1 = expectation(description: "DispatchQueue CatchMixin recover cancellable") + Promise(error: Error.dummy).cancellize().recover(on: .global(qos: .background), flags: .barrier) { _ in + Promise.value(42).cancellize() }.ensure(on: .global(qos: .background), flags: .barrier) { }.ensureThen(on: .global(qos: .background), flags: .barrier) { Promise.value(42).asVoid().cancellize() - }.recover(on: .global(qos: .background), flags: .barrier) { _ in + }.done(on: .global(qos: .background), flags: .barrier) { + XCTAssertEqual($0, 42) + ex1.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier) { _ in + XCTFail() + } + + let ex2 = expectation(description: "DispatchQueue CatchMixin recover standard") + Promise(error: Error.dummy).cancellize().recover(on: .global(qos: .background), flags: .barrier) { _ in + Promise.value(42) + }.done(on: .global(qos: .background), flags: .barrier) { + XCTAssertEqual($0, 42) + ex2.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier) { _ in + XCTFail() + } + + let ex3 = expectation(description: "DispatchQueue CatchMixin recover void standard") + Promise(error: Error.dummy).cancellize().recover(on: .global(qos: .background), flags: .barrier) { _ in + }.done(on: .global(qos: .background), flags: .barrier) { + ex3.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier) { _ in + XCTFail() + } + + waitForExpectations(timeout: 1) + } + + func testRecoverIsCancelled() { + let ex1 = expectation(description: "DispatchQueue CatchMixin recover cancellable isCancelled") + Promise(error: Error.cancelled).cancellize().recover(on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in Promise.value(42).cancellize() - }.recover(on: .global(qos: .background), flags: .barrier) { _ in + }.ensure(on: .global(qos: .background), flags: .barrier) { + }.ensureThen(on: .global(qos: .background), flags: .barrier) { + Promise.value(42).asVoid().cancellize() + }.done(on: .global(qos: .background), flags: .barrier) { + XCTAssertEqual($0, 42) + ex1.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + XCTFail() + } + + let ex2 = expectation(description: "DispatchQueue CatchMixin recover standard isCancelled") + Promise(error: Error.cancelled).cancellize().recover(on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in Promise.value(42) }.done(on: .global(qos: .background), flags: .barrier) { XCTAssertEqual($0, 42) + ex2.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + XCTFail() + } + + let ex3 = expectation(description: "DispatchQueue CatchMixin recover void standard isCancelled") + Promise(error: Error.cancelled).cancellize().recover(on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + }.done(on: .global(qos: .background), flags: .barrier) { + ex3.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + XCTFail() + } + + waitForExpectations(timeout: 1) + } + + func testCatchOnly() { + let ex1 = expectation(description: "DispatchQueue CatchMixin catch-only") + Promise(error: Error.dummy).cancellize().done(on: .global(qos: .background), flags: .barrier) { + XCTFail() + }.catch(Error.dummy, on: .global(qos: .background), flags: .barrier) { ex1.fulfill() }.catch(on: .global(qos: .background), flags: .barrier) { _ in XCTFail() } - let ex2 = expectation(description: "DispatchQueue CatchMixin Void recover") - firstly { - Promise.value(42).asVoid() - }.cancellize().recover(on: .global(qos: .background), flags: .barrier) { _ in - }.done { + let ex2 = expectation(description: "DispatchQueue CatchMixin catch-type") + Promise(error: Error.dummy).cancellize().done(on: .global(qos: .background), flags: .barrier) { + XCTFail() + }.catch(Error.self, on: .global(qos: .background), flags: .barrier) { _ in ex2.fulfill() }.catch(on: .global(qos: .background), flags: .barrier) { _ in XCTFail() } - + + let ex3 = expectation(description: "DispatchQueue CascadingFinalizer catch") + Promise(error: Error.dummy).cancellize().done(on: .global(qos: .background), flags: .barrier) { + XCTFail() + }.catch(Error.cancelled, on: .global(qos: .background), flags: .barrier) { + XCTFail() + }.catch(on: .global(qos: .background), flags: .barrier) { _ in + ex3.fulfill() + } + + let ex4 = expectation(description: "DispatchQueue CascadingFinalizer catch-only") + Promise(error: Error.dummy).cancellize().done(on: .global(qos: .background), flags: .barrier) { + XCTFail() + }.catch(Error.cancelled, on: .global(qos: .background), flags: .barrier) { + XCTFail() + }.catch(Error.dummy, on: .global(qos: .background), flags: .barrier) { + ex4.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier) { _ in + XCTFail() + } + + let ex5 = expectation(description: "DispatchQueue CascadingFinalizer catch-type") + Promise(error: Error.dummy).cancellize().done(on: .global(qos: .background), flags: .barrier) { + XCTFail() + }.catch(Error.cancelled, on: .global(qos: .background), flags: .barrier) { + XCTFail() + }.catch(Error.self, on: .global(qos: .background), flags: .barrier) { _ in + ex5.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier) { _ in + XCTFail() + } + waitForExpectations(timeout: 1) } + + func testCatchOnlyIsCancelled() { + let ex1 = expectation(description: "DispatchQueue CatchMixin catch-only isCancelled") + Promise(error: Error.cancelled).cancellize().done(on: .global(qos: .background), flags: .barrier) { + XCTFail() + }.catch(Error.cancelled, on: .global(qos: .background), flags: .barrier) { + ex1.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + XCTFail() + } + + let ex2 = expectation(description: "DispatchQueue CatchMixin catch-type isCancelled") + Promise(error: Error.cancelled).cancellize().done(on: .global(qos: .background), flags: .barrier) { + XCTFail() + }.catch(Error.self, on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + ex2.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + XCTFail() + } + + let ex3 = expectation(description: "DispatchQueue CascadingFinalizer catch isCancelled") + Promise(error: Error.cancelled).cancellize().done(on: .global(qos: .background), flags: .barrier) { + XCTFail() + }.catch(Error.cancelled, on: .global(qos: .background), flags: .barrier) { + ex3.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + XCTFail() + } + + let ex4 = expectation(description: "DispatchQueue CascadingFinalizer catch-only isCancelled") + Promise(error: Error.cancelled).cancellize().done(on: .global(qos: .background), flags: .barrier) { + XCTFail() + }.catch(Error.dummy, on: .global(qos: .background), flags: .barrier) { + XCTFail() + }.catch(Error.cancelled, on: .global(qos: .background), flags: .barrier) { + ex4.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + XCTFail() + } + + let ex5 = expectation(description: "DispatchQueue CascadingFinalizer catch-type isCancelled") + Promise(error: Error.cancelled).cancellize().done(on: .global(qos: .background), flags: .barrier) { + XCTFail() + }.catch(Error.dummy, on: .global(qos: .background), flags: .barrier) { + XCTFail() + }.catch(Error.self, on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + ex5.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + XCTFail() + } + + waitForExpectations(timeout: 1) + } + + func testRecoverOnly() { + let ex1 = expectation(description: "DispatchQueue CatchMixin recover-only cancellable") + Promise(error: Error.dummy).cancellize().recover(Error.dummy, on: .global(qos: .background), flags: .barrier) { + Promise.value(42).cancellize() + }.done(on: .global(qos: .background), flags: .barrier) { + XCTAssertEqual($0, 42) + ex1.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier) { _ in + XCTFail() + } + + let ex2 = expectation(description: "DispatchQueue CatchMixin recover-only standard") + Promise(error: Error.dummy).cancellize().recover(Error.dummy, on: .global(qos: .background), flags: .barrier) { + Promise.value(42) + }.done(on: .global(qos: .background), flags: .barrier) { + XCTAssertEqual($0, 42) + ex2.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier) { _ in + XCTFail() + } + + let ex3 = expectation(description: "DispatchQueue CatchMixin recover-type cancellable") + Promise(error: Error.dummy).cancellize().recover(Error.self, on: .global(qos: .background), flags: .barrier) { _ in + Promise.value(42).cancellize() + }.done(on: .global(qos: .background), flags: .barrier) { + XCTAssertEqual($0, 42) + ex3.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier) { _ in + XCTFail() + } + + let ex4 = expectation(description: "DispatchQueue CatchMixin recover-type standard") + Promise(error: Error.dummy).cancellize().recover(Error.self, on: .global(qos: .background), flags: .barrier) { _ in + Promise.value(42) + }.done(on: .global(qos: .background), flags: .barrier) { + XCTAssertEqual($0, 42) + ex4.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier) { _ in + XCTFail() + } + + let ex5 = expectation(description: "DispatchQueue CatchMixin recover-only-void cancellable") + Promise(error: Error.dummy).cancellize().recover(Error.dummy, on: .global(qos: .background), flags: .barrier) { + }.done(on: .global(qos: .background), flags: .barrier) { + ex5.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier) { _ in + XCTFail() + } + + let ex6 = expectation(description: "DispatchQueue CatchMixin recover-type-void standard") + Promise(error: Error.dummy).cancellize().recover(Error.self, on: .global(qos: .background), flags: .barrier) { _ in + }.done(on: .global(qos: .background), flags: .barrier) { + ex6.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier) { _ in + XCTFail() + } + + waitForExpectations(timeout: 1) + } + + func testRecoverOnlyIsCancelled() { + let ex1 = expectation(description: "DispatchQueue CatchMixin recover-only cancellable isCancelled") + Promise(error: Error.cancelled).cancellize().recover(Error.cancelled, on: .global(qos: .background), flags: .barrier) { + Promise.value(42).cancellize() + }.done(on: .global(qos: .background), flags: .barrier) { + XCTAssertEqual($0, 42) + ex1.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier) { _ in + XCTFail() + } + + let ex2 = expectation(description: "DispatchQueue CatchMixin recover-only standard isCancelled") + Promise(error: Error.cancelled).cancellize().recover(Error.cancelled, on: .global(qos: .background), flags: .barrier) { + Promise.value(42) + }.done(on: .global(qos: .background), flags: .barrier) { + XCTAssertEqual($0, 42) + ex2.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier) { _ in + XCTFail() + } + + let ex3 = expectation(description: "DispatchQueue CatchMixin recover-type cancellable isCancelled") + Promise(error: Error.cancelled).cancellize().recover(Error.self, on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + Promise.value(42).cancellize() + }.done(on: .global(qos: .background), flags: .barrier) { + XCTAssertEqual($0, 42) + ex3.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier) { _ in + XCTFail() + } + + let ex4 = expectation(description: "DispatchQueue CatchMixin recover-type standard isCancelled") + Promise(error: Error.cancelled).cancellize().recover(Error.self, on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + Promise.value(42) + }.done(on: .global(qos: .background), flags: .barrier) { + XCTAssertEqual($0, 42) + ex4.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier) { _ in + XCTFail() + } + + let ex5 = expectation(description: "DispatchQueue CatchMixin recover-only-void cancellable isCancelled") + Promise(error: Error.cancelled).cancellize().recover(Error.cancelled, on: .global(qos: .background), flags: .barrier) { + }.done(on: .global(qos: .background), flags: .barrier) { + ex5.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier) { _ in + XCTFail() + } + + let ex6 = expectation(description: "DispatchQueue CatchMixin recover-type-void standard isCancelled") + Promise(error: Error.cancelled).cancellize().recover(Error.self, on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + }.done(on: .global(qos: .background), flags: .barrier) { + ex6.fulfill() + }.catch(on: .global(qos: .background), flags: .barrier) { _ in + XCTFail() + } + + waitForExpectations(timeout: 20) + } @available(macOS 10.10, iOS 2.0, tvOS 10.0, watchOS 2.0, *) func testDispatcherExtensionReturnsGuarantee() { @@ -203,3 +468,12 @@ class DispatcherTests: XCTestCase { } } + +private enum Error: CancellableError { + case dummy + case cancelled + + var isCancelled: Bool { + return self == Error.cancelled + } +} diff --git a/Tests/Cancel/XCTestManifests.swift b/Tests/Cancel/XCTestManifests.swift index cf90f8da5..e9f1aa98b 100644 --- a/Tests/Cancel/XCTestManifests.swift +++ b/Tests/Cancel/XCTestManifests.swift @@ -100,11 +100,44 @@ extension CatchableTests { ("test__void_specialized_full_recover__fulfilled_path", test__void_specialized_full_recover__fulfilled_path), ("testCancellableFinalizerHelpers", testCancellableFinalizerHelpers), ("testCancellableRecoverFromError", testCancellableRecoverFromError), + ("testCatchOnly", testCatchOnly), + ("testCatchOnly_BaseCatchIsCalledWhenCatchOnlyDoesNotExecute", testCatchOnly_BaseCatchIsCalledWhenCatchOnlyDoesNotExecute), + ("testCatchOnly_BaseCatchIsNotCalledAfterCatchOnlyExecutes", testCatchOnly_BaseCatchIsNotCalledAfterCatchOnlyExecutes), + ("testCatchOnly_Mixed", testCatchOnly_Mixed), + ("testCatchOnly_PatternMatch_1", testCatchOnly_PatternMatch_1), + ("testCatchOnly_PatternMatch_2", testCatchOnly_PatternMatch_2), + ("testCatchOnly_Type", testCatchOnly_Type), + ("testCatchOnly_Type_BaseCatchIsNotCalledAfterCatchOnlyExecutes", testCatchOnly_Type_BaseCatchIsNotCalledAfterCatchOnlyExecutes), + ("testCatchOnly_Type_Cancellation_Handle", testCatchOnly_Type_Cancellation_Handle), + ("testCatchOnly_Type_Cancellation_Ignore", testCatchOnly_Type_Cancellation_Ignore), + ("testCatchOnly_Type_Ignored", testCatchOnly_Type_Ignored), + ("testCatchOnly_Type_PatternMatch_1", testCatchOnly_Type_PatternMatch_1), + ("testCatchOnly_Type_PatternMatch_2", testCatchOnly_Type_PatternMatch_2), ("testCauterize", testCauterize), ("testEnsureThen_Error", testEnsureThen_Error), ("testEnsureThen_Value", testEnsureThen_Value), ("testEnsureThen_Value_NotCancelled", testEnsureThen_Value_NotCancelled), ("testFinally", testFinally), + ("testRecoverOnly_BaseRecoverIsNotCalledAfterRecoverOnlyExecutes", testRecoverOnly_BaseRecoverIsNotCalledAfterRecoverOnlyExecutes), + ("testRecoverOnly_Chaining", testRecoverOnly_Chaining), + ("testRecoverOnly_Object", testRecoverOnly_Object), + ("testRecoverOnly_Object_DoesNotReturnSelf", testRecoverOnly_Object_DoesNotReturnSelf), + ("testRecoverOnly_Object_Ignored", testRecoverOnly_Object_Ignored), + ("testRecoverOnly_Object_PatternMatch", testRecoverOnly_Object_PatternMatch), + ("testRecoverOnly_Object_Void", testRecoverOnly_Object_Void), + ("testRecoverOnly_Object_Void_Fufilled", testRecoverOnly_Object_Void_Fufilled), + ("testRecoverOnly_Object_Void_Ignored", testRecoverOnly_Object_Void_Ignored), + ("testRecoverOnly_Type", testRecoverOnly_Type), + ("testRecoverOnly_Type_Cancellation_Handle", testRecoverOnly_Type_Cancellation_Handle), + ("testRecoverOnly_Type_Cancellation_Ignore", testRecoverOnly_Type_Cancellation_Ignore), + ("testRecoverOnly_Type_DoesNotReturnSelf", testRecoverOnly_Type_DoesNotReturnSelf), + ("testRecoverOnly_Type_Ignored", testRecoverOnly_Type_Ignored), + ("testRecoverOnly_Type_PatternMatch", testRecoverOnly_Type_PatternMatch), + ("testRecoverOnly_Type_Void", testRecoverOnly_Type_Void), + ("testRecoverOnly_Type_Void_Cancellation_Ignore", testRecoverOnly_Type_Void_Cancellation_Ignore), + ("testRecoverOnly_Type_Void_Fufilled", testRecoverOnly_Type_Void_Fufilled), + ("testRecoverOnly_Type_Void_Ignored", testRecoverOnly_Type_Void_Ignored), + ("testRecoverOnly_Type_Void_Rethrow", testRecoverOnly_Type_Void_Rethrow), ] } @@ -113,12 +146,17 @@ extension DispatcherTests { // `swift test --generate-linuxmain` // to regenerate. static let __allTests__DispatcherTests = [ + ("testCatchOnly", testCatchOnly), + ("testCatchOnlyIsCancelled", testCatchOnlyIsCancelled), ("testDispatcherExtensionCanThrowInBody", testDispatcherExtensionCanThrowInBody), ("testDispatcherExtensionReturnsGuarantee", testDispatcherExtensionReturnsGuarantee), ("testDispatcherWithThrow", testDispatcherWithThrow), ("testDispatchQueueSelection", testDispatchQueueSelection), ("testMapValues", testMapValues), ("testRecover", testRecover), + ("testRecoverIsCancelled", testRecoverIsCancelled), + ("testRecoverOnly", testRecoverOnly), + ("testRecoverOnlyIsCancelled", testRecoverOnlyIsCancelled), ] } diff --git a/Tests/Core/DispatcherTests.swift b/Tests/Core/DispatcherTests.swift index b4288660b..dc1eb83f9 100644 --- a/Tests/Core/DispatcherTests.swift +++ b/Tests/Core/DispatcherTests.swift @@ -167,7 +167,7 @@ class DispatcherTests: XCTestCase { XCTFail() } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testRecover() { From 7600e18972c294859b9ba36b74a198009f42015f Mon Sep 17 00:00:00 2001 From: Max Howell Date: Mon, 25 Mar 2019 21:35:20 -0400 Subject: [PATCH 39/81] [travis] Swift 5 GM; Fix Linux compile --- .travis.yml | 2 +- Sources/hang.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1dbda4920..31cc6b05a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,7 +42,7 @@ jobs: xcode_destination: 'OS=12.2,name=Apple TV' - name: Linux - env: SWIFT_VERSION='5.0-DEVELOPMENT-SNAPSHOT-2019-01-22-a' + env: SWIFT_VERSION='5.0' os: linux language: generic osx_image: null diff --git a/Sources/hang.swift b/Sources/hang.swift index 80892e327..a4810400c 100644 --- a/Sources/hang.swift +++ b/Sources/hang.swift @@ -14,7 +14,7 @@ import CoreFoundation */ public func hang(_ promise: Promise) throws -> T { #if os(Linux) || os(Android) -#if swift(>=4.2) +#if compiler(>=5) let runLoopMode: CFRunLoopMode = kCFRunLoopDefaultMode #else // isMainThread is not yet implemented on Linux. From ed7c6bcdf18d220113d34b91ecd1b1d5f053e447 Mon Sep 17 00:00:00 2001 From: Max Howell Date: Sun, 7 Apr 2019 21:45:04 -0400 Subject: [PATCH 40/81] Add `Accio` to spelling skips --- .github/spelling-skip-words | 1 + Documents/Installation.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/spelling-skip-words b/.github/spelling-skip-words index 57c7f9b52..01eae0876 100644 --- a/.github/spelling-skip-words +++ b/.github/spelling-skip-words @@ -128,3 +128,4 @@ PromiseURLSessionresumewait unusedResult discardableResultcatchreturncauterize PMKFinalizercancelthenMapthenFlatMapthendonePromise +Accio diff --git a/Documents/Installation.md b/Documents/Installation.md index 13a66fc8c..098cdc63e 100644 --- a/Documents/Installation.md +++ b/Documents/Installation.md @@ -45,7 +45,7 @@ to Xcode 10.2 dropping support for Swift 3. ## Accio -Add the following to your Package.swift: +Add the following to your `Package.swift`: ```swift .package(url: "https://github.com/mxcl/PromiseKit.git", .upToNextMajor(from: "6.8.4")), From 3cfc3a7893dfc49d1901657f7da871258d787c34 Mon Sep 17 00:00:00 2001 From: Garth Snyder Date: Sun, 24 Mar 2019 17:37:57 -0700 Subject: [PATCH 41/81] Rename CancellableTask.swift to Cancellable.swift (match protocol) --- Sources/{CancellableTask.swift => Cancellable.swift} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Sources/{CancellableTask.swift => Cancellable.swift} (100%) diff --git a/Sources/CancellableTask.swift b/Sources/Cancellable.swift similarity index 100% rename from Sources/CancellableTask.swift rename to Sources/Cancellable.swift From 0ca0b8bc28a70b4c8b387b5ccbc34fa2f6a4104a Mon Sep 17 00:00:00 2001 From: Garth Snyder Date: Sun, 24 Mar 2019 17:39:19 -0700 Subject: [PATCH 42/81] Move cancellation-related implementation files to subdirectory --- Sources/{ => Cancellation}/CancelContext.swift | 0 Sources/{ => Cancellation}/Cancellable.swift | 0 Sources/{ => Cancellation}/CancellableCatchable.swift | 0 Sources/{ => Cancellation}/CancellablePromise.swift | 0 Sources/{ => Cancellation}/CancellableThenable.swift | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename Sources/{ => Cancellation}/CancelContext.swift (100%) rename Sources/{ => Cancellation}/Cancellable.swift (100%) rename Sources/{ => Cancellation}/CancellableCatchable.swift (100%) rename Sources/{ => Cancellation}/CancellablePromise.swift (100%) rename Sources/{ => Cancellation}/CancellableThenable.swift (100%) diff --git a/Sources/CancelContext.swift b/Sources/Cancellation/CancelContext.swift similarity index 100% rename from Sources/CancelContext.swift rename to Sources/Cancellation/CancelContext.swift diff --git a/Sources/Cancellable.swift b/Sources/Cancellation/Cancellable.swift similarity index 100% rename from Sources/Cancellable.swift rename to Sources/Cancellation/Cancellable.swift diff --git a/Sources/CancellableCatchable.swift b/Sources/Cancellation/CancellableCatchable.swift similarity index 100% rename from Sources/CancellableCatchable.swift rename to Sources/Cancellation/CancellableCatchable.swift diff --git a/Sources/CancellablePromise.swift b/Sources/Cancellation/CancellablePromise.swift similarity index 100% rename from Sources/CancellablePromise.swift rename to Sources/Cancellation/CancellablePromise.swift diff --git a/Sources/CancellableThenable.swift b/Sources/Cancellation/CancellableThenable.swift similarity index 100% rename from Sources/CancellableThenable.swift rename to Sources/Cancellation/CancellableThenable.swift From a64f4c24c83ea422574d7e515cd92c4dc652c6f3 Mon Sep 17 00:00:00 2001 From: Garth Snyder Date: Mon, 1 Apr 2019 21:26:27 -0700 Subject: [PATCH 43/81] Edit inline documentation for catchables --- .../Cancellation/CancellableCatchable.swift | 26 ++--- Sources/Catchable.swift | 95 +++++++++++-------- 2 files changed, 69 insertions(+), 52 deletions(-) diff --git a/Sources/Cancellation/CancellableCatchable.swift b/Sources/Cancellation/CancellableCatchable.swift index fe6775f31..77129105c 100644 --- a/Sources/Cancellation/CancellableCatchable.swift +++ b/Sources/Cancellation/CancellableCatchable.swift @@ -192,7 +192,7 @@ public extension CancellableCatchMixin { /** The provided closure executes when this cancellable promise rejects. - Unlike `catch`, `recover` continues the chain. + Unlike `catch`, `recover` continues the chain. It can return a replacement promise or rethrow. Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: let context = firstly { @@ -233,7 +233,7 @@ public extension CancellableCatchMixin { /** The provided closure executes when this cancellable promise rejects. - Unlike `catch`, `recover` continues the chain. + Unlike `catch`, `recover` continues the chain. It can return a replacement promise or rethrow. Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: let context = firstly { @@ -248,6 +248,7 @@ public extension CancellableCatchMixin { context.cancel() - Parameter on: The dispatcher that executes the provided closure. + - Parameter policy: The default policy does not execute your handler for cancellation errors. - Parameter body: The handler to execute if this promise is rejected. - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) */ @@ -275,7 +276,7 @@ public extension CancellableCatchMixin { /** The provided closure executes when this cancellable promise rejects with the specific error passed in. - Unlike `catch`, `recover` continues the chain. + Unlike `catch`, `recover` continues the chain. It can return a replacement promise or rethrow. Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: firstly { @@ -284,10 +285,10 @@ public extension CancellableCatchMixin { return .value(CLLocation.chicago) } - - Parameter only: The specific error to be recovered. - - Parameter on: The queue to which the provided closure dispatches. + - Parameter only: The specific error to be recovered (e.g., `PMKError.emptySequence`) + - Parameter on: The dispatcher that executes the provided closure. - Parameter body: The handler to execute if this promise is rejected with the provided error. - - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. + - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) */ func recover(_ only: E, on: Dispatcher = conf.D.map, _ body: @escaping() -> V) -> CancellablePromise where V.U.T == C.T, E: Equatable { @@ -313,7 +314,7 @@ public extension CancellableCatchMixin { /** The provided closure executes when this cancellable promise rejects with the specific error passed in. - Unlike `catch`, `recover` continues the chain. + Unlike `catch`, `recover` continues the chain. It can return a replacement promise or rethrow. Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: firstly { @@ -349,7 +350,7 @@ public extension CancellableCatchMixin { /** The provided closure executes when this cancellable promise rejects with an error of the type passed in. - Unlike `catch`, `recover` continues the chain. + Unlike `catch`, `recover` continues the chain. It can return a replacement promise or rethrow. Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: firstly { @@ -361,7 +362,8 @@ public extension CancellableCatchMixin { } - Parameter only: The error type to be recovered. - - Parameter on: The queue to which the provided closure dispatches. + - Parameter on: The dispatcher that executes the provided closure. + - Parameter policy: The default policy does not execute your handler for cancellation errors. - Parameter body: The handler to execute if this promise is rejected with the provided error type. - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) */ @@ -388,7 +390,7 @@ public extension CancellableCatchMixin { /** The provided closure executes when this cancellable promise rejects with an error of the type passed in. - Unlike `catch`, `recover` continues the chain. + Unlike `catch`, `recover` continues the chain. It can return a replacement promise or rethrow. Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: firstly { @@ -559,7 +561,7 @@ public extension CancellableCatchMixin where C.T == Void { /** The provided closure executes when this cancellable promise rejects with the specific error passed in. - Unlike `catch`, `recover` continues the chain. + Unlike `catch`, `recover` continues the chain. It can return a replacement promise or rethrow. Use `recover` in circumstances where recovering the chain from certain errors is a possibility. - Parameter only: The specific error to be recovered. @@ -584,7 +586,7 @@ public extension CancellableCatchMixin where C.T == Void { /** The provided closure executes when this cancellable promise rejects with an error of the type passed in. - Unlike `catch`, `recover` continues the chain. + Unlike `catch`, `recover` continues the chain. It can return a replacement promise or rethrow. Use `recover` in circumstances where recovering the chain from certain errors is a possibility. - Parameter only: The error type to be recovered. diff --git a/Sources/Catchable.swift b/Sources/Catchable.swift index 8c1b21828..3117d716d 100644 --- a/Sources/Catchable.swift +++ b/Sources/Catchable.swift @@ -16,7 +16,7 @@ public extension CatchMixin { - Parameter on: The dispatcher that executes the provided closure. - Parameter policy: The default policy does not execute your handler for cancellation errors. - - Parameter execute: The handler to execute if this promise is rejected. + - Parameter body: The handler to execute if this promise is rejected. - Returns: A promise finalizer. - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documents/CommonPatterns.md#cancellation) */ @@ -48,10 +48,11 @@ public extension CatchMixin { of a chain. Often utility promises will not have a catch, instead delegating the error handling to the caller. - - Parameter only: The specific error to be caught and handled. - - Parameter on: The queue to which the provided closure dispatches. - - Parameter execute: The handler to execute if this promise is rejected with the provided error. - - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. + - Parameter only: The specific error to be caught and handled (e.g., `PMKError.emptySequence`). + - Parameter on: The dispatcher that executes the provided closure. + - Parameter body: The handler to execute if this promise is rejected with the provided error. + - Returns: A promise finalizer that accepts additional `catch` clauses. + - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ func `catch`(_ only: E, on: Dispatcher = conf.D.return, _ body: @escaping() -> Void) -> PMKCascadingFinalizer where E: Equatable { @@ -80,9 +81,12 @@ public extension CatchMixin { of a chain. Often utility promises will not have a catch, instead delegating the error handling to the caller. - - Parameter only: The error type to be caught and handled. - - Parameter on: The queue to which the provided closure dispatches. - - Parameter execute: The handler to execute if this promise is rejected with the provided error type. + - Parameter only: The error type to be caught and handled (e.g., `PMKError`). + - Parameter on: The dispatcher that executes the provided closure. + - Parameter policy: A `CatchPolicy` that further constrains the errors this handler will see. E.g., if + you are receiving `PMKError` errors, do you want to see even those that result from cancellation? + - Parameter body: The handler to execute if this promise is rejected with the provided error type. + - Returns: A promise finalizer that accepts additional `catch` clauses. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ func `catch`(_ only: E.Type, on: Dispatcher = conf.D.return, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) -> Void) -> PMKCascadingFinalizer { @@ -129,9 +133,9 @@ public class PMKCascadingFinalizer { of a chain. Often utility promises will not have a catch, instead delegating the error handling to the caller. - - Parameter on: The queue to which the provided closure dispatches. + - Parameter on: The dispatcher that executes the provided closure. - Parameter policy: The default policy does not execute your handler for cancellation errors. - - Parameter execute: The handler to execute if this promise is rejected. + - Parameter body: The handler to execute if this promise is rejected. - Returns: A promise finalizer. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ @@ -150,10 +154,11 @@ public class PMKCascadingFinalizer { of a chain. Often utility promises will not have a catch, instead delegating the error handling to the caller. - - Parameter only: The specific error to be caught and handled. - - Parameter on: The queue to which the provided closure dispatches. - - Parameter execute: The handler to execute if this promise is rejected with the provided error. - - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. + - Parameter only: The specific error to be caught and handled (e.g., `PMKError.emptySequence`). + - Parameter on: The dispatcher that executes the provided closure. + - Parameter body: The handler to execute if this promise is rejected with the provided error. + - Returns: A promise finalizer that accepts additional `catch` clauses. + - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ public func `catch`(_ only: E, on: Dispatcher = conf.D.return, _ body: @escaping() -> Void) -> PMKCascadingFinalizer where E: Equatable { @@ -170,9 +175,10 @@ public class PMKCascadingFinalizer { of a chain. Often utility promises will not have a catch, instead delegating the error handling to the caller. - - Parameter only: The error type to be caught and handled. - - Parameter on: The queue to which the provided closure dispatches. - - Parameter execute: The handler to execute if this promise is rejected with the provided error type. + - Parameter only: The error type to be caught and handled (e.g., `PMKError`). + - Parameter on: The dispatcher that executes the provided closure. + - Parameter body: The handler to execute if this promise is rejected with the provided error type. + - Returns: A promise finalizer that accepts additional `catch` clauses. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ public func `catch`(_ only: E.Type, on: Dispatcher = conf.D.return, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) -> Void) -> PMKCascadingFinalizer { @@ -187,7 +193,7 @@ public extension CatchMixin { /** The provided closure executes when this promise rejects. - Unlike `catch`, `recover` continues the chain. + Unlike `catch`, `recover` continues the chain. It can return a replacement promise or rethrow. Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: firstly { @@ -198,6 +204,7 @@ public extension CatchMixin { } - Parameter on: The dispatcher that executes the provided closure. + - Parameter policy: The default policy does not execute your handler for cancellation errors. - Parameter body: The handler to execute if this promise is rejected. - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documents/CommonPatterns.md#cancellation) */ @@ -229,7 +236,7 @@ public extension CatchMixin { /** The provided closure executes when this promise rejects with the specific error passed in. - Unlike `catch`, `recover` continues the chain. + Unlike `catch`, `recover` continues the chain. It can return a replacement promise or rethrow. Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: firstly { @@ -238,10 +245,10 @@ public extension CatchMixin { return .value(CLLocation.chicago) } - - Parameter only: The specific error to be recovered. - - Parameter on: The queue to which the provided closure dispatches. + - Parameter only: The specific error to be recovered (e.g., `PMKError.emptySequence`) + - Parameter on: The dispatcher that executes the provided closure. - Parameter body: The handler to execute if this promise is rejected with the provided error. - - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. + - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ func recover(_ only: E, on: Dispatcher = conf.D.map, _ body: @escaping() -> U) -> Promise where U.T == T, E: Equatable { @@ -270,7 +277,7 @@ public extension CatchMixin { /** The provided closure executes when this promise rejects with an error of the type passed in. - Unlike `catch`, `recover` continues the chain. + Unlike `catch`, `recover` continues the chain. It can return a replacement promise or rethrow. Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: firstly { @@ -281,8 +288,9 @@ public extension CatchMixin { return .value(dataWithDefaultImage) } - - Parameter only: The error type to be recovered. - - Parameter on: The queue to which the provided closure dispatches. + - Parameter only: The error type to be recovered (e.g., `PMKError`). + - Parameter on: The dispatcher that executes the provided closure. + - Parameter policy: The default policy does not execute your handler for cancellation errors. - Parameter body: The handler to execute if this promise is rejected with the provided error type. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ @@ -315,8 +323,11 @@ public extension CatchMixin { /** The provided closure executes when this promise rejects. - This variant of `recover` requires the handler to return a Guarantee, thus it returns a Guarantee itself and your closure cannot `throw`. - - Note it is logically impossible for this to take a `catchPolicy`, thus `allErrors` are handled. + This variant of `recover` requires the handler to return a Guarantee; your closure cannot `throw`. + + It is logically impossible for this variant to accept a `catchPolicy`. All errors will be presented + to your closure for processing. + - Parameter on: The dispatcher that executes the provided closure. - Parameter body: The handler to execute if this promise is rejected. - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documents/CommonPatterns.md#cancellation) @@ -414,7 +425,9 @@ public extension CatchMixin where T == Void { /** The provided closure executes when this promise rejects. - This variant of `recover` is specialized for `Void` promises and de-errors your chain returning a `Guarantee`, thus you cannot `throw` and you must handle all errors including cancellation. + This variant of `recover` is specialized for `Void` promises and de-errors your chain, + returning a `Guarantee`. Thus, you cannot `throw` and you must handle all error types, + including cancellation. - Parameter on: The dispatcher that executes the provided closure. - Parameter body: The handler to execute if this promise is rejected. @@ -440,7 +453,8 @@ public extension CatchMixin where T == Void { /** The provided closure executes when this promise rejects. - This variant of `recover` ensures that no error is thrown from the handler and allows specifying a catch policy. + This variant of `recover` ensures that no error is thrown from the handler + and allows you to specify a catch policy. - Parameter on: The dispatcher that executes the provided closure. - Parameter body: The handler to execute if this promise is rejected. @@ -471,14 +485,14 @@ public extension CatchMixin where T == Void { /** The provided closure executes when this promise rejects with the specific error passed in. - - Unlike `catch`, `recover` continues the chain. + + Unlike `catch`, `recover` continues the chain. It can return a replacement promise or rethrow. Use `recover` in circumstances where recovering the chain from certain errors is a possibility. - - - Parameter only: The specific error to be recovered. - - Parameter on: The queue to which the provided closure dispatches. + + - Parameter only: The specific error to be recovered (e.g., `PMKError.emptySequence`) + - Parameter on: The dispatcher that executes the provided closure. - Parameter body: The handler to execute if this promise is rejected with the provided error. - - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. + - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ func recover(_ only: E, on: Dispatcher = conf.D.map, _ body: @escaping() -> Void) -> Promise where E: Equatable { @@ -501,12 +515,13 @@ public extension CatchMixin where T == Void { /** The provided closure executes when this promise rejects with an error of the type passed in. - - Unlike `catch`, `recover` continues the chain. + + Unlike `catch`, `recover` continues the chain. It can return a replacement promise or rethrow. Use `recover` in circumstances where recovering the chain from certain errors is a possibility. - - - Parameter only: The error type to be recovered. - - Parameter on: The queue to which the provided closure dispatches. + + - Parameter only: The error type to be recovered (e.g., `PMKError`). + - Parameter on: The dispatcher that executes the provided closure. + - Parameter policy: The default policy does not execute your handler for cancellation errors. - Parameter body: The handler to execute if this promise is rejected with the provided error type. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ From 71ffdce045fe6da5ed7e18a94c445239689fc106 Mon Sep 17 00:00:00 2001 From: Garth Snyder Date: Mon, 1 Apr 2019 21:28:07 -0700 Subject: [PATCH 44/81] Add cauterize() for cascading finalizers --- Sources/Cancellation/CancellableCatchable.swift | 13 ++++++++++++- Sources/Catchable.swift | 11 +++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/Sources/Cancellation/CancellableCatchable.swift b/Sources/Cancellation/CancellableCatchable.swift index 77129105c..16a082a4d 100644 --- a/Sources/Cancellation/CancellableCatchable.swift +++ b/Sources/Cancellation/CancellableCatchable.swift @@ -186,6 +186,17 @@ public class CancellableCascadingFinalizer: CancelContextFinalizer { public func `catch`(_ only: E.Type, on: Dispatcher = conf.D.return, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) -> Void) -> CancellableCascadingFinalizer { return CancellableCascadingFinalizer(pmkCascadingFinalizer.catch(only, on: on, policy: policy, body), cancel: cancelContext) } + + /** + Consumes the Swift unused-result warning. + - Note: You should `catch`, but in situations where you know you don’t need a `catch`, `cauterize` makes your intentions clear. + */ + @discardableResult + public func cauterize() -> CancellableFinalizer { + return self.catch(policy: .allErrors) { + conf.logHandler(.cauterized($0)) + } + } } public extension CancellableCatchMixin { @@ -526,7 +537,7 @@ public extension CancellableCatchMixin { @discardableResult func cauterize() -> CancellableFinalizer { return self.catch(policy: .allErrors) { - Swift.print("PromiseKit:cauterized-error:", $0) + conf.logHandler(.cauterized($0)) } } } diff --git a/Sources/Catchable.swift b/Sources/Catchable.swift index 3117d716d..efce5301d 100644 --- a/Sources/Catchable.swift +++ b/Sources/Catchable.swift @@ -186,6 +186,17 @@ public class PMKCascadingFinalizer { body($0) } } + + /** + Consumes the Swift unused-result warning. + - Note: You should `catch`, but in situations where you know you don’t need a `catch`, `cauterize` makes your intentions clear. + */ + @discardableResult + public func cauterize() -> PMKFinalizer { + return self.catch { + conf.logHandler(.cauterized($0)) + } + } } public extension CatchMixin { From 43662ff2670a00a4cbfb2cfbc5b9ddc93327b538 Mon Sep 17 00:00:00 2001 From: Garth Snyder Date: Mon, 1 Apr 2019 21:29:08 -0700 Subject: [PATCH 45/81] Allow recover() closures to throw even on specific error --- .../Cancellation/CancellableCatchable.swift | 20 ++++++++++--------- Sources/Catchable.swift | 14 ++++++++----- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/Sources/Cancellation/CancellableCatchable.swift b/Sources/Cancellation/CancellableCatchable.swift index 16a082a4d..181ba31d2 100644 --- a/Sources/Cancellation/CancellableCatchable.swift +++ b/Sources/Cancellation/CancellableCatchable.swift @@ -302,12 +302,12 @@ public extension CancellableCatchMixin { - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) */ - func recover(_ only: E, on: Dispatcher = conf.D.map, _ body: @escaping() -> V) -> CancellablePromise where V.U.T == C.T, E: Equatable { + func recover(_ only: E, on: Dispatcher = conf.D.map, _ body: @escaping() throws -> V) -> CancellablePromise where V.U.T == C.T, E: Equatable { let cancelItemList = CancelItemList() - let cancelBody = { () -> V.U in + let cancelBody = { () throws -> V.U in _ = self.cancelContext.removeItems(self.cancelItemList, clearList: true) - let rval = body() + let rval = try body() if only.isCancelled { self.cancelContext.recover() } @@ -340,10 +340,10 @@ public extension CancellableCatchMixin { - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) */ - func recover(_ only: E, on: Dispatcher = conf.D.map, _ body: @escaping() -> V) -> CancellablePromise where V.T == C.T, E: Equatable { - let cancelBody = { () -> V in + func recover(_ only: E, on: Dispatcher = conf.D.map, _ body: @escaping() throws -> V) -> CancellablePromise where V.T == C.T, E: Equatable { + let cancelBody = { () throws -> V in _ = self.cancelContext.removeItems(self.cancelItemList, clearList: true) - let rval = body() + let rval = try body() if only.isCancelled { self.cancelContext.recover() } @@ -581,10 +581,12 @@ public extension CancellableCatchMixin where C.T == Void { - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) */ - func recover(_ only: E, on: Dispatcher = conf.D.map, _ body: @escaping() -> Void) -> CancellablePromise where E: Equatable { - let cancelBody = { () -> Void in + func recover(_ only: E, on: Dispatcher = conf.D.map, _ body: @escaping() throws -> Void) + -> CancellablePromise where E: Equatable + { + let cancelBody = { () throws -> Void in _ = self.cancelContext.removeItems(self.cancelItemList, clearList: true) - body() + try body() } let promise = self.catchable.recover(only, on: on, cancelBody) diff --git a/Sources/Catchable.swift b/Sources/Catchable.swift index efce5301d..7fbe603f0 100644 --- a/Sources/Catchable.swift +++ b/Sources/Catchable.swift @@ -262,7 +262,7 @@ public extension CatchMixin { - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func recover(_ only: E, on: Dispatcher = conf.D.map, _ body: @escaping() -> U) -> Promise where U.T == T, E: Equatable { + func recover(_ only: E, on: Dispatcher = conf.D.map, _ body: @escaping() throws -> U) -> Promise where U.T == T, E: Equatable { let rp = Promise(.pending) pipe { switch $0 { @@ -271,7 +271,7 @@ public extension CatchMixin { case .failure(let error as E) where error == only: on.dispatch { do { - let rv = body() + let rv = try body() guard rv !== rp else { throw PMKError.returnedSelf } rv.pipe(to: rp.box.seal) } catch { @@ -506,7 +506,7 @@ public extension CatchMixin where T == Void { - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func recover(_ only: E, on: Dispatcher = conf.D.map, _ body: @escaping() -> Void) -> Promise where E: Equatable { + func recover(_ only: E, on: Dispatcher = conf.D.map, _ body: @escaping() throws -> Void) -> Promise where E: Equatable { let rp = Promise(.pending) pipe { switch $0 { @@ -514,8 +514,12 @@ public extension CatchMixin where T == Void { rp.box.seal(.success(())) case .failure(let error as E) where error == only: on.dispatch { - body() - rp.box.seal(.success(())) + do { + try body() + rp.box.seal(.success(())) + } catch { + rp.box.seal(.failure(error)) + } } case .failure(let error): rp.box.seal(.failure(error)) From a10e6ea8dc848f3df1999ea34cbfc94274fb42fb Mon Sep 17 00:00:00 2001 From: Garth Snyder Date: Mon, 1 Apr 2019 21:15:01 -0700 Subject: [PATCH 46/81] Systematize wrappers, omit duplication, move to subdirectory --- Sources/Dispatcher.swift | 1157 +--------------------- Sources/Wrappers/CatchWrappers.swift | 73 ++ Sources/Wrappers/EnsureWrappers.swift | 51 + Sources/Wrappers/FinallyWrappers.swift | 11 + Sources/Wrappers/GuaranteeWrappers.swift | 39 + Sources/Wrappers/RecoverWrappers.swift | 275 +++++ Sources/Wrappers/SequenceWrappers.swift | 261 +++++ Sources/Wrappers/ThenableWrappers.swift | 240 +++++ Sources/Wrappers/WrapperProtocols.swift | 82 ++ 9 files changed, 1049 insertions(+), 1140 deletions(-) create mode 100644 Sources/Wrappers/CatchWrappers.swift create mode 100644 Sources/Wrappers/EnsureWrappers.swift create mode 100644 Sources/Wrappers/FinallyWrappers.swift create mode 100644 Sources/Wrappers/GuaranteeWrappers.swift create mode 100644 Sources/Wrappers/RecoverWrappers.swift create mode 100644 Sources/Wrappers/SequenceWrappers.swift create mode 100644 Sources/Wrappers/ThenableWrappers.swift create mode 100644 Sources/Wrappers/WrapperProtocols.swift diff --git a/Sources/Dispatcher.swift b/Sources/Dispatcher.swift index bc38d8200..e27d92220 100644 --- a/Sources/Dispatcher.swift +++ b/Sources/Dispatcher.swift @@ -39,22 +39,6 @@ public struct DispatchQueueDispatcher: Dispatcher { } } -// Avoid having to hard-code any particular defaults for qos or flags -internal extension DispatchQueue { - final func asyncD(group: DispatchGroup? = nil, qos: DispatchQoS? = nil, flags: DispatchWorkItemFlags? = nil, execute body: @escaping () -> Void) { - switch (qos, flags) { - case (nil, nil): - async(group: group, execute: body) - case (nil, let flags?): - async(group: group, flags: flags, execute: body) - case (let qos?, nil): - async(group: group, qos: qos, execute: body) - case (let qos?, let flags?): - async(group: group, qos: qos, flags: flags, execute: body) - } - } -} - /// A `Dispatcher` class that executes all closures synchronously on /// the current thread. /// @@ -97,12 +81,28 @@ public extension DispatchQueue { } } +// Avoid having to hard-code any particular defaults for qos or flags +internal extension DispatchQueue { + final func asyncD(group: DispatchGroup? = nil, qos: DispatchQoS? = nil, flags: DispatchWorkItemFlags? = nil, execute body: @escaping () -> Void) { + switch (qos, flags) { + case (nil, nil): + async(group: group, execute: body) + case (nil, let flags?): + async(group: group, flags: flags, execute: body) + case (let qos?, nil): + async(group: group, qos: qos, execute: body) + case (let qos?, let flags?): + async(group: group, qos: qos, flags: flags, execute: body) + } + } +} + // This hairball disambiguates all the various combinations of explicit arguments, default // arguments, and configured defaults. In particular, a method that is given explicit work item // flags but no DispatchQueue should still work (that is, the dispatcher should use those flags) // as long as the configured default is actually some kind of DispatchQueue. -fileprivate func selectDispatcher(given: DispatchQueue?, configured: Dispatcher, flags: DispatchWorkItemFlags?) -> Dispatcher { +internal func selectDispatcher(given: DispatchQueue?, configured: Dispatcher, flags: DispatchWorkItemFlags?) -> Dispatcher { guard let given = given else { if flags != nil { conf.logHandler(.nilDispatchQueueWithFlags) @@ -118,1126 +118,3 @@ fileprivate func selectDispatcher(given: DispatchQueue?, configured: Dispatcher, } return configured } - -// Backward compatibility for DispatchQueues in public API - -public extension Guarantee { - - @discardableResult - func done(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) -> Void) -> Guarantee { - let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) - return done(on: dispatcher, body) - } - - func get(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping (T) -> Void) -> Guarantee { - let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) - return get(on: dispatcher, body) - } - - func map(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) -> U) -> Guarantee { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return map(on: dispatcher, body) - } - - @discardableResult - func then(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) -> Guarantee) -> Guarantee { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return then(on: dispatcher, body) - } - -} - -public extension Guarantee where T: Sequence { - - func thenMap(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) -> Guarantee) -> Guarantee<[U]> { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return thenMap(on: dispatcher, transform) - } - -} - -public extension Thenable { - - /** - The provided closure executes when this promise resolves. - - This allows chaining promises. The promise returned by the provided closure is resolved before the promise returned by this closure resolves. - - - Parameter on: The queue to which the provided closure dispatches. - - Parameter body: The closure that executes when this promise fulfills. It must return a promise. - - Returns: A new promise that resolves when the promise returned from the provided closure resolves. For example: - - firstly { - URLSession.shared.dataTask(.promise, with: url1) - }.then { response in - transform(data: response.data) - }.done { transformation in - //… - } - */ - func then(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) throws -> U) -> Promise { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return then(on: dispatcher, body) - } - - /** - The provided closure is executed when this promise is resolved. - - This is like `then` but it requires the closure to return a non-promise. - - - Parameter on: The queue to which the provided closure dispatches. - - Parameter transform: The closure that is executed when this Promise is fulfilled. It must return a non-promise. - - Returns: A new promise that is resolved with the value returned from the provided closure. For example: - - firstly { - URLSession.shared.dataTask(.promise, with: url1) - }.map { response in - response.data.length - }.done { length in - //… - } - */ - func map(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T) throws -> U) -> Promise { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return map(on: dispatcher, transform) - } - - /** - The provided closure is executed when this promise is resolved. - - In your closure return an `Optional`, if you return `nil` the resulting promise is rejected with `PMKError.compactMap`, otherwise the promise is fulfilled with the unwrapped value. - - firstly { - URLSession.shared.dataTask(.promise, with: url) - }.compactMap { - try JSONSerialization.jsonObject(with: $0.data) as? [String: String] - }.done { dictionary in - //… - }.catch { - // either `PMKError.compactMap` or a `JSONError` - } - */ - func compactMap(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T) throws -> U?) -> Promise { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return compactMap(on: dispatcher, transform) - } - - /** - The provided closure is executed when this promise is resolved. - - Equivalent to `map { x -> Void in`, but since we force the `Void` return Swift - is happier and gives you less hassle about your closure’s qualification. - - - Parameter on: The queue to which the provided closure dispatches. - - Parameter body: The closure that is executed when this Promise is fulfilled. - - Returns: A new promise fulfilled as `Void`. - - firstly { - URLSession.shared.dataTask(.promise, with: url) - }.done { response in - print(response.data) - } - */ - func done(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) throws -> Void) -> Promise { - let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) - return done(on: dispatcher, body) - } - - /** - The provided closure is executed when this promise is resolved. - - This is like `done` but it returns the same value that the handler is fed. - `get` immutably accesses the fulfilled value; the returned Promise maintains that value. - - - Parameter on: The queue to which the provided closure dispatches. - - Parameter body: The closure that is executed when this Promise is fulfilled. - - Returns: A new promise that is resolved with the value that the handler is fed. For example: - - firstly { - .value(1) - }.get { foo in - print(foo, " is 1") - }.done { foo in - print(foo, " is 1") - }.done { foo in - print(foo, " is Void") - } - */ - func get(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping (T) throws -> Void) -> Promise { - let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) - return get(on: dispatcher, body) - } - - /** - The provided closure is executed with promise result. - - This is like `get` but provides the Result of the Promise so you can inspect the value of the chain at this point without causing any side effects. - - - Parameter on: The queue to which the provided closure dispatches. - - Parameter body: The closure that is executed with Result of Promise. - - Returns: A new promise that is resolved with the result that the handler is fed. For example: - - promise.tap{ print($0) }.then{ /*…*/ } - */ - func tap(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Result) -> Void) -> Promise { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return tap(on: dispatcher, body) - } - -} - -public extension Thenable where T: Sequence { - /** - `Promise<[T]>` => `T` -> `U` => `Promise<[U]>` - - firstly { - .value([1,2,3]) - }.mapValues { integer in - integer * 2 - }.done { - // $0 => [2,4,6] - } - */ - func mapValues(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U]> { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return mapValues(on: dispatcher, transform) - } - - /** - `Promise<[T]>` => `T` -> `[U]` => `Promise<[U]>` - - firstly { - .value([1,2,3]) - }.flatMapValues { integer in - [integer, integer] - }.done { - // $0 => [1,1,2,2,3,3] - } - */ - func flatMapValues(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U.Iterator.Element]> { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return flatMapValues(on: dispatcher, transform) - } - - /** - `Promise<[T]>` => `T` -> `U?` => `Promise<[U]>` - - firstly { - .value(["1","2","a","3"]) - }.compactMapValues { - Int($0) - }.done { - // $0 => [1,2,3] - } - */ - func compactMapValues(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) throws -> U?) -> Promise<[U]> { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return compactMapValues(on: dispatcher, transform) - } - - /** - `Promise<[T]>` => `T` -> `Promise` => `Promise<[U]>` - - firstly { - .value([1,2,3]) - }.thenMap { integer in - .value(integer * 2) - }.done { - // $0 => [2,4,6] - } - */ - func thenMap(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U.T]> { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return thenMap(on: dispatcher, transform) - } - - /** - `Promise<[T]>` => `T` -> `Promise<[U]>` => `Promise<[U]>` - - firstly { - .value([1,2,3]) - }.thenFlatMap { integer in - .value([integer, integer]) - }.done { - // $0 => [1,1,2,2,3,3] - } - */ - func thenFlatMap(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U.T.Iterator.Element]> where U.T: Sequence { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return thenFlatMap(on: dispatcher, transform) - } - - /** - `Promise<[T]>` => `T` -> Bool => `Promise<[U]>` - - firstly { - .value([1,2,3]) - }.filterValues { - $0 > 1 - }.done { - // $0 => [2,3] - } - */ - func filterValues(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ isIncluded: @escaping (T.Iterator.Element) -> Bool) -> Promise<[T.Iterator.Element]> { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return filterValues(on: dispatcher, isIncluded) - } -} - -public extension Thenable where T: Collection { - func firstValue(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, where test: @escaping (T.Iterator.Element) -> Bool) -> Promise { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return firstValue(on: dispatcher, where: test) - } -} - -public extension Thenable where T: Sequence, T.Iterator.Element: Comparable { - /// - Returns: a promise fulfilled with the sorted values of this `Sequence`. - func sortedValues(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil) -> Promise<[T.Iterator.Element]> { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return sortedValues(on: dispatcher) - } -} - -public extension CatchMixin { - /** - The provided closure executes when this promise rejects. - - Rejecting a promise cascades: rejecting all subsequent promises (unless - recover is invoked) thus you will typically place your catch at the end - of a chain. Often utility promises will not have a catch, instead - delegating the error handling to the caller. - - - Parameter on: The queue to which the provided closure dispatches. - - Parameter policy: The default policy does not execute your handler for cancellation errors. - - Parameter execute: The handler to execute if this promise is rejected. - - Returns: A promise finalizer. - - SeeAlso: [Cancellation](http://https://github.com/mxcl/PromiseKit/blob/master/Documents/CommonPatterns.md#cancellation/docs/) - */ - @discardableResult - func `catch`(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) -> Void) -> PMKFinalizer { - let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) - return `catch`(on: dispatcher, policy: policy, body) - } - - /** - The provided closure executes when this promise rejects. - - Unlike `catch`, `recover` continues the chain. - Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: - - firstly { - CLLocationManager.requestLocation() - }.recover { error in - guard error == CLError.unknownLocation else { throw error } - return .value(CLLocation.chicago) - } - - - Parameter on: The queue to which the provided closure dispatches. - - Parameter body: The handler to execute if this promise is rejected. - - SeeAlso: [Cancellation](http://https://github.com/mxcl/PromiseKit/blob/master/Documents/CommonPatterns.md#cancellation/docs/) - */ - func recover(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) throws -> U) -> Promise where U.T == T { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return recover(on: dispatcher, policy: policy, body) - } - - /** - The provided closure executes when this promise rejects. - This variant of `recover` requires the handler to return a Guarantee, thus it returns a Guarantee itself and your closure cannot `throw`. - - Note it is logically impossible for this to take a `catchPolicy`, thus `allErrors` are handled. - - Parameter on: The queue to which the provided closure dispatches. - - Parameter body: The handler to execute if this promise is rejected. - - SeeAlso: [Cancellation](http://https://github.com/mxcl/PromiseKit/blob/master/Documents/CommonPatterns.md#cancellation/docs/) - */ - @discardableResult - func recover(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Error) -> Guarantee) -> Guarantee { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return recover(on: dispatcher, body) - } - - /** - The provided closure executes when this promise resolves, whether it rejects or not. - - firstly { - UIApplication.shared.networkActivityIndicatorVisible = true - }.done { - //… - }.ensure { - UIApplication.shared.networkActivityIndicatorVisible = false - }.catch { - //… - } - - - Parameter on: The queue to which the provided closure dispatches. - - Parameter body: The closure that executes when this promise resolves. - - Returns: A new promise, resolved with this promise’s resolution. - */ - func ensure(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping () -> Void) -> Promise { - let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) - return ensure(on: dispatcher, body) - } - - /** - The provided closure executes when this promise resolves, whether it rejects or not. - The chain waits on the returned `Guarantee`. - - firstly { - setup() - }.done { - //… - }.ensureThen { - teardown() // -> Guarante - }.catch { - //… - } - - - Parameter on: The queue to which the provided closure dispatches. - - Parameter body: The closure that executes when this promise resolves. - - Returns: A new promise, resolved with this promise’s resolution. - */ - func ensureThen(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping () -> Guarantee) -> Promise { - let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) - return ensureThen(on: dispatcher, body) - } -} - -public extension PMKFinalizer { - /// `finally` is the same as `ensure`, but it is not chainable - func finally(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping () -> Void) { - let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) - return finally(on: dispatcher, body) - } -} - -public extension CatchMixin where T == Void { - - /** - The provided closure executes when this promise rejects. - - This variant of `recover` is specialized for `Void` promises and de-errors your chain returning a `Guarantee`, thus you cannot `throw` and you must handle all errors including cancellation. - - - Parameter on: The queue to which the provided closure dispatches. - - Parameter body: The handler to execute if this promise is rejected. - - SeeAlso: [Cancellation](http://https://github.com/mxcl/PromiseKit/blob/master/Documents/CommonPatterns.md#cancellation/docs/) - */ - @discardableResult - func recover(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Error) -> Void) -> Guarantee { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return recover(on: dispatcher, body) - } - - /** - The provided closure executes when this promise rejects. - - This variant of `recover` ensures that no error is thrown from the handler and allows specifying a catch policy. - - - Parameter on: The queue to which the provided closure dispatches. - - Parameter body: The handler to execute if this promise is rejected. - - SeeAlso: [Cancellation](http://https://github.com/mxcl/PromiseKit/blob/master/Documents/CommonPatterns.md#cancellation/docs/) - */ - func recover(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) throws -> Void) -> Promise { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return recover(on: dispatcher, policy: policy, body) - } -} - -//////////////////////////////////////////////////////////// Cancellation - -public extension CancellableThenable { - /** - The provided closure executes when this cancellable promise resolves. - - This allows chaining promises. The cancellable promise returned by the provided closure is resolved before the cancellable promise returned by this closure resolves. - - - Parameter on: The queue to which the provided closure dispatches. - - Parameter body: The closure that executes when this cancellable promise fulfills. It must return a cancellable promise. - - Returns: A new cancellable promise that resolves when the promise returned from the provided closure resolves. For example: - - let context = firstly { - URLSession.shared.dataTask(.promise, with: url1) - }.cancellize().then { response in - transform(data: response.data) // returns a CancellablePromise - }.done { transformation in - //… - }.cancelContext - - //… - - context.cancel() - */ - func then(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping (U.T) throws -> V) -> CancellablePromise { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return then(on: dispatcher, body) - } - - /** - The provided closure executes when this cancellable promise resolves. - - This allows chaining promises. The promise returned by the provided closure is resolved before the cancellable promise returned by this closure resolves. - - - Parameter on: The dispatcher that executes the provided closure. - - Parameter body: The closure that executes when this cancellable promise fulfills. It must return a promise (not a cancellable promise). - - Returns: A new cancellable promise that resolves when the promise returned from the provided closure resolves. For example: - - let context = firstly { - URLSession.shared.dataTask(.promise, with: url1) - }.cancellize().then { response in - transform(data: response.data) // returns a Promise - }.done { transformation in - //… - }.cancelContext - - //… - - context.cancel() - */ - func then(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping (U.T) throws -> V) -> CancellablePromise { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return then(on: dispatcher, body) - } - - /** - The provided closure is executed when this cancellable promise is resolved. - - This is like `then` but it requires the closure to return a non-promise and non-cancellable-promise. - - - Parameter on: The queue to which the provided closure dispatches. - - Parameter transform: The closure that is executed when this CancellablePromise is fulfilled. It must return a non-promise and non-cancellable-promise. - - Returns: A new cancellable promise that is resolved with the value returned from the provided closure. For example: - - let context = firstly { - URLSession.shared.dataTask(.promise, with: url1) - }.cancellize().map { response in - response.data.length - }.done { length in - //… - }.cancelContext - - //… - - context.cancel() - */ - func map(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping (U.T) throws -> V) -> CancellablePromise { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return map(on: dispatcher, transform) - } - - /** - The provided closure is executed when this cancellable promise is resolved. - - In your closure return an `Optional`, if you return `nil` the resulting cancellable promise is rejected with `PMKError.compactMap`, otherwise the cancellable promise is fulfilled with the unwrapped value. - - let context = firstly { - URLSession.shared.dataTask(.promise, with: url) - }.cancellize().compactMap { - try JSONSerialization.jsonObject(with: $0.data) as? [String: String] - }.done { dictionary in - //… - }.catch { - // either `PMKError.compactMap` or a `JSONError` - }.cancelContext - - //… - - context.cancel() - */ - func compactMap(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping (U.T) throws -> V?) -> CancellablePromise { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return compactMap(on: dispatcher, transform) - } - - /** - The provided closure is executed when this cancellable promise is resolved. - - Equivalent to `map { x -> Void in`, but since we force the `Void` return Swift - is happier and gives you less hassle about your closure’s qualification. - - - Parameter on: The queue to which the provided closure dispatches. - - Parameter body: The closure that is executed when this promise is fulfilled. - - Returns: A new cancellable promise fulfilled as `Void`. - - let context = firstly { - URLSession.shared.dataTask(.promise, with: url) - }.cancellize().done { response in - print(response.data) - }.cancelContext - - //… - - context.cancel() - */ - func done(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping (U.T) throws -> Void) -> CancellablePromise { - let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) - return done(on: dispatcher, body) - } - - /** - The provided closure is executed when this cancellable promise is resolved. - - This is like `done` but it returns the same value that the handler is fed. - `get` immutably accesses the fulfilled value; the returned CancellablePromise maintains that value. - - - Parameter on: The queue to which the provided closure dispatches. - - Parameter body: The closure that is executed when this CancellablePromise is fulfilled. - - Returns: A new cancellable promise that is resolved with the value that the handler is fed. For example: - - let context = firstly { - cancellize(Promise.value(1)) - }.get { foo in - print(foo, " is 1") - }.done { foo in - print(foo, " is 1") - }.done { foo in - print(foo, " is Void") - }.cancelContext - - //… - - context.cancel() - */ - func get(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping (U.T) throws -> Void) -> CancellablePromise { - let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) - return get(on: dispatcher, body) - } - - /** - The provided closure is executed with cancellable promise result. - - This is like `get` but provides the Result of the CancellablePromise so you can inspect the value of the chain at this point without causing any side effects. - - - Parameter on: The queue to which the provided closure dispatches. - - Parameter body: The closure that is executed with Result of CancellablePromise. - - Returns: A new cancellable promise that is resolved with the result that the handler is fed. For example: - - promise.tap{ print($0) }.then{ /*…*/ } - */ - func tap(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Result) -> Void) -> CancellablePromise { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return tap(on: dispatcher, body) - } -} - -public extension CancellableThenable where U.T: Sequence { - /** - `CancellablePromise<[U.T]>` => `U.T` -> `V` => `CancellablePromise<[V]>` - - firstly { - cancellize(Promise.value([1,2,3])) - }.mapValues { integer in - integer * 2 - }.done { - // $0 => [2,4,6] - } - */ - func mapValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(U.T.Iterator.Element) throws -> V) -> CancellablePromise<[V]> { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return mapValues(on: dispatcher, transform) - } - - /** - `CancellablePromise<[U.T]>` => `U.T` -> `[V]` => `CancellablePromise<[V]>` - - firstly { - cancellize(Promise.value([1,2,3])) - }.flatMapValues { integer in - [integer, integer] - }.done { - // $0 => [1,1,2,2,3,3] - } - */ - func flatMapValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(U.T.Iterator.Element) throws -> V) -> CancellablePromise<[V.Iterator.Element]> { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return flatMapValues(on: dispatcher, transform) - } - - /** - `CancellablePromise<[U.T]>` => `U.T` -> `V?` => `CancellablePromise<[V]>` - - firstly { - cancellize(Promise.value(["1","2","a","3"])) - }.compactMapValues { - Int($0) - }.done { - // $0 => [1,2,3] - } - */ - func compactMapValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(U.T.Iterator.Element) throws -> V?) -> CancellablePromise<[V]> { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return compactMapValues(on: dispatcher, transform) - } - - /** - `CancellablePromise<[U.T]>` => `U.T` -> `CancellablePromise` => `CancellablePromise<[V]>` - - firstly { - cancellize(Promise.value([1,2,3])) - }.thenMap { integer in - cancellize(Promise.value(integer * 2)) - }.done { - // $0 => [2,4,6] - } - */ - func thenMap(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(U.T.Iterator.Element) throws -> V) -> CancellablePromise<[V.U.T]> { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return thenMap(on: dispatcher, transform) - } - - /** - `CancellablePromise<[U.T]>` => `U.T` -> `Promise` => `CancellablePromise<[V]>` - - firstly { - Promise.value([1,2,3]) - }.cancellize().thenMap { integer in - .value(integer * 2) - }.done { - // $0 => [2,4,6] - } - */ - func thenMap(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(U.T.Iterator.Element) throws -> V) -> CancellablePromise<[V.T]> { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return thenMap(on: dispatcher, transform) - } - - /** - `CancellablePromise<[T]>` => `T` -> `CancellablePromise<[U]>` => `CancellablePromise<[U]>` - - firstly { - cancellize(Promise.value([1,2,3])) - }.thenFlatMap { integer in - cancellize(Promise.value([integer, integer])) - }.done { - // $0 => [1,1,2,2,3,3] - } - */ - func thenFlatMap(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(U.T.Iterator.Element) throws -> V) -> CancellablePromise<[V.U.T.Iterator.Element]> where V.U.T: Sequence { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return thenFlatMap(on: dispatcher, transform) - } - - /** - `CancellablePromise<[T]>` => `T` -> `Promise<[U]>` => `CancellablePromise<[U]>` - - firstly { - Promise.value([1,2,3]) - }.cancellize().thenFlatMap { integer in - .value([integer, integer]) - }.done { - // $0 => [1,1,2,2,3,3] - } - */ - func thenFlatMap(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(U.T.Iterator.Element) throws -> V) -> CancellablePromise<[V.T.Iterator.Element]> where V.T: Sequence { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return thenFlatMap(on: dispatcher, transform) - } - - /** - `CancellablePromise<[T]>` => `T` -> Bool => `CancellablePromise<[U]>` - - firstly { - cancellize(Promise.value([1,2,3])) - }.filterValues { - $0 > 1 - }.done { - // $0 => [2,3] - } - */ - func filterValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ isIncluded: @escaping (U.T.Iterator.Element) -> Bool) -> CancellablePromise<[U.T.Iterator.Element]> { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return filterValues(on: dispatcher, isIncluded) - } -} - -public extension CancellableThenable where U.T: Collection { - func firstValue(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, where test: @escaping (U.T.Iterator.Element) -> Bool) -> CancellablePromise { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return firstValue(on: dispatcher, where: test) - } -} - -public extension CancellableThenable where U.T: Sequence, U.T.Iterator.Element: Comparable { - /// - Returns: a cancellable promise fulfilled with the sorted values of this `Sequence`. - func sortedValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil) -> CancellablePromise<[U.T.Iterator.Element]> { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return sortedValues(on: dispatcher) - } -} - -public extension CancellableCatchMixin { - /** - The provided closure executes when this cancellable promise rejects. - - Rejecting a promise cascades: rejecting all subsequent promises (unless - recover is invoked) thus you will typically place your catch at the end - of a chain. Often utility promises will not have a catch, instead - delegating the error handling to the caller. - - - Parameter on: The queue to which the provided closure dispatches. - - Parameter policy: The default policy does not execute your handler for cancellation errors. - - Parameter body: The handler to execute if this promise is rejected. - - Returns: A promise finalizer. - - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) - */ - @discardableResult - func `catch`(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) -> Void) -> CancellableFinalizer { - let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) - return `catch`(on: dispatcher, policy: policy, body) - } - - /** - The provided closure executes when this cancellable promise rejects with the specific error passed in. A final `catch` is still required at the end of the chain. - - Rejecting a promise cascades: rejecting all subsequent promises (unless - recover is invoked) thus you will typically place your catch at the end - of a chain. Often utility promises will not have a catch, instead - delegating the error handling to the caller. - - - Parameter only: The specific error to be caught and handled. - - Parameter on: The queue to which the provided closure dispatches. - - Parameter execute: The handler to execute if this promise is rejected with the provided error. - - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. - - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) - */ - func `catch`(_ only: E, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping() -> Void) -> CancellableCascadingFinalizer where E: Equatable { - let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) - return `catch`(only, on: dispatcher, body) - } - - /** - The provided closure executes when this promise rejects with an error of the type passed in. A final `catch` is still required at the end of the chain. - - Rejecting a promise cascades: rejecting all subsequent promises (unless - recover is invoked) thus you will typically place your catch at the end - of a chain. Often utility promises will not have a catch, instead - delegating the error handling to the caller. - - - Parameter only: The error type to be caught and handled. - - Parameter on: The queue to which the provided closure dispatches. - - Parameter execute: The handler to execute if this promise is rejected with the provided error type. - - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) - */ - func `catch`(_ only: E.Type, on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) -> Void) -> CancellableCascadingFinalizer { - let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) - return `catch`(only, on: dispatcher, policy: policy, body) - } - - /** - The provided closure executes when this cancellable promise rejects. - - Unlike `catch`, `recover` continues the chain. - Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: - - let context = firstly { - CLLocationManager.requestLocation() - }.recover { error in - guard error == CLError.unknownLocation else { throw error } - return .value(CLLocation.chicago) - }.cancelContext - - //… - - context.cancel() - - - Parameter on: The queue to which the provided closure dispatches. - - Parameter policy: The default policy does not execute your handler for cancellation errors. - - Parameter body: The handler to execute if this promise is rejected. - - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) - */ - func recover(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) throws -> V) -> CancellablePromise where V.U.T == C.T { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return recover(on: dispatcher, policy: policy, body) - } - - /** - The provided closure executes when this cancellable promise rejects. - - Unlike `catch`, `recover` continues the chain. - Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: - - let context = firstly { - CLLocationManager.requestLocation() - }.cancellize().recover { error in - guard error == CLError.unknownLocation else { throw error } - return .value(CLLocation.chicago) - }.cancelContext - - //… - - context.cancel() - - - Parameter on: The queue to which the provided closure dispatches. - - Parameter policy: The default policy does not execute your handler for cancellation errors. - - Parameter body: The handler to execute if this promise is rejected. - - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) - */ - func recover(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) throws -> V) -> CancellablePromise where V.T == C.T { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return recover(on: dispatcher, policy: policy ,body) - } - - /** - The provided closure executes when this cancellable promise rejects with the specific error passed in. - - Unlike `catch`, `recover` continues the chain. - Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: - - firstly { - CLLocationManager.requestLocation() - }.recover(CLError.unknownLocation) { - return .value(CLLocation.chicago) - } - - - Parameter only: The specific error to be recovered. - - Parameter on: The queue to which the provided closure dispatches. - - Parameter body: The handler to execute if this promise is rejected with the provided error. - - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. - - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) - */ - func recover(_ only: E, on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping() -> V) -> CancellablePromise where V.U.T == C.T, E: Equatable { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return recover(only, on: dispatcher, body) - } - - /** - The provided closure executes when this cancellable promise rejects with the specific error passed in. - - Unlike `catch`, `recover` continues the chain. - Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: - - firstly { - CLLocationManager.requestLocation() - }.recover(CLError.unknownLocation) { - return Promise.value(CLLocation.chicago) - } - - - Parameter only: The specific error to be recovered. - - Parameter on: The queue to which the provided closure dispatches. - - Parameter body: The handler to execute if this promise is rejected with the provided error. - - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. - - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) - */ - func recover(_ only: E, on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping() -> V) -> CancellablePromise where V.T == C.T, E: Equatable { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return recover(only, on: dispatcher, body) - } - - /** - The provided closure executes when this cancellable promise rejects with an error of the type passed in. - - Unlike `catch`, `recover` continues the chain. - Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: - - firstly { - API.fetchData() - }.recover(FetchError.self) { error in - guard case .missingImage(let partialData) = error else { throw error } - //… - return .value(dataWithDefaultImage) - } - - - Parameter only: The error type to be recovered. - - Parameter on: The queue to which the provided closure dispatches. - - Parameter body: The handler to execute if this promise is rejected with the provided error type. - - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) - */ - func recover(_ only: E.Type, on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> V) -> CancellablePromise where V.U.T == C.T { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return recover(only, on: dispatcher, policy: policy, body) - } - - /** - The provided closure executes when this cancellable promise rejects with an error of the type passed in. - - Unlike `catch`, `recover` continues the chain. - Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: - - firstly { - API.fetchData() - }.recover(FetchError.self) { error in - guard case .missingImage(let partialData) = error else { throw error } - //… - return Promise.value(dataWithDefaultImage) - } - - - Parameter only: The error type to be recovered. - - Parameter on: The queue to which the provided closure dispatches. - - Parameter body: The handler to execute if this promise is rejected with the provided error type. - - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) - */ - func recover(_ only: E.Type, on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> V) -> CancellablePromise where V.T == C.T { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return recover(only, on: dispatcher, policy: policy, body) - } - - /** - The provided closure executes when this cancellable promise resolves, whether it rejects or not. - - let context = firstly { - UIApplication.shared.networkActivityIndicatorVisible = true - //… returns a cancellable promise - }.done { - //… - }.ensure { - UIApplication.shared.networkActivityIndicatorVisible = false - }.catch { - //… - }.cancelContext - - //… - - context.cancel() - - - Parameter on: The queue to which the provided closure dispatches. - - Parameter body: The closure that executes when this promise resolves. - - Returns: A new promise, resolved with this promise’s resolution. - */ - func ensure(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping () -> Void) -> CancellablePromise { - let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) - return ensure(on: dispatcher, body) - } - - /** - The provided closure executes when this cancellable promise resolves, whether it rejects or not. - The chain waits on the returned `CancellablePromise`. - - let context = firstly { - setup() // returns a cancellable promise - }.done { - //… - }.ensureThen { - teardown() // -> CancellablePromise - }.catch { - //… - }.cancelContext - - //… - - context.cancel() - - - Parameter on: The queue to which the provided closure dispatches. - - Parameter body: The closure that executes when this promise resolves. - - Returns: A new cancellable promise, resolved with this promise’s resolution. - */ - func ensureThen(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping () -> CancellablePromise) -> CancellablePromise { - let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) - return ensureThen(on: dispatcher, body) - } -} - -public extension CancellableFinalizer { - /// `finally` is the same as `ensure`, but it is not chainable - @discardableResult - func finally(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping () -> Void) -> CancelContext { - let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) - return finally(on: dispatcher, body) - } -} - -public extension CancellableCascadingFinalizer { - /** - The provided closure executes when this promise rejects. - - Rejecting a promise cascades: rejecting all subsequent promises (unless - recover is invoked) thus you will typically place your catch at the end - of a chain. Often utility promises will not have a catch, instead - delegating the error handling to the caller. - - - Parameter on: The queue to which the provided closure dispatches. - - Parameter policy: The default policy does not execute your handler for cancellation errors. - - Parameter execute: The handler to execute if this promise is rejected. - - Returns: A promise finalizer. - - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) - */ - @discardableResult - func `catch`(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) -> Void) -> CancellableFinalizer { - let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) - return `catch`(on: dispatcher, policy: policy, body) - } - - /** - The provided closure executes when this promise rejects with the specific error passed in. A final `catch` is still required at the end of the chain. - - Rejecting a promise cascades: rejecting all subsequent promises (unless - recover is invoked) thus you will typically place your catch at the end - of a chain. Often utility promises will not have a catch, instead - delegating the error handling to the caller. - - - Parameter only: The specific error to be caught and handled. - - Parameter on: The queue to which the provided closure dispatches. - - Parameter execute: The handler to execute if this promise is rejected with the provided error. - - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. - - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) - */ - func `catch`(_ only: E, on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping() -> Void) -> CancellableCascadingFinalizer where E: Equatable { - let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) - return `catch`(only, on: dispatcher, body) - } - - /** - The provided closure executes when this promise rejects with an error of the type passed in. A final `catch` is still required at the end of the chain. - - Rejecting a promise cascades: rejecting all subsequent promises (unless - recover is invoked) thus you will typically place your catch at the end - of a chain. Often utility promises will not have a catch, instead - delegating the error handling to the caller. - - - Parameter only: The error type to be caught and handled. - - Parameter on: The queue to which the provided closure dispatches. - - Parameter execute: The handler to execute if this promise is rejected with the provided error type. - - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) - */ - func `catch`(_ only: E.Type, on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) -> Void) -> CancellableCascadingFinalizer { - let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) - return `catch`(only, on: dispatcher, policy: policy, body) - } -} - -public extension CancellableCatchMixin where C.T == Void { - /** - The provided closure executes when this cancellable promise rejects. - - This variant of `recover` ensures that no error is thrown from the handler and allows specifying a catch policy. - - - Parameter on: The queue to which the provided closure dispatches. - - Parameter policy: The default policy does not execute your handler for cancellation errors. - - Parameter body: The handler to execute if this promise is rejected. - - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) - */ - func recover(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) throws -> Void) -> CancellablePromise { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return recover(on: dispatcher, policy: policy, body) - } - - /** - The provided closure executes when this cancellable promise rejects with the specific error passed in. - - Unlike `catch`, `recover` continues the chain. - Use `recover` in circumstances where recovering the chain from certain errors is a possibility. - - - Parameter only: The specific error to be recovered. - - Parameter on: The queue to which the provided closure dispatches. - - Parameter body: The handler to execute if this promise is rejected with the provided error. - - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. - - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) - */ - func recover(_ only: E, on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping() -> Void) -> CancellablePromise where E: Equatable { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return recover(only, on: dispatcher, body) - } - - /** - The provided closure executes when this cancellable promise rejects with an error of the type passed in. - - Unlike `catch`, `recover` continues the chain. - Use `recover` in circumstances where recovering the chain from certain errors is a possibility. - - - Parameter only: The error type to be recovered. - - Parameter on: The queue to which the provided closure dispatches. - - Parameter body: The handler to execute if this promise is rejected with the provided error type. - - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) - */ - func recover(_ only: E.Type, on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> Void) -> CancellablePromise { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return recover(only, on: dispatcher, policy: policy, body) - } -} diff --git a/Sources/Wrappers/CatchWrappers.swift b/Sources/Wrappers/CatchWrappers.swift new file mode 100644 index 000000000..cace7ff59 --- /dev/null +++ b/Sources/Wrappers/CatchWrappers.swift @@ -0,0 +1,73 @@ +import Dispatch + +public extension _PMKCatchWrappers { + + /** + The provided closure executes when this promise rejects. + + Rejecting a promise cascades: rejecting all subsequent promises (unless + recover is invoked) thus you will typically place your catch at the end + of a chain. Often utility promises will not have a catch, instead + delegating the error handling to the caller. + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter flags: `DispatchWorkItemFlags` to be applied when dispatching. + - Parameter policy: The default policy does not execute your handler for cancellation errors. + - Parameter body: The handler to execute if this promise is rejected. + - Returns: A promise finalizer. + - SeeAlso: [Cancellation](http://https://github.com/mxcl/PromiseKit/blob/master/Documents/CommonPatterns.md#cancellation/docs/) + */ + @discardableResult + func `catch`(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) -> Void) -> Finalizer { + let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) + return `catch`(on: dispatcher, policy: policy, body) + } + + /** + The provided closure executes when this promise rejects with the specific error passed in. A final `catch` is still required at the end of the chain. + + Rejecting a promise cascades: rejecting all subsequent promises (unless + recover is invoked) thus you will typically place your catch at the end + of a chain. Often utility promises will not have a catch, instead + delegating the error handling to the caller. + + - Parameter only: The specific error to be caught and handled (e.g., `PMKError.emptySequence`). + - Parameter on: The queue to which the provided closure dispatches. + - Parameter flags: `DispatchWorkItemFlags` to be applied when dispatching. + - Parameter body: The handler to execute if this promise is rejected with the provided error. + - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. + - SeeAlso: [Cancellation](http://promisekit.org/docs/) + */ + func `catch`(_ only: E, on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping() -> Void) + -> CascadingFinalizer where E: Equatable + { + let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) + return `catch`(only, on: dispatcher, body) + } + + /** + The provided closure executes when this promise rejects with an error of the type passed in. A final `catch` is still required at the end of the chain. + + Rejecting a promise cascades: rejecting all subsequent promises (unless + recover is invoked) thus you will typically place your catch at the end + of a chain. Often utility promises will not have a catch, instead + delegating the error handling to the caller. + + - Parameter only: The error type to be caught and handled (e.g., `PMKError`). + - Parameter on: The queue to which the provided closure dispatches. + - Parameter flags: `DispatchWorkItemFlags` to be applied when dispatching. + - Parameter policy: A `CatchPolicy` that further constrains the errors this handler will see. E.g., if + you are receiving `PMKError` errors, do you want to see even those that result from cancellation? + - Parameter body: The handler to execute if this promise is rejected with the provided error type. + - SeeAlso: [Cancellation](http://promisekit.org/docs/) + */ + func `catch`(_ only: E.Type, on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, + policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) -> Void) -> CascadingFinalizer + { + let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) + return `catch`(only, on: dispatcher, policy: policy, body) + } + +} + + diff --git a/Sources/Wrappers/EnsureWrappers.swift b/Sources/Wrappers/EnsureWrappers.swift new file mode 100644 index 000000000..592890ee7 --- /dev/null +++ b/Sources/Wrappers/EnsureWrappers.swift @@ -0,0 +1,51 @@ +import Dispatch + +public extension _PMKSharedWrappers { + + /** + The provided closure executes when this promise resolves, whether it rejects or not. + + firstly { + UIApplication.shared.networkActivityIndicatorVisible = true + }.done { + //… + }.ensure { + UIApplication.shared.networkActivityIndicatorVisible = false + }.catch { + //… + } + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter flags: `DispatchWorkItemFlags` to be applied when dispatching. + - Parameter body: The closure that executes when this promise resolves. + - Returns: A new promise, resolved with this promise’s resolution. + */ + func ensure(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping () -> Void) -> BaseOfT { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return ensure(on: dispatcher, body) + } + + /** + The provided closure executes when this promise resolves, whether it rejects or not. + The chain waits on the returned `Guarantee`. + + firstly { + setup() + }.done { + //… + }.ensureThen { + teardown() + }.catch { + //… + } + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter flags: `DispatchWorkItemFlags` to be applied when dispatching. + - Parameter body: The closure that executes when this promise resolves. + - Returns: A new promise, resolved with this promise’s resolution. + */ + func ensureThen(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping () -> VoidReturn) -> BaseOfT { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return ensureThen(on: dispatcher, body) + } +} diff --git a/Sources/Wrappers/FinallyWrappers.swift b/Sources/Wrappers/FinallyWrappers.swift new file mode 100644 index 000000000..e69a1a156 --- /dev/null +++ b/Sources/Wrappers/FinallyWrappers.swift @@ -0,0 +1,11 @@ +import Dispatch + +public extension _PMKFinallyWrappers { + /// `finally` is the same as `ensure`, but it is not chainable + @discardableResult + func finally(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping () -> Void) -> FinallyReturn { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return finally(on: dispatcher, body) + } +} + diff --git a/Sources/Wrappers/GuaranteeWrappers.swift b/Sources/Wrappers/GuaranteeWrappers.swift new file mode 100644 index 000000000..a3bd2d404 --- /dev/null +++ b/Sources/Wrappers/GuaranteeWrappers.swift @@ -0,0 +1,39 @@ +// Since Guarantees have no error path, closures in the API are nonthrowing, which +// makes them different from the shared Promise/CancellablePromise API. + +import Dispatch + +public extension Guarantee { + + @discardableResult + func then(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) -> Guarantee) -> Guarantee { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return then(on: dispatcher, body) + } + + func map(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) -> U) -> Guarantee { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return map(on: dispatcher, body) + } + + @discardableResult + func done(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) -> Void) -> Guarantee { + let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) + return done(on: dispatcher, body) + } + + func get(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping (T) -> Void) -> Guarantee { + let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) + return get(on: dispatcher, body) + } +} + +public extension Guarantee where T: Sequence { + + func thenMap(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) -> Guarantee) -> Guarantee<[U]> { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return thenMap(on: dispatcher, transform) + } + +} + diff --git a/Sources/Wrappers/RecoverWrappers.swift b/Sources/Wrappers/RecoverWrappers.swift new file mode 100644 index 000000000..66740856c --- /dev/null +++ b/Sources/Wrappers/RecoverWrappers.swift @@ -0,0 +1,275 @@ +import Dispatch + +public extension _PMKSharedWrappers { + + /** + The provided closure executes when this promise rejects. + + Unlike `catch`, `recover` continues the chain. It can return a replacement promise or rethrow. + Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: + + firstly { + CLLocationManager.requestLocation() + }.recover { error in + guard error == CLError.unknownLocation else { throw error } + return .value(CLLocation.chicago) + } + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter flags: `DispatchWorkItemFlags` to be applied when dispatching. + - Parameter policy: The default policy does not execute your handler for cancellation errors. + - Parameter body: The handler to execute if this promise is rejected. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documents/CommonPatterns.md#cancellation) + */ + func recover(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, + _ body: @escaping(Error) throws -> U) -> BaseOfT where U.T == T + { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return recover(on: dispatcher, policy: policy, body) + } + + /** + The provided closure executes when this promise rejects with the specific error passed in. + + Unlike `catch`, `recover` continues the chain. It can return a replacement promise or rethrow. + Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: + + firstly { + CLLocationManager.requestLocation() + }.recover(CLError.unknownLocation) { + return .value(CLLocation.chicago) + } + + - Parameter only: The specific error to be recovered (e.g., `PMKError.emptySequence`) + - Parameter on: The queue to which the provided closure dispatches. + - Parameter flags: `DispatchWorkItemFlags` to be applied when dispatching. + - Parameter body: The handler to execute if this promise is rejected with the provided error. + - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. + - SeeAlso: [Cancellation](http://promisekit.org/docs/) + */ + func recover(_ only: E, on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, + _ body: @escaping() throws -> U) -> BaseOfT where U.T == T, E: Equatable + { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return recover(only, on: dispatcher, body) + } + + /** + The provided closure executes when this promise rejects with an error of the type passed in. + + Unlike `catch`, `recover` continues the chain. It can return a replacement promise or rethrow. + Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: + + firstly { + API.fetchData() + }.recover(FetchError.self) { error in + guard case .missingImage(let partialData) = error else { throw error } + //… + return .value(dataWithDefaultImage) + } + + - Parameter only: The error type to be recovered (e.g., `PMKError`). + - Parameter on: The queue to which the provided closure dispatches. + - Parameter flags: `DispatchWorkItemFlags` to be applied when dispatching. + - Parameter policy: The default policy does not execute your handler for cancellation errors. + - Parameter body: The handler to execute if this promise is rejected with the provided error type. + - SeeAlso: [Cancellation](http://promisekit.org/docs/) + */ + func recover(_ only: E.Type, on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, + policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> U) -> BaseOfT where U.T == T + { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return recover(only, on: dispatcher, policy: policy, body) + } +} + +public extension _PMKSharedVoidWrappers { + + /** + The provided closure executes when this promise rejects. + + This variant of `recover` ensures that no error is thrown from the handler + and allows you to specify a catch policy. + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter flags: `DispatchWorkItemFlags` to be applied when dispatching. + - Parameter policy: The default policy does not execute your handler for cancellation errors. + - Parameter body: The handler to execute if this promise is rejected. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documents/CommonPatterns.md#cancellation) + */ + func recover(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, + _ body: @escaping(Error) throws -> Void) -> BaseOfT + { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return recover(on: dispatcher, policy: policy, body) + } + + /** + The provided closure executes when this promise rejects with the specific error passed in. + + Unlike `catch`, `recover` continues the chain. It can return a replacement promise or rethrow. + Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: + + - Parameter only: The specific error to be recovered (e.g., `PMKError.emptySequence`) + - Parameter on: The queue to which the provided closure dispatches. + - Parameter flags: `DispatchWorkItemFlags` to be applied when dispatching. + - Parameter body: The handler to execute if this promise is rejected with the provided error. + - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. + - SeeAlso: [Cancellation](http://promisekit.org/docs/) + */ + func recover(_ only: E, on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, + _ body: @escaping() throws -> Void) -> BaseOfT where E: Equatable + { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return recover(only, on: dispatcher, body) + } + + /** + The provided closure executes when this promise rejects with an error of the type passed in. + + Unlike `catch`, `recover` continues the chain. It can return a replacement promise or rethrow. + Use `recover` in circumstances where recovering the chain from certain errors is a possibility. + + - Parameter only: The error type to be recovered (e.g., `PMKError`). + - Parameter on: The queue to which the provided closure dispatches. + - Parameter flags: `DispatchWorkItemFlags` to be applied when dispatching. + - Parameter policy: The default policy does not execute your handler for cancellation errors. + - Parameter body: The handler to execute if this promise is rejected with the provided error type. + - SeeAlso: [Cancellation](http://promisekit.org/docs/) + */ + func recover(_ only: E.Type, on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, + policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> Void) -> BaseOfT + { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return recover(only, on: dispatcher, policy: policy, body) + } +} + +public extension CatchMixin { + + /** + The provided closure executes when this promise rejects. + This variant of `recover` requires the handler to return a Guarantee; your closure cannot `throw`. + + It is logically impossible for this variant to accept a `catchPolicy`. All errors will be presented + to your closure for processing. + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter flags: `DispatchWorkItemFlags` to be applied when dispatching. + - Parameter body: The handler to execute if this promise is rejected. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documents/CommonPatterns.md#cancellation) + */ + @discardableResult + func recover(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Error) -> Guarantee) -> Guarantee { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return recover(on: dispatcher, body) + } +} + +public extension CatchMixin where T == Void { + + /** + The provided closure executes when this promise rejects. + + This variant of `recover` is specialized for `Void` promises and de-errors your chain, + returning a `Guarantee`. Thus, you cannot `throw` and you must handle all error types, + including cancellation. + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter flags: `DispatchWorkItemFlags` to be applied when dispatching. + - Parameter body: The handler to execute if this promise is rejected. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documents/CommonPatterns.md#cancellation) + */ + @discardableResult + func recover(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Error) -> Void) -> Guarantee { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return recover(on: dispatcher, body) + } +} + +public extension CancellableCatchMixin { + + /** + The provided closure executes when this cancellable promise rejects. + + Unlike `catch`, `recover` continues the chain. It can return a replacement promise or rethrow. + Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: + + let context = firstly { + CLLocationManager.requestLocation() + }.recover { error in + guard error == CLError.unknownLocation else { throw error } + return .value(CLLocation.chicago) + }.cancelContext + + //… + + context.cancel() + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter flags: `DispatchWorkItemFlags` to be applied when dispatching. + - Parameter policy: The default policy does not execute your handler for cancellation errors. + - Parameter body: The handler to execute if this promise is rejected. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + */ + func recover(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, + policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) throws -> V) -> CancellablePromise where V.U.T == C.T + { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return recover(on: dispatcher, policy: policy, body) + } + + /** + The provided closure executes when this cancellable promise rejects with the specific error passed in. + + Unlike `catch`, `recover` continues the chain. It can return a replacement promise or rethrow. + Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: + + firstly { + CLLocationManager.requestLocation() + }.recover(CLError.unknownLocation) { + return .value(CLLocation.chicago) + } + + - Parameter only: The specific error to be recovered (e.g., `PMKError.emptySequence`) + - Parameter on: The queue to which the provided closure dispatches. + - Parameter flags: `DispatchWorkItemFlags` to be applied when dispatching. + - Parameter body: The handler to execute if this promise is rejected with the provided error. + - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + */ + func recover(_ only: E, on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, + _ body: @escaping() throws -> V) -> CancellablePromise where V.U.T == C.T, E: Equatable + { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return recover(only, on: dispatcher, body) + } + + /** + The provided closure executes when this cancellable promise rejects with an error of the type passed in. + + Unlike `catch`, `recover` continues the chain. It can return a replacement promise or rethrow. + Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example: + + firstly { + API.fetchData() + }.recover(FetchError.self) { error in + guard case .missingImage(let partialData) = error else { throw error } + //… + return .value(dataWithDefaultImage) + } + + - Parameter only: The error type to be recovered. + - Parameter on: The queue to which the provided closure dispatches. + - Parameter flags: `DispatchWorkItemFlags` to be applied when dispatching. + - Parameter policy: The default policy does not execute your handler for cancellation errors. + - Parameter body: The handler to execute if this promise is rejected with the provided error type. + - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) + */ + func recover(_ only: E.Type, on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, + policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> V) -> CancellablePromise where V.U.T == C.T + { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return recover(only, on: dispatcher, policy: policy, body) + } +} diff --git a/Sources/Wrappers/SequenceWrappers.swift b/Sources/Wrappers/SequenceWrappers.swift new file mode 100644 index 000000000..afe2e5e98 --- /dev/null +++ b/Sources/Wrappers/SequenceWrappers.swift @@ -0,0 +1,261 @@ +import Dispatch + +public extension Thenable where T: Sequence { + /** + `Promise<[T]>` => `T` -> `U` => `Promise<[U]>` + + firstly { + .value([1,2,3]) + }.mapValues { integer in + integer * 2 + }.done { + // $0 => [2,4,6] + } + */ + func mapValues(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U]> { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return mapValues(on: dispatcher, transform) + } + + /** + `Promise<[T]>` => `T` -> `[U]` => `Promise<[U]>` + + firstly { + .value([1,2,3]) + }.flatMapValues { integer in + [integer, integer] + }.done { + // $0 => [1,1,2,2,3,3] + } + */ + func flatMapValues(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U.Iterator.Element]> { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return flatMapValues(on: dispatcher, transform) + } + + /** + `Promise<[T]>` => `T` -> `U?` => `Promise<[U]>` + + firstly { + .value(["1","2","a","3"]) + }.compactMapValues { + Int($0) + }.done { + // $0 => [1,2,3] + } + */ + func compactMapValues(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) throws -> U?) -> Promise<[U]> { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return compactMapValues(on: dispatcher, transform) + } + + /** + `Promise<[T]>` => `T` -> `Promise` => `Promise<[U]>` + + firstly { + .value([1,2,3]) + }.thenMap { integer in + .value(integer * 2) + }.done { + // $0 => [2,4,6] + } + */ + func thenMap(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U.T]> { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return thenMap(on: dispatcher, transform) + } + + /** + `Promise<[T]>` => `T` -> `Promise<[U]>` => `Promise<[U]>` + + firstly { + .value([1,2,3]) + }.thenFlatMap { integer in + .value([integer, integer]) + }.done { + // $0 => [1,1,2,2,3,3] + } + */ + func thenFlatMap(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U.T.Iterator.Element]> where U.T: Sequence { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return thenFlatMap(on: dispatcher, transform) + } + + /** + `Promise<[T]>` => `T` -> Bool => `Promise<[U]>` + + firstly { + .value([1,2,3]) + }.filterValues { + $0 > 1 + }.done { + // $0 => [2,3] + } + */ + func filterValues(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ isIncluded: @escaping (T.Iterator.Element) -> Bool) -> Promise<[T.Iterator.Element]> { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return filterValues(on: dispatcher, isIncluded) + } +} + +public extension Thenable where T: Collection { + func firstValue(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, where test: @escaping (T.Iterator.Element) -> Bool) -> Promise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return firstValue(on: dispatcher, where: test) + } +} + +public extension Thenable where T: Sequence, T.Iterator.Element: Comparable { + /// - Returns: a promise fulfilled with the sorted values of this `Sequence`. + func sortedValues(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil) -> Promise<[T.Iterator.Element]> { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return sortedValues(on: dispatcher) + } +} + +public extension CancellableThenable where U.T: Sequence { + /** + `CancellablePromise<[U.T]>` => `U.T` -> `V` => `CancellablePromise<[V]>` + + firstly { + cancellize(Promise.value([1,2,3])) + }.mapValues { integer in + integer * 2 + }.done { + // $0 => [2,4,6] + } + */ + func mapValues(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(U.T.Iterator.Element) throws -> V) -> CancellablePromise<[V]> { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return mapValues(on: dispatcher, transform) + } + + /** + `CancellablePromise<[U.T]>` => `U.T` -> `[V]` => `CancellablePromise<[V]>` + + firstly { + cancellize(Promise.value([1,2,3])) + }.flatMapValues { integer in + [integer, integer] + }.done { + // $0 => [1,1,2,2,3,3] + } + */ + func flatMapValues(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(U.T.Iterator.Element) throws -> V) -> CancellablePromise<[V.Iterator.Element]> { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return flatMapValues(on: dispatcher, transform) + } + + /** + `CancellablePromise<[U.T]>` => `U.T` -> `V?` => `CancellablePromise<[V]>` + + firstly { + cancellize(Promise.value(["1","2","a","3"])) + }.compactMapValues { + Int($0) + }.done { + // $0 => [1,2,3] + } + */ + func compactMapValues(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(U.T.Iterator.Element) throws -> V?) -> CancellablePromise<[V]> { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return compactMapValues(on: dispatcher, transform) + } + + /** + `CancellablePromise<[U.T]>` => `U.T` -> `CancellablePromise` => `CancellablePromise<[V]>` + + firstly { + cancellize(Promise.value([1,2,3])) + }.thenMap { integer in + cancellize(Promise.value(integer * 2)) + }.done { + // $0 => [2,4,6] + } + */ + func thenMap(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(U.T.Iterator.Element) throws -> V) -> CancellablePromise<[V.U.T]> { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return thenMap(on: dispatcher, transform) + } + + /** + `CancellablePromise<[U.T]>` => `U.T` -> `Promise` => `CancellablePromise<[V]>` + + firstly { + Promise.value([1,2,3]) + }.cancellize().thenMap { integer in + .value(integer * 2) + }.done { + // $0 => [2,4,6] + } + */ + func thenMap(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(U.T.Iterator.Element) throws -> V) -> CancellablePromise<[V.T]> { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return thenMap(on: dispatcher, transform) + } + + /** + `CancellablePromise<[T]>` => `T` -> `CancellablePromise<[U]>` => `CancellablePromise<[U]>` + + firstly { + cancellize(Promise.value([1,2,3])) + }.thenFlatMap { integer in + cancellize(Promise.value([integer, integer])) + }.done { + // $0 => [1,1,2,2,3,3] + } + */ + func thenFlatMap(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(U.T.Iterator.Element) throws -> V) -> CancellablePromise<[V.U.T.Iterator.Element]> where V.U.T: Sequence { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return thenFlatMap(on: dispatcher, transform) + } + + /** + `CancellablePromise<[T]>` => `T` -> `Promise<[U]>` => `CancellablePromise<[U]>` + + firstly { + Promise.value([1,2,3]) + }.cancellize().thenFlatMap { integer in + .value([integer, integer]) + }.done { + // $0 => [1,1,2,2,3,3] + } + */ + func thenFlatMap(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(U.T.Iterator.Element) throws -> V) -> CancellablePromise<[V.T.Iterator.Element]> where V.T: Sequence { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return thenFlatMap(on: dispatcher, transform) + } + + /** + `CancellablePromise<[T]>` => `T` -> Bool => `CancellablePromise<[U]>` + + firstly { + cancellize(Promise.value([1,2,3])) + }.filterValues { + $0 > 1 + }.done { + // $0 => [2,3] + } + */ + func filterValues(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ isIncluded: @escaping (U.T.Iterator.Element) -> Bool) -> CancellablePromise<[U.T.Iterator.Element]> { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return filterValues(on: dispatcher, isIncluded) + } +} + +public extension CancellableThenable where U.T: Collection { + func firstValue(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, where test: @escaping (U.T.Iterator.Element) -> Bool) -> CancellablePromise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return firstValue(on: dispatcher, where: test) + } +} + +public extension CancellableThenable where U.T: Sequence, U.T.Iterator.Element: Comparable { + /// - Returns: a cancellable promise fulfilled with the sorted values of this `Sequence`. + func sortedValues(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil) -> CancellablePromise<[U.T.Iterator.Element]> { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return sortedValues(on: dispatcher) + } +} + + diff --git a/Sources/Wrappers/ThenableWrappers.swift b/Sources/Wrappers/ThenableWrappers.swift new file mode 100644 index 000000000..c9ea63b72 --- /dev/null +++ b/Sources/Wrappers/ThenableWrappers.swift @@ -0,0 +1,240 @@ +import Dispatch + +public extension _PMKSharedWrappers { + + /** + The provided closure is executed when this promise is resolved. + + Equivalent to `map { x -> Void in`, but since we force the `Void` return Swift + is happier and gives you less hassle about your closure’s qualification. + + firstly { + URLSession.shared.dataTask(.promise, with: url) + }.done { response in + print(response.data) + } + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter flags: `DispatchWorkItemFlags` to be applied when dispatching. + - Parameter body: The closure that is executed when this Promise is fulfilled. + - Returns: A new promise fulfilled as `Void`. + */ + func done(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) throws -> Void) -> BaseOfVoid { + let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) + return done(on: dispatcher, body) + } + + /** + The provided closure is executed when this promise is resolved. + + This is like `done` but it returns the same value that the handler is fed. + `get` immutably accesses the fulfilled value; the returned Promise maintains that value. + + firstly { + .value(1) + }.get { foo in + print(foo, " is 1") + }.done { foo in + print(foo, " is 1") + }.done { foo in + print(foo, " is Void") + } + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter flags: `DispatchWorkItemFlags` to be applied when dispatching. + - Parameter body: The closure that is executed when this Promise is fulfilled. + - Returns: A new promise that is resolved with the value that the handler is fed. + */ + func get(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping (T) throws -> Void) -> BaseOfT { + let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) + return get(on: dispatcher, body) + } + + /** + The provided closure is executed with promise result. + + This is like `get` but provides the Result of the Promise so you can inspect the value of the chain at this point without causing any side effects. + + promise.tap{ print($0) }.then{ /*…*/ } + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter flags: `DispatchWorkItemFlags` to be applied when dispatching. + - Parameter body: The closure that is executed with Result of Promise. + - Returns: A new promise that is resolved with the result that the handler is fed. + */ + func tap(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Result) -> Void) -> BaseOfT { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return tap(on: dispatcher, body) + } +} + +public extension Thenable { + + /** + The provided closure executes when this promise resolves. + + This allows chaining promises. The promise returned by the provided closure is resolved before the promise returned by this closure resolves. + + firstly { + URLSession.shared.dataTask(.promise, with: url1) + }.then { response in + transform(data: response.data) + }.done { transformation in + //… + } + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter flags: `DispatchWorkItemFlags` to be applied when dispatching. + - Parameter body: The closure that executes when this promise fulfills. It must return a promise. + - Returns: A new promise that resolves when the promise returned from the provided closure resolves. + */ + func then(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) throws -> U) -> Promise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return then(on: dispatcher, body) + } + + /** + The provided closure is executed when this promise is resolved. + + This is like `then` but it requires the closure to return a non-promise. + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter flags: `DispatchWorkItemFlags` to be applied when dispatching. + - Parameter transform: The closure that is executed when this Promise is fulfilled. It must return a non-promise. + - Returns: A new promise that is resolved with the value returned from the provided closure. For example: + + firstly { + URLSession.shared.dataTask(.promise, with: url1) + }.map { response in + response.data.length + }.done { length in + //… + } + */ + func map(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T) throws -> U) -> Promise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return map(on: dispatcher, transform) + } + + /** + The provided closure is executed when this promise is resolved. + + In your closure return an `Optional`, if you return `nil` the resulting promise is rejected with `PMKError.compactMap`, otherwise the promise is fulfilled with the unwrapped value. + + firstly { + URLSession.shared.dataTask(.promise, with: url) + }.compactMap { + try JSONSerialization.jsonObject(with: $0.data) as? [String: String] + }.done { dictionary in + //… + }.catch { + // either `PMKError.compactMap` or a `JSONError` + } + */ + func compactMap(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T) throws -> U?) -> Promise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return compactMap(on: dispatcher, transform) + } +} + +public extension CancellableThenable { + + /** + The provided closure executes when this cancellable promise resolves. + + This allows chaining promises. The cancellable promise returned by the provided closure is resolved before the cancellable promise returned by this closure resolves. + + let context = firstly { + URLSession.shared.dataTask(.promise, with: url1) + }.cancellize().then { response in + transform(data: response.data) // returns a CancellablePromise + }.done { transformation in + //… + }.cancelContext + //… + context.cancel() + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter flags: `DispatchWorkItemFlags` to be applied when dispatching. + - Parameter body: The closure that executes when this cancellable promise fulfills. It must return a cancellable promise. + - Returns: A new cancellable promise that resolves when the promise returned from the provided closure resolves. + */ + func then(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping (U.T) throws -> V) -> CancellablePromise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return then(on: dispatcher, body) + } + + /** + The provided closure executes when this cancellable promise resolves. + + This allows chaining promises. The promise returned by the provided closure is resolved before the cancellable promise returned by this closure resolves. + + let context = firstly { + URLSession.shared.dataTask(.promise, with: url1) + }.cancellize().then { response in + transform(data: response.data) // returns a Promise + }.done { transformation in + //… + }.cancelContext + //… + context.cancel() + + - Parameter on: The dispatcher that executes the provided closure. + - Parameter flags: `DispatchWorkItemFlags` to be applied when dispatching. + - Parameter body: The closure that executes when this cancellable promise fulfills. It must return a promise (not a cancellable promise). + - Returns: A new cancellable promise that resolves when the promise returned from the provided closure resolves. + */ + func then(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping (U.T) throws -> V) -> CancellablePromise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return then(on: dispatcher, body) + } + + /** + The provided closure is executed when this cancellable promise is resolved. + + This is like `then` but it requires the closure to return a non-promise and non-cancellable-promise. + + let context = firstly { + URLSession.shared.dataTask(.promise, with: url1) + }.cancellize().map { response in + response.data.length + }.done { length in + //… + }.cancelContext + //… + context.cancel() + + - Parameter on: The queue to which the provided closure dispatches. + - Parameter flags: `DispatchWorkItemFlags` to be applied when dispatching. + - Parameter transform: The closure that is executed when this CancellablePromise is fulfilled. It must return a non-promise and non-cancellable-promise. + - Returns: A new cancellable promise that is resolved with the value returned from the provided closure. + */ + func map(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping (U.T) throws -> V) -> CancellablePromise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return map(on: dispatcher, transform) + } + + /** + The provided closure is executed when this cancellable promise is resolved. + + In your closure return an `Optional`, if you return `nil` the resulting cancellable promise is rejected + with `PMKError.compactMap`, otherwise the cancellable promise is fulfilled with the unwrapped value. + + let context = firstly { + URLSession.shared.dataTask(.promise, with: url) + }.cancellize().compactMap { + try JSONSerialization.jsonObject(with: $0.data) as? [String: String] + }.done { dictionary in + //… + }.catch { + // either `PMKError.compactMap` or a `JSONError` + }.cancelContext + //… + context.cancel() + */ + func compactMap(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping (U.T) throws -> V?) -> CancellablePromise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return compactMap(on: dispatcher, transform) + } +} + diff --git a/Sources/Wrappers/WrapperProtocols.swift b/Sources/Wrappers/WrapperProtocols.swift new file mode 100644 index 000000000..0b686faac --- /dev/null +++ b/Sources/Wrappers/WrapperProtocols.swift @@ -0,0 +1,82 @@ +import Dispatch + +// These are protocols that define DispatchQueue-based wrappers for functions that are found on multiple +// entities. By putting the wrappers in a separate mixin protocol, they can be added to the objects +// without duplication. +// +// Ideally, we would just add the mixin to protocols such as Thenable. However, Swift (as of v5) +// does not allow protocol extension that add conformance to other protocols. The underlying issue is the risk +// of overlapping conformances. See https://goo.gl/rViwWS. The workaround is to declare conformance on each +// underlying object separately. +// +// Associated types within protocols may not be generic, so there are many functions that can't be genericized +// in this way. For example, anything of the form `func foo(_ body: () -> U) -> Promise` is unrepresentable. +// +// These protocols have to be public to make their contents accessible to users, but the protocols themselves +// should never appear in Xcode or in the documentation. + +public protocol _PMKSharedWrappers { + + associatedtype T + associatedtype BaseOfT + associatedtype BaseOfVoid + associatedtype VoidReturn + + func done(on: Dispatcher, _ body: @escaping(T) throws -> Void) -> BaseOfVoid + func get(on: Dispatcher, _ body: @escaping(T) throws -> Void) -> BaseOfT + func tap(on: Dispatcher, _ body: @escaping(Result) -> Void) -> BaseOfT + + func recover(on: Dispatcher, policy: CatchPolicy, _ body: @escaping(Error) throws -> U) -> BaseOfT where U.T == T + func recover(_ only: E, on: Dispatcher, _ body: @escaping() throws -> U) -> BaseOfT where U.T == T, E: Equatable + func recover(_ only: E.Type, on: Dispatcher, policy: CatchPolicy, _ body: @escaping(E) throws -> U) -> BaseOfT where U.T == T + + func ensure(on: Dispatcher, _ body: @escaping () -> Void) -> BaseOfT + func ensureThen(on: Dispatcher, _ body: @escaping () -> VoidReturn) -> BaseOfT +} + +extension Promise: _PMKSharedWrappers { + public typealias T = T + public typealias BaseOfT = Promise +} + +extension CancellablePromise: _PMKSharedWrappers { + public typealias T = T + public typealias BaseOfT = CancellablePromise +} + +public protocol _PMKSharedVoidWrappers { + + associatedtype BaseOfT + + func recover(on: Dispatcher, policy: CatchPolicy, _ body: @escaping(Error) throws -> Void) -> BaseOfT + func recover(_ only: E, on: Dispatcher, _ body: @escaping() throws -> Void) -> BaseOfT where E: Equatable + func recover(_ only: E.Type, on: Dispatcher, policy: CatchPolicy, _ body: @escaping(E) throws -> Void) -> BaseOfT +} + +extension Promise: _PMKSharedVoidWrappers where T == Void {} +extension CancellablePromise: _PMKSharedVoidWrappers where C.T == Void {} + +public protocol _PMKCatchWrappers { + + associatedtype Finalizer + associatedtype CascadingFinalizer + + func `catch`(on: Dispatcher, policy: CatchPolicy, _ body: @escaping(Error) -> Void) -> Finalizer + func `catch`(_ only: E, on: Dispatcher, _ body: @escaping() -> Void) -> CascadingFinalizer where E: Equatable + func `catch`(_ only: E.Type, on: Dispatcher, policy: CatchPolicy, _ body: @escaping(E) -> Void) -> CascadingFinalizer +} + +extension Promise: _PMKCatchWrappers {} +extension PMKCascadingFinalizer: _PMKCatchWrappers {} +extension CancellablePromise: _PMKCatchWrappers {} +extension CancellableCascadingFinalizer: _PMKCatchWrappers {} + +public protocol _PMKFinallyWrappers { + + associatedtype FinallyReturn + + func finally(on: Dispatcher, _ body: @escaping () -> Void) -> FinallyReturn +} + +extension PMKFinalizer: _PMKFinallyWrappers {} +extension CancellableFinalizer: _PMKFinallyWrappers {} From bc28be9e91b9f435f340ec5a844068ef5d342ef4 Mon Sep 17 00:00:00 2001 From: Garth Snyder Date: Mon, 1 Apr 2019 21:17:36 -0700 Subject: [PATCH 47/81] Use CurrentThreadDispatcher directly in case where exact type isn't known --- Sources/Cancellation/CancellablePromise.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Cancellation/CancellablePromise.swift b/Sources/Cancellation/CancellablePromise.swift index 195a0a740..b6056050c 100644 --- a/Sources/Cancellation/CancellablePromise.swift +++ b/Sources/Cancellation/CancellablePromise.swift @@ -72,7 +72,7 @@ public class CancellablePromise: CancellableThenable, CancellableCatchMixin { // Wrapper promise promise = Promise { seal in reject = seal.reject - bridge.done(on: nil) { + bridge.done(on: CurrentThreadDispatcher()) { seal.fulfill($0) }.catch(policy: .allErrors) { seal.reject($0) From cc117234db4d9be69f72f2bd266d7bfbe2c6f773 Mon Sep 17 00:00:00 2001 From: Garth Snyder Date: Mon, 1 Apr 2019 21:15:23 -0700 Subject: [PATCH 48/81] Test full set of API wrappers for both standard and cancellable promises --- Tests/Cancel/DispatchWrapperTests.swift | 188 ++++++++++++++++++++++++ Tests/Core/DispatchWrapperTests.swift | 187 +++++++++++++++++++++++ 2 files changed, 375 insertions(+) create mode 100644 Tests/Cancel/DispatchWrapperTests.swift create mode 100644 Tests/Core/DispatchWrapperTests.swift diff --git a/Tests/Cancel/DispatchWrapperTests.swift b/Tests/Cancel/DispatchWrapperTests.swift new file mode 100644 index 000000000..8b1597cc7 --- /dev/null +++ b/Tests/Cancel/DispatchWrapperTests.swift @@ -0,0 +1,188 @@ +import Dispatch +import PromiseKit +import XCTest + +// Exercise the whole API through DispatchQueue wrappers. The test here is really +// that everything compiles smoothly, not anything that happens at test time. + +class DispatchWrapperTests: XCTestCase { + + enum TestError: Error, Equatable { + case errorOne + case errorTwo + case errorThree + case errorFour + case errorFive + case errorSix + case errorSeven + case errorEight + } + + enum OtherError: Error, Equatable { + case errorOne + } + + func testWrappedCancellablePromiseThenableAPI() { + let ex = expectation(description: "DispatchQueue Promise API") + Promise.value(42).cancellize().then(on: .global()) { v -> Promise in + Promise.value(v + 10) + }.then(on: .global()) { v -> CancellablePromise in + Promise.value(v + 10).cancellize() + }.map(on: .global()) { v -> Int in + v + 10 + }.get(on: .global()) { + XCTAssert($0 == 72) + }.tap(on: .global()) { result in + if case let .success(x) = result { + XCTAssert(x == 72) + } else { + XCTFail() + } + }.compactMap(on: .global()) { v -> Int in + v + 10 + }.done(on: .global()) { + XCTAssert($0 == 82) + ex.fulfill() + }.catch(on: .global()) { _ in + XCTFail() + } + waitForExpectations(timeout: 1) + } + + func testWrappedCancellablePromiseRecoverAPI() { + + let ex = expectation(description: "DispatchQueue Promise recover API") + var value = 0 + Promise.value(42).cancellize().then { _ -> Promise in + throw TestError.errorOne + // Specific error + }.recover(TestError.errorOne, on: .global()) { () -> Promise in + value += 1 + throw TestError.errorTwo + // Error type + }.recover(TestError.self, on: .global()) { error -> Promise in + XCTAssert(error == .errorTwo) + value += 10 + throw TestError.errorThree + // Any error + }.recover(on: .global()) { error -> Promise in + if let error = error as? TestError { + XCTAssert(error == TestError.errorThree) + } else { + XCTFail() + } + value += 100 + throw TestError.errorFour + // Specific error, cancellable + }.recover(TestError.errorFour, on: .global()) { () -> CancellablePromise in + value += 1_000 + throw TestError.errorTwo + // Error type, cancellable + }.recover(TestError.self, on: .global()) { error -> CancellablePromise in + XCTAssert(error == .errorTwo) + value += 10_000 + throw TestError.errorThree + // Any error, cancellable + }.recover(on: .global()) { error -> CancellablePromise in + if let error = error as? TestError { + XCTAssert(error == TestError.errorThree) + } else { + XCTFail() + } + value += 100_000 + throw TestError.errorFour + }.map(on: .global()) { _ -> Void in + // NOP + // Non-matching specific error + }.recover(TestError.errorThree, on: .global()) { + XCTFail() + // Specific error, void return + }.recover(TestError.errorFour, on: .global()) { () -> Void in + value += 1_000_000 + throw OtherError.errorOne + // Non-matching error class, void return + }.recover(TestError.self, on: .global()) { error -> Void in + XCTFail() + }.recover(OtherError.self, on: .global()) { error in + value += 10_000_000 + throw TestError.errorFive + }.ensure(on: .global()) { + value += 100_000_000 + }.catch { error in + if let error = error as? TestError { + XCTAssert(error == TestError.errorFive) + } else { + XCTFail() + } + value += 1_000_000_000 + ex.fulfill() + } + waitForExpectations(timeout: 1) + XCTAssert(value == 1_111_111_111) + + let g: Any = Promise.value(42).cancellize().recover(on: .global()) { error in + Guarantee.value(52) + } + XCTAssert(g is CancellablePromise) + + let g2: Any = Promise().cancellize().recover(on: .global()) { error -> Void in } + XCTAssert(g2 is CancellablePromise) + } + + func testWrappedCancellablePromiseCatchAPI() { + let ex = expectation(description: "DispatchQueue Promise catch API") + Promise.value(42).cancellize().then(on: .global()) { _ -> Promise in + throw TestError.errorOne + }.catch(OtherError.self, on: .global()) { error in + XCTFail() + }.catch(TestError.errorTwo, on: .global()) { + XCTFail() + }.catch(TestError.self, on: .global()) { error in + XCTAssert(error == .errorOne) + ex.fulfill() + }.cauterize() + waitForExpectations(timeout: 1) + } + + func testWrappedCancellablePromiseEnsureAPI() { + let ex = expectation(description: "DispatchQueue Promise ensure API") + var value = 0 + Promise.value(42).cancellize().ensure(on: .global()) { + value += 1 + }.ensureThen(on: .global()) { () -> CancellablePromise in + value += 10 + return Promise().cancellize() + }.done(on: .global()) { _ in + value += 100 + ex.fulfill() + }.cauterize() + waitForExpectations(timeout: 1) + XCTAssert(value == 111) + } + + func testWrappedCancellablePromiseSequenceAPI() { + let ex = expectation(description: "DispatchQueue Promise sequence API") + Promise.value([42, 52]).cancellize().mapValues(on: .global()) { + $0 + 10 + }.flatMapValues(on: .global()) { + [$0] + }.compactMapValues(on: .global()) { + $0 + }.thenMap(on: .global()) { v -> Promise in + Promise.value(v) + }.thenFlatMap(on: .global()) { v -> Promise<[Int]> in + Promise.value([v]) + }.filterValues(on: .global()) { v -> Bool in + v > 10 + }.firstValue(on: .global()) { v -> Bool in + v > 60 + }.map(on: .global()) { v -> [Int] in + XCTAssert(v == 62) + return [82, 72] + }.sortedValues(on: .global()).done { v in + XCTAssert(v == [72, 82]) + ex.fulfill() + }.cauterize() + waitForExpectations(timeout: 1) + } +} diff --git a/Tests/Core/DispatchWrapperTests.swift b/Tests/Core/DispatchWrapperTests.swift new file mode 100644 index 000000000..52444faef --- /dev/null +++ b/Tests/Core/DispatchWrapperTests.swift @@ -0,0 +1,187 @@ +import Dispatch +import PromiseKit +import XCTest + +// Exercise the whole API through DispatchQueue wrappers. The test here is really +// that everything compiles smoothly, not anything that happens at test time. + +class DispatchWrapperTests: XCTestCase { + + enum TestError: Error, Equatable { + case errorOne + case errorTwo + case errorThree + case errorFour + case errorFive + case errorSix + case errorSeven + case errorEight + } + + enum OtherError: Error, Equatable { + case errorOne + } + + func testWrappedPromiseThenableAPI() { + let ex = expectation(description: "DispatchQueue Promise API") + Promise.value(42).then(on: .global()) { + Promise.value($0 + 10) + }.map(on: .global()) { + $0 + 10 + }.get(on: .global()) { + XCTAssert($0 == 62) + }.tap(on: .global()) { result in + if case let .success(x) = result { + XCTAssert(x == 62) + } else { + XCTFail() + } + }.compactMap(on: .global()) { + $0 + 10 + }.done(on: .global()) { + XCTAssert($0 == 72) + ex.fulfill() + }.catch(on: .global()) { _ in + XCTFail() + } + waitForExpectations(timeout: 1) + } + + func testWrappedPromiseRecoverAPI() { + + let ex = expectation(description: "DispatchQueue Promise recover API") + var value = 0 + Promise.value(42).then { _ -> Promise in + throw TestError.errorOne + // Specific error + }.recover(TestError.errorOne, on: .global()) { () -> Promise in + value += 1 + throw TestError.errorTwo + // Error type + }.recover(TestError.self, on: .global()) { error -> Promise in + XCTAssert(error == .errorTwo) + value += 10 + throw TestError.errorThree + // Any error + }.recover(on: .global()) { error -> Promise in + if let error = error as? TestError { + XCTAssert(error == TestError.errorThree) + } else { + XCTFail() + } + value += 100 + throw TestError.errorFour + }.map(on: .global()) { _ -> Void in + // NOP + // Non-matching specific error + }.recover(TestError.errorThree, on: .global()) { + XCTFail() + // Specific error, void return + }.recover(TestError.errorFour, on: .global()) { () -> Void in + value += 1_000 + throw OtherError.errorOne + // Non-matching error class, void return + }.recover(TestError.self, on: .global()) { error -> Void in + XCTFail() + }.recover(OtherError.self, on: .global()) { error in + value += 10_000 + throw TestError.errorFive + }.ensure(on: .global()) { + value += 100_000 + }.catch { error in + if let error = error as? TestError { + XCTAssert(error == TestError.errorFive) + } else { + XCTFail() + } + value += 1_000_000 + ex.fulfill() + } + waitForExpectations(timeout: 1) + XCTAssert(value == 1_111_111) + + let g: Any = Promise.value(42).recover(on: .global()) { error in + Guarantee.value(52) + } + XCTAssert(g is Guarantee) + + let g2: Any = Promise.value(()).recover(on: .global()) { error -> Void in } + XCTAssert(g2 is Guarantee) + } + + func testWrappedPromiseCatchAPI() { + let ex = expectation(description: "DispatchQueue Promise catch API") + Promise.value(42).then(on: .global()) { _ -> Promise in + throw TestError.errorOne + }.catch(OtherError.self, on: .global()) { error in + XCTFail() + }.catch(TestError.errorTwo, on: .global()) { + XCTFail() + }.catch(TestError.self, on: .global()) { error in + XCTAssert(error == .errorOne) + ex.fulfill() + }.cauterize() + waitForExpectations(timeout: 1) + } + + func testWrappedPromiseEnsureAPI() { + let ex = expectation(description: "DispatchQueue Promise ensure API") + var value = 0 + Promise.value(42).ensure(on: .global()) { + value += 1 + }.ensureThen(on: .global()) { () -> Guarantee in + value += 10 + return Guarantee() + }.done(on: .global()) { _ in + value += 100 + ex.fulfill() + }.cauterize() + waitForExpectations(timeout: 1) + XCTAssert(value == 111) + } + + func testWrappedPromiseSequenceAPI() { + let ex = expectation(description: "DispatchQueue Promise sequence API") + Promise.value([42, 52]).mapValues(on: .global()) { + $0 + 10 + }.flatMapValues(on: .global()) { + [$0] + }.compactMapValues(on: .global()) { + $0 + }.thenMap(on: .global()) { + Promise.value($0) + }.thenFlatMap(on: .global()) { + Promise.value([$0]) + }.filterValues(on: .global()) { + $0 > 10 + }.firstValue(on: .global()) { + $0 > 60 + }.map(on: .global()) { v -> [Int] in + XCTAssert(v == 62) + return [82, 72] + }.sortedValues(on: .global()).done { v in + XCTAssert(v == [72, 82]) + ex.fulfill() + }.cauterize() + waitForExpectations(timeout: 1) + } + + func testWrappedGuaranteeAPI() { + let ex = expectation(description: "DispatchQueue Guarantee API") + Guarantee.value(42).then(on: .global()) { + Guarantee.value($0 + 10) + }.map(on: .global()) { + $0 + 10 + }.get(on: .global()) { + XCTAssert($0 == 62) + }.map { + [$0, $0] + }.thenMap(on: .global()) { + Guarantee.value($0 + 10) + }.done(on: .global()) { + XCTAssert($0 == [72, 72]) + ex.fulfill() + } + waitForExpectations(timeout: 1) + } +} From 3e21581aba479a72f745c5b738d59cd66a77fa69 Mon Sep 17 00:00:00 2001 From: Garth Snyder Date: Sat, 6 Apr 2019 12:59:00 -0700 Subject: [PATCH 49/81] Make Dispatcher type implementation classes final --- Sources/Dispatchers/ConcurrencyLimitedDispatcher.swift | 2 +- Sources/Dispatchers/RateLimitedDispatcher.swift | 2 +- Sources/Dispatchers/StrictRateLimitedDispatcher.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/Dispatchers/ConcurrencyLimitedDispatcher.swift b/Sources/Dispatchers/ConcurrencyLimitedDispatcher.swift index 47525de64..fe7eadbe0 100644 --- a/Sources/Dispatchers/ConcurrencyLimitedDispatcher.swift +++ b/Sources/Dispatchers/ConcurrencyLimitedDispatcher.swift @@ -3,7 +3,7 @@ import Foundation /// A PromiseKit Dispatcher that allows no more than X simultaneous /// executions at once. -public class ConcurrencyLimitedDispatcher: Dispatcher { +public final class ConcurrencyLimitedDispatcher: Dispatcher { let queue: Dispatcher let serializer: DispatchQueue = DispatchQueue(label: "CLD serializer") diff --git a/Sources/Dispatchers/RateLimitedDispatcher.swift b/Sources/Dispatchers/RateLimitedDispatcher.swift index 3a8ddca41..abbe12ad3 100644 --- a/Sources/Dispatchers/RateLimitedDispatcher.swift +++ b/Sources/Dispatchers/RateLimitedDispatcher.swift @@ -19,7 +19,7 @@ import Foundation /// /// 100% thread safe. -public class RateLimitedDispatcher: RateLimitedDispatcherBase { +public final class RateLimitedDispatcher: RateLimitedDispatcherBase { private var tokensInBucket: Double = 0 private var latestAccrual: DispatchTime = DispatchTime.now() diff --git a/Sources/Dispatchers/StrictRateLimitedDispatcher.swift b/Sources/Dispatchers/StrictRateLimitedDispatcher.swift index 5d2644407..2a2883d26 100644 --- a/Sources/Dispatchers/StrictRateLimitedDispatcher.swift +++ b/Sources/Dispatchers/StrictRateLimitedDispatcher.swift @@ -19,7 +19,7 @@ import Foundation /// /// 100% thread safe. -public class StrictRateLimitedDispatcher: RateLimitedDispatcherBase { +public final class StrictRateLimitedDispatcher: RateLimitedDispatcherBase { internal var startTimeHistory: Queue private var immediateDispatchesAvailable: Int From b8c82085bc1235799f098773f2f0d417d466d08e Mon Sep 17 00:00:00 2001 From: Garth Snyder Date: Sun, 7 Apr 2019 22:00:06 -0700 Subject: [PATCH 50/81] Regenerate Linux testing manifests --- Tests/Cancel/XCTestManifests.swift | 14 ++++++++++++++ Tests/Core/XCTestManifests.swift | 15 +++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/Tests/Cancel/XCTestManifests.swift b/Tests/Cancel/XCTestManifests.swift index e9f1aa98b..2973390df 100644 --- a/Tests/Cancel/XCTestManifests.swift +++ b/Tests/Cancel/XCTestManifests.swift @@ -141,6 +141,19 @@ extension CatchableTests { ] } +extension DispatchWrapperTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__DispatchWrapperTests = [ + ("testWrappedCancellablePromiseCatchAPI", testWrappedCancellablePromiseCatchAPI), + ("testWrappedCancellablePromiseEnsureAPI", testWrappedCancellablePromiseEnsureAPI), + ("testWrappedCancellablePromiseRecoverAPI", testWrappedCancellablePromiseRecoverAPI), + ("testWrappedCancellablePromiseSequenceAPI", testWrappedCancellablePromiseSequenceAPI), + ("testWrappedCancellablePromiseThenableAPI", testWrappedCancellablePromiseThenableAPI), + ] +} + extension DispatcherTests { // DO NOT MODIFY: This is autogenerated, use: // `swift test --generate-linuxmain` @@ -398,6 +411,7 @@ public func __allTests() -> [XCTestCaseEntry] { testCase(CancellablePromiseTests.__allTests__CancellablePromiseTests), testCase(CancellationTests.__allTests__CancellationTests), testCase(CatchableTests.__allTests__CatchableTests), + testCase(DispatchWrapperTests.__allTests__DispatchWrapperTests), testCase(DispatcherTests.__allTests__DispatcherTests), testCase(GuaranteeTests.__allTests__GuaranteeTests), testCase(HangTests.__allTests__HangTests), diff --git a/Tests/Core/XCTestManifests.swift b/Tests/Core/XCTestManifests.swift index ff22bfaa9..055e694d6 100644 --- a/Tests/Core/XCTestManifests.swift +++ b/Tests/Core/XCTestManifests.swift @@ -83,6 +83,20 @@ extension CatchableTests { ] } +extension DispatchWrapperTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__DispatchWrapperTests = [ + ("testWrappedGuaranteeAPI", testWrappedGuaranteeAPI), + ("testWrappedPromiseCatchAPI", testWrappedPromiseCatchAPI), + ("testWrappedPromiseEnsureAPI", testWrappedPromiseEnsureAPI), + ("testWrappedPromiseRecoverAPI", testWrappedPromiseRecoverAPI), + ("testWrappedPromiseSequenceAPI", testWrappedPromiseSequenceAPI), + ("testWrappedPromiseThenableAPI", testWrappedPromiseThenableAPI), + ] +} + extension DispatcherTests { // DO NOT MODIFY: This is autogenerated, use: // `swift test --generate-linuxmain` @@ -330,6 +344,7 @@ public func __allTests() -> [XCTestCaseEntry] { testCase(AfterTests.__allTests__AfterTests), testCase(CancellationTests.__allTests__CancellationTests), testCase(CatchableTests.__allTests__CatchableTests), + testCase(DispatchWrapperTests.__allTests__DispatchWrapperTests), testCase(DispatcherTests.__allTests__DispatcherTests), testCase(DispatcherTypeTests.__allTests__DispatcherTypeTests), testCase(GuaranteeTests.__allTests__GuaranteeTests), From f634434ad0ae130a6b4a35dbcf0f7ed55e99d041 Mon Sep 17 00:00:00 2001 From: Nathan Hosselton Date: Mon, 1 Apr 2019 21:47:50 -0400 Subject: [PATCH 51/81] =?UTF-8?q?Require=20parameter=20name=20`only:`=20so?= =?UTF-8?q?=E2=80=A6=20`catch(Error.value)`=20->=20`catch(only:=20Error.va?= =?UTF-8?q?lue)`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/Catchable.swift | 20 ++++----- Tests/Core/CatchableTests.swift | 78 ++++++++++++++++----------------- 2 files changed, 49 insertions(+), 49 deletions(-) diff --git a/Sources/Catchable.swift b/Sources/Catchable.swift index 7fbe603f0..e1c096368 100644 --- a/Sources/Catchable.swift +++ b/Sources/Catchable.swift @@ -55,7 +55,7 @@ public extension CatchMixin { - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func `catch`(_ only: E, on: Dispatcher = conf.D.return, _ body: @escaping() -> Void) -> PMKCascadingFinalizer where E: Equatable { + func `catch`(only: E, on: Dispatcher = conf.D.return, _ body: @escaping() -> Void) -> PMKCascadingFinalizer where E: Equatable { let finalizer = PMKCascadingFinalizer() pipe { switch $0 { @@ -89,7 +89,7 @@ public extension CatchMixin { - Returns: A promise finalizer that accepts additional `catch` clauses. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func `catch`(_ only: E.Type, on: Dispatcher = conf.D.return, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) -> Void) -> PMKCascadingFinalizer { + func `catch`(only: E.Type, on: Dispatcher = conf.D.return, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) -> Void) -> PMKCascadingFinalizer { let finalizer = PMKCascadingFinalizer() pipe { switch $0 { @@ -161,8 +161,8 @@ public class PMKCascadingFinalizer { - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - public func `catch`(_ only: E, on: Dispatcher = conf.D.return, _ body: @escaping() -> Void) -> PMKCascadingFinalizer where E: Equatable { - return pending.promise.catch(only, on: on) { + public func `catch`(only: E, on: Dispatcher = conf.D.return, _ body: @escaping() -> Void) -> PMKCascadingFinalizer where E: Equatable { + return pending.promise.catch(only: only, on: on) { body() } } @@ -181,8 +181,8 @@ public class PMKCascadingFinalizer { - Returns: A promise finalizer that accepts additional `catch` clauses. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - public func `catch`(_ only: E.Type, on: Dispatcher = conf.D.return, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) -> Void) -> PMKCascadingFinalizer { - return pending.promise.catch(only, on: on, policy: policy) { + public func `catch`(only: E.Type, on: Dispatcher = conf.D.return, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) -> Void) -> PMKCascadingFinalizer { + return pending.promise.catch(only: only, on: on, policy: policy) { body($0) } } @@ -262,7 +262,7 @@ public extension CatchMixin { - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func recover(_ only: E, on: Dispatcher = conf.D.map, _ body: @escaping() throws -> U) -> Promise where U.T == T, E: Equatable { + func recover(only: E, on: Dispatcher = conf.D.map, _ body: @escaping() throws -> U) -> Promise where U.T == T, E: Equatable { let rp = Promise(.pending) pipe { switch $0 { @@ -305,7 +305,7 @@ public extension CatchMixin { - Parameter body: The handler to execute if this promise is rejected with the provided error type. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func recover(_ only: E.Type, on: Dispatcher = conf.D.map, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> U) -> Promise where U.T == T { + func recover(only: E.Type, on: Dispatcher = conf.D.map, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> U) -> Promise where U.T == T { let rp = Promise(.pending) pipe { switch $0 { @@ -506,7 +506,7 @@ public extension CatchMixin where T == Void { - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func recover(_ only: E, on: Dispatcher = conf.D.map, _ body: @escaping() throws -> Void) -> Promise where E: Equatable { + func recover(only: E, on: Dispatcher = conf.D.map, _ body: @escaping() throws -> Void) -> Promise where E: Equatable { let rp = Promise(.pending) pipe { switch $0 { @@ -540,7 +540,7 @@ public extension CatchMixin where T == Void { - Parameter body: The handler to execute if this promise is rejected with the provided error type. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func recover(_ only: E.Type, on: Dispatcher = conf.D.map, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> Void) -> Promise { + func recover(only: E.Type, on: Dispatcher = conf.D.map, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> Void) -> Promise { let rp = Promise(.pending) pipe { switch $0 { diff --git a/Tests/Core/CatchableTests.swift b/Tests/Core/CatchableTests.swift index 9a07f434c..a67fb843c 100644 --- a/Tests/Core/CatchableTests.swift +++ b/Tests/Core/CatchableTests.swift @@ -262,7 +262,7 @@ extension CatchableTests { func testCatchOnly() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).catch(Error.dummy) { + Promise(error: Error.dummy).catch(only: Error.dummy) { x.fulfill() }.silenceWarning() @@ -272,9 +272,9 @@ extension CatchableTests { func testCatchOnly_PatternMatch_1() { let x = expectation(description: "Pattern match only Error.dummy") - Promise(error: Error.dummy).catch(Error.dummy) { + Promise(error: Error.dummy).catch(only: Error.dummy) { x.fulfill() - }.catch(Error.cancelled) { + }.catch(only: Error.cancelled) { XCTFail() x.fulfill() }.silenceWarning() @@ -285,10 +285,10 @@ extension CatchableTests { func testCatchOnly_PatternMatch_2() { let x = expectation(description: "Pattern match only Error.dummy") - Promise(error: Error.dummy).catch(Error.cancelled) { + Promise(error: Error.dummy).catch(only: Error.cancelled) { XCTFail() x.fulfill() - }.catch(Error.dummy) { + }.catch(only: Error.dummy) { x.fulfill() }.silenceWarning() @@ -298,7 +298,7 @@ extension CatchableTests { func testCatchOnly_BaseCatchIsNotCalledAfterCatchOnlyExecutes() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).catch(Error.dummy) { + Promise(error: Error.dummy).catch(only: Error.dummy) { x.fulfill() }.catch { _ in XCTFail() @@ -311,7 +311,7 @@ extension CatchableTests { func testCatchOnly_BaseCatchIsCalledWhenCatchOnlyDoesNotExecute() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).catch(Error.cancelled) { + Promise(error: Error.dummy).catch(only: Error.cancelled) { XCTFail() x.fulfill() }.catch { _ in @@ -324,7 +324,7 @@ extension CatchableTests { func testCatchOnly_Type() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).catch(Error.self) { _ in + Promise(error: Error.dummy).catch(only: Error.self) { _ in x.fulfill() }.silenceWarning() @@ -336,7 +336,7 @@ extension CatchableTests { enum Foo: Swift.Error {} - Promise(error: Error.dummy).catch(Foo.self) { _ in + Promise(error: Error.dummy).catch(only: Foo.self) { _ in XCTFail() x.fulfill() }.catch { _ in @@ -349,9 +349,9 @@ extension CatchableTests { func testCatchOnly_Type_PatternMatch_1() { let x = expectation(description: "Pattern match only Error.Type") - Promise(error: Error.dummy).catch(Error.self) { _ in + Promise(error: Error.dummy).catch(only: Error.self) { _ in x.fulfill() - }.catch(Error.dummy) { + }.catch(only: Error.dummy) { XCTFail() x.fulfill() }.silenceWarning() @@ -362,9 +362,9 @@ extension CatchableTests { func testCatchOnly_Type_PatternMatch_2() { let x = expectation(description: "Pattern match only Error.dummy") - Promise(error: Error.dummy).catch(Error.dummy) { + Promise(error: Error.dummy).catch(only: Error.dummy) { x.fulfill() - }.catch(Error.self) { _ in + }.catch(only: Error.self) { _ in XCTFail() x.fulfill() }.silenceWarning() @@ -375,7 +375,7 @@ extension CatchableTests { func testCatchOnly_Type_BaseCatchIsNotCalledAfterCatchOnlyExecutes() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).catch(Error.self) { _ in + Promise(error: Error.dummy).catch(only: Error.self) { _ in x.fulfill() }.catch { _ in XCTFail() @@ -388,7 +388,7 @@ extension CatchableTests { func testCatchOnly_Type_Cancellation_Ignore() { let x = expectation(description: #file + #function) - Promise(error: Error.cancelled).catch(Error.self) { _ in + Promise(error: Error.cancelled).catch(only: Error.self) { _ in XCTFail() x.fulfill() }.catch(policy: .allErrors) { _ in @@ -401,7 +401,7 @@ extension CatchableTests { func testCatchOnly_Type_Cancellation_Handle() { let x = expectation(description: #file + #function) - Promise(error: Error.cancelled).catch(Error.self, policy: .allErrors) { _ in + Promise(error: Error.cancelled).catch(only: Error.self, policy: .allErrors) { _ in x.fulfill() }.catch { _ in XCTFail() @@ -416,10 +416,10 @@ extension CatchableTests { enum Foo: Swift.Error { case bar } - Promise(error: Foo.bar).catch(Error.dummy) { + Promise(error: Foo.bar).catch(only: Error.dummy) { XCTFail() x.fulfill() - }.catch(Foo.self) { _ in + }.catch(only: Foo.self) { _ in x.fulfill() }.silenceWarning() @@ -432,7 +432,7 @@ extension CatchableTests { func testRecoverOnly_Object() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).recover(Error.dummy) { + Promise(error: Error.dummy).recover(only: Error.dummy) { return Promise.value(1) }.done { _ in x.fulfill() @@ -447,7 +447,7 @@ extension CatchableTests { func testRecoverOnly_Object_Ignored() { let x = expectation(description: #file + #function) - Promise.value(1).recover(Error.dummy) { + Promise.value(1).recover(only: Error.dummy) { return Promise(error: Error.dummy) }.done { _ in x.fulfill() @@ -462,7 +462,7 @@ extension CatchableTests { func testRecoverOnly_Object_PatternMatch() { let x = expectation(description: #file + #function) - Promise(error: Error.cancelled).recover(Error.dummy) { + Promise(error: Error.cancelled).recover(only: Error.dummy) { return Promise.value(1) }.done { _ in XCTFail() @@ -477,7 +477,7 @@ extension CatchableTests { func testRecoverOnly_Type() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).recover(Error.self) { _ in + Promise(error: Error.dummy).recover(only: Error.self) { _ in return Promise.value(1) }.done { _ in x.fulfill() @@ -492,7 +492,7 @@ extension CatchableTests { func testRecoverOnly_Type_Ignored() { let x = expectation(description: #file + #function) - Promise.value(1).recover(Error.self) { _ in + Promise.value(1).recover(only: Error.self) { _ in return Promise(error: Error.dummy) }.done { _ in x.fulfill() @@ -509,7 +509,7 @@ extension CatchableTests { enum Foo: Swift.Error {} - Promise(error: Error.dummy).recover(Foo.self) { _ in + Promise(error: Error.dummy).recover(only: Foo.self) { _ in return Promise.value(1) }.done { _ in XCTFail() @@ -524,7 +524,7 @@ extension CatchableTests { func testRecoverOnly_Type_Cancellation_Ignore() { let x = expectation(description: #file + #function) - Promise(error: Error.cancelled).recover(Error.self) { _ in + Promise(error: Error.cancelled).recover(only: Error.self) { _ in return Promise.value(1) }.done { _ in XCTFail() @@ -539,7 +539,7 @@ extension CatchableTests { func testRecoverOnly_Type_Cancellation_Handle() { let x = expectation(description: #file + #function) - Promise(error: Error.cancelled).recover(Error.self, policy: .allErrors) { _ in + Promise(error: Error.cancelled).recover(only: Error.self, policy: .allErrors) { _ in return Promise.value(1) }.done { _ in x.fulfill() @@ -556,9 +556,9 @@ extension CatchableTests { enum Foo: Swift.Error { case bar } - Promise(error: Error.dummy).recover(Foo.self) { _ in + Promise(error: Error.dummy).recover(only: Foo.self) { _ in return Promise(error: Foo.bar) - }.recover(Error.dummy) { + }.recover(only: Error.dummy) { return Promise.value(1) }.done { _ in x.fulfill() @@ -573,7 +573,7 @@ extension CatchableTests { func testRecoverOnly_BaseRecoverIsNotCalledAfterRecoverOnlyExecutes() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).recover(Error.dummy) { + Promise(error: Error.dummy).recover(only: Error.dummy) { return Promise.value(1) }.recover { _ in return Promise(error: Error.dummy) @@ -590,7 +590,7 @@ extension CatchableTests { func testRecoverOnly_Object_DoesNotReturnSelf() { let x = expectation(description: #file + #function) var promise: Promise! - promise = Promise(error: Error.dummy).recover(Error.dummy) { () -> Promise in + promise = Promise(error: Error.dummy).recover(only: Error.dummy) { () -> Promise in return promise } promise.catch { err in @@ -605,7 +605,7 @@ extension CatchableTests { func testRecoverOnly_Type_DoesNotReturnSelf() { let x = expectation(description: #file + #function) var promise: Promise! - promise = Promise(error: Error.dummy).recover(Error.self) { _ -> Promise in + promise = Promise(error: Error.dummy).recover(only: Error.self) { _ -> Promise in return promise } promise.catch { err in @@ -623,7 +623,7 @@ extension CatchableTests { func testRecoverOnly_Object_Void() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).recover(Error.dummy) { + Promise(error: Error.dummy).recover(only: Error.dummy) { return () }.done { x.fulfill() @@ -638,7 +638,7 @@ extension CatchableTests { func testRecoverOnly_Object_Void_Fufilled() { let x = expectation(description: #file + #function) - Promise.value(()).recover(Error.dummy) { + Promise.value(()).recover(only: Error.dummy) { XCTFail() x.fulfill() }.done { @@ -653,7 +653,7 @@ extension CatchableTests { enum Foo: Swift.Error { case bar } - Promise(error: Error.dummy).recover(Foo.bar) { + Promise(error: Error.dummy).recover(only: Foo.bar) { XCTFail() x.fulfill() }.done { @@ -669,7 +669,7 @@ extension CatchableTests { func testRecoverOnly_Type_Void() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).recover(Error.self) { _ in }.done { + Promise(error: Error.dummy).recover(only: Error.self) { _ in }.done { x.fulfill() }.catch { _ in XCTFail() @@ -682,7 +682,7 @@ extension CatchableTests { func testRecoverOnly_Type_Void_Fufilled() { let x = expectation(description: #file + #function) - Promise.value(()).recover(Error.self) { _ in + Promise.value(()).recover(only: Error.self) { _ in XCTFail() x.fulfill() }.done { @@ -697,7 +697,7 @@ extension CatchableTests { enum Foo: Swift.Error { case bar } - Promise(error: Error.dummy).recover(Foo.self) { _ in + Promise(error: Error.dummy).recover(only: Foo.self) { _ in XCTFail() x.fulfill() }.done { @@ -713,7 +713,7 @@ extension CatchableTests { func testRecoverOnly_Type_Void_Rethrow() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).recover(Error.self) { _ in + Promise(error: Error.dummy).recover(only: Error.self) { _ in throw Error.dummy }.done { XCTFail() @@ -728,7 +728,7 @@ extension CatchableTests { func testRecoverOnly_Type_Void_Cancellation_Ignore() { let x = expectation(description: #file + #function) - Promise(error: Error.cancelled).recover(Error.self) { _ in }.done { + Promise(error: Error.cancelled).recover(only: Error.self) { _ in }.done { XCTFail() x.fulfill() }.catch(policy: .allErrors) { _ in From 342c059a46006882678a510832e1b378e166a888 Mon Sep 17 00:00:00 2001 From: Nathan Hosselton Date: Mon, 1 Apr 2019 21:55:39 -0400 Subject: [PATCH 52/81] Allow throwing from `recover(only: E)` closures --- Sources/Catchable.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Sources/Catchable.swift b/Sources/Catchable.swift index e1c096368..2c42bb5b9 100644 --- a/Sources/Catchable.swift +++ b/Sources/Catchable.swift @@ -515,8 +515,7 @@ public extension CatchMixin where T == Void { case .failure(let error as E) where error == only: on.dispatch { do { - try body() - rp.box.seal(.success(())) + rp.box.seal(.success(try body())) } catch { rp.box.seal(.failure(error)) } From 43394a7fe7d0fa5bb3aa68ffec948076ff71a789 Mon Sep 17 00:00:00 2001 From: Nathan Hosselton Date: Mon, 1 Apr 2019 22:09:54 -0400 Subject: [PATCH 53/81] Include error param in catch/recover(only:) bodies rolls back 193ecbc1 --- Sources/Catchable.swift | 16 +++++++------- Tests/Core/CatchableTests.swift | 38 ++++++++++++++++----------------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/Sources/Catchable.swift b/Sources/Catchable.swift index 2c42bb5b9..e3c75e987 100644 --- a/Sources/Catchable.swift +++ b/Sources/Catchable.swift @@ -55,13 +55,13 @@ public extension CatchMixin { - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func `catch`(only: E, on: Dispatcher = conf.D.return, _ body: @escaping() -> Void) -> PMKCascadingFinalizer where E: Equatable { + func `catch`(only: E, on: Dispatcher = conf.D.return, _ body: @escaping(E) -> Void) -> PMKCascadingFinalizer where E: Equatable { let finalizer = PMKCascadingFinalizer() pipe { switch $0 { case .failure(let error as E) where error == only: on.dispatch { - body() + body(error) finalizer.pending.resolver.fulfill(()) } case .failure(let error): @@ -161,9 +161,9 @@ public class PMKCascadingFinalizer { - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - public func `catch`(only: E, on: Dispatcher = conf.D.return, _ body: @escaping() -> Void) -> PMKCascadingFinalizer where E: Equatable { + public func `catch`(only: E, on: Dispatcher = conf.D.return, _ body: @escaping(E) -> Void) -> PMKCascadingFinalizer where E: Equatable { return pending.promise.catch(only: only, on: on) { - body() + body($0) } } @@ -262,7 +262,7 @@ public extension CatchMixin { - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func recover(only: E, on: Dispatcher = conf.D.map, _ body: @escaping() throws -> U) -> Promise where U.T == T, E: Equatable { + func recover(only: E, on: Dispatcher = conf.D.map, _ body: @escaping(E) throws -> U) -> Promise where U.T == T, E: Equatable { let rp = Promise(.pending) pipe { switch $0 { @@ -271,7 +271,7 @@ public extension CatchMixin { case .failure(let error as E) where error == only: on.dispatch { do { - let rv = try body() + let rv = try body(error) guard rv !== rp else { throw PMKError.returnedSelf } rv.pipe(to: rp.box.seal) } catch { @@ -506,7 +506,7 @@ public extension CatchMixin where T == Void { - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func recover(only: E, on: Dispatcher = conf.D.map, _ body: @escaping() throws -> Void) -> Promise where E: Equatable { + func recover(only: E, on: Dispatcher = conf.D.map, _ body: @escaping(E) throws -> Void) -> Promise where E: Equatable { let rp = Promise(.pending) pipe { switch $0 { @@ -515,7 +515,7 @@ public extension CatchMixin where T == Void { case .failure(let error as E) where error == only: on.dispatch { do { - rp.box.seal(.success(try body())) + rp.box.seal(.success(try body(error))) } catch { rp.box.seal(.failure(error)) } diff --git a/Tests/Core/CatchableTests.swift b/Tests/Core/CatchableTests.swift index a67fb843c..774ba1b25 100644 --- a/Tests/Core/CatchableTests.swift +++ b/Tests/Core/CatchableTests.swift @@ -262,7 +262,7 @@ extension CatchableTests { func testCatchOnly() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).catch(only: Error.dummy) { + Promise(error: Error.dummy).catch(only: Error.dummy) { _ in x.fulfill() }.silenceWarning() @@ -272,9 +272,9 @@ extension CatchableTests { func testCatchOnly_PatternMatch_1() { let x = expectation(description: "Pattern match only Error.dummy") - Promise(error: Error.dummy).catch(only: Error.dummy) { + Promise(error: Error.dummy).catch(only: Error.dummy) { _ in x.fulfill() - }.catch(only: Error.cancelled) { + }.catch(only: Error.cancelled) { _ in XCTFail() x.fulfill() }.silenceWarning() @@ -285,10 +285,10 @@ extension CatchableTests { func testCatchOnly_PatternMatch_2() { let x = expectation(description: "Pattern match only Error.dummy") - Promise(error: Error.dummy).catch(only: Error.cancelled) { + Promise(error: Error.dummy).catch(only: Error.cancelled) { _ in XCTFail() x.fulfill() - }.catch(only: Error.dummy) { + }.catch(only: Error.dummy) { _ in x.fulfill() }.silenceWarning() @@ -298,7 +298,7 @@ extension CatchableTests { func testCatchOnly_BaseCatchIsNotCalledAfterCatchOnlyExecutes() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).catch(only: Error.dummy) { + Promise(error: Error.dummy).catch(only: Error.dummy) { _ in x.fulfill() }.catch { _ in XCTFail() @@ -311,7 +311,7 @@ extension CatchableTests { func testCatchOnly_BaseCatchIsCalledWhenCatchOnlyDoesNotExecute() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).catch(only: Error.cancelled) { + Promise(error: Error.dummy).catch(only: Error.cancelled) { _ in XCTFail() x.fulfill() }.catch { _ in @@ -351,7 +351,7 @@ extension CatchableTests { Promise(error: Error.dummy).catch(only: Error.self) { _ in x.fulfill() - }.catch(only: Error.dummy) { + }.catch(only: Error.dummy) { _ in XCTFail() x.fulfill() }.silenceWarning() @@ -362,7 +362,7 @@ extension CatchableTests { func testCatchOnly_Type_PatternMatch_2() { let x = expectation(description: "Pattern match only Error.dummy") - Promise(error: Error.dummy).catch(only: Error.dummy) { + Promise(error: Error.dummy).catch(only: Error.dummy) { _ in x.fulfill() }.catch(only: Error.self) { _ in XCTFail() @@ -416,7 +416,7 @@ extension CatchableTests { enum Foo: Swift.Error { case bar } - Promise(error: Foo.bar).catch(only: Error.dummy) { + Promise(error: Foo.bar).catch(only: Error.dummy) { _ in XCTFail() x.fulfill() }.catch(only: Foo.self) { _ in @@ -432,7 +432,7 @@ extension CatchableTests { func testRecoverOnly_Object() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).recover(only: Error.dummy) { + Promise(error: Error.dummy).recover(only: Error.dummy) { _ in return Promise.value(1) }.done { _ in x.fulfill() @@ -447,7 +447,7 @@ extension CatchableTests { func testRecoverOnly_Object_Ignored() { let x = expectation(description: #file + #function) - Promise.value(1).recover(only: Error.dummy) { + Promise.value(1).recover(only: Error.dummy) { _ in return Promise(error: Error.dummy) }.done { _ in x.fulfill() @@ -462,7 +462,7 @@ extension CatchableTests { func testRecoverOnly_Object_PatternMatch() { let x = expectation(description: #file + #function) - Promise(error: Error.cancelled).recover(only: Error.dummy) { + Promise(error: Error.cancelled).recover(only: Error.dummy) { _ in return Promise.value(1) }.done { _ in XCTFail() @@ -558,7 +558,7 @@ extension CatchableTests { Promise(error: Error.dummy).recover(only: Foo.self) { _ in return Promise(error: Foo.bar) - }.recover(only: Error.dummy) { + }.recover(only: Error.dummy) { _ in return Promise.value(1) }.done { _ in x.fulfill() @@ -573,7 +573,7 @@ extension CatchableTests { func testRecoverOnly_BaseRecoverIsNotCalledAfterRecoverOnlyExecutes() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).recover(only: Error.dummy) { + Promise(error: Error.dummy).recover(only: Error.dummy) { _ in return Promise.value(1) }.recover { _ in return Promise(error: Error.dummy) @@ -590,7 +590,7 @@ extension CatchableTests { func testRecoverOnly_Object_DoesNotReturnSelf() { let x = expectation(description: #file + #function) var promise: Promise! - promise = Promise(error: Error.dummy).recover(only: Error.dummy) { () -> Promise in + promise = Promise(error: Error.dummy).recover(only: Error.dummy) { (_) -> Promise in return promise } promise.catch { err in @@ -623,7 +623,7 @@ extension CatchableTests { func testRecoverOnly_Object_Void() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).recover(only: Error.dummy) { + Promise(error: Error.dummy).recover(only: Error.dummy) { _ in return () }.done { x.fulfill() @@ -638,7 +638,7 @@ extension CatchableTests { func testRecoverOnly_Object_Void_Fufilled() { let x = expectation(description: #file + #function) - Promise.value(()).recover(only: Error.dummy) { + Promise.value(()).recover(only: Error.dummy) { _ in XCTFail() x.fulfill() }.done { @@ -653,7 +653,7 @@ extension CatchableTests { enum Foo: Swift.Error { case bar } - Promise(error: Error.dummy).recover(only: Foo.bar) { + Promise(error: Error.dummy).recover(only: Foo.bar) { _ in XCTFail() x.fulfill() }.done { From 46517fee00ed0d76036b1f9b21e5def9864320b6 Mon Sep 17 00:00:00 2001 From: Garth Snyder Date: Sun, 7 Apr 2019 22:24:13 -0700 Subject: [PATCH 54/81] Duplicate Nathan's partial-catch updates for cancellable promises, update tests --- .../Cancellation/CancellableCatchable.swift | 59 ++++++------- Sources/Wrappers/CatchWrappers.swift | 8 +- Sources/Wrappers/RecoverWrappers.swift | 30 +++---- Sources/Wrappers/WrapperProtocols.swift | 12 +-- Tests/Cancel/CatchableTests.swift | 84 +++++++++---------- Tests/Cancel/DispatchWrapperTests.swift | 22 ++--- Tests/Cancel/DispatcherTests.swift | 52 ++++++------ Tests/Core/CatchableTests.swift | 6 +- Tests/Core/DispatchWrapperTests.swift | 18 ++-- 9 files changed, 147 insertions(+), 144 deletions(-) diff --git a/Sources/Cancellation/CancellableCatchable.swift b/Sources/Cancellation/CancellableCatchable.swift index 181ba31d2..e03b1cf01 100644 --- a/Sources/Cancellation/CancellableCatchable.swift +++ b/Sources/Cancellation/CancellableCatchable.swift @@ -44,8 +44,8 @@ public extension CancellableCatchMixin { - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) */ - func `catch`(_ only: E, on: Dispatcher = conf.D.return, _ body: @escaping() -> Void) -> CancellableCascadingFinalizer where E: Equatable { - return CancellableCascadingFinalizer(self.catchable.catch(only, on: on, body), cancel: self.cancelContext) + func `catch`(only: E, on: Dispatcher = conf.D.return, _ body: @escaping(E) -> Void) -> CancellableCascadingFinalizer where E: Equatable { + return CancellableCascadingFinalizer(self.catchable.catch(only: only, on: on, body), cancel: self.cancelContext) } /** @@ -62,8 +62,8 @@ public extension CancellableCatchMixin { - Parameter execute: The handler to execute if this promise is rejected with the provided error type. - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) */ - func `catch`(_ only: E.Type, on: Dispatcher = conf.D.return, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) -> Void) -> CancellableCascadingFinalizer { - return CancellableCascadingFinalizer(self.catchable.catch(only, on: on, policy: policy, body), cancel: self.cancelContext) + func `catch`(only: E.Type, on: Dispatcher = conf.D.return, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) -> Void) -> CancellableCascadingFinalizer { + return CancellableCascadingFinalizer(self.catchable.catch(only: only, on: on, policy: policy, body), cancel: self.cancelContext) } } @@ -166,8 +166,8 @@ public class CancellableCascadingFinalizer: CancelContextFinalizer { - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) */ - public func `catch`(_ only: E, on: Dispatcher = conf.D.return, _ body: @escaping() -> Void) -> CancellableCascadingFinalizer where E: Equatable { - return CancellableCascadingFinalizer(pmkCascadingFinalizer.catch(only, on: on, body), cancel: cancelContext) + public func `catch`(only: E, on: Dispatcher = conf.D.return, _ body: @escaping(E) -> Void) -> CancellableCascadingFinalizer where E: Equatable { + return CancellableCascadingFinalizer(pmkCascadingFinalizer.catch(only: only, on: on, body), cancel: cancelContext) } /** @@ -183,8 +183,8 @@ public class CancellableCascadingFinalizer: CancelContextFinalizer { - Parameter execute: The handler to execute if this promise is rejected with the provided error type. - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) */ - public func `catch`(_ only: E.Type, on: Dispatcher = conf.D.return, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) -> Void) -> CancellableCascadingFinalizer { - return CancellableCascadingFinalizer(pmkCascadingFinalizer.catch(only, on: on, policy: policy, body), cancel: cancelContext) + public func `catch`(only: E.Type, on: Dispatcher = conf.D.return, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) -> Void) -> CancellableCascadingFinalizer { + return CancellableCascadingFinalizer(pmkCascadingFinalizer.catch(only: only, on: on, policy: policy, body), cancel: cancelContext) } /** @@ -302,20 +302,20 @@ public extension CancellableCatchMixin { - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) */ - func recover(_ only: E, on: Dispatcher = conf.D.map, _ body: @escaping() throws -> V) -> CancellablePromise where V.U.T == C.T, E: Equatable { + func recover(only: E, on: Dispatcher = conf.D.map, _ body: @escaping(E) throws -> V) -> CancellablePromise where V.U.T == C.T, E: Equatable { let cancelItemList = CancelItemList() - let cancelBody = { () throws -> V.U in + let cancelBody = { (error: E) throws -> V.U in _ = self.cancelContext.removeItems(self.cancelItemList, clearList: true) - let rval = try body() - if only.isCancelled { + let rval = try body(error) + if error.isCancelled { self.cancelContext.recover() } self.cancelContext.append(context: rval.cancelContext, thenableCancelItemList: cancelItemList) return rval.thenable } - let promise = self.catchable.recover(only, on: on, cancelBody) + let promise = self.catchable.recover(only: only, on: on, cancelBody) if thenable.result != nil && only.isCancelled { self.cancelContext.recover() } @@ -340,17 +340,17 @@ public extension CancellableCatchMixin { - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) */ - func recover(_ only: E, on: Dispatcher = conf.D.map, _ body: @escaping() throws -> V) -> CancellablePromise where V.T == C.T, E: Equatable { - let cancelBody = { () throws -> V in + func recover(only: E, on: Dispatcher = conf.D.map, _ body: @escaping(E) throws -> V) -> CancellablePromise where V.T == C.T, E: Equatable { + let cancelBody = { (error: E) throws -> V in _ = self.cancelContext.removeItems(self.cancelItemList, clearList: true) - let rval = try body() - if only.isCancelled { + let rval = try body(error) + if error.isCancelled { self.cancelContext.recover() } return rval } - let promise = self.catchable.recover(only, on: on, cancelBody) + let promise = self.catchable.recover(only: only, on: on, cancelBody) let cancellablePromise = CancellablePromise(promise: promise, context: self.cancelContext) if let cancellable = promise.cancellable { self.cancelContext.append(cancellable: cancellable, reject: promise.rejectIfCancelled, thenable: cancellablePromise) @@ -378,7 +378,7 @@ public extension CancellableCatchMixin { - Parameter body: The handler to execute if this promise is rejected with the provided error type. - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) */ - func recover(_ only: E.Type, on: Dispatcher = conf.D.map, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> V) -> CancellablePromise where V.U.T == C.T { + func recover(only: E.Type, on: Dispatcher = conf.D.map, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> V) -> CancellablePromise where V.U.T == C.T { let cancelItemList = CancelItemList() let cancelBody = { (error: E) throws -> V.U in @@ -391,7 +391,7 @@ public extension CancellableCatchMixin { return rval.thenable } - let promise = self.catchable.recover(only, on: on, policy: policy, cancelBody) + let promise = self.catchable.recover(only: only, on: on, policy: policy, cancelBody) if thenable.result != nil && policy == .allErrors { self.cancelContext.recover() } @@ -417,7 +417,7 @@ public extension CancellableCatchMixin { - Parameter body: The handler to execute if this promise is rejected with the provided error type. - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) */ - func recover(_ only: E.Type, on: Dispatcher = conf.D.map, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> V) -> CancellablePromise where V.T == C.T { + func recover(only: E.Type, on: Dispatcher = conf.D.map, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> V) -> CancellablePromise where V.T == C.T { let cancelBody = { (error: E) throws -> V in _ = self.cancelContext.removeItems(self.cancelItemList, clearList: true) let rval = try body(error) @@ -427,7 +427,7 @@ public extension CancellableCatchMixin { return rval } - let promise = self.catchable.recover(only, on: on, policy: policy, cancelBody) + let promise = self.catchable.recover(only: only, on: on, policy: policy, cancelBody) if thenable.result != nil && policy == .allErrors { self.cancelContext.recover() } @@ -581,15 +581,18 @@ public extension CancellableCatchMixin where C.T == Void { - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. You can instead specify e.g. your cancellable error. - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) */ - func recover(_ only: E, on: Dispatcher = conf.D.map, _ body: @escaping() throws -> Void) + func recover(only: E, on: Dispatcher = conf.D.map, _ body: @escaping(E) throws -> Void) -> CancellablePromise where E: Equatable { - let cancelBody = { () throws -> Void in + let cancelBody = { (error: E) throws -> Void in _ = self.cancelContext.removeItems(self.cancelItemList, clearList: true) - try body() + try body(error) + if error.isCancelled { + self.cancelContext.recover() + } } - let promise = self.catchable.recover(only, on: on, cancelBody) + let promise = self.catchable.recover(only: only, on: on, cancelBody) if thenable.result != nil && only.isCancelled { self.cancelContext.recover() } @@ -607,7 +610,7 @@ public extension CancellableCatchMixin where C.T == Void { - Parameter body: The handler to execute if this promise is rejected with the provided error type. - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) */ - func recover(_ only: E.Type, on: Dispatcher = conf.D.map, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> Void) -> CancellablePromise { + func recover(only: E.Type, on: Dispatcher = conf.D.map, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> Void) -> CancellablePromise { let cancelBody = { (error: E) throws -> Void in _ = self.cancelContext.removeItems(self.cancelItemList, clearList: true) try body(error) @@ -616,7 +619,7 @@ public extension CancellableCatchMixin where C.T == Void { } } - let promise = self.catchable.recover(only, on: on, policy: policy, cancelBody) + let promise = self.catchable.recover(only: only, on: on, policy: policy, cancelBody) if thenable.result != nil && policy == .allErrors { self.cancelContext.recover() } diff --git a/Sources/Wrappers/CatchWrappers.swift b/Sources/Wrappers/CatchWrappers.swift index cace7ff59..c8802a47b 100644 --- a/Sources/Wrappers/CatchWrappers.swift +++ b/Sources/Wrappers/CatchWrappers.swift @@ -38,11 +38,11 @@ public extension _PMKCatchWrappers { - Note: Since this method handles only specific errors, supplying a `CatchPolicy` is unsupported. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func `catch`(_ only: E, on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping() -> Void) + func `catch`(only: E, on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(E) -> Void) -> CascadingFinalizer where E: Equatable { let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) - return `catch`(only, on: dispatcher, body) + return `catch`(only: only, on: dispatcher, body) } /** @@ -61,11 +61,11 @@ public extension _PMKCatchWrappers { - Parameter body: The handler to execute if this promise is rejected with the provided error type. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func `catch`(_ only: E.Type, on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, + func `catch`(only: E.Type, on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) -> Void) -> CascadingFinalizer { let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) - return `catch`(only, on: dispatcher, policy: policy, body) + return `catch`(only: only, on: dispatcher, policy: policy, body) } } diff --git a/Sources/Wrappers/RecoverWrappers.swift b/Sources/Wrappers/RecoverWrappers.swift index 66740856c..e253d1a34 100644 --- a/Sources/Wrappers/RecoverWrappers.swift +++ b/Sources/Wrappers/RecoverWrappers.swift @@ -47,11 +47,11 @@ public extension _PMKSharedWrappers { - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func recover(_ only: E, on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, - _ body: @escaping() throws -> U) -> BaseOfT where U.T == T, E: Equatable + func recover(only: E, on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, + _ body: @escaping(E) throws -> U) -> BaseOfT where U.T == T, E: Equatable { let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return recover(only, on: dispatcher, body) + return recover(only: only, on: dispatcher, body) } /** @@ -75,11 +75,11 @@ public extension _PMKSharedWrappers { - Parameter body: The handler to execute if this promise is rejected with the provided error type. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func recover(_ only: E.Type, on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, + func recover(only: E.Type, on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> U) -> BaseOfT where U.T == T { let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return recover(only, on: dispatcher, policy: policy, body) + return recover(only: only, on: dispatcher, policy: policy, body) } } @@ -117,11 +117,11 @@ public extension _PMKSharedVoidWrappers { - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func recover(_ only: E, on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, - _ body: @escaping() throws -> Void) -> BaseOfT where E: Equatable + func recover(only: E, on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, + _ body: @escaping(E) throws -> Void) -> BaseOfT where E: Equatable { let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return recover(only, on: dispatcher, body) + return recover(only: only, on: dispatcher, body) } /** @@ -137,11 +137,11 @@ public extension _PMKSharedVoidWrappers { - Parameter body: The handler to execute if this promise is rejected with the provided error type. - SeeAlso: [Cancellation](http://promisekit.org/docs/) */ - func recover(_ only: E.Type, on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, + func recover(only: E.Type, on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> Void) -> BaseOfT { let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return recover(only, on: dispatcher, policy: policy, body) + return recover(only: only, on: dispatcher, policy: policy, body) } } @@ -238,11 +238,11 @@ public extension CancellableCatchMixin { - Note: Since this method recovers only specific errors, supplying a `CatchPolicy` is unsupported. - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) */ - func recover(_ only: E, on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, - _ body: @escaping() throws -> V) -> CancellablePromise where V.U.T == C.T, E: Equatable + func recover(only: E, on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, + _ body: @escaping(E) throws -> V) -> CancellablePromise where V.U.T == C.T, E: Equatable { let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return recover(only, on: dispatcher, body) + return recover(only: only, on: dispatcher, body) } /** @@ -266,10 +266,10 @@ public extension CancellableCatchMixin { - Parameter body: The handler to execute if this promise is rejected with the provided error type. - SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation) */ - func recover(_ only: E.Type, on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, + func recover(only: E.Type, on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(E) throws -> V) -> CancellablePromise where V.U.T == C.T { let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) - return recover(only, on: dispatcher, policy: policy, body) + return recover(only: only, on: dispatcher, policy: policy, body) } } diff --git a/Sources/Wrappers/WrapperProtocols.swift b/Sources/Wrappers/WrapperProtocols.swift index 0b686faac..c668583b8 100644 --- a/Sources/Wrappers/WrapperProtocols.swift +++ b/Sources/Wrappers/WrapperProtocols.swift @@ -27,8 +27,8 @@ public protocol _PMKSharedWrappers { func tap(on: Dispatcher, _ body: @escaping(Result) -> Void) -> BaseOfT func recover(on: Dispatcher, policy: CatchPolicy, _ body: @escaping(Error) throws -> U) -> BaseOfT where U.T == T - func recover(_ only: E, on: Dispatcher, _ body: @escaping() throws -> U) -> BaseOfT where U.T == T, E: Equatable - func recover(_ only: E.Type, on: Dispatcher, policy: CatchPolicy, _ body: @escaping(E) throws -> U) -> BaseOfT where U.T == T + func recover(only: E, on: Dispatcher, _ body: @escaping(E) throws -> U) -> BaseOfT where U.T == T, E: Equatable + func recover(only: E.Type, on: Dispatcher, policy: CatchPolicy, _ body: @escaping(E) throws -> U) -> BaseOfT where U.T == T func ensure(on: Dispatcher, _ body: @escaping () -> Void) -> BaseOfT func ensureThen(on: Dispatcher, _ body: @escaping () -> VoidReturn) -> BaseOfT @@ -49,8 +49,8 @@ public protocol _PMKSharedVoidWrappers { associatedtype BaseOfT func recover(on: Dispatcher, policy: CatchPolicy, _ body: @escaping(Error) throws -> Void) -> BaseOfT - func recover(_ only: E, on: Dispatcher, _ body: @escaping() throws -> Void) -> BaseOfT where E: Equatable - func recover(_ only: E.Type, on: Dispatcher, policy: CatchPolicy, _ body: @escaping(E) throws -> Void) -> BaseOfT + func recover(only: E, on: Dispatcher, _ body: @escaping(E) throws -> Void) -> BaseOfT where E: Equatable + func recover(only: E.Type, on: Dispatcher, policy: CatchPolicy, _ body: @escaping(E) throws -> Void) -> BaseOfT } extension Promise: _PMKSharedVoidWrappers where T == Void {} @@ -62,8 +62,8 @@ public protocol _PMKCatchWrappers { associatedtype CascadingFinalizer func `catch`(on: Dispatcher, policy: CatchPolicy, _ body: @escaping(Error) -> Void) -> Finalizer - func `catch`(_ only: E, on: Dispatcher, _ body: @escaping() -> Void) -> CascadingFinalizer where E: Equatable - func `catch`(_ only: E.Type, on: Dispatcher, policy: CatchPolicy, _ body: @escaping(E) -> Void) -> CascadingFinalizer + func `catch`(only: E, on: Dispatcher, _ body: @escaping(E) -> Void) -> CascadingFinalizer where E: Equatable + func `catch`(only: E.Type, on: Dispatcher, policy: CatchPolicy, _ body: @escaping(E) -> Void) -> CascadingFinalizer } extension Promise: _PMKCatchWrappers {} diff --git a/Tests/Cancel/CatchableTests.swift b/Tests/Cancel/CatchableTests.swift index 878515d2e..abfdb3630 100644 --- a/Tests/Cancel/CatchableTests.swift +++ b/Tests/Cancel/CatchableTests.swift @@ -385,12 +385,12 @@ extension CatchableTests { } } -/// `Promise.catch(_ only:)` +/// `Promise.catch(only:)` extension CatchableTests { func testCatchOnly() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).cancellize().catch(Error.dummy) { + Promise(error: Error.dummy).cancellize().catch(only: Error.dummy) { _ in x.fulfill() }.catch(policy: .allErrors) { $0.isCancelled ? x.fulfill() : XCTFail() @@ -402,9 +402,9 @@ extension CatchableTests { func testCatchOnly_PatternMatch_1() { let x = expectation(description: "Pattern match only Error.dummy") - Promise(error: Error.dummy).cancellize().catch(Error.dummy) { + Promise(error: Error.dummy).cancellize().catch(only: Error.dummy) { _ in x.fulfill() - }.catch(Error.cancelled) { + }.catch(only: Error.cancelled) { _ in XCTFail() x.fulfill() }.catch(policy: .allErrors) { @@ -417,10 +417,10 @@ extension CatchableTests { func testCatchOnly_PatternMatch_2() { let x = expectation(description: "Pattern match only Error.dummy") - Promise(error: Error.dummy).cancellize().catch(Error.cancelled) { + Promise(error: Error.dummy).cancellize().catch(only: Error.cancelled) { _ in XCTFail() x.fulfill() - }.catch(Error.dummy) { + }.catch(only: Error.dummy) { _ in x.fulfill() }.catch(policy: .allErrors) { $0.isCancelled ? x.fulfill() : XCTFail() @@ -432,7 +432,7 @@ extension CatchableTests { func testCatchOnly_BaseCatchIsNotCalledAfterCatchOnlyExecutes() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).cancellize().catch(Error.dummy) { + Promise(error: Error.dummy).cancellize().catch(only: Error.dummy) { _ in x.fulfill() }.catch { _ in XCTFail() @@ -445,7 +445,7 @@ extension CatchableTests { func testCatchOnly_BaseCatchIsCalledWhenCatchOnlyDoesNotExecute() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).cancellize().catch(Error.cancelled) { + Promise(error: Error.dummy).cancellize().catch(only: Error.cancelled) { _ in XCTFail() x.fulfill() }.catch { _ in @@ -458,7 +458,7 @@ extension CatchableTests { func testCatchOnly_Type() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).cancellize().catch(Error.self) { _ in + Promise(error: Error.dummy).cancellize().catch(only: Error.self) { _ in x.fulfill() }.catch(policy: .allErrors) { $0.isCancelled ? x.fulfill() : XCTFail() @@ -472,7 +472,7 @@ extension CatchableTests { enum Foo: Swift.Error {} - Promise(error: Error.dummy).cancellize().catch(Foo.self) { _ in + Promise(error: Error.dummy).cancellize().catch(only: Foo.self) { _ in XCTFail() x.fulfill() }.catch { _ in @@ -485,9 +485,9 @@ extension CatchableTests { func testCatchOnly_Type_PatternMatch_1() { let x = expectation(description: "Pattern match only Error.Type") - Promise(error: Error.dummy).cancellize().catch(Error.self) { _ in + Promise(error: Error.dummy).cancellize().catch(only: Error.self) { _ in x.fulfill() - }.catch(Error.dummy) { + }.catch(only: Error.dummy) { _ in XCTFail() x.fulfill() }.catch(policy: .allErrors) { @@ -500,9 +500,9 @@ extension CatchableTests { func testCatchOnly_Type_PatternMatch_2() { let x = expectation(description: "Pattern match only Error.dummy") - Promise(error: Error.dummy).cancellize().catch(Error.dummy) { + Promise(error: Error.dummy).cancellize().catch(only: Error.dummy) { _ in x.fulfill() - }.catch(Error.self) { _ in + }.catch(only: Error.self) { _ in XCTFail() x.fulfill() }.catch(policy: .allErrors) { @@ -515,7 +515,7 @@ extension CatchableTests { func testCatchOnly_Type_BaseCatchIsNotCalledAfterCatchOnlyExecutes() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).cancellize().catch(Error.self) { _ in + Promise(error: Error.dummy).cancellize().catch(only: Error.self) { _ in x.fulfill() }.catch { _ in XCTFail() @@ -528,7 +528,7 @@ extension CatchableTests { func testCatchOnly_Type_Cancellation_Ignore() { let x = expectation(description: #file + #function) - Promise(error: Error.cancelled).cancellize().catch(Error.self) { _ in + Promise(error: Error.cancelled).cancellize().catch(only: Error.self) { _ in XCTFail() x.fulfill() }.catch(policy: .allErrors) { _ in @@ -541,7 +541,7 @@ extension CatchableTests { func testCatchOnly_Type_Cancellation_Handle() { let x = expectation(description: #file + #function) - Promise(error: Error.cancelled).cancellize().catch(Error.self, policy: .allErrors) { _ in + Promise(error: Error.cancelled).cancellize().catch(only: Error.self, policy: .allErrors) { _ in x.fulfill() }.catch { _ in XCTFail() @@ -556,10 +556,10 @@ extension CatchableTests { enum Foo: Swift.Error { case bar } - Promise(error: Foo.bar).cancellize().catch(Error.dummy) { + Promise(error: Foo.bar).cancellize().catch(only: Error.dummy) { _ in XCTFail() x.fulfill() - }.catch(Foo.self) { _ in + }.catch(only: Foo.self) { _ in x.fulfill() }.catch(policy: .allErrors) { $0.isCancelled ? x.fulfill() : XCTFail() @@ -569,12 +569,12 @@ extension CatchableTests { } } -/// `Promise.recover(_ only:)` +/// `Promise.recover(only:)` extension CatchableTests { func testRecoverOnly_Object() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).cancellize().recover(Error.dummy) { + Promise(error: Error.dummy).cancellize().recover(only: Error.dummy) { _ in return Promise.value(1) }.done { _ in x.fulfill() @@ -589,7 +589,7 @@ extension CatchableTests { func testRecoverOnly_Object_Ignored() { let x = expectation(description: #file + #function) - Promise.value(1).cancellize().recover(Error.dummy) { + Promise.value(1).cancellize().recover(only: Error.dummy) { _ in return Promise(error: Error.dummy) }.done { _ in x.fulfill() @@ -603,7 +603,7 @@ extension CatchableTests { func testRecoverOnly_Object_PatternMatch() { let x = expectation(description: #file + #function) - Promise(error: Error.cancelled).cancellize().recover(Error.dummy) { + Promise(error: Error.cancelled).cancellize().recover(only: Error.dummy) { _ in return Promise.value(1) }.done { _ in XCTFail() @@ -618,7 +618,7 @@ extension CatchableTests { func testRecoverOnly_Type() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).cancellize().recover(Error.self) { _ in + Promise(error: Error.dummy).cancellize().recover(only: Error.self) { _ in return Promise.value(1) }.done { _ in x.fulfill() @@ -632,7 +632,7 @@ extension CatchableTests { func testRecoverOnly_Type_Ignored() { let x = expectation(description: #file + #function) - Promise.value(1).cancellize().recover(Error.self) { _ in + Promise.value(1).cancellize().recover(only: Error.self) { _ in return Promise(error: Error.dummy) }.done { _ in x.fulfill() @@ -649,7 +649,7 @@ extension CatchableTests { enum Foo: Swift.Error {} - Promise(error: Error.dummy).cancellize().recover(Foo.self) { _ in + Promise(error: Error.dummy).cancellize().recover(only: Foo.self) { _ in return Promise.value(1) }.done { _ in XCTFail() @@ -664,7 +664,7 @@ extension CatchableTests { func testRecoverOnly_Type_Cancellation_Ignore() { let x = expectation(description: #file + #function) - Promise(error: Error.cancelled).cancellize().recover(Error.self) { _ in + Promise(error: Error.cancelled).cancellize().recover(only: Error.self) { _ in return Promise.value(1) }.done { _ in XCTFail() @@ -679,7 +679,7 @@ extension CatchableTests { func testRecoverOnly_Type_Cancellation_Handle() { let x = expectation(description: #file + #function) - Promise(error: Error.cancelled).cancellize().recover(Error.self, policy: .allErrors) { _ in + Promise(error: Error.cancelled).cancellize().recover(only: Error.self, policy: .allErrors) { _ in return Promise.value(1) }.done { _ in x.fulfill() @@ -696,9 +696,9 @@ extension CatchableTests { enum Foo: Swift.Error { case bar } - Promise(error: Error.dummy).cancellize().recover(Foo.self) { _ in + Promise(error: Error.dummy).cancellize().recover(only: Foo.self) { _ in return Promise(error: Foo.bar) - }.recover(Error.dummy) { + }.recover(only: Error.dummy) { _ in return Promise.value(1) }.done { _ in x.fulfill() @@ -713,7 +713,7 @@ extension CatchableTests { func testRecoverOnly_BaseRecoverIsNotCalledAfterRecoverOnlyExecutes() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).cancellize().recover(Error.dummy) { + Promise(error: Error.dummy).cancellize().recover(only: Error.dummy) { _ in return Promise.value(1) }.recover { _ in return Promise(error: Error.dummy) @@ -730,7 +730,7 @@ extension CatchableTests { func testRecoverOnly_Object_DoesNotReturnSelf() { let x = expectation(description: #file + #function) var promise: CancellablePromise! - promise = Promise(error: Error.dummy).cancellize().recover(Error.dummy) { () -> CancellablePromise in + promise = Promise(error: Error.dummy).cancellize().recover(only: Error.dummy) { _ -> CancellablePromise in return promise } promise.catch { err in @@ -745,7 +745,7 @@ extension CatchableTests { func testRecoverOnly_Type_DoesNotReturnSelf() { let x = expectation(description: #file + #function) var promise: CancellablePromise! - promise = Promise(error: Error.dummy).cancellize().recover(Error.self) { _ -> CancellablePromise in + promise = Promise(error: Error.dummy).cancellize().recover(only: Error.self) { _ -> CancellablePromise in return promise } promise.catch(policy: .allErrors) { err in @@ -757,12 +757,12 @@ extension CatchableTests { } } -/// `Promise.recover(_ only:)` +/// `Promise.recover(only:)` extension CatchableTests { func testRecoverOnly_Object_Void() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).cancellize().recover(Error.dummy) { + Promise(error: Error.dummy).cancellize().recover(only: Error.dummy) { _ in return () }.done { x.fulfill() @@ -777,7 +777,7 @@ extension CatchableTests { func testRecoverOnly_Object_Void_Fufilled() { let x = expectation(description: #file + #function) - Promise.value(()).cancellize().recover(Error.dummy) { + Promise.value(()).cancellize().recover(only: Error.dummy) { _ in XCTFail() x.fulfill() }.done { @@ -794,7 +794,7 @@ extension CatchableTests { enum Foo: Swift.Error { case bar } - Promise(error: Error.dummy).cancellize().recover(Foo.bar) { + Promise(error: Error.dummy).cancellize().recover(only: Foo.bar) { _ in XCTFail() x.fulfill() }.done { @@ -810,7 +810,7 @@ extension CatchableTests { func testRecoverOnly_Type_Void() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).cancellize().recover(Error.self) { _ in }.done { + Promise(error: Error.dummy).cancellize().recover(only: Error.self) { _ in }.done { x.fulfill() }.catch { _ in XCTFail() @@ -823,7 +823,7 @@ extension CatchableTests { func testRecoverOnly_Type_Void_Fufilled() { let x = expectation(description: #file + #function) - Promise.value(()).cancellize().recover(Error.self) { _ in + Promise.value(()).cancellize().recover(only: Error.self) { _ in XCTFail() x.fulfill() }.done { @@ -840,7 +840,7 @@ extension CatchableTests { enum Foo: Swift.Error { case bar } - Promise(error: Error.dummy).cancellize().recover(Foo.self) { _ in + Promise(error: Error.dummy).cancellize().recover(only: Foo.self) { _ in XCTFail() x.fulfill() }.done { @@ -856,7 +856,7 @@ extension CatchableTests { func testRecoverOnly_Type_Void_Rethrow() { let x = expectation(description: #file + #function) - Promise(error: Error.dummy).cancellize().recover(Error.self) { _ in + Promise(error: Error.dummy).cancellize().recover(only: Error.self) { _ in throw Error.dummy }.done { XCTFail() @@ -871,7 +871,7 @@ extension CatchableTests { func testRecoverOnly_Type_Void_Cancellation_Ignore() { let x = expectation(description: #file + #function) - Promise(error: Error.cancelled).cancellize().recover(Error.self) { _ in }.done { + Promise(error: Error.cancelled).cancellize().recover(only: Error.self) { _ in }.done { XCTFail() x.fulfill() }.catch(policy: .allErrors) { _ in diff --git a/Tests/Cancel/DispatchWrapperTests.swift b/Tests/Cancel/DispatchWrapperTests.swift index 8b1597cc7..b9d3ba0c8 100644 --- a/Tests/Cancel/DispatchWrapperTests.swift +++ b/Tests/Cancel/DispatchWrapperTests.swift @@ -56,11 +56,11 @@ class DispatchWrapperTests: XCTestCase { Promise.value(42).cancellize().then { _ -> Promise in throw TestError.errorOne // Specific error - }.recover(TestError.errorOne, on: .global()) { () -> Promise in + }.recover(only: TestError.errorOne, on: .global()) { _ -> Promise in value += 1 throw TestError.errorTwo // Error type - }.recover(TestError.self, on: .global()) { error -> Promise in + }.recover(only: TestError.self, on: .global()) { error -> Promise in XCTAssert(error == .errorTwo) value += 10 throw TestError.errorThree @@ -74,11 +74,11 @@ class DispatchWrapperTests: XCTestCase { value += 100 throw TestError.errorFour // Specific error, cancellable - }.recover(TestError.errorFour, on: .global()) { () -> CancellablePromise in + }.recover(only: TestError.errorFour, on: .global()) { _ -> CancellablePromise in value += 1_000 throw TestError.errorTwo // Error type, cancellable - }.recover(TestError.self, on: .global()) { error -> CancellablePromise in + }.recover(only: TestError.self, on: .global()) { error -> CancellablePromise in XCTAssert(error == .errorTwo) value += 10_000 throw TestError.errorThree @@ -94,16 +94,16 @@ class DispatchWrapperTests: XCTestCase { }.map(on: .global()) { _ -> Void in // NOP // Non-matching specific error - }.recover(TestError.errorThree, on: .global()) { + }.recover(only: TestError.errorThree, on: .global()) { _ in XCTFail() // Specific error, void return - }.recover(TestError.errorFour, on: .global()) { () -> Void in + }.recover(only: TestError.errorFour, on: .global()) { _ -> Void in value += 1_000_000 throw OtherError.errorOne // Non-matching error class, void return - }.recover(TestError.self, on: .global()) { error -> Void in + }.recover(only: TestError.self, on: .global()) { error -> Void in XCTFail() - }.recover(OtherError.self, on: .global()) { error in + }.recover(only: OtherError.self, on: .global()) { error in value += 10_000_000 throw TestError.errorFive }.ensure(on: .global()) { @@ -133,11 +133,11 @@ class DispatchWrapperTests: XCTestCase { let ex = expectation(description: "DispatchQueue Promise catch API") Promise.value(42).cancellize().then(on: .global()) { _ -> Promise in throw TestError.errorOne - }.catch(OtherError.self, on: .global()) { error in + }.catch(only: OtherError.self, on: .global()) { error in XCTFail() - }.catch(TestError.errorTwo, on: .global()) { + }.catch(only: TestError.errorTwo, on: .global()) { _ in XCTFail() - }.catch(TestError.self, on: .global()) { error in + }.catch(only: TestError.self, on: .global()) { error in XCTAssert(error == .errorOne) ex.fulfill() }.cauterize() diff --git a/Tests/Cancel/DispatcherTests.swift b/Tests/Cancel/DispatcherTests.swift index 763528bac..574c8b493 100644 --- a/Tests/Cancel/DispatcherTests.swift +++ b/Tests/Cancel/DispatcherTests.swift @@ -217,7 +217,7 @@ class DispatcherTests: XCTestCase { let ex1 = expectation(description: "DispatchQueue CatchMixin catch-only") Promise(error: Error.dummy).cancellize().done(on: .global(qos: .background), flags: .barrier) { XCTFail() - }.catch(Error.dummy, on: .global(qos: .background), flags: .barrier) { + }.catch(only: Error.dummy, on: .global(qos: .background), flags: .barrier) { _ in ex1.fulfill() }.catch(on: .global(qos: .background), flags: .barrier) { _ in XCTFail() @@ -226,7 +226,7 @@ class DispatcherTests: XCTestCase { let ex2 = expectation(description: "DispatchQueue CatchMixin catch-type") Promise(error: Error.dummy).cancellize().done(on: .global(qos: .background), flags: .barrier) { XCTFail() - }.catch(Error.self, on: .global(qos: .background), flags: .barrier) { _ in + }.catch(only: Error.self, on: .global(qos: .background), flags: .barrier) { _ in ex2.fulfill() }.catch(on: .global(qos: .background), flags: .barrier) { _ in XCTFail() @@ -235,7 +235,7 @@ class DispatcherTests: XCTestCase { let ex3 = expectation(description: "DispatchQueue CascadingFinalizer catch") Promise(error: Error.dummy).cancellize().done(on: .global(qos: .background), flags: .barrier) { XCTFail() - }.catch(Error.cancelled, on: .global(qos: .background), flags: .barrier) { + }.catch(only: Error.cancelled, on: .global(qos: .background), flags: .barrier) { _ in XCTFail() }.catch(on: .global(qos: .background), flags: .barrier) { _ in ex3.fulfill() @@ -244,9 +244,9 @@ class DispatcherTests: XCTestCase { let ex4 = expectation(description: "DispatchQueue CascadingFinalizer catch-only") Promise(error: Error.dummy).cancellize().done(on: .global(qos: .background), flags: .barrier) { XCTFail() - }.catch(Error.cancelled, on: .global(qos: .background), flags: .barrier) { + }.catch(only: Error.cancelled, on: .global(qos: .background), flags: .barrier) { _ in XCTFail() - }.catch(Error.dummy, on: .global(qos: .background), flags: .barrier) { + }.catch(only: Error.dummy, on: .global(qos: .background), flags: .barrier) { _ in ex4.fulfill() }.catch(on: .global(qos: .background), flags: .barrier) { _ in XCTFail() @@ -255,9 +255,9 @@ class DispatcherTests: XCTestCase { let ex5 = expectation(description: "DispatchQueue CascadingFinalizer catch-type") Promise(error: Error.dummy).cancellize().done(on: .global(qos: .background), flags: .barrier) { XCTFail() - }.catch(Error.cancelled, on: .global(qos: .background), flags: .barrier) { + }.catch(only: Error.cancelled, on: .global(qos: .background), flags: .barrier) { _ in XCTFail() - }.catch(Error.self, on: .global(qos: .background), flags: .barrier) { _ in + }.catch(only: Error.self, on: .global(qos: .background), flags: .barrier) { _ in ex5.fulfill() }.catch(on: .global(qos: .background), flags: .barrier) { _ in XCTFail() @@ -270,7 +270,7 @@ class DispatcherTests: XCTestCase { let ex1 = expectation(description: "DispatchQueue CatchMixin catch-only isCancelled") Promise(error: Error.cancelled).cancellize().done(on: .global(qos: .background), flags: .barrier) { XCTFail() - }.catch(Error.cancelled, on: .global(qos: .background), flags: .barrier) { + }.catch(only: Error.cancelled, on: .global(qos: .background), flags: .barrier) { _ in ex1.fulfill() }.catch(on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in XCTFail() @@ -279,7 +279,7 @@ class DispatcherTests: XCTestCase { let ex2 = expectation(description: "DispatchQueue CatchMixin catch-type isCancelled") Promise(error: Error.cancelled).cancellize().done(on: .global(qos: .background), flags: .barrier) { XCTFail() - }.catch(Error.self, on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + }.catch(only: Error.self, on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in ex2.fulfill() }.catch(on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in XCTFail() @@ -288,7 +288,7 @@ class DispatcherTests: XCTestCase { let ex3 = expectation(description: "DispatchQueue CascadingFinalizer catch isCancelled") Promise(error: Error.cancelled).cancellize().done(on: .global(qos: .background), flags: .barrier) { XCTFail() - }.catch(Error.cancelled, on: .global(qos: .background), flags: .barrier) { + }.catch(only: Error.cancelled, on: .global(qos: .background), flags: .barrier) { _ in ex3.fulfill() }.catch(on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in XCTFail() @@ -297,9 +297,9 @@ class DispatcherTests: XCTestCase { let ex4 = expectation(description: "DispatchQueue CascadingFinalizer catch-only isCancelled") Promise(error: Error.cancelled).cancellize().done(on: .global(qos: .background), flags: .barrier) { XCTFail() - }.catch(Error.dummy, on: .global(qos: .background), flags: .barrier) { + }.catch(only: Error.dummy, on: .global(qos: .background), flags: .barrier) { _ in XCTFail() - }.catch(Error.cancelled, on: .global(qos: .background), flags: .barrier) { + }.catch(only: Error.cancelled, on: .global(qos: .background), flags: .barrier) { _ in ex4.fulfill() }.catch(on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in XCTFail() @@ -308,9 +308,9 @@ class DispatcherTests: XCTestCase { let ex5 = expectation(description: "DispatchQueue CascadingFinalizer catch-type isCancelled") Promise(error: Error.cancelled).cancellize().done(on: .global(qos: .background), flags: .barrier) { XCTFail() - }.catch(Error.dummy, on: .global(qos: .background), flags: .barrier) { + }.catch(only: Error.dummy, on: .global(qos: .background), flags: .barrier) { _ in XCTFail() - }.catch(Error.self, on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + }.catch(only: Error.self, on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in ex5.fulfill() }.catch(on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in XCTFail() @@ -321,7 +321,7 @@ class DispatcherTests: XCTestCase { func testRecoverOnly() { let ex1 = expectation(description: "DispatchQueue CatchMixin recover-only cancellable") - Promise(error: Error.dummy).cancellize().recover(Error.dummy, on: .global(qos: .background), flags: .barrier) { + Promise(error: Error.dummy).cancellize().recover(only: Error.dummy, on: .global(qos: .background), flags: .barrier) { _ in Promise.value(42).cancellize() }.done(on: .global(qos: .background), flags: .barrier) { XCTAssertEqual($0, 42) @@ -331,7 +331,7 @@ class DispatcherTests: XCTestCase { } let ex2 = expectation(description: "DispatchQueue CatchMixin recover-only standard") - Promise(error: Error.dummy).cancellize().recover(Error.dummy, on: .global(qos: .background), flags: .barrier) { + Promise(error: Error.dummy).cancellize().recover(only: Error.dummy, on: .global(qos: .background), flags: .barrier) { _ in Promise.value(42) }.done(on: .global(qos: .background), flags: .barrier) { XCTAssertEqual($0, 42) @@ -341,7 +341,7 @@ class DispatcherTests: XCTestCase { } let ex3 = expectation(description: "DispatchQueue CatchMixin recover-type cancellable") - Promise(error: Error.dummy).cancellize().recover(Error.self, on: .global(qos: .background), flags: .barrier) { _ in + Promise(error: Error.dummy).cancellize().recover(only: Error.self, on: .global(qos: .background), flags: .barrier) { _ in Promise.value(42).cancellize() }.done(on: .global(qos: .background), flags: .barrier) { XCTAssertEqual($0, 42) @@ -351,7 +351,7 @@ class DispatcherTests: XCTestCase { } let ex4 = expectation(description: "DispatchQueue CatchMixin recover-type standard") - Promise(error: Error.dummy).cancellize().recover(Error.self, on: .global(qos: .background), flags: .barrier) { _ in + Promise(error: Error.dummy).cancellize().recover(only: Error.self, on: .global(qos: .background), flags: .barrier) { _ in Promise.value(42) }.done(on: .global(qos: .background), flags: .barrier) { XCTAssertEqual($0, 42) @@ -361,7 +361,7 @@ class DispatcherTests: XCTestCase { } let ex5 = expectation(description: "DispatchQueue CatchMixin recover-only-void cancellable") - Promise(error: Error.dummy).cancellize().recover(Error.dummy, on: .global(qos: .background), flags: .barrier) { + Promise(error: Error.dummy).cancellize().recover(only: Error.dummy, on: .global(qos: .background), flags: .barrier) { _ in }.done(on: .global(qos: .background), flags: .barrier) { ex5.fulfill() }.catch(on: .global(qos: .background), flags: .barrier) { _ in @@ -369,7 +369,7 @@ class DispatcherTests: XCTestCase { } let ex6 = expectation(description: "DispatchQueue CatchMixin recover-type-void standard") - Promise(error: Error.dummy).cancellize().recover(Error.self, on: .global(qos: .background), flags: .barrier) { _ in + Promise(error: Error.dummy).cancellize().recover(only: Error.self, on: .global(qos: .background), flags: .barrier) { _ in }.done(on: .global(qos: .background), flags: .barrier) { ex6.fulfill() }.catch(on: .global(qos: .background), flags: .barrier) { _ in @@ -381,7 +381,7 @@ class DispatcherTests: XCTestCase { func testRecoverOnlyIsCancelled() { let ex1 = expectation(description: "DispatchQueue CatchMixin recover-only cancellable isCancelled") - Promise(error: Error.cancelled).cancellize().recover(Error.cancelled, on: .global(qos: .background), flags: .barrier) { + Promise(error: Error.cancelled).cancellize().recover(only: Error.cancelled, on: .global(qos: .background), flags: .barrier) { _ in Promise.value(42).cancellize() }.done(on: .global(qos: .background), flags: .barrier) { XCTAssertEqual($0, 42) @@ -391,7 +391,7 @@ class DispatcherTests: XCTestCase { } let ex2 = expectation(description: "DispatchQueue CatchMixin recover-only standard isCancelled") - Promise(error: Error.cancelled).cancellize().recover(Error.cancelled, on: .global(qos: .background), flags: .barrier) { + Promise(error: Error.cancelled).cancellize().recover(only: Error.cancelled, on: .global(qos: .background), flags: .barrier) { _ in Promise.value(42) }.done(on: .global(qos: .background), flags: .barrier) { XCTAssertEqual($0, 42) @@ -401,7 +401,7 @@ class DispatcherTests: XCTestCase { } let ex3 = expectation(description: "DispatchQueue CatchMixin recover-type cancellable isCancelled") - Promise(error: Error.cancelled).cancellize().recover(Error.self, on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + Promise(error: Error.cancelled).cancellize().recover(only: Error.self, on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in Promise.value(42).cancellize() }.done(on: .global(qos: .background), flags: .barrier) { XCTAssertEqual($0, 42) @@ -411,7 +411,7 @@ class DispatcherTests: XCTestCase { } let ex4 = expectation(description: "DispatchQueue CatchMixin recover-type standard isCancelled") - Promise(error: Error.cancelled).cancellize().recover(Error.self, on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + Promise(error: Error.cancelled).cancellize().recover(only: Error.self, on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in Promise.value(42) }.done(on: .global(qos: .background), flags: .barrier) { XCTAssertEqual($0, 42) @@ -421,7 +421,7 @@ class DispatcherTests: XCTestCase { } let ex5 = expectation(description: "DispatchQueue CatchMixin recover-only-void cancellable isCancelled") - Promise(error: Error.cancelled).cancellize().recover(Error.cancelled, on: .global(qos: .background), flags: .barrier) { + Promise(error: Error.cancelled).cancellize().recover(only: Error.cancelled, on: .global(qos: .background), flags: .barrier) { _ in }.done(on: .global(qos: .background), flags: .barrier) { ex5.fulfill() }.catch(on: .global(qos: .background), flags: .barrier) { _ in @@ -429,7 +429,7 @@ class DispatcherTests: XCTestCase { } let ex6 = expectation(description: "DispatchQueue CatchMixin recover-type-void standard isCancelled") - Promise(error: Error.cancelled).cancellize().recover(Error.self, on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + Promise(error: Error.cancelled).cancellize().recover(only: Error.self, on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in }.done(on: .global(qos: .background), flags: .barrier) { ex6.fulfill() }.catch(on: .global(qos: .background), flags: .barrier) { _ in diff --git a/Tests/Core/CatchableTests.swift b/Tests/Core/CatchableTests.swift index 774ba1b25..3e1037a3f 100644 --- a/Tests/Core/CatchableTests.swift +++ b/Tests/Core/CatchableTests.swift @@ -257,7 +257,7 @@ extension CatchableTests { } } -/// `Promise.catch(_ only:)` +/// `Promise.catch(only:)` extension CatchableTests { func testCatchOnly() { let x = expectation(description: #file + #function) @@ -427,7 +427,7 @@ extension CatchableTests { } } -/// `Promise.recover(_ only:)` +/// `Promise.recover(only:)` extension CatchableTests { func testRecoverOnly_Object() { let x = expectation(description: #file + #function) @@ -618,7 +618,7 @@ extension CatchableTests { } } -/// `Promise.recover(_ only:)` +/// `Promise.recover(only:)` extension CatchableTests { func testRecoverOnly_Object_Void() { let x = expectation(description: #file + #function) diff --git a/Tests/Core/DispatchWrapperTests.swift b/Tests/Core/DispatchWrapperTests.swift index 52444faef..934a7707e 100644 --- a/Tests/Core/DispatchWrapperTests.swift +++ b/Tests/Core/DispatchWrapperTests.swift @@ -54,11 +54,11 @@ class DispatchWrapperTests: XCTestCase { Promise.value(42).then { _ -> Promise in throw TestError.errorOne // Specific error - }.recover(TestError.errorOne, on: .global()) { () -> Promise in + }.recover(only: TestError.errorOne, on: .global()) { error -> Promise in value += 1 throw TestError.errorTwo // Error type - }.recover(TestError.self, on: .global()) { error -> Promise in + }.recover(only: TestError.self, on: .global()) { error -> Promise in XCTAssert(error == .errorTwo) value += 10 throw TestError.errorThree @@ -74,16 +74,16 @@ class DispatchWrapperTests: XCTestCase { }.map(on: .global()) { _ -> Void in // NOP // Non-matching specific error - }.recover(TestError.errorThree, on: .global()) { + }.recover(only: TestError.errorThree, on: .global()) { error in XCTFail() // Specific error, void return - }.recover(TestError.errorFour, on: .global()) { () -> Void in + }.recover(only: TestError.errorFour, on: .global()) { error -> Void in value += 1_000 throw OtherError.errorOne // Non-matching error class, void return - }.recover(TestError.self, on: .global()) { error -> Void in + }.recover(only: TestError.self, on: .global()) { error -> Void in XCTFail() - }.recover(OtherError.self, on: .global()) { error in + }.recover(only: OtherError.self, on: .global()) { error in value += 10_000 throw TestError.errorFive }.ensure(on: .global()) { @@ -113,11 +113,11 @@ class DispatchWrapperTests: XCTestCase { let ex = expectation(description: "DispatchQueue Promise catch API") Promise.value(42).then(on: .global()) { _ -> Promise in throw TestError.errorOne - }.catch(OtherError.self, on: .global()) { error in + }.catch(only: OtherError.self, on: .global()) { error in XCTFail() - }.catch(TestError.errorTwo, on: .global()) { + }.catch(only: TestError.errorTwo, on: .global()) { error in XCTFail() - }.catch(TestError.self, on: .global()) { error in + }.catch(only: TestError.self, on: .global()) { error in XCTAssert(error == .errorOne) ex.fulfill() }.cauterize() From c52bca72583666146c2d0d12512417c6b7c0afa9 Mon Sep 17 00:00:00 2001 From: Garth Snyder Date: Mon, 8 Apr 2019 09:55:34 -0700 Subject: [PATCH 55/81] Fix a couple of incorrect map vs. return wrapper settings --- Sources/Wrappers/EnsureWrappers.swift | 4 ++-- Sources/Wrappers/FinallyWrappers.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/Wrappers/EnsureWrappers.swift b/Sources/Wrappers/EnsureWrappers.swift index 592890ee7..442907545 100644 --- a/Sources/Wrappers/EnsureWrappers.swift +++ b/Sources/Wrappers/EnsureWrappers.swift @@ -21,7 +21,7 @@ public extension _PMKSharedWrappers { - Returns: A new promise, resolved with this promise’s resolution. */ func ensure(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping () -> Void) -> BaseOfT { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) return ensure(on: dispatcher, body) } @@ -45,7 +45,7 @@ public extension _PMKSharedWrappers { - Returns: A new promise, resolved with this promise’s resolution. */ func ensureThen(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping () -> VoidReturn) -> BaseOfT { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) return ensureThen(on: dispatcher, body) } } diff --git a/Sources/Wrappers/FinallyWrappers.swift b/Sources/Wrappers/FinallyWrappers.swift index e69a1a156..09272102c 100644 --- a/Sources/Wrappers/FinallyWrappers.swift +++ b/Sources/Wrappers/FinallyWrappers.swift @@ -4,7 +4,7 @@ public extension _PMKFinallyWrappers { /// `finally` is the same as `ensure`, but it is not chainable @discardableResult func finally(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping () -> Void) -> FinallyReturn { - let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) return finally(on: dispatcher, body) } } From ca4ee531efef1dfed15ffdf491295105502e2a62 Mon Sep 17 00:00:00 2001 From: Doug Stein Date: Sat, 20 Apr 2019 15:21:45 -0700 Subject: [PATCH 56/81] Fix for #1049: Some tests seem to depend on dispatching that PromiseKit should perhaps not be doing The cancellable promise bridge now runs 'catch' on the current thread dispatcher. Updated tests for the change. --- Sources/Cancellation/CancellablePromise.swift | 2 +- Tests/Cancel/CatchableTests.swift | 2 +- Tests/Cancel/ThenableTests.swift | 2 +- Tests/Cancel/WhenConcurrentTests.swift | 2 +- Tests/Cancel/WhenTests.swift | 6 +++--- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Sources/Cancellation/CancellablePromise.swift b/Sources/Cancellation/CancellablePromise.swift index b6056050c..93e42002a 100644 --- a/Sources/Cancellation/CancellablePromise.swift +++ b/Sources/Cancellation/CancellablePromise.swift @@ -74,7 +74,7 @@ public class CancellablePromise: CancellableThenable, CancellableCatchMixin { reject = seal.reject bridge.done(on: CurrentThreadDispatcher()) { seal.fulfill($0) - }.catch(policy: .allErrors) { + }.catch(on: CurrentThreadDispatcher(), policy: .allErrors) { seal.reject($0) } } diff --git a/Tests/Cancel/CatchableTests.swift b/Tests/Cancel/CatchableTests.swift index abfdb3630..4224463ba 100644 --- a/Tests/Cancel/CatchableTests.swift +++ b/Tests/Cancel/CatchableTests.swift @@ -749,7 +749,7 @@ extension CatchableTests { return promise } promise.catch(policy: .allErrors) { err in - err.isCancelled ? x.fulfill() : XCTFail() + err.isCancelled ? XCTFail() : x.fulfill() } promise.cancel() diff --git a/Tests/Cancel/ThenableTests.swift b/Tests/Cancel/ThenableTests.swift index e16a4c3ee..2ea983007 100644 --- a/Tests/Cancel/ThenableTests.swift +++ b/Tests/Cancel/ThenableTests.swift @@ -131,7 +131,7 @@ class ThenableTests: XCTestCase { XCTFail() return .value(x) }.catch(policy: .allErrors) { - $0.isCancelled ? ex.fulfill() : XCTFail() + $0.isCancelled ? XCTFail() : ex.fulfill() }.cancel() wait(for: [ex], timeout: 1) } diff --git a/Tests/Cancel/WhenConcurrentTests.swift b/Tests/Cancel/WhenConcurrentTests.swift index d2e9879ea..d55a9b941 100644 --- a/Tests/Cancel/WhenConcurrentTests.swift +++ b/Tests/Cancel/WhenConcurrentTests.swift @@ -267,7 +267,7 @@ class WhenConcurrentTestCase_Swift: XCTestCase { when(fulfilled: generator, concurrently: 1).done { XCTFail("\($0)") }.catch(policy: .allErrors) { - $0.isCancelled ? ex.fulfill() : XCTFail() + $0.isCancelled ? XCTFail() : ex.fulfill() }.cancel() waitForExpectations(timeout: 3) diff --git a/Tests/Cancel/WhenTests.swift b/Tests/Cancel/WhenTests.swift index 5edeab944..ae067bce8 100644 --- a/Tests/Cancel/WhenTests.swift +++ b/Tests/Cancel/WhenTests.swift @@ -279,7 +279,7 @@ class WhenTests: XCTestCase { let p1 = CancellablePromise(error: Error.test) let p2 = after(.milliseconds(100)).cancellize() cancellableWhen(fulfilled: p1, p2).done{ _ in XCTFail() }.catch(policy: .allErrors) { - $0.isCancelled ? ex.fulfill() : XCTFail() + $0.isCancelled ? XCTFail() : ex.fulfill() }.cancel() waitForExpectations(timeout: 1, handler: nil) @@ -300,7 +300,7 @@ class WhenTests: XCTestCase { let p3 = after(.milliseconds(200)).cancellize().done { throw Error.straggler } cancellableWhen(fulfilled: p1, p2, p3).catch(policy: .allErrors) { - $0.isCancelled ? ex1.fulfill() : XCTFail() + $0.isCancelled ? XCTFail() : ex1.fulfill() }.cancel() p2.ensure { after(.milliseconds(100)).done(ex2.fulfill) }.silenceWarning() @@ -322,7 +322,7 @@ class WhenTests: XCTestCase { let p3 = CancellablePromise(error: Error.test3) when(fulfilled: p1, p2, p3).catch(policy: .allErrors) { - $0.isCancelled ? ex.fulfill() : XCTFail() + $0.isCancelled ? XCTFail() : ex.fulfill() }.cancel() waitForExpectations(timeout: 1) From e4f52c22d6330fb32572dfc4d67eb1641635c1a5 Mon Sep 17 00:00:00 2001 From: Max Howell Date: Wed, 5 Jun 2019 14:26:23 -0400 Subject: [PATCH 57/81] Specify SwiftPM instructions --- README.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 0cde1f4e4..8661ccd7c 100644 --- a/README.md +++ b/README.md @@ -51,14 +51,12 @@ PromiseKit 7 adds support for cancelling promises and promise chains. # Quick Start -In your [Podfile]: +In your `Package.swift`: -```ruby -use_frameworks! - -target "Change Me!" do - pod "PromiseKit", :git => 'https://github.com/mxcl/PromiseKit.git', :branch => 'v7' -end +```swift +package.dependencies.append( + .package(url: "https://github.com/mxcl/PromiseKit", from: Version(7, 0, 0, prereleaseIdentifiers: [“alpha”, “1”])) +) ``` PromiseKit 7 supports Swift 5.x; Xcode >= 10.2; iOS, macOS, tvOS, watchOS, Linux @@ -186,10 +184,14 @@ if after that you still have a question, ask at our [Gitter chat channel] or on # Contributing -Generate the Xcode project: +## Xcode 10 swift package generate-xcodeproj + open PromiseKit.xcodeproj + +## Xcode 11 + open Package.swift [badge-pod]: https://img.shields.io/cocoapods/v/PromiseKit.svg?label=version [badge-pms]: https://img.shields.io/badge/supports-CocoaPods%20%7C%20Carthage%20%7C%20Accio%20%7C%20SwiftPM-green.svg From 216ba58be7d636d34d37292748d02fdeaa5aa716 Mon Sep 17 00:00:00 2001 From: Max Howell Date: Tue, 11 Jun 2019 13:09:41 -0400 Subject: [PATCH 58/81] Make tests work with Xcode 11 --- .gitignore | 1 + Tests/Core/ResolverTests.swift | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index dcbd654f6..f699dd53e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ .DS_Store /build /Tests/A+/JavaScript/build +/.swiftpm diff --git a/Tests/Core/ResolverTests.swift b/Tests/Core/ResolverTests.swift index d577f8d22..a02a5ccfa 100644 --- a/Tests/Core/ResolverTests.swift +++ b/Tests/Core/ResolverTests.swift @@ -175,8 +175,8 @@ class WrapTests: XCTestCase { bar().done(ex.fulfill).cauterize() wait(for: [ex], timeout: 10) - #if swift(>=5.1) - // ^^ ambiguous in Swift 5.0, testing again in next version + #if swift(>=5.2) + // ^^ ambiguous in Swift 5.0 & 5.1, testing again in next version let ex2 = expectation(description: "") Guarantee { seal in after(.microseconds(10)).done(seal) From 9a08dcc51263e9ed41397555a9e7f58c38210016 Mon Sep 17 00:00:00 2001 From: Max Howell Date: Tue, 9 Jul 2019 10:13:58 -0400 Subject: [PATCH 59/81] Support Xcode 11 / Swift 5.1 --- .travis.yml | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 31cc6b05a..86dc381b4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ branches: stages: - name: pretest - - name: lint + - name: permutations - name: test - name: deploy if: branch =~ ^\d+\.\d+\.\d+$ @@ -29,6 +29,14 @@ jobs: install: swift test --generate-linuxmain script: git diff --exit-code + - &swiftpm + name: macOS SwiftPM 5.0 + stage: permutations + script: swift build + - <<: *swiftpm + name: macOS SwiftPM 5.1 + osx_image: xcode11 + - &test name: macOS stage: test @@ -40,14 +48,20 @@ jobs: - <<: *test name: tvOS xcode_destination: 'OS=12.2,name=Apple TV' + - <<: *test + name: Xcode 11 + osx_image: xcode11 - - name: Linux + - &linux + name: Linux env: SWIFT_VERSION='5.0' os: linux language: generic osx_image: null install: eval "$(curl -sL https://swiftenv.fuller.li/install.sh)" script: swift test + - <<: *linux + env: SWIFT_VERSION=5.1-DEVELOPMENT-SNAPSHOT-2019-07-03-a - name: JavaScript Promises/A+ install: | From 8e0dc32b1eb81b295299deef93df87a8f0ea958a Mon Sep 17 00:00:00 2001 From: Max Howell Date: Tue, 1 Jun 2021 09:55:39 -0400 Subject: [PATCH 60/81] [ci] fix; [swiftc] fix --- .github/workflows/cd.yml | 46 -- .github/workflows/ci.yml | 84 +--- Documents/Installation.md | 40 +- Package.swift | 10 +- README.md | 25 +- .../Cancellation/CancellableThenable.swift | 2 +- Sources/Configuration.swift | 11 +- Sources/LogEvent.swift | 32 +- Sources/race.swift | 8 +- Sources/when.swift | 6 +- Tests/A+/JavaScript/XCTestManifests.swift | 18 - Tests/A+/Swift/XCTestManifests.swift | 108 ----- Tests/Cancel/DispatcherTests.swift | 245 +++++----- Tests/Cancel/XCTestManifests.swift | 432 ------------------ Tests/Core/LoggingTests.swift | 33 +- Tests/Core/ResolverTests.swift | 10 +- 16 files changed, 230 insertions(+), 880 deletions(-) delete mode 100644 Tests/A+/JavaScript/XCTestManifests.swift delete mode 100644 Tests/A+/Swift/XCTestManifests.swift delete mode 100644 Tests/Cancel/XCTestManifests.swift diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 4a8b06c96..1578e5d21 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -34,52 +34,6 @@ jobs: status: ${{ job.status }} deployment_id: ${{ steps.deployment.outputs.deployment_id }} - carthage: - runs-on: macos-latest - steps: - - - name: Start Deployment - uses: bobheadxi/deployments@v0.5.2 - id: deployment - with: - step: start - token: ${{ secrets.GITHUB_TOKEN }} - env: carthage - - - uses: maxim-lobanov/setup-xcode@v1 - with: - xcode-version: ^11 - # Waiting on https://github.com/Carthage/Carthage/issues/3103 for Xcode 12 - - - uses: joutvhu/get-release@v1 - id: release - with: - tag_name: ${{ github.event.inputs.version }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - uses: actions/checkout@v2 - - run: carthage build --no-skip-current --platform macOS,iOS,watchOS,tvOS --archive - - run: mv PromiseKit.framework.zip PromiseKit-$v.framework.zip - - - uses: actions/upload-release-asset@v1 - with: - upload_url: ${{ steps.release.outputs.upload_url }} - asset_path: ./PromiseKit-${{ github.event.inputs.version }}.framework.zip - asset_name: PromiseKit-${{ github.event.inputs.version }}.framework.zip - asset_content_type: application/zip - env: - GITHUB_TOKEN: ${{ github.token }} - - - name: Seal Deployment - uses: bobheadxi/deployments@v0.5.2 - if: always() - with: - step: finish - token: ${{ secrets.GITHUB_TOKEN }} - status: ${{ job.status }} - deployment_id: ${{ steps.deployment.outputs.deployment_id }} - docs: runs-on: macos-latest steps: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 574dbf8cb..b1ef34a96 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,11 +1,11 @@ name: CI on: - workflow_dispatch: pull_request: paths: - Sources/** - Tests/** - .github/workflows/ci.yml + workflow_dispatch: jobs: auto-cancel: runs-on: ubuntu-latest @@ -13,105 +13,53 @@ jobs: - uses: technote-space/auto-cancel-redundant-job@v1 linux: - needs: auto-cancel - runs-on: ubuntu-18.04 - strategy: - matrix: - swift: - - 4.0.3 - - 4.1.3 - - 4.2.4 - container: - image: swift:${{ matrix.swift }} - steps: - - uses: actions/checkout@v2 - - run: swift build -Xswiftc -warnings-as-errors -Xswiftc -swift-version -Xswiftc 3 - - run: swift build # generated linuxmain requires Swift 5 sadly - - linux-code-cov: name: linux - needs: auto-cancel runs-on: ubuntu-18.04 strategy: matrix: swift: - - 5.0.3 - - 5.1.5 - - 5.2.5 - - 5.3.3 - - 5.4.1 + - 5.3 + - 5.4 container: image: swift:${{ matrix.swift }} steps: - uses: actions/checkout@v2 - - run: swift build -Xswiftc -warnings-as-errors -Xswiftc -swift-version -Xswiftc 4 - - run: swift build -Xswiftc -warnings-as-errors -Xswiftc -swift-version -Xswiftc 4.2 - - run: swift test --enable-code-coverage --parallel + - run: swift test --enable-code-coverage --parallel --enable-test-discovery - name: Generate Coverage Report - if: ${{ matrix.swift != '5.4.1' }} # fails for SOME REASON + if: ${{ matrix.swift == '5.3' }} # 5.4 and above needs llvm-cov-12 run: | apt-get -qq update - apt-get -qq install llvm-10 curl - export b=$(swift build --show-bin-path) && llvm-cov-10 \ - export -format lcov \ - -instr-profile=$b/codecov/default.profdata \ + apt-get -qq install curl + b=$(swift build --show-bin-path) + llvm-cov export \ + -format lcov \ + -instr-profile="$b"/codecov/default.profdata \ --ignore-filename-regex='\.build/' \ - $b/*.xctest \ + "$b"/*.xctest \ > info.lcov - uses: codecov/codecov-action@v1 with: file: ./info.lcov - verify-linuxmain: - runs-on: macos-latest - name: linux (validate manifests) - steps: - - uses: actions/checkout@v2 - - run: swift test --generate-linuxmain - - run: git diff --exit-code - - test: + macOS: runs-on: macos-latest strategy: matrix: dst: - platform=macOS - - platform=tvOS Simulator,OS=14.3,name=Apple TV - - platform=iOS Simulator,OS=14.4,name=iPhone 12 + - platform=tvOS Simulator,OS=latest,name=Apple TV + - platform=iOS Simulator,OS=latest,name=iPhone 12 steps: - uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: 12.4 + xcode-version: ^12 - uses: actions/checkout@v2 - uses: sersoft-gmbh/xcodebuild-action@v1 with: - project: PromiseKit.xcodeproj + spm-package: ./ scheme: PromiseKit destination: ${{ matrix.dst }} action: test enable-code-coverage: true - uses: codecov/codecov-action@v1 - - carthage: - runs-on: macos-latest - strategy: - matrix: - xcode: [^10, ^11, ^12] - steps: - - uses: maxim-lobanov/setup-xcode@v1 - with: - xcode-version: ${{ matrix.xcode }} - - uses: actions/checkout@v2 - - - uses: mingjun97/file-regex-replace@v1 - with: - regex: SWIFT_TREAT_WARNINGS_AS_ERRORS = NO - replacement: SWIFT_TREAT_WARNINGS_AS_ERRORS = YES - include: project.pbxproj - - - run: | - if [ ${{ matrix.xcode }} == ^12 ]; then - echo "CARTHAGE_ARGS=--use-xcframeworks" >> $GITHUB_ENV - fi - - run: carthage build --no-skip-current --no-use-binaries $CARTHAGE_ARGS diff --git a/Documents/Installation.md b/Documents/Installation.md index 3328eeb81..479627f5e 100644 --- a/Documents/Installation.md +++ b/Documents/Installation.md @@ -1,8 +1,21 @@ -# Xcode 8.3, 9.x or 10.x / Swift 3 or 4 +# PromiseKit v7 + +We only support SwiftPM since supporting all package managers is untennable and +SwiftPM is the easiest. + +```swift +package.dependencies.append( + .package(url: "https://github.com/mxcl/PromiseKit", from: "7.0.0-alpha.1") +) +``` + +# PromiseKit v6 + +## Xcode 8.3, 9.x or 10.x / Swift 3 or 4 We recommend Carthage over CocoaPods, but both installation methods are supported. -## CocoaPods +### CocoaPods ```ruby use_frameworks! @@ -31,7 +44,7 @@ Adjust the value for `SWIFT_VERSION` as needed. CocoaPods are aware of this [issue](https://github.com/CocoaPods/CocoaPods/issues/7134). -## Carthage +### Carthage ```ruby github "mxcl/PromiseKit" ~> 6.8 @@ -47,7 +60,7 @@ From Xcode 12, you will likely need to build using `--use-xcframeworks`, eg: carthage build --use-xcframeworks -## Accio +### Accio Add the following to your `Package.swift`: @@ -68,7 +81,7 @@ Next, add `PromiseKit` to your App targets dependencies like so: Then run `accio update`. -## SwiftPM +### SwiftPM ```swift package.dependencies.append( @@ -76,7 +89,7 @@ package.dependencies.append( ) ``` -## Manually +### Manually You can just drop `PromiseKit.xcodeproj` into your project and then add `PromiseKit.framework` to your app’s embedded frameworks. @@ -86,14 +99,15 @@ You can just drop `PromiseKit.xcodeproj` into your project and then add PromiseKit contains Swift, so there have been rev-lock issues with Xcode: -| PromiseKit | Swift | Xcode | CI Status | Release Notes | -| ---------- | ----------------------- | -------- | ------------ | ----------------- | -| 6 | 3.2, 3.3, 4.x, 5.x | 8.3, 9.x, 10.x | ![ci-master] | [2018/02][news-6] | -| 5 | 3.1, 3.2, 3.3, 4.x | 8.3, 9.x, 10.1 | *Deprecated* | *n/a* | +| PromiseKit | Swift | Xcode | CI Status | Release Notes | +| ---------- | ----------------------- | -------------- | ------------ | ----------------- | +| 7 | >=5.3 | 12.x | ![ci-master] | | +| 6 | 3.2, 3.3, 4.x, 5.x | 8.3, 9.x, 10.x | ![ci-master] | [2018/02][news-6] | +| 5 | *Deprecated* | *n/a* | *n/a* | *n/a* | | 4 | 3.0, 3.1, 3.2, 3.3, 4.x | 8.x, 9.x, 10.1 | ![ci-master] | [2016/09][news-4] | -| 3 | 2.x | 7.x, 8.0 | ![ci-swift2] | [2015/10][news-3] | -| 2 | 1.x | 7.x | *Deprecated* | [2015/10][news-3] | -| 1† | *N/A* | * | ![ci-legacy] | – | +| 3 | 2.x | 7.x, 8.0 | ![ci-swift2] | [2015/10][news-3] | +| 2 | 1.x | 7.x | *Deprecated* | [2015/10][news-3] | +| 1† | *N/A* | * | ![ci-legacy] | – | † PromiseKit 1 is pure Objective-C and thus can be used with any Xcode, it is also your only choice if you need to support iOS 7 or below. diff --git a/Package.swift b/Package.swift index 748cc1d70..0c6a87176 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.0 +// swift-tools-version:5.3 import PackageDescription @@ -13,12 +13,14 @@ pkg.products = [ .library(name: "PromiseKit", targets: ["PromiseKit"]), ] pkg.swiftLanguageVersions = [ - .v5 // grab PromiseKit-6.x if you want Swift 3.1‒4.2 + .v5 // grab PromiseKit-6.x if you want Swift 3.1‒5.2 ] pkg.targets = [ .target(name: "PromiseKit", path: "Sources"), .testTarget(name: "Core", dependencies: ["PromiseKit"], path: "Tests/Core"), .testTarget(name: "Cancel", dependencies: ["PromiseKit"], path: "Tests/Cancel"), - .testTarget(name: "A+.swift", dependencies: ["PromiseKit"], path: "Tests/A+/Swift"), - .testTarget(name: "A+.js", dependencies: ["PromiseKit"], path: "Tests/A+/JavaScript"), + .testTarget(name: "APlusSwift", dependencies: ["PromiseKit"], path: "Tests/A+/Swift"), + .testTarget(name: "APlusJS", dependencies: ["PromiseKit"], path: "Tests/A+/JavaScript", exclude: [ + "index.js", "package-lock.json", "package.json", "README.md", "webpack.config.js" + ]), ] diff --git a/README.md b/README.md index fae119786..e4fda5a66 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ![PromiseKit](../gh-pages/public/img/logo-tight.png) -[![badge-pod][]][cocoapods] ![badge-languages][] ![badge-pms][] ![badge-platforms][] [![badge-travis][]][travis] +![badge-languages][] ![badge-platforms][] --- @@ -59,14 +59,15 @@ package.dependencies.append( ) ``` -PromiseKit 7 supports Swift 5.x; Xcode >= 10.2; iOS, macOS, tvOS, watchOS, Linux +PromiseKit 7 supports Swift >= 5.3; Xcode >= 12; iOS, macOS, tvOS, watchOS, Linux and Android; SwiftPM. PromiseKits 6 and 4 support Xcode 8.3, 9.x and 10.0; Swift 3.1, 3.2, 3.3, 3.4, 4.0, 4.1, 4.2 and 5.0; iOS, macOS, tvOS, watchOS, Linux and Android; CocoaPods, Carthage and SwiftPM; ([CI Matrix](https://travis-ci.org/mxcl/PromiseKit)). -For Carthage, SwiftPM, Accio, etc., or for instructions when using older Swifts or Xcodes, see our [Installation Guide]. +For Carthage, SwiftPM, Accio, etc., or for instructions when using older Swifts +or Xcodes, see our [Installation Guide]. # Professionally Supported PromiseKit is Now Available @@ -77,20 +78,10 @@ tools. [Get Professional Support for PromiseKit with TideLift](https://tidelift.com/subscription/pkg/cocoapods-promisekit?utm_source=cocoapods-promisekit&utm_medium=referral&utm_campaign=readme). -# PromiseKit is Thousands of Hours of Work +## Other Sponsorship -Hey there, I’m Max Howell. I’m a prolific producer of open source software and -probably you already use some of it (I created [`brew`]). I work full-time on -open source and it’s hard; currently *I earn less than minimum wage*. Please -help me continue my work, I appreciate it 🙏🏻 - - - - - -[Other ways to say thanks](http://mxcl.dev/#donate). - -[`brew`]: https://brew.sh +Maintaining this project is work, if your company uses this project please +sponsor it either via Tidelift or GitHub Sponsors. # Documentation @@ -189,7 +180,7 @@ https://tidelift.com/security [badge-pod]: https://img.shields.io/cocoapods/v/PromiseKit.svg?label=version [badge-pms]: https://img.shields.io/badge/supports-CocoaPods%20%7C%20Carthage%20%7C%20Accio%20%7C%20SwiftPM-green.svg -[badge-languages]: https://img.shields.io/badge/languages-Swift%20%7C%20ObjC-orange.svg +[badge-languages]: https://img.shields.io/badge/languages-Swift-orange.svg [badge-platforms]: https://img.shields.io/badge/platforms-macOS%20%7C%20iOS%20%7C%20watchOS%20%7C%20tvOS%20%7C%20Linux-lightgrey.svg [badge-mit]: https://img.shields.io/badge/license-MIT-blue.svg [OMGHTTPURLRQ]: https://github.com/PromiseKit/OMGHTTPURLRQ diff --git a/Sources/Cancellation/CancellableThenable.swift b/Sources/Cancellation/CancellableThenable.swift index 646ada924..5ca4ee68d 100644 --- a/Sources/Cancellation/CancellableThenable.swift +++ b/Sources/Cancellation/CancellableThenable.swift @@ -3,7 +3,7 @@ import Dispatch /** CancellableThenable represents an asynchronous operation that can be both chained and cancelled. When chained, all CancellableThenable members of the chain are cancelled when `cancel` is called on the associated CancelContext. */ -public protocol CancellableThenable: class { +public protocol CancellableThenable: AnyObject { /// Type of the delegate `thenable` associatedtype U: Thenable diff --git a/Sources/Configuration.swift b/Sources/Configuration.swift index aa2af275f..b9ff7b6c8 100644 --- a/Sources/Configuration.swift +++ b/Sources/Configuration.swift @@ -28,16 +28,7 @@ public struct PMKConfiguration { /// Not thread safe; change before processing any promises. /// - Note: The default handler calls `print()` public var logHandler: (LogEvent) -> Void = { event in - switch event { - case .waitOnMainThread: - print("PromiseKit: warning: `wait()` called on main thread!") - case .pendingPromiseDeallocated: - print("PromiseKit: warning: pending promise deallocated") - case .pendingGuaranteeDeallocated: - print("PromiseKit: warning: pending guarantee deallocated") - case .cauterized (let error): - print("PromiseKit:cauterized-error: \(error)") - } + print("PromiseKit:", event.description) } } diff --git a/Sources/LogEvent.swift b/Sources/LogEvent.swift index 7dc4349e6..9d46526c0 100644 --- a/Sources/LogEvent.swift +++ b/Sources/LogEvent.swift @@ -33,23 +33,23 @@ public enum LogEvent { /// DispatchWorkItem flags specified for non-DispatchQueue Dispatcher case extraneousFlagsSpecified - - public func asString() -> String { - var message: String +} + +extension LogEvent: CustomStringConvertible { + public var description: String { switch self { - case .waitOnMainThread: - message = " warning: `wait()` called on main thread!" - case .pendingPromiseDeallocated: - message = " warning: pending promise deallocated" - case .pendingGuaranteeDeallocated: - message = " warning: pending guarantee deallocated" - case .cauterized(let error): - message = "cauterized-error: \(error)" - case .nilDispatchQueueWithFlags: - message = " warning: nil DispatchQueue specified, but DispatchWorkItemFlags were also supplied (ignored)" - case .extraneousFlagsSpecified: - message = " warning: DispatchWorkItemFlags flags specified, but default Dispatcher is not a DispatchQueue (ignored)" + case .waitOnMainThread: + return "warning: `wait()` called on main thread!" + case .pendingPromiseDeallocated: + return "warning: pending promise deallocated" + case .pendingGuaranteeDeallocated: + return "warning: pending guarantee deallocated" + case .cauterized(let error): + return "cauterized-error: \(error)" + case .nilDispatchQueueWithFlags: + return "warning: nil DispatchQueue specified, but DispatchWorkItemFlags were also supplied (ignored)" + case .extraneousFlagsSpecified: + return "warning: DispatchWorkItemFlags flags specified, but default Dispatcher is not a DispatchQueue (ignored)" } - return "PromiseKit:\(message)" } } diff --git a/Sources/race.swift b/Sources/race.swift index 475f45975..eaab34eed 100644 --- a/Sources/race.swift +++ b/Sources/race.swift @@ -146,16 +146,16 @@ public func race(fulfilled thenables: [U]) -> Promise { promise.pipe { result in barrier.sync(flags: .barrier) { switch result { - case .rejected: + case .failure: guard rp.isPending else { return } countdown -= 1 if countdown == 0 { - rp.box.seal(.rejected(PMKError.noWinner)) + rp.box.seal(.failure(PMKError.noWinner)) } - case .fulfilled(let value): + case .success(let value): guard rp.isPending else { return } countdown = 0 - rp.box.seal(.fulfilled(value)) + rp.box.seal(.success(value)) } } } diff --git a/Sources/when.swift b/Sources/when.swift index fbcb2ac53..9dbec6336 100644 --- a/Sources/when.swift +++ b/Sources/when.swift @@ -276,13 +276,13 @@ No more than three downloads will occur simultaneously. Downloads will continue */ #if swift(>=5.3) public func when(resolved promiseIterator: It, concurrently: Int) - -> Guarantee<[Result]> where It.Element: Thenable { + -> Guarantee<[Result]> where It.Element: Thenable { guard concurrently > 0 else { - return Guarantee.value([Result.rejected(PMKError.badInput)]) + return Guarantee.value([Result.failure(PMKError.badInput)]) } var generator = promiseIterator - let root = Guarantee<[Result]>.pending() + let root = Guarantee<[Result]>.pending() var pendingPromises = 0 var promises: [It.Element] = [] diff --git a/Tests/A+/JavaScript/XCTestManifests.swift b/Tests/A+/JavaScript/XCTestManifests.swift deleted file mode 100644 index 79a7d0f2a..000000000 --- a/Tests/A+/JavaScript/XCTestManifests.swift +++ /dev/null @@ -1,18 +0,0 @@ -#if !canImport(ObjectiveC) -import XCTest - -extension AllTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__AllTests = [ - ("testAll", testAll), - ] -} - -public func __allTests() -> [XCTestCaseEntry] { - return [ - testCase(AllTests.__allTests__AllTests), - ] -} -#endif diff --git a/Tests/A+/Swift/XCTestManifests.swift b/Tests/A+/Swift/XCTestManifests.swift deleted file mode 100644 index 5f78de59e..000000000 --- a/Tests/A+/Swift/XCTestManifests.swift +++ /dev/null @@ -1,108 +0,0 @@ -#if !canImport(ObjectiveC) -import XCTest - -extension Test212 { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__Test212 = [ - ("test", test), - ] -} - -extension Test213 { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__Test213 = [ - ("test", test), - ] -} - -extension Test222 { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__Test222 = [ - ("test", test), - ] -} - -extension Test223 { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__Test223 = [ - ("test", test), - ] -} - -extension Test224 { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__Test224 = [ - ("test", test), - ] -} - -extension Test226 { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__Test226 = [ - ("test", test), - ] -} - -extension Test227 { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__Test227 = [ - ("test", test), - ] -} - -extension Test231 { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__Test231 = [ - ("test", test), - ] -} - -extension Test232 { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__Test232 = [ - ("test", test), - ] -} - -extension Test234 { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__Test234 = [ - ("test", test), - ] -} - -public func __allTests() -> [XCTestCaseEntry] { - return [ - testCase(Test212.__allTests__Test212), - testCase(Test213.__allTests__Test213), - testCase(Test222.__allTests__Test222), - testCase(Test223.__allTests__Test223), - testCase(Test224.__allTests__Test224), - testCase(Test226.__allTests__Test226), - testCase(Test227.__allTests__Test227), - testCase(Test231.__allTests__Test231), - testCase(Test232.__allTests__Test232), - testCase(Test234.__allTests__Test234), - ] -} -#endif diff --git a/Tests/Cancel/DispatcherTests.swift b/Tests/Cancel/DispatcherTests.swift index 574c8b493..d79c13417 100644 --- a/Tests/Cancel/DispatcherTests.swift +++ b/Tests/Cancel/DispatcherTests.swift @@ -51,18 +51,18 @@ class DispatcherTests: XCTestCase { let oldConf = PromiseKit.conf.D PromiseKit.conf.D = (map: dispatcher, return: dispatcher) - let background = DispatchQueue.global(qos: .background) + let background = DispatchQueue.global() background.setSpecific(key: queueIDKey, value: 100) DispatchQueue.main.setSpecific(key: queueIDKey, value: 102) dispatcher.queue.setSpecific(key: queueIDKey, value: 103) - Promise.value(42).cancellize().map(on: .global(qos: .background), flags: .barrier) { (x: Int) -> Int in + Promise.value(42).cancellize().map(on: .global(), flags: .barrier) { (x: Int) -> Int in let queueID = DispatchQueue.getSpecific(key: queueIDKey) XCTAssertNotNil(queueID) XCTAssertEqual(queueID!, 100) return x + 10 - }.get(on: .global(qos: .background), flags: .barrier) { _ in - }.tap(on: .global(qos: .background), flags: .barrier) { _ in + }.get(on: .global(), flags: .barrier) { _ in + }.tap(on: .global(), flags: .barrier) { _ in }.then(on: .main, flags: []) { (x: Int) -> CancellablePromise in XCTAssertEqual(x, 52) let queueID = DispatchQueue.getSpecific(key: queueIDKey) @@ -92,86 +92,93 @@ class DispatcherTests: XCTestCase { PromiseKit.conf.D = oldConf } - + +#if false + // test takes > 30 seconds to fail to compile, I have no clue what this is for + // or why it has been designed to be so ridiculuously complex for the type + // checker. It is stupid. func testMapValues() { let ex1 = expectation(description: "DispatchQueue MapValues compatibility") - Promise.value([42, 52]).cancellize().then(on: .global(qos: .background), flags: .barrier) { v -> Promise<[Int]> in + Promise.value([42, 52]).cancellize() + .then(on: .global(), flags: .barrier) { v -> Promise<[Int]> in Promise.value(v) - }.compactMap(on: .global(qos: .background), flags: .barrier) { - $0 - }.mapValues(on: .global(qos: .background), flags: .barrier) { - $0 + 10 - }.flatMapValues(on: .global(qos: .background), flags: .barrier) { - [$0 + 10] - }.compactMapValues(on: .global(qos: .background), flags: .barrier) { - $0 + 10 - }.thenMap(on: .global(qos: .background), flags: .barrier) { v -> CancellablePromise in + }.compactMap(on: .global(), flags: .barrier) { (v: Int) -> Int in + v + }.mapValues(on: .global(), flags: .barrier) { (v: Int) -> Int in + v + 10 + }.flatMapValues(on: .global(), flags: .barrier) { (v: Int) -> [Int] in + [v + 10] + }.compactMapValues(on: .global(), flags: .barrier) { (v: Int) -> Int in + v + 10 + }.thenMap(on: .global(), flags: .barrier) { (v: Int) -> CancellablePromise in Promise.value(v + 10).cancellize() - }.thenMap(on: .global(qos: .background), flags: .barrier) { v -> Promise in + }.thenMap(on: .global(), flags: .barrier) { (v: Int) -> Promise in Promise.value(v + 10) - }.thenFlatMap(on: .global(qos: .background), flags: .barrier) { v -> CancellablePromise<[Int]> in + }.thenFlatMap(on: .global(), flags: .barrier) { (v: Int) -> CancellablePromise<[Int]> in Promise.value([v + 10]).cancellize() - }.thenFlatMap(on: .global(qos: .background), flags: .barrier) { v -> Promise<[Int]> in + }.thenFlatMap(on: .global(), flags: .barrier) { (v: Int) -> Promise<[Int]> in Promise.value([v + 10]) - }.filterValues(on: .global(qos: .background), flags: .barrier) { _ in + }.filterValues(on: .global(), flags: .barrier) { (_: Int) in true - }.sortedValues(on: .global(qos: .background), flags: .barrier).firstValue(on: .global(qos: .background), flags: .barrier) { _ in + }.sortedValues(on: .global(), flags: .barrier) + .firstValue(on: .global(), flags: .barrier) { (_: Int) -> Bool in true - }.done(on: .global(qos: .background), flags: .barrier) { - XCTAssertEqual($0, 112) + }.done(on: .global(), flags: .barrier) { (b: Bool) -> Void in + XCTAssertEqual(b, 112) ex1.fulfill() - }.catch(on: .global(qos: .background), flags: .barrier) { _ in + }.catch(on: .global(), flags: .barrier) { _ in XCTFail() } let ex2 = expectation(description: "DispatchQueue firstValue property") - Promise.value([42, 52]).cancellize().firstValue.done(on: .global(qos: .background), flags: .barrier) { + Promise.value([42, 52]).cancellize().firstValue.done(on: .global(), flags: .barrier) { XCTAssertEqual($0, 42) ex2.fulfill() - }.catch(on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + }.catch(on: .global(), flags: .barrier, policy: .allErrors) { _ in XCTFail() } let ex3 = expectation(description: "DispatchQueue lastValue property") - Promise.value([42, 52]).cancellize().lastValue.done(on: .global(qos: .background), flags: .barrier) { + Promise.value([42, 52]).cancellize().lastValue.done(on: .global(), flags: .barrier) { XCTAssertEqual($0, 52) ex3.fulfill() - }.catch(on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + }.catch(on: .global(), flags: .barrier, policy: .allErrors) { _ in XCTFail() } waitForExpectations(timeout: 5) } +#endif func testRecover() { let ex1 = expectation(description: "DispatchQueue CatchMixin recover cancellable") - Promise(error: Error.dummy).cancellize().recover(on: .global(qos: .background), flags: .barrier) { _ in + Promise(error: Error.dummy).cancellize().recover(on: .global(), flags: .barrier) { _ in Promise.value(42).cancellize() - }.ensure(on: .global(qos: .background), flags: .barrier) { - }.ensureThen(on: .global(qos: .background), flags: .barrier) { + }.ensure(on: .global(), flags: .barrier) { + }.ensureThen(on: .global(), flags: .barrier) { Promise.value(42).asVoid().cancellize() - }.done(on: .global(qos: .background), flags: .barrier) { + }.done(on: .global(), flags: .barrier) { XCTAssertEqual($0, 42) ex1.fulfill() - }.catch(on: .global(qos: .background), flags: .barrier) { _ in + }.catch(on: .global(), flags: .barrier) { _ in XCTFail() } let ex2 = expectation(description: "DispatchQueue CatchMixin recover standard") - Promise(error: Error.dummy).cancellize().recover(on: .global(qos: .background), flags: .barrier) { _ in + Promise(error: Error.dummy).cancellize().recover(on: .global(), flags: .barrier) { _ in Promise.value(42) - }.done(on: .global(qos: .background), flags: .barrier) { + }.done(on: .global(), flags: .barrier) { XCTAssertEqual($0, 42) ex2.fulfill() - }.catch(on: .global(qos: .background), flags: .barrier) { _ in + }.catch(on: .global(), flags: .barrier) { _ in XCTFail() } let ex3 = expectation(description: "DispatchQueue CatchMixin recover void standard") - Promise(error: Error.dummy).cancellize().recover(on: .global(qos: .background), flags: .barrier) { _ in - }.done(on: .global(qos: .background), flags: .barrier) { + Promise(error: Error.dummy).cancellize().recover(on: .global(), flags: .barrier) { _ in + }.done(on: .global(), flags: .barrier) { ex3.fulfill() - }.catch(on: .global(qos: .background), flags: .barrier) { _ in + }.catch(on: .global(), flags: .barrier) { _ in XCTFail() } @@ -180,33 +187,33 @@ class DispatcherTests: XCTestCase { func testRecoverIsCancelled() { let ex1 = expectation(description: "DispatchQueue CatchMixin recover cancellable isCancelled") - Promise(error: Error.cancelled).cancellize().recover(on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + Promise(error: Error.cancelled).cancellize().recover(on: .global(), flags: .barrier, policy: .allErrors) { _ in Promise.value(42).cancellize() - }.ensure(on: .global(qos: .background), flags: .barrier) { - }.ensureThen(on: .global(qos: .background), flags: .barrier) { + }.ensure(on: .global(), flags: .barrier) { + }.ensureThen(on: .global(), flags: .barrier) { Promise.value(42).asVoid().cancellize() - }.done(on: .global(qos: .background), flags: .barrier) { + }.done(on: .global(), flags: .barrier) { XCTAssertEqual($0, 42) ex1.fulfill() - }.catch(on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + }.catch(on: .global(), flags: .barrier, policy: .allErrors) { _ in XCTFail() } let ex2 = expectation(description: "DispatchQueue CatchMixin recover standard isCancelled") - Promise(error: Error.cancelled).cancellize().recover(on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + Promise(error: Error.cancelled).cancellize().recover(on: .global(), flags: .barrier, policy: .allErrors) { _ in Promise.value(42) - }.done(on: .global(qos: .background), flags: .barrier) { + }.done(on: .global(), flags: .barrier) { XCTAssertEqual($0, 42) ex2.fulfill() - }.catch(on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + }.catch(on: .global(), flags: .barrier, policy: .allErrors) { _ in XCTFail() } let ex3 = expectation(description: "DispatchQueue CatchMixin recover void standard isCancelled") - Promise(error: Error.cancelled).cancellize().recover(on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in - }.done(on: .global(qos: .background), flags: .barrier) { + Promise(error: Error.cancelled).cancellize().recover(on: .global(), flags: .barrier, policy: .allErrors) { _ in + }.done(on: .global(), flags: .barrier) { ex3.fulfill() - }.catch(on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + }.catch(on: .global(), flags: .barrier, policy: .allErrors) { _ in XCTFail() } @@ -215,51 +222,51 @@ class DispatcherTests: XCTestCase { func testCatchOnly() { let ex1 = expectation(description: "DispatchQueue CatchMixin catch-only") - Promise(error: Error.dummy).cancellize().done(on: .global(qos: .background), flags: .barrier) { + Promise(error: Error.dummy).cancellize().done(on: .global(), flags: .barrier) { XCTFail() - }.catch(only: Error.dummy, on: .global(qos: .background), flags: .barrier) { _ in + }.catch(only: Error.dummy, on: .global(), flags: .barrier) { _ in ex1.fulfill() - }.catch(on: .global(qos: .background), flags: .barrier) { _ in + }.catch(on: .global(), flags: .barrier) { _ in XCTFail() } let ex2 = expectation(description: "DispatchQueue CatchMixin catch-type") - Promise(error: Error.dummy).cancellize().done(on: .global(qos: .background), flags: .barrier) { + Promise(error: Error.dummy).cancellize().done(on: .global(), flags: .barrier) { XCTFail() - }.catch(only: Error.self, on: .global(qos: .background), flags: .barrier) { _ in + }.catch(only: Error.self, on: .global(), flags: .barrier) { _ in ex2.fulfill() - }.catch(on: .global(qos: .background), flags: .barrier) { _ in + }.catch(on: .global(), flags: .barrier) { _ in XCTFail() } let ex3 = expectation(description: "DispatchQueue CascadingFinalizer catch") - Promise(error: Error.dummy).cancellize().done(on: .global(qos: .background), flags: .barrier) { + Promise(error: Error.dummy).cancellize().done(on: .global(), flags: .barrier) { XCTFail() - }.catch(only: Error.cancelled, on: .global(qos: .background), flags: .barrier) { _ in + }.catch(only: Error.cancelled, on: .global(), flags: .barrier) { _ in XCTFail() - }.catch(on: .global(qos: .background), flags: .barrier) { _ in + }.catch(on: .global(), flags: .barrier) { _ in ex3.fulfill() } let ex4 = expectation(description: "DispatchQueue CascadingFinalizer catch-only") - Promise(error: Error.dummy).cancellize().done(on: .global(qos: .background), flags: .barrier) { + Promise(error: Error.dummy).cancellize().done(on: .global(), flags: .barrier) { XCTFail() - }.catch(only: Error.cancelled, on: .global(qos: .background), flags: .barrier) { _ in + }.catch(only: Error.cancelled, on: .global(), flags: .barrier) { _ in XCTFail() - }.catch(only: Error.dummy, on: .global(qos: .background), flags: .barrier) { _ in + }.catch(only: Error.dummy, on: .global(), flags: .barrier) { _ in ex4.fulfill() - }.catch(on: .global(qos: .background), flags: .barrier) { _ in + }.catch(on: .global(), flags: .barrier) { _ in XCTFail() } let ex5 = expectation(description: "DispatchQueue CascadingFinalizer catch-type") - Promise(error: Error.dummy).cancellize().done(on: .global(qos: .background), flags: .barrier) { + Promise(error: Error.dummy).cancellize().done(on: .global(), flags: .barrier) { XCTFail() - }.catch(only: Error.cancelled, on: .global(qos: .background), flags: .barrier) { _ in + }.catch(only: Error.cancelled, on: .global(), flags: .barrier) { _ in XCTFail() - }.catch(only: Error.self, on: .global(qos: .background), flags: .barrier) { _ in + }.catch(only: Error.self, on: .global(), flags: .barrier) { _ in ex5.fulfill() - }.catch(on: .global(qos: .background), flags: .barrier) { _ in + }.catch(on: .global(), flags: .barrier) { _ in XCTFail() } @@ -268,51 +275,51 @@ class DispatcherTests: XCTestCase { func testCatchOnlyIsCancelled() { let ex1 = expectation(description: "DispatchQueue CatchMixin catch-only isCancelled") - Promise(error: Error.cancelled).cancellize().done(on: .global(qos: .background), flags: .barrier) { + Promise(error: Error.cancelled).cancellize().done(on: .global(), flags: .barrier) { XCTFail() - }.catch(only: Error.cancelled, on: .global(qos: .background), flags: .barrier) { _ in + }.catch(only: Error.cancelled, on: .global(), flags: .barrier) { _ in ex1.fulfill() - }.catch(on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + }.catch(on: .global(), flags: .barrier, policy: .allErrors) { _ in XCTFail() } let ex2 = expectation(description: "DispatchQueue CatchMixin catch-type isCancelled") - Promise(error: Error.cancelled).cancellize().done(on: .global(qos: .background), flags: .barrier) { + Promise(error: Error.cancelled).cancellize().done(on: .global(), flags: .barrier) { XCTFail() - }.catch(only: Error.self, on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + }.catch(only: Error.self, on: .global(), flags: .barrier, policy: .allErrors) { _ in ex2.fulfill() - }.catch(on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + }.catch(on: .global(), flags: .barrier, policy: .allErrors) { _ in XCTFail() } let ex3 = expectation(description: "DispatchQueue CascadingFinalizer catch isCancelled") - Promise(error: Error.cancelled).cancellize().done(on: .global(qos: .background), flags: .barrier) { + Promise(error: Error.cancelled).cancellize().done(on: .global(), flags: .barrier) { XCTFail() - }.catch(only: Error.cancelled, on: .global(qos: .background), flags: .barrier) { _ in + }.catch(only: Error.cancelled, on: .global(), flags: .barrier) { _ in ex3.fulfill() - }.catch(on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + }.catch(on: .global(), flags: .barrier, policy: .allErrors) { _ in XCTFail() } let ex4 = expectation(description: "DispatchQueue CascadingFinalizer catch-only isCancelled") - Promise(error: Error.cancelled).cancellize().done(on: .global(qos: .background), flags: .barrier) { + Promise(error: Error.cancelled).cancellize().done(on: .global(), flags: .barrier) { XCTFail() - }.catch(only: Error.dummy, on: .global(qos: .background), flags: .barrier) { _ in + }.catch(only: Error.dummy, on: .global(), flags: .barrier) { _ in XCTFail() - }.catch(only: Error.cancelled, on: .global(qos: .background), flags: .barrier) { _ in + }.catch(only: Error.cancelled, on: .global(), flags: .barrier) { _ in ex4.fulfill() - }.catch(on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + }.catch(on: .global(), flags: .barrier, policy: .allErrors) { _ in XCTFail() } let ex5 = expectation(description: "DispatchQueue CascadingFinalizer catch-type isCancelled") - Promise(error: Error.cancelled).cancellize().done(on: .global(qos: .background), flags: .barrier) { + Promise(error: Error.cancelled).cancellize().done(on: .global(), flags: .barrier) { XCTFail() - }.catch(only: Error.dummy, on: .global(qos: .background), flags: .barrier) { _ in + }.catch(only: Error.dummy, on: .global(), flags: .barrier) { _ in XCTFail() - }.catch(only: Error.self, on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + }.catch(only: Error.self, on: .global(), flags: .barrier, policy: .allErrors) { _ in ex5.fulfill() - }.catch(on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + }.catch(on: .global(), flags: .barrier, policy: .allErrors) { _ in XCTFail() } @@ -321,58 +328,58 @@ class DispatcherTests: XCTestCase { func testRecoverOnly() { let ex1 = expectation(description: "DispatchQueue CatchMixin recover-only cancellable") - Promise(error: Error.dummy).cancellize().recover(only: Error.dummy, on: .global(qos: .background), flags: .barrier) { _ in + Promise(error: Error.dummy).cancellize().recover(only: Error.dummy, on: .global(), flags: .barrier) { _ in Promise.value(42).cancellize() - }.done(on: .global(qos: .background), flags: .barrier) { + }.done(on: .global(), flags: .barrier) { XCTAssertEqual($0, 42) ex1.fulfill() - }.catch(on: .global(qos: .background), flags: .barrier) { _ in + }.catch(on: .global(), flags: .barrier) { _ in XCTFail() } let ex2 = expectation(description: "DispatchQueue CatchMixin recover-only standard") - Promise(error: Error.dummy).cancellize().recover(only: Error.dummy, on: .global(qos: .background), flags: .barrier) { _ in + Promise(error: Error.dummy).cancellize().recover(only: Error.dummy, on: .global(), flags: .barrier) { _ in Promise.value(42) - }.done(on: .global(qos: .background), flags: .barrier) { + }.done(on: .global(), flags: .barrier) { XCTAssertEqual($0, 42) ex2.fulfill() - }.catch(on: .global(qos: .background), flags: .barrier) { _ in + }.catch(on: .global(), flags: .barrier) { _ in XCTFail() } let ex3 = expectation(description: "DispatchQueue CatchMixin recover-type cancellable") - Promise(error: Error.dummy).cancellize().recover(only: Error.self, on: .global(qos: .background), flags: .barrier) { _ in + Promise(error: Error.dummy).cancellize().recover(only: Error.self, on: .global(), flags: .barrier) { _ in Promise.value(42).cancellize() - }.done(on: .global(qos: .background), flags: .barrier) { + }.done(on: .global(), flags: .barrier) { XCTAssertEqual($0, 42) ex3.fulfill() - }.catch(on: .global(qos: .background), flags: .barrier) { _ in + }.catch(on: .global(), flags: .barrier) { _ in XCTFail() } let ex4 = expectation(description: "DispatchQueue CatchMixin recover-type standard") - Promise(error: Error.dummy).cancellize().recover(only: Error.self, on: .global(qos: .background), flags: .barrier) { _ in + Promise(error: Error.dummy).cancellize().recover(only: Error.self, on: .global(), flags: .barrier) { _ in Promise.value(42) - }.done(on: .global(qos: .background), flags: .barrier) { + }.done(on: .global(), flags: .barrier) { XCTAssertEqual($0, 42) ex4.fulfill() - }.catch(on: .global(qos: .background), flags: .barrier) { _ in + }.catch(on: .global(), flags: .barrier) { _ in XCTFail() } let ex5 = expectation(description: "DispatchQueue CatchMixin recover-only-void cancellable") - Promise(error: Error.dummy).cancellize().recover(only: Error.dummy, on: .global(qos: .background), flags: .barrier) { _ in - }.done(on: .global(qos: .background), flags: .barrier) { + Promise(error: Error.dummy).cancellize().recover(only: Error.dummy, on: .global(), flags: .barrier) { _ in + }.done(on: .global(), flags: .barrier) { ex5.fulfill() - }.catch(on: .global(qos: .background), flags: .barrier) { _ in + }.catch(on: .global(), flags: .barrier) { _ in XCTFail() } let ex6 = expectation(description: "DispatchQueue CatchMixin recover-type-void standard") - Promise(error: Error.dummy).cancellize().recover(only: Error.self, on: .global(qos: .background), flags: .barrier) { _ in - }.done(on: .global(qos: .background), flags: .barrier) { + Promise(error: Error.dummy).cancellize().recover(only: Error.self, on: .global(), flags: .barrier) { _ in + }.done(on: .global(), flags: .barrier) { ex6.fulfill() - }.catch(on: .global(qos: .background), flags: .barrier) { _ in + }.catch(on: .global(), flags: .barrier) { _ in XCTFail() } @@ -381,58 +388,58 @@ class DispatcherTests: XCTestCase { func testRecoverOnlyIsCancelled() { let ex1 = expectation(description: "DispatchQueue CatchMixin recover-only cancellable isCancelled") - Promise(error: Error.cancelled).cancellize().recover(only: Error.cancelled, on: .global(qos: .background), flags: .barrier) { _ in + Promise(error: Error.cancelled).cancellize().recover(only: Error.cancelled, on: .global(), flags: .barrier) { _ in Promise.value(42).cancellize() - }.done(on: .global(qos: .background), flags: .barrier) { + }.done(on: .global(), flags: .barrier) { XCTAssertEqual($0, 42) ex1.fulfill() - }.catch(on: .global(qos: .background), flags: .barrier) { _ in + }.catch(on: .global(), flags: .barrier) { _ in XCTFail() } let ex2 = expectation(description: "DispatchQueue CatchMixin recover-only standard isCancelled") - Promise(error: Error.cancelled).cancellize().recover(only: Error.cancelled, on: .global(qos: .background), flags: .barrier) { _ in + Promise(error: Error.cancelled).cancellize().recover(only: Error.cancelled, on: .global(), flags: .barrier) { _ in Promise.value(42) - }.done(on: .global(qos: .background), flags: .barrier) { + }.done(on: .global(), flags: .barrier) { XCTAssertEqual($0, 42) ex2.fulfill() - }.catch(on: .global(qos: .background), flags: .barrier) { _ in + }.catch(on: .global(), flags: .barrier) { _ in XCTFail() } let ex3 = expectation(description: "DispatchQueue CatchMixin recover-type cancellable isCancelled") - Promise(error: Error.cancelled).cancellize().recover(only: Error.self, on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + Promise(error: Error.cancelled).cancellize().recover(only: Error.self, on: .global(), flags: .barrier, policy: .allErrors) { _ in Promise.value(42).cancellize() - }.done(on: .global(qos: .background), flags: .barrier) { + }.done(on: .global(), flags: .barrier) { XCTAssertEqual($0, 42) ex3.fulfill() - }.catch(on: .global(qos: .background), flags: .barrier) { _ in + }.catch(on: .global(), flags: .barrier) { _ in XCTFail() } let ex4 = expectation(description: "DispatchQueue CatchMixin recover-type standard isCancelled") - Promise(error: Error.cancelled).cancellize().recover(only: Error.self, on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in + Promise(error: Error.cancelled).cancellize().recover(only: Error.self, on: .global(), flags: .barrier, policy: .allErrors) { _ in Promise.value(42) - }.done(on: .global(qos: .background), flags: .barrier) { + }.done(on: .global(), flags: .barrier) { XCTAssertEqual($0, 42) ex4.fulfill() - }.catch(on: .global(qos: .background), flags: .barrier) { _ in + }.catch(on: .global(), flags: .barrier) { _ in XCTFail() } let ex5 = expectation(description: "DispatchQueue CatchMixin recover-only-void cancellable isCancelled") - Promise(error: Error.cancelled).cancellize().recover(only: Error.cancelled, on: .global(qos: .background), flags: .barrier) { _ in - }.done(on: .global(qos: .background), flags: .barrier) { + Promise(error: Error.cancelled).cancellize().recover(only: Error.cancelled, on: .global(), flags: .barrier) { _ in + }.done(on: .global(), flags: .barrier) { ex5.fulfill() - }.catch(on: .global(qos: .background), flags: .barrier) { _ in + }.catch(on: .global(), flags: .barrier) { _ in XCTFail() } let ex6 = expectation(description: "DispatchQueue CatchMixin recover-type-void standard isCancelled") - Promise(error: Error.cancelled).cancellize().recover(only: Error.self, on: .global(qos: .background), flags: .barrier, policy: .allErrors) { _ in - }.done(on: .global(qos: .background), flags: .barrier) { + Promise(error: Error.cancelled).cancellize().recover(only: Error.self, on: .global(), flags: .barrier, policy: .allErrors) { _ in + }.done(on: .global(), flags: .barrier) { ex6.fulfill() - }.catch(on: .global(qos: .background), flags: .barrier) { _ in + }.catch(on: .global(), flags: .barrier) { _ in XCTFail() } diff --git a/Tests/Cancel/XCTestManifests.swift b/Tests/Cancel/XCTestManifests.swift deleted file mode 100644 index 2973390df..000000000 --- a/Tests/Cancel/XCTestManifests.swift +++ /dev/null @@ -1,432 +0,0 @@ -#if !canImport(ObjectiveC) -import XCTest - -extension AfterTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__AfterTests = [ - ("testCancelForGuarantee_Done", testCancelForGuarantee_Done), - ("testCancelForPromise_Done", testCancelForPromise_Done), - ("testCancellableAfter", testCancellableAfter), - ("testNegative", testNegative), - ("testPositive", testPositive), - ("testZero", testZero), - ] -} - -extension CancelChain { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__CancelChain = [ - ("testCancelChainPAD", testCancelChainPAD), - ("testCancelChainPB", testCancelChainPB), - ("testCancelChainPC", testCancelChainPC), - ("testCancelChainSuccess", testCancelChainSuccess), - ] -} - -extension CancellableDefaultDispatchQueueTest { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__CancellableDefaultDispatchQueueTest = [ - ("testOverrodeDefaultAlwaysQueue", testOverrodeDefaultAlwaysQueue), - ("testOverrodeDefaultCatchQueue", testOverrodeDefaultCatchQueue), - ("testOverrodeDefaultThenQueue", testOverrodeDefaultThenQueue), - ] -} - -extension CancellableErrorTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__CancellableErrorTests = [ - ("testCustomDebugStringConvertible", testCustomDebugStringConvertible), - ("testCustomStringConvertible", testCustomStringConvertible), - ] -} - -extension CancellablePromiseTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__CancellablePromiseTests = [ - ("testBridge", testBridge), - ("testCancel", testCancel), - ("testCancellablePromiseEmbeddedInStandardPromiseChain", testCancellablePromiseEmbeddedInStandardPromiseChain), - ("testChain", testChain), - ("testFirstly", testFirstly), - ("testFirstlyWithPromise", testFirstlyWithPromise), - ("testReturnTypeForAMultiLineClosureIsNotExplicitlyStated", testReturnTypeForAMultiLineClosureIsNotExplicitlyStated), - ("testThenMapCancel", testThenMapCancel), - ("testThenMapSuccess", testThenMapSuccess), - ("testTryingToCancelAStandardPromiseChain", testTryingToCancelAStandardPromiseChain), - ] -} - -extension CancellationTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__CancellationTests = [ - ("testCancellation", testCancellation), - ("testFoundationBridging1", testFoundationBridging1), - ("testFoundationBridging2", testFoundationBridging2), - ("testIsCancelled", testIsCancelled), - ("testRecoverWithCancellation", testRecoverWithCancellation), - ("testThrowCancellableErrorThatIsNotCancelled", testThrowCancellableErrorThatIsNotCancelled), - ] -} - -extension CatchableTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__CatchableTests = [ - ("test__cancellable_conditional_recover__fulfilled_path", test__cancellable_conditional_recover__fulfilled_path), - ("test__conditional_recover", test__conditional_recover), - ("test__conditional_recover__fulfilled_path", test__conditional_recover__fulfilled_path), - ("test__conditional_recover__ignores_cancellation_but_fed_cancellation", test__conditional_recover__ignores_cancellation_but_fed_cancellation), - ("test__conditional_recover__no_recover", test__conditional_recover__no_recover), - ("test__full_recover", test__full_recover), - ("test__full_recover__fulfilled_path", test__full_recover__fulfilled_path), - ("test__void_specialized_conditional_recover", test__void_specialized_conditional_recover), - ("test__void_specialized_conditional_recover__fulfilled_path", test__void_specialized_conditional_recover__fulfilled_path), - ("test__void_specialized_conditional_recover__ignores_cancellation_but_fed_cancellation", test__void_specialized_conditional_recover__ignores_cancellation_but_fed_cancellation), - ("test__void_specialized_conditional_recover__no_recover", test__void_specialized_conditional_recover__no_recover), - ("test__void_specialized_full_recover", test__void_specialized_full_recover), - ("test__void_specialized_full_recover__fulfilled_path", test__void_specialized_full_recover__fulfilled_path), - ("testCancellableFinalizerHelpers", testCancellableFinalizerHelpers), - ("testCancellableRecoverFromError", testCancellableRecoverFromError), - ("testCatchOnly", testCatchOnly), - ("testCatchOnly_BaseCatchIsCalledWhenCatchOnlyDoesNotExecute", testCatchOnly_BaseCatchIsCalledWhenCatchOnlyDoesNotExecute), - ("testCatchOnly_BaseCatchIsNotCalledAfterCatchOnlyExecutes", testCatchOnly_BaseCatchIsNotCalledAfterCatchOnlyExecutes), - ("testCatchOnly_Mixed", testCatchOnly_Mixed), - ("testCatchOnly_PatternMatch_1", testCatchOnly_PatternMatch_1), - ("testCatchOnly_PatternMatch_2", testCatchOnly_PatternMatch_2), - ("testCatchOnly_Type", testCatchOnly_Type), - ("testCatchOnly_Type_BaseCatchIsNotCalledAfterCatchOnlyExecutes", testCatchOnly_Type_BaseCatchIsNotCalledAfterCatchOnlyExecutes), - ("testCatchOnly_Type_Cancellation_Handle", testCatchOnly_Type_Cancellation_Handle), - ("testCatchOnly_Type_Cancellation_Ignore", testCatchOnly_Type_Cancellation_Ignore), - ("testCatchOnly_Type_Ignored", testCatchOnly_Type_Ignored), - ("testCatchOnly_Type_PatternMatch_1", testCatchOnly_Type_PatternMatch_1), - ("testCatchOnly_Type_PatternMatch_2", testCatchOnly_Type_PatternMatch_2), - ("testCauterize", testCauterize), - ("testEnsureThen_Error", testEnsureThen_Error), - ("testEnsureThen_Value", testEnsureThen_Value), - ("testEnsureThen_Value_NotCancelled", testEnsureThen_Value_NotCancelled), - ("testFinally", testFinally), - ("testRecoverOnly_BaseRecoverIsNotCalledAfterRecoverOnlyExecutes", testRecoverOnly_BaseRecoverIsNotCalledAfterRecoverOnlyExecutes), - ("testRecoverOnly_Chaining", testRecoverOnly_Chaining), - ("testRecoverOnly_Object", testRecoverOnly_Object), - ("testRecoverOnly_Object_DoesNotReturnSelf", testRecoverOnly_Object_DoesNotReturnSelf), - ("testRecoverOnly_Object_Ignored", testRecoverOnly_Object_Ignored), - ("testRecoverOnly_Object_PatternMatch", testRecoverOnly_Object_PatternMatch), - ("testRecoverOnly_Object_Void", testRecoverOnly_Object_Void), - ("testRecoverOnly_Object_Void_Fufilled", testRecoverOnly_Object_Void_Fufilled), - ("testRecoverOnly_Object_Void_Ignored", testRecoverOnly_Object_Void_Ignored), - ("testRecoverOnly_Type", testRecoverOnly_Type), - ("testRecoverOnly_Type_Cancellation_Handle", testRecoverOnly_Type_Cancellation_Handle), - ("testRecoverOnly_Type_Cancellation_Ignore", testRecoverOnly_Type_Cancellation_Ignore), - ("testRecoverOnly_Type_DoesNotReturnSelf", testRecoverOnly_Type_DoesNotReturnSelf), - ("testRecoverOnly_Type_Ignored", testRecoverOnly_Type_Ignored), - ("testRecoverOnly_Type_PatternMatch", testRecoverOnly_Type_PatternMatch), - ("testRecoverOnly_Type_Void", testRecoverOnly_Type_Void), - ("testRecoverOnly_Type_Void_Cancellation_Ignore", testRecoverOnly_Type_Void_Cancellation_Ignore), - ("testRecoverOnly_Type_Void_Fufilled", testRecoverOnly_Type_Void_Fufilled), - ("testRecoverOnly_Type_Void_Ignored", testRecoverOnly_Type_Void_Ignored), - ("testRecoverOnly_Type_Void_Rethrow", testRecoverOnly_Type_Void_Rethrow), - ] -} - -extension DispatchWrapperTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__DispatchWrapperTests = [ - ("testWrappedCancellablePromiseCatchAPI", testWrappedCancellablePromiseCatchAPI), - ("testWrappedCancellablePromiseEnsureAPI", testWrappedCancellablePromiseEnsureAPI), - ("testWrappedCancellablePromiseRecoverAPI", testWrappedCancellablePromiseRecoverAPI), - ("testWrappedCancellablePromiseSequenceAPI", testWrappedCancellablePromiseSequenceAPI), - ("testWrappedCancellablePromiseThenableAPI", testWrappedCancellablePromiseThenableAPI), - ] -} - -extension DispatcherTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__DispatcherTests = [ - ("testCatchOnly", testCatchOnly), - ("testCatchOnlyIsCancelled", testCatchOnlyIsCancelled), - ("testDispatcherExtensionCanThrowInBody", testDispatcherExtensionCanThrowInBody), - ("testDispatcherExtensionReturnsGuarantee", testDispatcherExtensionReturnsGuarantee), - ("testDispatcherWithThrow", testDispatcherWithThrow), - ("testDispatchQueueSelection", testDispatchQueueSelection), - ("testMapValues", testMapValues), - ("testRecover", testRecover), - ("testRecoverIsCancelled", testRecoverIsCancelled), - ("testRecoverOnly", testRecoverOnly), - ("testRecoverOnlyIsCancelled", testRecoverOnlyIsCancelled), - ] -} - -extension GuaranteeTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__GuaranteeTests = [ - ("testCancellable", testCancellable), - ("testInit", testInit), - ("testSetCancellable", testSetCancellable), - ("testThenMap", testThenMap), - ("testWait", testWait), - ] -} - -extension HangTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__HangTests = [ - ("test", test), - ("testError", testError), - ] -} - -extension JoinTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__JoinTests = [ - ("testCancelledAfterAllResolve", testCancelledAfterAllResolve), - ("testFulfilledAfterAllResolve", testFulfilledAfterAllResolve), - ("testImmediates", testImmediates), - ] -} - -extension PromiseTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__PromiseTests = [ - ("testBodyThrowsError", testBodyThrowsError), - ("testCancellable", testCancellable), - ("testCanMakeVoidPromise", testCanMakeVoidPromise), - ("testCannotFulfillWithError", testCannotFulfillWithError), - ("testCustomStringConvertible", testCustomStringConvertible), - ("testDispatchQueueAsyncExtensionCanThrowInBody", testDispatchQueueAsyncExtensionCanThrowInBody), - ("testDispatchQueueAsyncExtensionReturnsPromise", testDispatchQueueAsyncExtensionReturnsPromise), - ("testInitCancellable", testInitCancellable), - ("testInitVoidCancellable", testInitVoidCancellable), - ("testIsFulfilled", testIsFulfilled), - ("testIsPending", testIsPending), - ("testIsRejected", testIsRejected), - ("testIsResolved", testIsResolved), - ("testPipeForResolved", testPipeForResolved), - ("testSetCancellable", testSetCancellable), - ("testThrowInFirstly", testThrowInFirstly), - ("testThrowInInitializer", testThrowInInitializer), - ("testWait", testWait), - ] -} - -extension RaceTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__RaceTests = [ - ("test1", test1), - ("test1Array", test1Array), - ("test2", test2), - ("test2Array", test2Array), - ("testCancelInner", testCancelInner), - ("testEmptyArray", testEmptyArray), - ("testReject", testReject), - ] -} - -extension RegressionTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__RegressionTests = [ - ("testReturningPreviousPromiseWorks", testReturningPreviousPromiseWorks), - ] -} - -extension StressTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__StressTests = [ - ("testCancelContextConcurrentAppend", testCancelContextConcurrentAppend), - ("testCancelContextConcurrentCancel", testCancelContextConcurrentCancel), - ("testCancelContextConcurrentReadWrite", testCancelContextConcurrentReadWrite), - ("testThenDataRace", testThenDataRace), - ("testThensAreSequentialForLongTime", testThensAreSequentialForLongTime), - ("testZalgoDataRace", testZalgoDataRace), - ] -} - -extension ThenableTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__ThenableTests = [ - ("testBarrier", testBarrier), - ("testCompactMap", testCompactMap), - ("testCompactMapThrows", testCompactMapThrows), - ("testCompactMapValues", testCompactMapValues), - ("testDispatchFlagsSyntax", testDispatchFlagsSyntax), - ("testFirstValueForEmpty", testFirstValueForEmpty), - ("testGet", testGet), - ("testLastValueForEmpty", testLastValueForEmpty), - ("testPMKErrorCompactMap", testPMKErrorCompactMap), - ("testRejectedPromiseCompactMap", testRejectedPromiseCompactMap), - ("testThenFlatMap", testThenFlatMap), - ("testThenMap", testThenMap), - ("testThenOffRejected", testThenOffRejected), - ] -} - -extension TimeoutTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__TimeoutTests = [ - ("testCancelBeforeTimeout", testCancelBeforeTimeout), - ("testCancelRaceBeforeTimeout", testCancelRaceBeforeTimeout), - ("testDoubleTimeout", testDoubleTimeout), - ("testMixTypes", testMixTypes), - ("testNoTimeout", testNoTimeout), - ("testReset", testReset), - ("testTimeout", testTimeout), - ] -} - -extension ValueTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__ValueTests = [ - ("testCancelForPromise_Then", testCancelForPromise_Then), - ("testCancelForPromise_ThenDone", testCancelForPromise_ThenDone), - ("testFirstlyThenValueDone", testFirstlyThenValueDone), - ("testFirstlyValueDifferentContextDone", testFirstlyValueDifferentContextDone), - ("testFirstlyValueDone", testFirstlyValueDone), - ("testFirstlyValueDoneDifferentContext", testFirstlyValueDoneDifferentContext), - ("testValueContext", testValueContext), - ("testValueDone", testValueDone), - ("testValueThen", testValueThen), - ] -} - -extension WhenConcurrentTestCase_Swift { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__WhenConcurrentTestCase_Swift = [ - ("testStopsDequeueingOnceRejected", testStopsDequeueingOnceRejected), - ("testWhenCancel", testWhenCancel), - ("testWhenConcurrencyCancel", testWhenConcurrencyCancel), - ("testWhenConcurrencyLessThanZero", testWhenConcurrencyLessThanZero), - ("testWhenConcurrencySucceed", testWhenConcurrencySucceed), - ("testWhenEmptyGeneratorCancel", testWhenEmptyGeneratorCancel), - ("testWhenEmptyGeneratorSucceed", testWhenEmptyGeneratorSucceed), - ("testWhenGeneratorErrorCancel", testWhenGeneratorErrorCancel), - ("testWhenGeneratorErrorSucceed", testWhenGeneratorErrorSucceed), - ("testWhenSucceed", testWhenSucceed), - ] -} - -extension WhenTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__WhenTests = [ - ("testAllSealedRejectedFirstOneRejects", testAllSealedRejectedFirstOneRejects), - ("testDoubleTupleCancel", testDoubleTupleCancel), - ("testDoubleTupleSucceed", testDoubleTupleSucceed), - ("testEmpty", testEmpty), - ("testGuaranteeWhen", testGuaranteeWhen), - ("testInt", testInt), - ("testIntAlt", testIntAlt), - ("testProgress", testProgress), - ("testProgressDoesNotExceed100PercentCancel", testProgressDoesNotExceed100PercentCancel), - ("testProgressDoesNotExceed100PercentSucceed", testProgressDoesNotExceed100PercentSucceed), - ("testQuadrupleTuple", testQuadrupleTuple), - ("testQuintupleTuple", testQuintupleTuple), - ("testRejected", testRejected), - ("testTripleTuple", testTripleTuple), - ("testUnhandledErrorHandlerDoesNotFire", testUnhandledErrorHandlerDoesNotFire), - ("testUnhandledErrorHandlerDoesNotFireForStragglers", testUnhandledErrorHandlerDoesNotFireForStragglers), - ("testVoid", testVoid), - ] -} - -extension WrapTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__WrapTests = [ - ("testError", testError), - ("testErrorCancellableKitten", testErrorCancellableKitten), - ("testErrorNoDelay", testErrorNoDelay), - ("testInvalidCallingConvention", testInvalidCallingConvention), - ("testInvalidCallingConventionCancellableKitten", testInvalidCallingConventionCancellableKitten), - ("testInvertedCallingConvention", testInvertedCallingConvention), - ("testInvertedCallingConventionCancellableKitten", testInvertedCallingConventionCancellableKitten), - ("testIsFulfilled", testIsFulfilled), - ("testNonOptionalFirstParameter", testNonOptionalFirstParameter), - ("testNonOptionalFirstParameterCancellableKitten", testNonOptionalFirstParameterCancellableKitten), - ("testPendingPromiseDeallocated", testPendingPromiseDeallocated), - ("testSuccess", testSuccess), - ("testVoidCompletionValue", testVoidCompletionValue), - ("testVoidCompletionValueCancellableKitten", testVoidCompletionValueCancellableKitten), - ] -} - -extension ZalgoTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__ZalgoTests = [ - ("test1", test1), - ("test2", test2), - ("test3Cancel", test3Cancel), - ("test3Succeed", test3Succeed), - ("test4", test4), - ] -} - -public func __allTests() -> [XCTestCaseEntry] { - return [ - testCase(AfterTests.__allTests__AfterTests), - testCase(CancelChain.__allTests__CancelChain), - testCase(CancellableDefaultDispatchQueueTest.__allTests__CancellableDefaultDispatchQueueTest), - testCase(CancellableErrorTests.__allTests__CancellableErrorTests), - testCase(CancellablePromiseTests.__allTests__CancellablePromiseTests), - testCase(CancellationTests.__allTests__CancellationTests), - testCase(CatchableTests.__allTests__CatchableTests), - testCase(DispatchWrapperTests.__allTests__DispatchWrapperTests), - testCase(DispatcherTests.__allTests__DispatcherTests), - testCase(GuaranteeTests.__allTests__GuaranteeTests), - testCase(HangTests.__allTests__HangTests), - testCase(JoinTests.__allTests__JoinTests), - testCase(PromiseTests.__allTests__PromiseTests), - testCase(RaceTests.__allTests__RaceTests), - testCase(RegressionTests.__allTests__RegressionTests), - testCase(StressTests.__allTests__StressTests), - testCase(ThenableTests.__allTests__ThenableTests), - testCase(TimeoutTests.__allTests__TimeoutTests), - testCase(ValueTests.__allTests__ValueTests), - testCase(WhenConcurrentTestCase_Swift.__allTests__WhenConcurrentTestCase_Swift), - testCase(WhenTests.__allTests__WhenTests), - testCase(WrapTests.__allTests__WrapTests), - testCase(ZalgoTests.__allTests__ZalgoTests), - ] -} -#endif diff --git a/Tests/Core/LoggingTests.swift b/Tests/Core/LoggingTests.swift index 8391733db..9aa3fd3d5 100644 --- a/Tests/Core/LoggingTests.swift +++ b/Tests/Core/LoggingTests.swift @@ -2,6 +2,19 @@ import Dispatch import XCTest +private extension LogEvent { + var rawDescription: String { + switch self { + case .waitOnMainThread: return "waitOnMainThread" + case .pendingPromiseDeallocated: return "pendingPromiseDeallocated" + case .pendingGuaranteeDeallocated: return "pendingGuaranteeDeallocated" + case .cauterized(let a): return "cauterized(\(a))" + case .nilDispatchQueueWithFlags: return "nilDispatchQueueWithFlags" + case .extraneousFlagsSpecified: return "extraneousFlagsSpecified" + } + } +} + class LoggingTests: XCTestCase { enum ForTesting: Error, CustomDebugStringConvertible { @@ -14,7 +27,7 @@ class LoggingTests: XCTestCase { var logOutput: String? = nil func captureLogger(_ event: LogEvent) { - logOutput = "\(event)" + logOutput = event.rawDescription } /** @@ -130,22 +143,12 @@ class LoggingTests: XCTestCase { // Verify pendingGuaranteeDeallocated is logged func testPendingGuaranteeDeallocatedIsLogged() { - var logOutput: String? = nil - let loggingClosure: (PromiseKit.LogEvent) -> Void = { event in - switch event { - case .waitOnMainThread, .pendingPromiseDeallocated, .pendingGuaranteeDeallocated: - logOutput = "\(event)" - case .cauterized: - // Using an enum with associated value does not convert to a string properly in - // earlier versions of swift - logOutput = "cauterized" - } - } - conf.logHandler = loggingClosure + var logOutput = "" + conf.logHandler = { logOutput = $0.rawDescription } do { - let _ = Guarantee.pending() + _ = Guarantee.pending() } - XCTAssertEqual ("pendingGuaranteeDeallocated", logOutput!) + XCTAssertEqual ("pendingGuaranteeDeallocated", logOutput) } // Verify nilDispatchQueueWithFlags is logged diff --git a/Tests/Core/ResolverTests.swift b/Tests/Core/ResolverTests.swift index bb888c50b..a729e2d80 100644 --- a/Tests/Core/ResolverTests.swift +++ b/Tests/Core/ResolverTests.swift @@ -11,7 +11,7 @@ class WrapTests: XCTestCase { self.error = error } - func fetchWithCompletionBlock(block: @escaping(Int?, Error?) -> Void) { + func fetchWithCompletionBlock(block: @escaping(Int?, Swift.Error?) -> Void) { after(.milliseconds(20)).done { block(self.value, self.error) } @@ -23,7 +23,7 @@ class WrapTests: XCTestCase { } } - func fetchWithCompletionBlock3(block: @escaping(Int, Error?) -> Void) { + func fetchWithCompletionBlock3(block: @escaping(Int, Swift.Error?) -> Void) { after(.milliseconds(20)).done { block(self.value ?? -99, self.error) } @@ -35,8 +35,7 @@ class WrapTests: XCTestCase { } } -#if swift(>=5.0) - func fetchWithCompletionBlock5(block: @escaping(Swift.Result) -> Void) { + func fetchWithCompletionBlock5(block: @escaping(Swift.Result) -> Void) { after(.milliseconds(20)).done { if let value = self.value { block(.success(value)) @@ -45,7 +44,6 @@ class WrapTests: XCTestCase { } } } -#endif } func testSuccess() { @@ -146,7 +144,7 @@ class WrapTests: XCTestCase { #if swift(>=5.0) let ex = expectation(description: "") let kittenFetcher = KittenFetcher(value: 2, error: nil) - Promise { seal in + Promise { seal in kittenFetcher.fetchWithCompletionBlock5(block: seal.resolve) }.done { XCTAssertEqual($0, 2) From 88af2d788d564af7b6c4d6a13db91992bcb1dc17 Mon Sep 17 00:00:00 2001 From: Max Howell Date: Tue, 1 Jun 2021 17:32:09 -0400 Subject: [PATCH 61/81] =?UTF-8?q?Monorepo=20=E2=88=B5=20much=20simpler=20m?= =?UTF-8?q?aintenance?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/cd.yml | 13 +- .github/workflows/ci.yml | 35 +- Documents/Installation.md | 231 +------------ Documents/ObjectiveC.md | 219 ------------ Documents/README.md | 1 - Documents/Troubleshooting.md | 2 +- Package.resolved | 16 + Package.swift | 63 +++- PromiseKit.podspec | 116 +++++++ README.md | 102 +----- Sources/PMKCloudKit/CKContainer+Promise.swift | 53 +++ Sources/PMKCloudKit/CKDatabase+Promise.swift | 91 +++++ Sources/PMKCoreLocation/.gitignore | 5 + Sources/PMKCoreLocation/.travis.yml | 78 +++++ .../PMKCoreLocation/CLGeocoder+Promise.swift | 85 +++++ .../CLLocationManager+Promise.swift | 315 ++++++++++++++++++ .../NSNotificationCenter+Promise.swift | 33 ++ Sources/PMKFoundation/NSObject+Promise.swift | 61 ++++ .../PMKFoundation/NSURLSession+Promise.swift | 240 +++++++++++++ Sources/PMKFoundation/Process+Promise.swift | 175 ++++++++++ Sources/PMKFoundation/afterlife.swift | 30 ++ Sources/PMKHealthKit/.gitignore | 5 + Sources/PMKHealthKit/.travis.yml | 76 +++++ Sources/PMKHealthKit/HealthKit+Promise.swift | 87 +++++ Sources/PMKHomeKit/.gitignore | 5 + Sources/PMKHomeKit/.travis.yml | 67 ++++ .../HMAcessoryBrowser+Promise.swift | 76 +++++ Sources/PMKHomeKit/HMActionSet+Promise.swift | 24 ++ .../PMKHomeKit/HMCharacteristic+Promise.swift | 48 +++ .../PMKHomeKit/HMEventTrigger+Promise.swift | 19 ++ Sources/PMKHomeKit/HMHome+Promise.swift | 114 +++++++ .../PMKHomeKit/HMHomeManager+Promise.swift | 64 ++++ Sources/PMKHomeKit/HMTrigger+Promise.swift | 39 +++ Sources/PMKHomeKit/Utils.swift | 46 +++ Sources/PMKMapKit/.gitignore | 5 + Sources/PMKMapKit/.travis.yml | 80 +++++ Sources/PMKMapKit/MKDirections+Promise.swift | 23 ++ .../PMKMapKit/MKMapSnapshotter+Promise.swift | 18 + Sources/PMKPhotos/.gitignore | 5 + Sources/PMKPhotos/.travis.yml | 73 ++++ .../PMKPhotos/PHPhotoLibrary+Promise.swift | 22 ++ Sources/PMKStoreKit/.gitignore | 5 + Sources/PMKStoreKit/.travis.yml | 91 +++++ Sources/PMKStoreKit/SKPayment+Promise.swift | 51 +++ .../PMKStoreKit/SKPaymentQueue+Promise.swift | 61 ++++ .../SKProductsRequest+Promise.swift | 57 ++++ .../SKReceiptRefreshRequest+Promise.swift | 41 +++ Sources/{ => PromiseKit}/Box.swift | 0 .../Cancellation/CancelContext.swift | 0 .../Cancellation/Cancellable.swift | 0 .../Cancellation/CancellableCatchable.swift | 0 .../Cancellation/CancellablePromise.swift | 0 .../Cancellation/CancellableThenable.swift | 0 Sources/{ => PromiseKit}/Catchable.swift | 0 Sources/{ => PromiseKit}/Configuration.swift | 0 .../CustomStringConvertible.swift | 0 Sources/{ => PromiseKit}/Dispatcher.swift | 0 .../ConcurrencyLimitedDispatcher.swift | 0 .../Dispatchers/CoreDataDispatcher.swift | 0 .../{ => PromiseKit}/Dispatchers/Queue.swift | 0 .../Dispatchers/RateLimitedDispatcher.swift | 0 .../RateLimitedDispatcherBase.swift | 0 .../StrictRateLimitedDispatcher.swift | 0 Sources/{ => PromiseKit}/Error.swift | 0 Sources/{ => PromiseKit}/Guarantee.swift | 68 ---- Sources/{ => PromiseKit}/LogEvent.swift | 0 Sources/{ => PromiseKit}/Promise.swift | 0 Sources/{ => PromiseKit}/Resolver.swift | 0 Sources/{ => PromiseKit}/Thenable.swift | 109 ------ .../Wrappers/CatchWrappers.swift | 0 .../Wrappers/EnsureWrappers.swift | 0 .../Wrappers/FinallyWrappers.swift | 0 .../Wrappers/GuaranteeWrappers.swift | 0 .../Wrappers/RecoverWrappers.swift | 0 .../Wrappers/SequenceWrappers.swift | 0 .../Wrappers/ThenableWrappers.swift | 0 .../Wrappers/WrapperProtocols.swift | 0 Sources/{ => PromiseKit}/after.swift | 0 Sources/{ => PromiseKit}/firstly.swift | 0 Sources/{ => PromiseKit}/hang.swift | 0 Sources/{ => PromiseKit}/race.swift | 0 Sources/{ => PromiseKit}/when.swift | 2 - Tests/A+/JavaScript/AllTests.swift | 14 +- Tests/A+/JavaScript/JSAdapter.swift | 4 +- Tests/A+/JavaScript/JSPromise.swift | 7 +- Tests/A+/JavaScript/JSUtils.swift | 4 +- Tests/A+/JavaScript/MockNodeEnvironment.swift | 3 +- Tests/Core/CancellableErrorTests.swift | 1 + Tests/Core/DispatcherTests.swift | 2 +- Tests/Core/GuaranteeTests.swift | 2 - Tests/Core/PromiseTests.swift | 2 - Tests/Core/ResolverTests.swift | 6 - Tests/Core/WhenConcurrentTests.swift | 2 - Tests/PMKCoreLocation/CLGeocoderTests.swift | 121 +++++++ .../CLLocationManagerTests.swift | 95 ++++++ .../TestNSNotificationCenter.swift | 22 ++ Tests/PMKFoundation/TestNSObject.swift | 80 +++++ Tests/PMKFoundation/TestNSTask.swift | 50 +++ Tests/PMKFoundation/TestNSURLSession.swift | 81 +++++ .../PMKHomeKit/HMAccessoryBrowserTests.swift | 84 +++++ Tests/PMKHomeKit/UtilsTests.swift | 67 ++++ Tests/PMKMapKit/TestMapKit.swift | 63 ++++ 102 files changed, 3287 insertions(+), 768 deletions(-) delete mode 100644 Documents/ObjectiveC.md create mode 100644 Package.resolved create mode 100644 PromiseKit.podspec create mode 100644 Sources/PMKCloudKit/CKContainer+Promise.swift create mode 100644 Sources/PMKCloudKit/CKDatabase+Promise.swift create mode 100644 Sources/PMKCoreLocation/.gitignore create mode 100644 Sources/PMKCoreLocation/.travis.yml create mode 100644 Sources/PMKCoreLocation/CLGeocoder+Promise.swift create mode 100644 Sources/PMKCoreLocation/CLLocationManager+Promise.swift create mode 100644 Sources/PMKFoundation/NSNotificationCenter+Promise.swift create mode 100644 Sources/PMKFoundation/NSObject+Promise.swift create mode 100644 Sources/PMKFoundation/NSURLSession+Promise.swift create mode 100644 Sources/PMKFoundation/Process+Promise.swift create mode 100644 Sources/PMKFoundation/afterlife.swift create mode 100644 Sources/PMKHealthKit/.gitignore create mode 100644 Sources/PMKHealthKit/.travis.yml create mode 100644 Sources/PMKHealthKit/HealthKit+Promise.swift create mode 100644 Sources/PMKHomeKit/.gitignore create mode 100644 Sources/PMKHomeKit/.travis.yml create mode 100644 Sources/PMKHomeKit/HMAcessoryBrowser+Promise.swift create mode 100644 Sources/PMKHomeKit/HMActionSet+Promise.swift create mode 100644 Sources/PMKHomeKit/HMCharacteristic+Promise.swift create mode 100644 Sources/PMKHomeKit/HMEventTrigger+Promise.swift create mode 100644 Sources/PMKHomeKit/HMHome+Promise.swift create mode 100644 Sources/PMKHomeKit/HMHomeManager+Promise.swift create mode 100644 Sources/PMKHomeKit/HMTrigger+Promise.swift create mode 100644 Sources/PMKHomeKit/Utils.swift create mode 100644 Sources/PMKMapKit/.gitignore create mode 100644 Sources/PMKMapKit/.travis.yml create mode 100644 Sources/PMKMapKit/MKDirections+Promise.swift create mode 100644 Sources/PMKMapKit/MKMapSnapshotter+Promise.swift create mode 100644 Sources/PMKPhotos/.gitignore create mode 100644 Sources/PMKPhotos/.travis.yml create mode 100644 Sources/PMKPhotos/PHPhotoLibrary+Promise.swift create mode 100644 Sources/PMKStoreKit/.gitignore create mode 100644 Sources/PMKStoreKit/.travis.yml create mode 100644 Sources/PMKStoreKit/SKPayment+Promise.swift create mode 100644 Sources/PMKStoreKit/SKPaymentQueue+Promise.swift create mode 100644 Sources/PMKStoreKit/SKProductsRequest+Promise.swift create mode 100644 Sources/PMKStoreKit/SKReceiptRefreshRequest+Promise.swift rename Sources/{ => PromiseKit}/Box.swift (100%) rename Sources/{ => PromiseKit}/Cancellation/CancelContext.swift (100%) rename Sources/{ => PromiseKit}/Cancellation/Cancellable.swift (100%) rename Sources/{ => PromiseKit}/Cancellation/CancellableCatchable.swift (100%) rename Sources/{ => PromiseKit}/Cancellation/CancellablePromise.swift (100%) rename Sources/{ => PromiseKit}/Cancellation/CancellableThenable.swift (100%) rename Sources/{ => PromiseKit}/Catchable.swift (100%) rename Sources/{ => PromiseKit}/Configuration.swift (100%) rename Sources/{ => PromiseKit}/CustomStringConvertible.swift (100%) rename Sources/{ => PromiseKit}/Dispatcher.swift (100%) rename Sources/{ => PromiseKit}/Dispatchers/ConcurrencyLimitedDispatcher.swift (100%) rename Sources/{ => PromiseKit}/Dispatchers/CoreDataDispatcher.swift (100%) rename Sources/{ => PromiseKit}/Dispatchers/Queue.swift (100%) rename Sources/{ => PromiseKit}/Dispatchers/RateLimitedDispatcher.swift (100%) rename Sources/{ => PromiseKit}/Dispatchers/RateLimitedDispatcherBase.swift (100%) rename Sources/{ => PromiseKit}/Dispatchers/StrictRateLimitedDispatcher.swift (100%) rename Sources/{ => PromiseKit}/Error.swift (100%) rename Sources/{ => PromiseKit}/Guarantee.swift (82%) rename Sources/{ => PromiseKit}/LogEvent.swift (100%) rename Sources/{ => PromiseKit}/Promise.swift (100%) rename Sources/{ => PromiseKit}/Resolver.swift (100%) rename Sources/{ => PromiseKit}/Thenable.swift (75%) rename Sources/{ => PromiseKit}/Wrappers/CatchWrappers.swift (100%) rename Sources/{ => PromiseKit}/Wrappers/EnsureWrappers.swift (100%) rename Sources/{ => PromiseKit}/Wrappers/FinallyWrappers.swift (100%) rename Sources/{ => PromiseKit}/Wrappers/GuaranteeWrappers.swift (100%) rename Sources/{ => PromiseKit}/Wrappers/RecoverWrappers.swift (100%) rename Sources/{ => PromiseKit}/Wrappers/SequenceWrappers.swift (100%) rename Sources/{ => PromiseKit}/Wrappers/ThenableWrappers.swift (100%) rename Sources/{ => PromiseKit}/Wrappers/WrapperProtocols.swift (100%) rename Sources/{ => PromiseKit}/after.swift (100%) rename Sources/{ => PromiseKit}/firstly.swift (100%) rename Sources/{ => PromiseKit}/hang.swift (100%) rename Sources/{ => PromiseKit}/race.swift (100%) rename Sources/{ => PromiseKit}/when.swift (99%) create mode 100644 Tests/PMKCoreLocation/CLGeocoderTests.swift create mode 100644 Tests/PMKCoreLocation/CLLocationManagerTests.swift create mode 100644 Tests/PMKFoundation/TestNSNotificationCenter.swift create mode 100644 Tests/PMKFoundation/TestNSObject.swift create mode 100644 Tests/PMKFoundation/TestNSTask.swift create mode 100644 Tests/PMKFoundation/TestNSURLSession.swift create mode 100644 Tests/PMKHomeKit/HMAccessoryBrowserTests.swift create mode 100644 Tests/PMKHomeKit/UtilsTests.swift create mode 100644 Tests/PMKMapKit/TestMapKit.swift diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 1578e5d21..ee996d511 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -18,12 +18,11 @@ jobs: env: pods - uses: actions/checkout@v2 - with: - submodules: true - run: pod trunk push --allow-warnings --skip-tests --skip-import-validation env: COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }} + PMKVersion: ${{ github.event.inputs.version }} - name: Seal Deployment uses: bobheadxi/deployments@v0.5.2 @@ -46,6 +45,10 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} env: docs + - run: echo "v=${v:0:1}" >> $GITHUB_ENV + env: + v: ${{ github.event.inputs.version }} + - uses: actions/checkout@v2 - run: gem install jazzy - run: | @@ -54,9 +57,9 @@ jobs: --module-version ${{ github.event.inputs.version }} - run: git remote update - run: git checkout gh-pages - - run: rm -rf reference/v6 - - run: mv output reference/v6 - - run: git add reference/v6 + - run: rm -rf reference/v$v + - run: mv output reference/v$v + - run: git add reference/v$v - run: git config user.name github-actions - run: git config user.email github-actions@github.com - run: git commit -m 'Updated docs for v${{ github.event.inputs.version }}' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b1ef34a96..c26e6ca23 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,14 +42,14 @@ jobs: with: file: ./info.lcov - macOS: + apple: runs-on: macos-latest strategy: matrix: dst: - - platform=macOS - - platform=tvOS Simulator,OS=latest,name=Apple TV - - platform=iOS Simulator,OS=latest,name=iPhone 12 + - platform=macOS + - platform=tvOS Simulator,OS=latest,name=Apple TV + - platform=iOS Simulator,OS=latest,name=iPhone 12 steps: - uses: maxim-lobanov/setup-xcode@v1 with: @@ -58,8 +58,33 @@ jobs: - uses: sersoft-gmbh/xcodebuild-action@v1 with: spm-package: ./ - scheme: PromiseKit + scheme: PromiseKit-Package destination: ${{ matrix.dst }} action: test enable-code-coverage: true - uses: codecov/codecov-action@v1 + + watchOS: + runs-on: macos-latest + name: apple (watchOS) + steps: + - uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: ^12 + - uses: actions/checkout@v2 + # with Xcode 12.5 we can use the above matrix, but with 12.4 + # testing is not supported and trying to build with the Package.swift + # tries to build the tests too 🙄 + - run: swift package generate-xcodeproj + - uses: sersoft-gmbh/xcodebuild-action@v1 + with: + project: PromiseKit.xcodeproj + scheme: PromiseKit-Package + destination: platform=watchOS Simulator,OS=latest,name=Apple Watch Series 5 - 40mm + action: build + + lint: + runs-on: macos-latest + steps: + - uses: actions/checkout@v2 + - run: pod lib lint --fail-fast diff --git a/Documents/Installation.md b/Documents/Installation.md index 479627f5e..567588f97 100644 --- a/Documents/Installation.md +++ b/Documents/Installation.md @@ -1,236 +1,23 @@ -# PromiseKit v7 - -We only support SwiftPM since supporting all package managers is untennable and -SwiftPM is the easiest. +We support SwiftPM: ```swift package.dependencies.append( .package(url: "https://github.com/mxcl/PromiseKit", from: "7.0.0-alpha.1") ) -``` - -# PromiseKit v6 - -## Xcode 8.3, 9.x or 10.x / Swift 3 or 4 - -We recommend Carthage over CocoaPods, but both installation methods are supported. - -### CocoaPods - -```ruby -use_frameworks! - -target "Change Me!" do - pod "PromiseKit", "~> 6.8" -end -``` - -If the generated Xcode project gives you a warning that PromiseKit needs to be upgraded to -Swift 4.0 or Swift 4.2, then add the following: - -```ruby -post_install do |installer| - installer.pods_project.targets.each do |target| - if target.name == 'PromiseKit' - target.build_configurations.each do |config| - config.build_settings['SWIFT_VERSION'] = '4.2' - end - end - end -end -``` - -Adjust the value for `SWIFT_VERSION` as needed. - -CocoaPods are aware of this [issue](https://github.com/CocoaPods/CocoaPods/issues/7134). - -### Carthage - -```ruby -github "mxcl/PromiseKit" ~> 6.8 -``` - -> Please note, since PromiseKit 6.8.1 our Carthage support has transitioned to -> Swift 4 and above only. Strictly we *do* still support Swift 3.1 for Carthage, -> and if you like you could edit the PromiseKit `project.pbxproj` file during -> `carthage bootstrap` to make this possible. This change was involuntary and due -> to Xcode 10.2 dropping support for Swift 3. -From Xcode 12, you will likely need to build using `--use-xcframeworks`, eg: - - carthage build --use-xcframeworks - -### Accio - -Add the following to your `Package.swift`: - -```swift -.package(url: "https://github.com/mxcl/PromiseKit.git", .upToNextMajor(from: "6.8.4")), -``` - -Next, add `PromiseKit` to your App targets dependencies like so: - -```swift -.target( - name: "App", - dependencies: [ - "PromiseKit", - ] -), -``` - -Then run `accio update`. - -### SwiftPM - -```swift -package.dependencies.append( - .package(url: "https://github.com/mxcl/PromiseKit", from: "6.8.0") +package.targets.append( + .target(name: "MyTarget", dependencies: ["PromiseKit", "PMKFoundation"]) ) ``` -### Manually - -You can just drop `PromiseKit.xcodeproj` into your project and then add -`PromiseKit.framework` to your app’s embedded frameworks. - - -# PromiseKit vs. Xcode - -PromiseKit contains Swift, so there have been rev-lock issues with Xcode: - -| PromiseKit | Swift | Xcode | CI Status | Release Notes | -| ---------- | ----------------------- | -------------- | ------------ | ----------------- | -| 7 | >=5.3 | 12.x | ![ci-master] | | -| 6 | 3.2, 3.3, 4.x, 5.x | 8.3, 9.x, 10.x | ![ci-master] | [2018/02][news-6] | -| 5 | *Deprecated* | *n/a* | *n/a* | *n/a* | -| 4 | 3.0, 3.1, 3.2, 3.3, 4.x | 8.x, 9.x, 10.1 | ![ci-master] | [2016/09][news-4] | -| 3 | 2.x | 7.x, 8.0 | ![ci-swift2] | [2015/10][news-3] | -| 2 | 1.x | 7.x | *Deprecated* | [2015/10][news-3] | -| 1† | *N/A* | * | ![ci-legacy] | – | - -† PromiseKit 1 is pure Objective-C and thus can be used with any Xcode, it is -also your only choice if you need to support iOS 7 or below. - ---- - -We also maintain a series of branches to aid migration for PromiseKit 2: - -| Xcode | Swift | PromiseKit | Branch | CI Status | -| ----- | ----- | -----------| --------------------------- | --------- | -| 8.0 | 2.3 | 2 | [swift-2.3-minimal-changes] | ![ci-23] | -| 7.3 | 2.2 | 2 | [swift-2.2-minimal-changes] | ![ci-22] | -| 7.2 | 2.2 | 2 | [swift-2.2-minimal-changes] | ![ci-22] | -| 7.1 | 2.1 | 2 | [swift-2.0-minimal-changes] | ![ci-20] | -| 7.0 | 2.0 | 2 | [swift-2.0-minimal-changes] | ![ci-20] | - -We do **not** usually backport fixes to these branches, but pull requests are welcome. - - -## Xcode 8 / Swift 2.3 or Xcode 7 +Or CocoaPods: ```ruby -# CocoaPods -swift_version = "2.3" -pod "PromiseKit", "~> 3.5" - -# Carthage -github "mxcl/PromiseKit" ~> 3.5 +pod "PromiseKit", "~> 6.8" +pod "PromiseKit/Foundation", "~> 6.8" ``` +## Carthage -[travis]: https://travis-ci.org/mxcl/PromiseKit -[ci-master]: https://travis-ci.org/mxcl/PromiseKit.svg?branch=master -[ci-legacy]: https://travis-ci.org/mxcl/PromiseKit.svg?branch=legacy-1.x -[ci-swift2]: https://travis-ci.org/mxcl/PromiseKit.svg?branch=swift-2.x -[ci-23]: https://travis-ci.org/mxcl/PromiseKit.svg?branch=swift-2.3-minimal-changes -[ci-22]: https://travis-ci.org/mxcl/PromiseKit.svg?branch=swift-2.2-minimal-changes -[ci-20]: https://travis-ci.org/mxcl/PromiseKit.svg?branch=swift-2.0-minimal-changes -[news-2]: http://mxcl.dev/PromiseKit/news/2015/05/PromiseKit-2.0-Released/ -[news-3]: https://github.com/mxcl/PromiseKit/blob/212f31f41864d1e3ec54f5dd529bd8e1e5697024/CHANGELOG.markdown#300-oct-1st-2015 -[news-4]: http://mxcl.dev/PromiseKit/news/2016/09/PromiseKit-4.0-Released/ -[news-6]: http://mxcl.dev/PromiseKit/news/2018/02/PromiseKit-6.0-Released/ -[swift-2.3-minimal-changes]: https://github.com/mxcl/PromiseKit/tree/swift-2.3-minimal-changes -[swift-2.2-minimal-changes]: https://github.com/mxcl/PromiseKit/tree/swift-2.2-minimal-changes -[swift-2.0-minimal-changes]: https://github.com/mxcl/PromiseKit/tree/swift-2.0-minimal-changes - - -# Using Git Submodules for PromiseKit’s Extensions - -> *Note*: This is a more advanced technique. - -If you use CocoaPods and a few PromiseKit extensions, then importing PromiseKit -causes that module to import all the extension frameworks. Thus, if you have an -app and a few app extensions (e.g., iOS app, iOS watch extension, iOS Today -extension) then all your final products that use PromiseKit will have forced -dependencies on all the Apple frameworks that PromiseKit provides extensions -for. - -This isn’t that bad, but every framework that loads entails overhead and -lengthens startup time. - -It’s both better and worse with Carthage. We build individual micro-frameworks -for each PromiseKit extension, so your final products link -against only the Apple frameworks that they actually need. However, Apple has -advised that apps link only against “about 12” frameworks for performance -reasons. So with Carthage, we are worse off on this metric. - -The solution is to instead import only CorePromise: - -```ruby -# CocoaPods -pod "PromiseKit/CorePromise" - -# Carthage -github "mxcl/PromiseKit" -# ^^ for Carthage *only* have this -``` - -And to use the extensions you need via `git submodules`: - -``` -git submodule init -git submodule add https://github.com/PromiseKit/UIKit Submodules/PMKUIKit -``` - -Then in Xcode you can add these sources to your targets on a per-target basis. - -Then when you `pod update`, ensure that you also update your submodules: - - pod update && git submodule update --recursive --remote - - - -# Release History - -## [6.0](https://github.com/mxcl/PromiseKit/releases/tag/6.0.0) Feb 13th, 2018 - -* [PromiseKit 6 announcement post][news-6]. - -## [4.0](https://github.com/mxcl/PromiseKit/releases/tag/4.0.0) - -* [PromiseKit 4 announcement post][news-4]. - -## [3.0](https://github.com/mxcl/PromiseKit/releases/tag/3.0.0) Oct 1st, 2015 - -In Swift 2.0 `catch` and `defer` became reserved keywords mandating we rename -our functions with these names. This forced a major semantic version change on -PromiseKit and thus we took the opportunity to make other minor (source -compatibility breaking) improvements. - -Thus if you cannot afford to adapt to PromiseKit 3 but still want to use -Xcode-7.0/Swift-2.0 we provide a [minimal changes branch] where `catch` and -`defer` are renamed `catch_` and `defer_` and all other changes are the bare -minimum to make PromiseKit 2 compile against Swift 2. - -If you still are using Xcode 6 and Swift 1.2 then use PromiseKit 2. - -[minimal changes branch]: https://github.com/mxcl/PromiseKit/tree/swift-2.0-minimal-changes - -## [2.0](https://github.com/mxcl/PromiseKit/releases/tag/2.0.0) May 14th, 2015 - -[PromiseKit 2 announcement post](http://mxcl.dev/PromiseKit/news/2015/05/PromiseKit-2.0-Released/). - -## [1.5](https://github.com/mxcl/PromiseKit/releases/tag/1.5.0) - -Swift 1.2 support. Xcode 6.3 required. +We will support Carthage if you can PR an automated solution for generating the `.xcodeproj` on release. +It will need to support all our extensions. diff --git a/Documents/ObjectiveC.md b/Documents/ObjectiveC.md deleted file mode 100644 index 5d8c6827e..000000000 --- a/Documents/ObjectiveC.md +++ /dev/null @@ -1,219 +0,0 @@ -# Objective-C - -PromiseKit has two promise classes: - -* `Promise` (Swift) -* `AnyPromise` (Objective-C) - -Each is designed to be an appropriate promise implementation for the strong points of its language: - -* `Promise` is strict, defined and precise. -* `AnyPromise` is loose and dynamic. - -Unlike most libraries, we have extensive bridging support, you can use PromiseKit -in mixed projects with mixed language targets and mixed language libraries. - - -# Using PromiseKit with Objective-C - -`AnyPromise` is our promise class for Objective-C. It behaves almost identically to `Promise`, our Swift promise class. - -```objc -myPromise.then(^(NSString *bar){ - return anotherPromise; -}).then(^{ - //… -}).catch(^(NSError *error){ - //… -}); -``` - -You make new promises using `promiseWithResolverBlock`: - -```objc -- (AnyPromise *)myPromise { - return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve){ - resolve(foo); // if foo is an NSError, rejects, else, resolves - }]; -} -``` - ---- - -You reject promises by throwing errors: - -```objc -myPromise.then(^{ - @throw [NSError errorWithDomain:domain code:code userInfo:nil]; -}).catch(^(NSError *error){ - //… -}); -``` - -One important feature is the syntactic flexibility of your handlers: - -```objc -myPromise.then(^{ - // no parameters is fine -}); - -myPromise.then(^(id foo){ - // one parameter is fine -}); - -myPromise.then(^(id a, id b, id c){ - // up to three parameter is fine, no crash! -}); - -myPromise.then(^{ - return @1; // return anything or nothing, it's fine, no crash -}); -``` - -We do runtime inspection of the block you pass to achieve this magic. - ---- - -Another important distinction is that the equivalent function to Swift’s `recover` is combined with `AnyPromise`’s `catch`. This is typical to other “dynamic” promise implementations and thus achieves our goal that `AnyPromise` is loose and dynamic while `Promise` is strict and specific. - -A sometimes unexpected consequence of this fact is that returning nothing from a `catch` *resolves* the returned promise: - -```objc -myPromise.catch(^{ - [UIAlertView …]; -}).then(^{ - // always executes! -}); -``` - ---- - -Another important distinction is that the `value` property returns even if the promise is rejected; in that case, it returns the `NSError` object with which the promise was rejected. - - -# Bridging Between Objective-C & Swift - -Let’s say you have: - -```objc -@interface Foo -- (AnyPromise *)myPromise; -@end -``` - -Ensure that this interface is included in your bridging header. You can now use the -following pattern in your Swift code: - -```swift -let foo = Foo() -foo.myPromise.then { (obj: AnyObject?) -> Int in - // it is not necessary to specify the type of `obj` - // we just do that for demonstrative purposes -} -``` - ---- - -Let’s say you have: - -```swift -@objc class Foo: NSObject { - func stringPromise() -> Promise - func barPromise() -> Promise -} - -@objc class Bar: NSObject { /*…*/ } -``` - -Ensure that your project is generating a `…-Swift.h` header so that Objective-C can see your Swift code. - -If you built this project and opened the `…-Swift.h` header, you would only see this: - -```objc -@interface Foo -@end - -@interface Bar -@end -``` - -That's because Objective-C cannot import Swift objects that are generic. So we need to write some stubs: - -```swift -@objc class Foo: NSObject { - @objc func stringPromise() -> AnyPromise { - return AnyPromise(stringPromise()) - } - @objc func barPromise() -> AnyPromise { - return AnyPromise(barPromise()) - } -} -``` - -If we built this and opened our generated header, we would now see: - -```objc -@interface Foo -- (AnyPromise *)stringPromise; -- (AnyPromise *)barPromise; -@end - -@interface Bar -@end -``` - -Perfect. - -Note that AnyPromise can only bridge objects that conform to `AnyObject` or derive from `NSObject`. This is a limitation of Objective-C. - -# Using ObjC AnyPromises from Swift - -Simply use them, the type of your handler parameter is `Any`: - -```objective-c -- (AnyPromise *)fetchThings { - return [AnyPromise promiseWithValue:@[@"a", @"b", @"c"]]; -} -``` - -Since ObjC is not type-safe and Swift is, you will (probably) need to cast the `Any` to whatever it is you actually are feeding: - -```swift -Foo.fetchThings().done { any in - let bar = any as! [String] -} -``` - -## :warning: Caution: - -ARC in Objective-C, unlike in Objective-C++, is not exception-safe by default. -So, throwing an error will result in keeping a strong reference to the closure -that contains the throw statement. -This pattern will consequently result in memory leaks if you're not careful. - -> *Note:* Only having a strong reference to the closure would result in memory leaks. -> In our case, PromiseKit automatically keeps a strong reference to the closure until it's released. - -__Workarounds:__ - -1. Return a Promise with value NSError\ -Instead of throwing a normal error, you can return a Promise with value NSError instead. - -```objc -myPromise.then(^{ - return [AnyPromise promiseWithValue:[NSError myCustomError]]; -}).catch(^(NSError *error){ - if ([error isEqual:[NSError myCustomError]]) { - // In case, same error as the one we thrown - return; - } - //… -}); -``` -2. Enable ARC for exceptions in Objective-C (not recommended)\ -You can add this ```-fobjc-arc-exceptions to your``` to your compiler flags to enable ARC for exceptions. -This is not recommended unless you've read the Apple documentation and are comfortable with the caveats. - -For more details on ARC and exceptions: -https://clang.llvm.org/docs/AutomaticReferenceCounting.html#exceptions - diff --git a/Documents/README.md b/Documents/README.md index 20da99cd0..2560462ee 100644 --- a/Documents/README.md +++ b/Documents/README.md @@ -8,7 +8,6 @@ * [Frequently Asked Questions](FAQ.md) * Manual * [Installation Guide](Installation.md) - * [Objective-C Guide](ObjectiveC.md) * [Troubleshooting](Troubleshooting.md) * [Appendix](Appendix.md) * [Examples](Examples) diff --git a/Documents/Troubleshooting.md b/Documents/Troubleshooting.md index b986190cf..37cba295a 100644 --- a/Documents/Troubleshooting.md +++ b/Documents/Troubleshooting.md @@ -24,7 +24,7 @@ What’s the real problem? `then` *must* return a `Promise`, and you're trying t ```swift return firstly { - URLSession.shared.dataTask(.promise, with: url) + URLSession.shared.dataTask(.promise, with: url) }.compactMap { JSONSerialization.jsonObject(with: $0.data) as? [String: Any] }.map { dict in diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 000000000..3e3af447c --- /dev/null +++ b/Package.resolved @@ -0,0 +1,16 @@ +{ + "object": { + "pins": [ + { + "package": "OHHTTPStubs", + "repositoryURL": "https://github.com/AliSoftware/OHHTTPStubs", + "state": { + "branch": null, + "revision": "12f19662426d0434d6c330c6974d53e2eb10ecd9", + "version": "9.1.0" + } + } + ] + }, + "version": 1 +} diff --git a/Package.swift b/Package.swift index 0c6a87176..d3d8b7d06 100644 --- a/Package.swift +++ b/Package.swift @@ -7,20 +7,59 @@ pkg.platforms = [ .macOS(.v10_12), //FIXME strictly 10.10 (only tests need 10.12) .iOS(.v10), //FIXME strictly 8.0 .tvOS(.v10), //FIXME strictly 9.0 - .watchOS(.v2) + .watchOS(.v3) ] -pkg.products = [ - .library(name: "PromiseKit", targets: ["PromiseKit"]), -] -pkg.swiftLanguageVersions = [ - .v5 // grab PromiseKit-6.x if you want Swift 3.1‒5.2 +pkg.swiftLanguageVersions = [.v5] + +#if !os(Linux) +pkg.dependencies = [ + .package(url: "https://github.com/AliSoftware/OHHTTPStubs", from: "9.1.0") ] -pkg.targets = [ - .target(name: "PromiseKit", path: "Sources"), - .testTarget(name: "Core", dependencies: ["PromiseKit"], path: "Tests/Core"), - .testTarget(name: "Cancel", dependencies: ["PromiseKit"], path: "Tests/Cancel"), - .testTarget(name: "APlusSwift", dependencies: ["PromiseKit"], path: "Tests/A+/Swift"), - .testTarget(name: "APlusJS", dependencies: ["PromiseKit"], path: "Tests/A+/JavaScript", exclude: [ +#endif + +func dependencies(for name: String) -> [Target.Dependency] { + switch name { + case "PromiseKit": + return [] + default: + return [.target(name: "PromiseKit")] + } +} + +func has(tests name: String) -> Target? { + switch name { + case "PMKFoundation": + var deps = [Target.Dependency.target(name: "PMKFoundation")] + #if !os(Linux) + deps.append(.product(name: "OHHTTPStubsSwift", package: "OHHTTPStubs")) + #endif + return .testTarget(name: "\(name)Tests", dependencies: deps, path: "Tests/\(name)") + case "PMKHomeKit", "PMKMapKit", "PMKCoreLocation": + return .testTarget(name: "\(name)Tests", dependencies: [.target(name: name)], path: "Tests/\(name)") + default: + return nil + } +} + +for name in ["PMKCloudKit", "PMKCoreLocation", "PMKFoundation", "PMKHealthKit", "PMKHomeKit", "PMKMapKit", "PMKPhotos", "PMKStoreKit", "PromiseKit"] { + + #if os(Linux) + guard name == "PromiseKit" || name == "PMKFoundation" else { continue } + #endif + + pkg.targets.append(.target(name: name, dependencies: dependencies(for: name))) + pkg.products.append(.library(name: name, targets: [name])) + + if let testTarget = has(tests: name) { + pkg.targets.append(testTarget) + } +} + +pkg.targets += [ + .testTarget(name: "Core", dependencies: ["PromiseKit"]), + .testTarget(name: "Cancel", dependencies: ["PromiseKit"]), + .testTarget(name: "APlusSwiftTests", dependencies: ["PromiseKit"], path: "Tests/A+/Swift"), + .testTarget(name: "APlusJSTests", dependencies: ["PromiseKit"], path: "Tests/A+/JavaScript", exclude: [ "index.js", "package-lock.json", "package.json", "README.md", "webpack.config.js" ]), ] diff --git a/PromiseKit.podspec b/PromiseKit.podspec new file mode 100644 index 000000000..544dc0412 --- /dev/null +++ b/PromiseKit.podspec @@ -0,0 +1,116 @@ +Pod::Spec.new do |s| + s.name = "PromiseKit" + + s.version = ENV['PMKVersion'] || '7.999.0' + + s.source = { + :git => "https://github.com/mxcl/#{s.name}.git", + :tag => s.version + } + + s.pod_target_xcconfig = { + 'OTHER_SWIFT_FLAGS' => '-DPMKCocoaPods', + } + + s.license = 'MIT' + s.summary = 'Promises for Swift & ObjC.' + s.homepage = 'http://mxcl.dev/PromiseKit/' + s.description = 'A thoughtful and complete implementation of promises for iOS, macOS, watchOS and tvOS with first-class support for both Objective-C and Swift.' + s.social_media_url = 'https://twitter.com/mxcl' + s.authors = { 'Max Howell' => 'mxcl@me.com' } + s.documentation_url = 'http://mxcl.dev/PromiseKit/reference/v7/Classes/Promise.html' + s.default_subspecs = 'CorePromise', 'Foundation' + s.requires_arc = true + + s.swift_versions = ['5.3', '5.4'] + + s.ios.deployment_target = '9.0' + s.osx.deployment_target = '10.10' + s.watchos.deployment_target = '3.0' + s.tvos.deployment_target = '9.0' + + s.subspec 'CloudKit' do |ss| + ss.source_files = 'Sources/PMKCloudKit/*' + ss.frameworks = 'CloudKit' + ss.dependency 'PromiseKit/CorePromise' + ss.ios.deployment_target = '10.0' + ss.osx.deployment_target = '10.12' + ss.tvos.deployment_target = '10.0' + ss.watchos.deployment_target = '3.0' + end + + s.subspec 'CorePromise' do |ss| + ss.source_files = 'Sources/PromiseKit/**/*' + ss.frameworks = 'Foundation' + ss.ios.deployment_target = '8.0' + ss.osx.deployment_target = '10.10' + ss.watchos.deployment_target = '2.0' + ss.tvos.deployment_target = '9.0' + end + + s.subspec 'CoreLocation' do |ss| + ss.source_files = 'Sources/PMKCoreLocation/*' + ss.dependency 'PromiseKit/CorePromise' + ss.frameworks = 'CoreLocation' + + ss.ios.deployment_target = '8.0' + ss.osx.deployment_target = '10.10' + ss.watchos.deployment_target = '3.0' + ss.tvos.deployment_target = '9.0' + end + + s.subspec 'Foundation' do |ss| + ss.source_files = 'Sources/PMKFoundation/*' + ss.dependency 'PromiseKit/CorePromise' + ss.frameworks = 'Foundation' + ss.ios.deployment_target = '8.0' + ss.osx.deployment_target = '10.10' + ss.watchos.deployment_target = '2.0' + ss.tvos.deployment_target = '9.0' + end + + s.subspec 'HealthKit' do |ss| + ss.source_files = 'Sources/PMKHealthKit/*' + ss.dependency 'PromiseKit/CorePromise' + ss.frameworks = 'HealthKit' + ss.ios.deployment_target = '9.0' + ss.watchos.deployment_target = '2.0' + end + + s.subspec 'HomeKit' do |ss| + ss.source_files = 'Sources/PMKHomeKit/*' + ss.dependency 'PromiseKit/CorePromise' + ss.frameworks = 'HomeKit' + ss.ios.deployment_target = '8.0' + ss.watchos.deployment_target = '3.0' + ss.tvos.deployment_target = '9.0' + end + + s.subspec 'MapKit' do |ss| + ss.ios.source_files = ss.osx.source_files = ss.tvos.source_files = 'Sources/PMKMapKit/*' + ss.ios.frameworks = ss.osx.frameworks = ss.tvos.frameworks = 'MapKit' + ss.dependency 'PromiseKit/CorePromise' + ss.ios.deployment_target = '8.0' + ss.osx.deployment_target = '10.10' + ss.watchos.deployment_target = '2.0' + ss.tvos.deployment_target = '9.2' + end + + s.subspec 'Photos' do |ss| + ss.ios.source_files = ss.tvos.source_files = ss.osx.source_files = 'Sources/PMKPhotos/*' + ss.ios.frameworks = ss.tvos.frameworks = ss.osx.frameworks = 'Photos' + ss.dependency 'PromiseKit/CorePromise' + ss.ios.deployment_target = '8.0' + ss.osx.deployment_target = '10.13' + ss.tvos.deployment_target = '10.0' + end + + s.subspec 'StoreKit' do |ss| + ss.ios.source_files = ss.osx.source_files = ss.tvos.source_files = 'Sources/PMKStoreKit/*' + ss.ios.frameworks = ss.osx.frameworks = ss.tvos.frameworks = 'StoreKit' + ss.dependency 'PromiseKit/CorePromise' + ss.ios.deployment_target = '8.0' + ss.osx.deployment_target = '10.10' + ss.tvos.deployment_target = '9.0' + end +end diff --git a/README.md b/README.md index e4fda5a66..12a2dce32 100644 --- a/README.md +++ b/README.md @@ -27,27 +27,17 @@ firstly { ``` PromiseKit is a thoughtful and complete implementation of promises for any -platform that has a `swiftc`. It has *excellent* Objective-C bridging and +platform that has a `swiftc`. It has *delightful* specializations for iOS, macOS, tvOS and watchOS. It is a top-100 pod used in many of the most popular apps in the world. [![codecov](https://codecov.io/gh/mxcl/PromiseKit/branch/master/graph/badge.svg)](https://codecov.io/gh/mxcl/PromiseKit) -# PromiseKit 7 Alpha +# Requirements -PromiseKit 7 is prerelease, if you’re using it: beware! +Xcode >= 12.0 or Swift >= 5.3. -PromiseKit 7 uses Swift 5’s `Result`, PromiseKit <7 use our own `Result` type. - -PromiseKit 7 generalizes `DispatchQueue`s to a `Dispatcher` protocol. However, -`DispatchQueue`s are `Dispatcher`-conformant, so existing code should not need -to change. Please report any issues related to this transition. - -PromiseKit 7 adds support for cancelling promises and promise chains. - -# PromiseKit 6 - -[Release notes and migration guide][PMK6]. +For earlier Swifts, Xcodes or for Objective-C support, use [PromiseKit 6](https://github.com/mxcl/PromiseKit/blob/v6/README.md). # Quick Start @@ -55,19 +45,12 @@ In your `Package.swift`: ```swift package.dependencies.append( - .package(url: "https://github.com/mxcl/PromiseKit", from: Version(7, 0, 0, prereleaseIdentifiers: [“alpha”, “1”])) + .package(url: "https://github.com/mxcl/PromiseKit", from: "7.0.0") ) ``` -PromiseKit 7 supports Swift >= 5.3; Xcode >= 12; iOS, macOS, tvOS, watchOS, Linux -and Android; SwiftPM. - -PromiseKits 6 and 4 support Xcode 8.3, 9.x and 10.0; Swift 3.1, 3.2, 3.3, 3.4, -4.0, 4.1, 4.2 and 5.0; iOS, macOS, tvOS, watchOS, Linux and Android; CocoaPods, -Carthage and SwiftPM; ([CI Matrix](https://travis-ci.org/mxcl/PromiseKit)). - -For Carthage, SwiftPM, Accio, etc., or for instructions when using older Swifts -or Xcodes, see our [Installation Guide]. +For more detailed installation instructions or for other package managers see our +[Installation Guide]. # Professionally Supported PromiseKit is Now Available @@ -92,7 +75,6 @@ sponsor it either via Tidelift or GitHub Sponsors. * [Frequently Asked Questions](Documents/FAQ.md) * Manual * [Installation Guide](Documents/Installation.md) - * [Objective-C Guide](Documents/ObjectiveC.md) * [Troubleshooting](Documents/Troubleshooting.md) (e.g., solutions to common compile errors) * [Appendix](Documents/Appendix.md) * [API Reference](https://mxcl.dev/PromiseKit/reference/v7/Classes/Promise.html) @@ -100,73 +82,13 @@ sponsor it either via Tidelift or GitHub Sponsors. # Extensions Promises are only as useful as the asynchronous tasks they represent. Thus, we -have converted (almost) all of Apple’s APIs to promises. The default CocoaPod -provides Promises and the extensions for Foundation and UIKit. The other -extensions are available by specifying additional subspecs in your `Podfile`, -e.g.: - -```ruby -pod "PMKMapKit" # MKDirections().calculate().then { /*…*/ } -pod "PMKCoreLocation" # CLLocationManager.requestLocation().then { /*…*/ } -``` - -All our extensions are separate repositories at the [PromiseKit organization]. - -## Choose Your Networking Library - -Promise chains commonly start with a network operation. Thus, we offer -extensions for `URLSession`: - -```swift -// pod 'PMKFoundation' # https://github.com/PromiseKit/PMKFoundation - -firstly { - URLSession.shared.dataTask(.promise, with: try makeUrlRequest()).validate() - // ^^ we provide `.validate()` so that eg. 404s get converted to errors -}.map { - try JSONDecoder().decode(Foo.self, with: $0.data) -}.done { foo in - //… -}.catch { error in - //… -} - -func makeUrlRequest() throws -> URLRequest { - var rq = URLRequest(url: url) - rq.httpMethod = "POST" - rq.addValue("application/json", forHTTPHeaderField: "Content-Type") - rq.addValue("application/json", forHTTPHeaderField: "Accept") - rq.httpBody = try JSONEncoder().encode(obj) - return rq -} -``` - -And [Alamofire]: - -```swift -// pod 'PMKAlamofire' # https://github.com/PromiseKit/PMKAlamofire - -firstly { - Alamofire - .request("http://example.com", method: .post, parameters: params) - .responseDecodable(Foo.self) -}.done { foo in - //… -}.catch { error in - //… -} -``` - -Nowadays, considering that: +have converted (almost) all of Apple’s APIs to promises. You can use the +extensions by adding the appropriate library to your `Package.swift` and then +importing it (eg. `import PMKFoundation`). -* We almost always POST JSON -* We now have `JSONDecoder` -* PromiseKit now has `map` and other functional primitives -* PromiseKit (like Alamofire, but not raw-`URLSession`) also defaults to having - callbacks go to the main thread +See our [Installation Guide](Documents/Installation.md) for usage details. -We recommend vanilla `URLSession`. It uses fewer black boxes and sticks closer to the metal. Alamofire was essential until the three bullet points above -became true, but nowadays it isn’t really necessary. +Browse the `Sources` folder here for a list of available extensions. # Support diff --git a/Sources/PMKCloudKit/CKContainer+Promise.swift b/Sources/PMKCloudKit/CKContainer+Promise.swift new file mode 100644 index 000000000..45871f7f9 --- /dev/null +++ b/Sources/PMKCloudKit/CKContainer+Promise.swift @@ -0,0 +1,53 @@ +#if canImport(CloudKit) + +#if !PMKCocoaPods +import PromiseKit +#endif +import CloudKit + +/** + To import the `CKContainer` category: + + use_frameworks! + pod "PromiseKit/CloudKit" + + And then in your sources: + + @import PromiseKit; +*/ +public extension CKContainer { + /// Reports whether the current user’s iCloud account can be accessed. + func accountStatus() -> Promise { + return Promise { accountStatus(completionHandler: $0.resolve) } + } + + /// Requests the specified permission from the user asynchronously. + func requestApplicationPermission(_ applicationPermissions: CKContainer_Application_Permissions) -> Promise { + return Promise { requestApplicationPermission(applicationPermissions, completionHandler: $0.resolve) } + } + + /// Checks the status of the specified permission asynchronously. + func status(forApplicationPermission applicationPermissions: CKContainer_Application_Permissions) -> Promise { + return Promise { status(forApplicationPermission: applicationPermissions, completionHandler: $0.resolve) } + } + + /// Retrieves information about a single user based on the ID of the corresponding user record. + func discoverUserIdentity(withUserRecordID recordID: CKRecord.ID) -> Promise { + return Promise { discoverUserIdentity(withUserRecordID: recordID, completionHandler: $0.resolve) } + } + + /// Returns the user record ID associated with the current user. + func fetchUserRecordID() -> Promise { + return Promise { fetchUserRecordID(completionHandler: $0.resolve) } + } +} + +#if !os(tvOS) +public extension CKContainer { + func discoverAllIdentities() -> Promise<[CKUserIdentity]> { + return Promise { discoverAllIdentities(completionHandler: $0.resolve) } + } +} +#endif + +#endif diff --git a/Sources/PMKCloudKit/CKDatabase+Promise.swift b/Sources/PMKCloudKit/CKDatabase+Promise.swift new file mode 100644 index 000000000..df667efbe --- /dev/null +++ b/Sources/PMKCloudKit/CKDatabase+Promise.swift @@ -0,0 +1,91 @@ +#if canImport(CloudKit) + +import CloudKit.CKDatabase +#if !PMKCocoaPods +import PromiseKit +#endif + + +/** + To import the `CKDatabase` category: + + use_frameworks! + pod "PromiseKit/CloudKit" + + And then in your sources: + + @import PromiseKit; +*/ +public extension CKDatabase { + /// Fetches one record asynchronously from the current database. + func fetch(withRecordID recordID: CKRecord.ID) -> Promise { + return Promise { fetch(withRecordID: recordID, completionHandler: $0.resolve) } + } + + /// Fetches one record zone asynchronously from the current database. + func fetch(withRecordZoneID recordZoneID: CKRecordZone.ID) -> Promise { + return Promise { fetch(withRecordZoneID: recordZoneID, completionHandler: $0.resolve) } + } + /// Fetches all record zones asynchronously from the current database. + func fetchAllRecordZones() -> Promise<[CKRecordZone]> { + return Promise { fetchAllRecordZones(completionHandler: $0.resolve) } + } + + /// Saves one record zone asynchronously to the current database. + func save(_ record: CKRecord) -> Promise { + return Promise { save(record, completionHandler: $0.resolve) } + } + + /// Saves one record zone asynchronously to the current database. + func save(_ recordZone: CKRecordZone) -> Promise { + return Promise { save(recordZone, completionHandler: $0.resolve) } + } + + /// Delete one subscription object asynchronously from the current database. + func delete(withRecordID recordID: CKRecord.ID) -> Promise { + return Promise { delete(withRecordID: recordID, completionHandler: $0.resolve) } + } + + /// Delete one subscription object asynchronously from the current database. + func delete(withRecordZoneID zoneID: CKRecordZone.ID) -> Promise { + return Promise { delete(withRecordZoneID: zoneID, completionHandler: $0.resolve) } + } + + /// Searches the specified zone asynchronously for records that match the query parameters. + func perform(_ query: CKQuery, inZoneWith zoneID: CKRecordZone.ID? = nil) -> Promise<[CKRecord]> { + return Promise { perform(query, inZoneWith: zoneID, completionHandler: $0.resolve) } + } + + /// Fetches the record for the current user. + func fetchUserRecord(_ container: CKContainer = CKContainer.default()) -> Promise { + return container.fetchUserRecordID().then(on: nil) { uid in + return self.fetch(withRecordID: uid) + } + } +} + +#if !os(watchOS) +public extension CKDatabase { + /// Fetches one record zone asynchronously from the current database. + func fetch(withSubscriptionID subscriptionID: String) -> Promise { + return Promise { fetch(withSubscriptionID: subscriptionID, completionHandler: $0.resolve) } + } + + /// Fetches all subscription objects asynchronously from the current database. + func fetchAllSubscriptions() -> Promise<[CKSubscription]> { + return Promise { fetchAllSubscriptions(completionHandler: $0.resolve) } + } + + /// Saves one subscription object asynchronously to the current database. + func save(_ subscription: CKSubscription) -> Promise { + return Promise { save(subscription, completionHandler: $0.resolve) } + } + + /// Delete one subscription object asynchronously from the current database. + func delete(withSubscriptionID subscriptionID: String) -> Promise { + return Promise { delete(withSubscriptionID: subscriptionID, completionHandler: $0.resolve) } + } +} +#endif + +#endif diff --git a/Sources/PMKCoreLocation/.gitignore b/Sources/PMKCoreLocation/.gitignore new file mode 100644 index 000000000..bec9c1741 --- /dev/null +++ b/Sources/PMKCoreLocation/.gitignore @@ -0,0 +1,5 @@ +*.xcodeproj/**/xcuserdata/ +*.xcscmblueprint +/Carthage +/.build +.DS_Store \ No newline at end of file diff --git a/Sources/PMKCoreLocation/.travis.yml b/Sources/PMKCoreLocation/.travis.yml new file mode 100644 index 000000000..95c076137 --- /dev/null +++ b/Sources/PMKCoreLocation/.travis.yml @@ -0,0 +1,78 @@ +osx_image: xcode10.2 +language: swift +os: osx + +branches: + only: + - master +stages: + - lint + - carthage + - test +jobs: + include: + - &pod + stage: lint + osx_image: xcode8.3 + env: SWIFT=3.1 + before_install: + gem install cocoapods --prerelease --version 1.7.0.beta.3 + install: + carthage bootstrap --no-build PromiseKit + script: | + cd Carthage/Checkouts/PromiseKit + mv .github/PromiseKit.podspec . + rm -rf Extensions/CoreLocation/Sources + cp -R ../../../Sources Extensions/CoreLocation + pod lib lint --subspec=PromiseKit/CoreLocation --fail-fast --swift-version=$SWIFT + - <<: *pod + osx_image: xcode9.2 + env: SWIFT=3.2 + - <<: *pod + osx_image: xcode9.4 + env: SWIFT=3.3 + - <<: *pod + osx_image: xcode10.1 + env: SWIFT=3.4 + - <<: *pod + osx_image: xcode9.2 + env: SWIFT=4.0 + - <<: *pod + osx_image: xcode9.4 + env: SWIFT=4.1 + - <<: *pod + osx_image: xcode10.1 + env: SWIFT=4.2 + - <<: *pod + osx_image: xcode10.2 + env: SWIFT=5.0 + + - &carthage + stage: carthage + osx_image: xcode9.2 + script: | + carthage bootstrap --cache-builds + sed -i '' "s/SWIFT_TREAT_WARNINGS_AS_ERRORS = NO;/SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;/" *.xcodeproj/project.pbxproj + carthage build --no-skip-current + cache.directories: + - Carthage + - <<: *carthage + osx_image: xcode9.4 + - <<: *carthage + osx_image: xcode10.1 + - <<: *carthage + osx_image: xcode10.2 + + - &test + stage: test + xcode_scheme: PMKCoreLocation + xcode_project: PMKCoreLocation.xcodeproj + xcode_destination: platform=macOS + cache.directories: + - Carthage + before_install: + carthage bootstrap --cache-builds --no-use-binaries + - <<: *test + xcode_destination: 'platform=iOS Simulator,OS=12.2,name=iPhone SE' + - <<: *test + xcode_destination: 'platform=tvOS Simulator,OS=12.2,name=Apple TV' diff --git a/Sources/PMKCoreLocation/CLGeocoder+Promise.swift b/Sources/PMKCoreLocation/CLGeocoder+Promise.swift new file mode 100644 index 000000000..792a01ce4 --- /dev/null +++ b/Sources/PMKCoreLocation/CLGeocoder+Promise.swift @@ -0,0 +1,85 @@ +#if canImport(CoreLocation) + +import CoreLocation.CLGeocoder +#if !PMKCocoaPods +import PromiseKit +#endif +#if os(iOS) || os(watchOS) || os(macOS) +import class Contacts.CNPostalAddress +#endif + +/** + To import the `CLGeocoder` category: + + use_frameworks! + pod "PromiseKit/CoreLocation" + + And then in your sources: + + import PromiseKit +*/ +extension CLGeocoder { + /// Submits a reverse-geocoding request for the specified location. + public func reverseGeocode(location: CLLocation) -> Promise<[CLPlacemark]> { + return Promise { seal in + reverseGeocodeLocation(location, completionHandler: seal.resolve) + } + } + + /// Submits a forward-geocoding request using the specified address dictionary. + @available(iOS, deprecated: 11.0) + public func geocode(_ addressDictionary: [String: String]) -> Promise<[CLPlacemark]> { + return Promise { seal in + geocodeAddressDictionary(addressDictionary, completionHandler: seal.resolve) + } + } + + /// Submits a forward-geocoding request using the specified address string. + public func geocode(_ addressString: String) -> Promise<[CLPlacemark]> { + return Promise { seal in + geocodeAddressString(addressString, completionHandler: seal.resolve) + } + } + + /// Submits a forward-geocoding request using the specified address string within the specified region. + public func geocode(_ addressString: String, region: CLRegion?) -> Promise<[CLPlacemark]> { + return Promise { seal in + geocodeAddressString(addressString, in: region, completionHandler: seal.resolve) + } + } + +#if !os(tvOS) && swift(>=3.2) + /// Submits a forward-geocoding request using the specified postal address. + @available(iOS 11.0, OSX 10.13, watchOS 4.0, *) + public func geocodePostalAddress(_ postalAddress: CNPostalAddress) -> Promise<[CLPlacemark]> { + return Promise { seal in + geocodePostalAddress(postalAddress, completionHandler: seal.resolve) + } + } + + /// Submits a forward-geocoding requesting using the specified locale and postal address + @available(iOS 11.0, OSX 10.13, watchOS 4.0, *) + public func geocodePostalAddress(_ postalAddress: CNPostalAddress, preferredLocale locale: Locale?) -> Promise<[CLPlacemark]> { + return Promise { seal in + geocodePostalAddress(postalAddress, preferredLocale: locale, completionHandler: seal.resolve) + } + } + + /// Submits a reverse-geocoding request for the specified location and a preferred locale. + @available(iOS 11.0, OSX 10.13, watchOS 4.0, *) + public func reverseGeocode(location: CLLocation, preferredLocale locale: Locale?) -> Promise<[CLPlacemark]> { + return Promise { seal in + reverseGeocodeLocation(location, preferredLocale: locale, completionHandler: seal.resolve) + } + } +#endif +} + +// TODO still not possible in Swift 3.2 +//extension CLError: CancellableError { +// public var isCancelled: Bool { +// return self == .geocodeCanceled +// } +//} + +#endif diff --git a/Sources/PMKCoreLocation/CLLocationManager+Promise.swift b/Sources/PMKCoreLocation/CLLocationManager+Promise.swift new file mode 100644 index 000000000..7944ef37d --- /dev/null +++ b/Sources/PMKCoreLocation/CLLocationManager+Promise.swift @@ -0,0 +1,315 @@ +#if canImport(CoreLocation) + +import CoreLocation.CLLocationManager +#if !PMKCocoaPods +import PromiseKit +#endif + +/** + To import the `CLLocationManager` category: + + use_frameworks! + pod "PromiseKit/CoreLocation" + + And then in your sources: + + import PromiseKit +*/ +extension CLLocationManager { + + /// The type of location permission we are asking for + public enum RequestAuthorizationType { + /// Determine the authorization from the application’s plist + case automatic + /// Request always-authorization + case always + /// Request when-in-use-authorization + case whenInUse + } + + public enum PMKError: Error { + case notAuthorized + case unhandledEnumCase + } + + /** + Request the current location. + - Note: to obtain a single location use `Promise.lastValue` + - Parameters: + - authorizationType: requestAuthorizationType: We read your Info plist and try to + determine the authorization type we should request automatically. If you + want to force one or the other, change this parameter from its default + value. + - block: A block by which to perform any filtering of the locations that are + returned. In order to only retrieve accurate locations, only return true if the + locations horizontal accuracy < 50 + - Returns: A new promise that fulfills with the most recent CLLocation that satisfies + the provided block if it exists. If the block does not exist, simply return the + last location. + */ + public class func requestLocation(authorizationType: RequestAuthorizationType = .automatic, satisfying block: ((CLLocation) -> Bool)? = nil) -> Promise<[CLLocation]> { + + func std() -> Promise<[CLLocation]> { + return LocationManager(satisfying: block).promise + } + + func auth() -> Promise { + #if os(macOS) + return Promise() + #else + func auth(type: PMKCLAuthorizationType) -> Promise { + return AuthorizationCatcher(type: type).promise.done(on: nil) { + switch $0 { + case .restricted, .denied: + throw PMKError.notAuthorized + default: + break + } + } + } + + switch authorizationType { + case .automatic: + switch Bundle.main.permissionType { + case .always, .both: + return auth(type: .always) + case .whenInUse: + return auth(type: .whenInUse) + } + case .whenInUse: + return auth(type: .whenInUse) + case .always: + return auth(type: .always) + } + #endif + } + + switch CLLocationManager.authorizationStatus() { + case .authorizedAlways, .authorizedWhenInUse: + return std() + case .notDetermined: + return auth().then(std) + case .denied, .restricted: + return Promise(error: PMKError.notAuthorized) + @unknown default: + return Promise(error: PMKError.unhandledEnumCase) + } + } + + @available(*, deprecated, renamed: "requestLocation") + public class func promise(_ requestAuthorizationType: RequestAuthorizationType = .automatic, satisfying block: ((CLLocation) -> Bool)? = nil) -> Promise<[CLLocation]> { + return requestLocation(authorizationType: requestAuthorizationType, satisfying: block) + } +} + +private class LocationManager: CLLocationManager, CLLocationManagerDelegate { + let (promise, seal) = Promise<[CLLocation]>.pending() + let satisfyingBlock: ((CLLocation) -> Bool)? + + @objc fileprivate func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { + if let block = satisfyingBlock { + let satisfiedLocations = locations.filter(block) + if !satisfiedLocations.isEmpty { + seal.fulfill(satisfiedLocations) + } else { + #if os(tvOS) + requestLocation() + #endif + } + } else { + seal.fulfill(locations) + } + } + + init(satisfying block: ((CLLocation) -> Bool)? = nil) { + satisfyingBlock = block + super.init() + delegate = self + #if !os(tvOS) + startUpdatingLocation() + #else + requestLocation() + #endif + _ = self.promise.ensure { + self.stopUpdatingLocation() + } + } + + @objc func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { + let (domain, code) = { ($0.domain, $0.code) }(error as NSError) + if code == CLError.locationUnknown.rawValue && domain == kCLErrorDomain { + // Apple docs say you should just ignore this error + print("PromiseKit: warning: CLError.locationUnknown") + } else { + seal.reject(error) + } + } +} + + +#if !os(macOS) + +extension CLLocationManager { + /** + Request CoreLocation authorization from the user + - Note: By default we try to determine the authorization type you want by inspecting your Info.plist + - Note: This method will not perform upgrades from “when-in-use” to “always” unless you specify `.always` for the value of `type`. + */ + @available(iOS 8, tvOS 9, watchOS 2, *) + public class func requestAuthorization(type requestedAuthorizationType: RequestAuthorizationType = .automatic) -> Guarantee { + + let currentStatus = CLLocationManager.authorizationStatus() + + func std(type: PMKCLAuthorizationType) -> Guarantee { + if currentStatus == .notDetermined { + return AuthorizationCatcher(type: type).promise + } else { + return .value(currentStatus) + } + } + + switch requestedAuthorizationType { + case .always: + func iOS11Check() -> Guarantee { + switch currentStatus { + case .notDetermined, .authorizedWhenInUse: + return AuthorizationCatcher(type: .always).promise + default: + return .value(currentStatus) + } + } + #if PMKiOS11 + // ^^ define PMKiOS11 if you deploy against the iOS 11 SDK + // otherwise the warning you get below cannot be removed + return iOS11Check() + #else + if #available(iOS 11, *) { + return iOS11Check() + } else { + return std(type: .always) + } + #endif + + case .whenInUse: + return std(type: .whenInUse) + + case .automatic: + if currentStatus == .notDetermined { + switch Bundle.main.permissionType { + case .both, .whenInUse: + return AuthorizationCatcher(type: .whenInUse).promise + case .always: + return AuthorizationCatcher(type: .always).promise + } + } else { + return .value(currentStatus) + } + } + } +} + +@available(iOS 8, *) +private class AuthorizationCatcher: CLLocationManager, CLLocationManagerDelegate { + let (promise, fulfill) = Guarantee.pending() + var retainCycle: AuthorizationCatcher? + let initialAuthorizationState = CLLocationManager.authorizationStatus() + + init(type: PMKCLAuthorizationType) { + super.init() + + func ask(type: PMKCLAuthorizationType) { + delegate = self + retainCycle = self + + switch type { + case .always: + #if os(tvOS) + fallthrough + #else + requestAlwaysAuthorization() + #endif + case .whenInUse: + requestWhenInUseAuthorization() + } + + promise.done { _ in + self.retainCycle = nil + } + } + + func iOS11Check() { + switch (initialAuthorizationState, type) { + case (.notDetermined, .always), (.authorizedWhenInUse, .always), (.notDetermined, .whenInUse): + ask(type: type) + default: + fulfill(initialAuthorizationState) + } + } + + #if PMKiOS11 + // ^^ define PMKiOS11 if you deploy against the iOS 11 SDK + // otherwise the warning you get below cannot be removed + iOS11Check() + #else + if #available(iOS 11, *) { + iOS11Check() + } else { + if initialAuthorizationState == .notDetermined { + ask(type: type) + } else { + fulfill(initialAuthorizationState) + } + } + #endif + } + + @objc fileprivate func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) { + // `didChange` is a lie; it fires this immediately with the current status. + if status != initialAuthorizationState { + fulfill(status) + } + } +} + +#endif + +private extension Bundle { + enum PermissionType { + case both + case always + case whenInUse + } + + var permissionType: PermissionType { + func hasInfoPlistKey(_ key: String) -> Bool { + let value = object(forInfoDictionaryKey: key) as? String ?? "" + return !value.isEmpty + } + + if hasInfoPlistKey("NSLocationAlwaysAndWhenInUseUsageDescription") { + return .both + } + if hasInfoPlistKey("NSLocationAlwaysUsageDescription") { + return .always + } + if hasInfoPlistKey("NSLocationWhenInUseUsageDescription") { + return .whenInUse + } + + if #available(iOS 11, *) { + NSLog("PromiseKit: warning: `NSLocationAlwaysAndWhenInUseUsageDescription` key not set") + } else { + NSLog("PromiseKit: warning: `NSLocationWhenInUseUsageDescription` key not set") + } + + // won't work, but we warned the user above at least + return .whenInUse + } +} + +private enum PMKCLAuthorizationType { + case always + case whenInUse +} + +#endif diff --git a/Sources/PMKFoundation/NSNotificationCenter+Promise.swift b/Sources/PMKFoundation/NSNotificationCenter+Promise.swift new file mode 100644 index 000000000..3b7f84345 --- /dev/null +++ b/Sources/PMKFoundation/NSNotificationCenter+Promise.swift @@ -0,0 +1,33 @@ +import Foundation +#if !PMKCocoaPods +import PromiseKit +#endif + +/** + To import the `NSNotificationCenter` category: + + use_frameworks! + pod "PromiseKit/Foundation" + + Or `NSNotificationCenter` is one of the categories imported by the umbrella pod: + + use_frameworks! + pod "PromiseKit" + + And then in your sources: + + import PromiseKit +*/ +extension NotificationCenter { + /// Observe the named notification once + public func observe(once name: Notification.Name, object: Any? = nil) -> Guarantee { + let (promise, fulfill) = Guarantee.pending() + #if os(Linux) && ((swift(>=4.0) && !swift(>=4.0.1)) || (swift(>=3.0) && !swift(>=3.2.1))) + let id = addObserver(forName: name, object: object, queue: nil, usingBlock: fulfill) + #else + let id = addObserver(forName: name, object: object, queue: nil, using: fulfill) + #endif + promise.done { _ in self.removeObserver(id) } + return promise + } +} diff --git a/Sources/PMKFoundation/NSObject+Promise.swift b/Sources/PMKFoundation/NSObject+Promise.swift new file mode 100644 index 000000000..7ed54004f --- /dev/null +++ b/Sources/PMKFoundation/NSObject+Promise.swift @@ -0,0 +1,61 @@ +#if !os(Linux) + +import Foundation +#if !PMKCocoaPods +import PromiseKit +#endif + +/** + To import the `NSObject` category: + + use_frameworks! + pod "PromiseKit/Foundation" + + Or `NSObject` is one of the categories imported by the umbrella pod: + + use_frameworks! + pod "PromiseKit" + + And then in your sources: + + import PromiseKit +*/ +extension NSObject { + /** + - Returns: A promise that resolves when the provided keyPath changes. + - Warning: *Important* The promise must not outlive the object under observation. + - SeeAlso: Apple’s KVO documentation. + */ + public func observe(_: PMKNamespacer, keyPath: String) -> Guarantee { + return Guarantee { KVOProxy(observee: self, keyPath: keyPath, resolve: $0) } + } +} + +private class KVOProxy: NSObject { + var retainCycle: KVOProxy? + let fulfill: (Any?) -> Void + + @discardableResult + init(observee: NSObject, keyPath: String, resolve: @escaping (Any?) -> Void) { + fulfill = resolve + super.init() + observee.addObserver(self, forKeyPath: keyPath, options: NSKeyValueObservingOptions.new, context: pointer) + retainCycle = self + } + + fileprivate override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { + if let change = change, context == pointer { + defer { retainCycle = nil } + fulfill(change[NSKeyValueChangeKey.newKey]) + if let object = object as? NSObject, let keyPath = keyPath { + object.removeObserver(self, forKeyPath: keyPath) + } + } + } + + private lazy var pointer: UnsafeMutableRawPointer = { + return Unmanaged.passUnretained(self).toOpaque() + }() +} + +#endif diff --git a/Sources/PMKFoundation/NSURLSession+Promise.swift b/Sources/PMKFoundation/NSURLSession+Promise.swift new file mode 100644 index 000000000..b23a0d229 --- /dev/null +++ b/Sources/PMKFoundation/NSURLSession+Promise.swift @@ -0,0 +1,240 @@ +import Foundation +#if !PMKCocoaPods +import PromiseKit +#endif +#if canImport(FoundationNetworking) +import FoundationNetworking +#endif + +/** + To import the `NSURLSession` category: + + use_frameworks! + pod "PromiseKit/Foundation" + + Or `NSURLSession` is one of the categories imported by the umbrella pod: + + use_frameworks! + pod "PromiseKit" + + And then in your sources: + + import PromiseKit +*/ +extension URLSession { + /** + Example usage: + + firstly { + URLSession.shared.dataTask(.promise, with: rq) + }.compactMap { data, _ in + try JSONSerialization.jsonObject(with: data) as? [String: Any] + }.then { json in + //… + } + + We recommend the use of [OMGHTTPURLRQ] which allows you to construct correct REST requests: + + firstly { + let rq = OMGHTTPURLRQ.POST(url, json: parameters) + URLSession.shared.dataTask(.promise, with: rq) + }.then { data, urlResponse in + //… + } + + We provide a convenience initializer for `String` specifically for this promise: + + firstly { + URLSession.shared.dataTask(.promise, with: rq) + }.compactMap(String.init).then { string in + // decoded per the string encoding specified by the server + }.then { string in + print("response: string") + } + + Other common types can be easily decoded using compactMap also: + + firstly { + URLSession.shared.dataTask(.promise, with: rq) + }.compactMap { + UIImage(data: $0) + }.then { + self.imageView.image = $0 + } + + Though if you do decode the image this way, we recommend inflating it on a background thread + first as this will improve main thread performance when rendering the image: + + firstly { + URLSession.shared.dataTask(.promise, with: rq) + }.compactMap(on: QoS.userInitiated) { data, _ in + guard let img = UIImage(data: data) else { return nil } + _ = cgImage?.dataProvider?.data + return img + }.then { + self.imageView.image = $0 + } + + - Parameter convertible: A URL or URLRequest. + - Returns: A promise that represents the URL request. + - SeeAlso: [OMGHTTPURLRQ] + - Remark: We deliberately don’t provide a `URLRequestConvertible` for `String` because in our experience, you should be explicit with this error path to make good apps. + + [OMGHTTPURLRQ]: https://github.com/mxcl/OMGHTTPURLRQ + */ + public func dataTask(_: PMKNamespacer, with convertible: URLRequestConvertible) -> Promise<(data: Data, response: URLResponse)> { + return Promise { dataTask(with: convertible.pmkRequest, completionHandler: adapter($0)).resume() } + } + + public func uploadTask(_: PMKNamespacer, with convertible: URLRequestConvertible, from data: Data) -> Promise<(data: Data, response: URLResponse)> { + return Promise { uploadTask(with: convertible.pmkRequest, from: data, completionHandler: adapter($0)).resume() } + } + + public func uploadTask(_: PMKNamespacer, with convertible: URLRequestConvertible, fromFile file: URL) -> Promise<(data: Data, response: URLResponse)> { + return Promise { uploadTask(with: convertible.pmkRequest, fromFile: file, completionHandler: adapter($0)).resume() } + } + + /// - Remark: we force a `to` parameter because Apple deletes the downloaded file immediately after the underyling completion handler returns. + /// - Note: we do not create the destination directory for you, because we move the file with FileManager.moveItem which changes it behavior depending on the directory status of the URL you provide. So create your own directory first! + public func downloadTask(_: PMKNamespacer, with convertible: URLRequestConvertible, to saveLocation: URL) -> Promise<(saveLocation: URL, response: URLResponse)> { + return Promise { seal in + downloadTask(with: convertible.pmkRequest, completionHandler: { tmp, rsp, err in + if let error = err { + seal.reject(error) + } else if let rsp = rsp, let tmp = tmp { + do { + try FileManager.default.moveItem(at: tmp, to: saveLocation) + seal.fulfill((saveLocation, rsp)) + } catch { + seal.reject(error) + } + } else { + seal.reject(PMKError.invalidCallingConvention) + } + }).resume() + } + } +} + + +public protocol URLRequestConvertible { + var pmkRequest: URLRequest { get } +} +extension URLRequest: URLRequestConvertible { + public var pmkRequest: URLRequest { return self } +} +extension URL: URLRequestConvertible { + public var pmkRequest: URLRequest { return URLRequest(url: self) } +} + + +#if !os(Linux) +public extension String { + /** + - Remark: useful when converting a `URLSession` response into a `String` + + firstly { + URLSession.shared.dataTask(.promise, with: rq) + }.map(String.init).done { + print($0) + } + */ + init?(data: Data, urlResponse: URLResponse) { + guard let str = String(bytes: data, encoding: urlResponse.stringEncoding ?? .utf8) else { + return nil + } + self.init(str) + } +} + +private extension URLResponse { + var stringEncoding: String.Encoding? { + guard let encodingName = textEncodingName else { return nil } + let encoding = CFStringConvertIANACharSetNameToEncoding(encodingName as CFString) + guard encoding != kCFStringEncodingInvalidId else { return nil } + return String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding(encoding)) + } +} +#endif + +private func adapter(_ seal: Resolver<(data: T, response: U)>) -> (T?, U?, Error?) -> Void { + return { t, u, e in + if let t = t, let u = u { + seal.fulfill((t, u)) + } else if let e = e { + seal.reject(e) + } else { + seal.reject(PMKError.invalidCallingConvention) + } + } +} + + +public enum PMKHTTPError: Error, LocalizedError, CustomStringConvertible { + case badStatusCode(Int, Data, HTTPURLResponse) + + public var errorDescription: String? { + func url(_ rsp: URLResponse) -> String { + return rsp.url?.absoluteString ?? "nil" + } + switch self { + case .badStatusCode(401, _, let response): + return "Unauthorized (\(url(response))" + case .badStatusCode(let code, _, let response): + return "Invalid HTTP response (\(code)) for \(url(response))." + } + } + + public func decodeResponse(_ t: T.Type, decoder: JSONDecoder = JSONDecoder()) -> T? { + switch self { + case .badStatusCode(_, let data, _): + return try? decoder.decode(t, from: data) + } + } + + //TODO rename responseJSON + public var jsonDictionary: Any? { + switch self { + case .badStatusCode(_, let data, _): + return try? JSONSerialization.jsonObject(with: data) + } + } + + var responseBodyString: String? { + switch self { + case .badStatusCode(_, let data, _): + return String(data: data, encoding: .utf8) + } + } + + public var failureReason: String? { + return responseBodyString + } + + public var description: String { + switch self { + case .badStatusCode(let code, let data, let response): + var dict: [String: Any] = [ + "Status Code": code, + "Body": String(data: data, encoding: .utf8) ?? "\(data.count) bytes" + ] + dict["URL"] = response.url + dict["Headers"] = response.allHeaderFields + return " \(NSDictionary(dictionary: dict))" // as NSDictionary makes the output look like NSHTTPURLResponse looks + } + } +} + +public extension Promise where T == (data: Data, response: URLResponse) { + func validate() -> Promise { + return map { + guard let response = $0.response as? HTTPURLResponse else { return $0 } + switch response.statusCode { + case 200..<300: + return $0 + case let code: + throw PMKHTTPError.badStatusCode(code, $0.data, response) + } + } + } +} diff --git a/Sources/PMKFoundation/Process+Promise.swift b/Sources/PMKFoundation/Process+Promise.swift new file mode 100644 index 000000000..a88cb159c --- /dev/null +++ b/Sources/PMKFoundation/Process+Promise.swift @@ -0,0 +1,175 @@ +#if os(macOS) + +import Foundation +#if !PMKCocoaPods +import PromiseKit +#endif + +/** + To import the `Process` category: + + use_frameworks! + pod "PromiseKit/Foundation" + + Or `Process` is one of the categories imported by the umbrella pod: + + use_frameworks! + pod "PromiseKit" + + And then in your sources: + + import PromiseKit + */ +extension Process { + /** + Launches the receiver and resolves when it exits. + + let proc = Process() + proc.launchPath = "/bin/ls" + proc.arguments = ["/bin"] + proc.launch(.promise).compactMap { std in + String(data: std.out.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) + }.then { stdout in + print(str) + } + */ + public func launch(_: PMKNamespacer) -> Promise<(out: Pipe, err: Pipe)> { + let (stdout, stderr) = (Pipe(), Pipe()) + + do { + standardOutput = stdout + standardError = stderr + + if #available(OSX 10.13, *) { + try run() + } else if let path = launchPath, FileManager.default.isExecutableFile(atPath: path) { + launch() + } else { + throw PMKError.notExecutable(launchPath) + } + } catch { + return Promise(error: error) + } + + + var q: DispatchQueue { + if #available(macOS 10.10, iOS 8.0, tvOS 9.0, watchOS 2.0, *) { + return DispatchQueue.global(qos: .default) + } else { + return DispatchQueue.global(priority: .default) + } + } + + return Promise { seal in + q.async { + self.waitUntilExit() + + guard self.terminationReason == .exit, self.terminationStatus == 0 else { + let stdoutData = try? self.readDataFromPipe(stdout) + let stderrData = try? self.readDataFromPipe(stderr) + + let stdoutString = stdoutData.flatMap { (data: Data) -> String? in String(data: data, encoding: .utf8) } + let stderrString = stderrData.flatMap { (data: Data) -> String? in String(data: data, encoding: .utf8) } + + return seal.reject(PMKError.execution(process: self, standardOutput: stdoutString, standardError: stderrString)) + } + seal.fulfill((stdout, stderr)) + } + } + } + + private func readDataFromPipe(_ pipe: Pipe) throws -> Data { + let handle = pipe.fileHandleForReading + defer { handle.closeFile() } + + // Someday, NSFileHandle will probably be updated with throwing equivalents to its read and write methods, + // as NSTask has, to avoid raising exceptions and crashing the app. + // Unfortunately that day has not yet come, so use the underlying BSD calls for now. + + let fd = handle.fileDescriptor + + let bufsize = 1024 * 8 + let buf = UnsafeMutablePointer.allocate(capacity: bufsize) + + defer { buf.deallocate() } + + var data = Data() + + while true { + let bytesRead = read(fd, buf, bufsize) + + if bytesRead == 0 { + break + } + + if bytesRead < 0 { + throw POSIXError.Code(rawValue: errno).map { POSIXError($0) } ?? CocoaError(.fileReadUnknown) + } + + data.append(buf, count: bytesRead) + } + + return data + } + + /** + The error generated by PromiseKit’s `Process` extension + */ + public enum PMKError { + /// NOT AVAILABLE ON 10.13 and above because Apple provide this error handling themselves + case notExecutable(String?) + case execution(process: Process, standardOutput: String?, standardError: String?) + } +} + + +extension Process.PMKError: LocalizedError { + public var errorDescription: String? { + switch self { + case .notExecutable(let path?): + return "File not executable: \(path)" + case .notExecutable(nil): + return "No launch path specified" + case .execution(process: let task, standardOutput: _, standardError: _): + return "Failed executing: `\(task)` (\(task.terminationStatus))." + } + } +} + +public extension Promise where T == (out: Pipe, err: Pipe) { + func print() -> Promise { + return tap { result in + switch result { + case .success(let raw): + let stdout = String(data: raw.out.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) + let stderr = String(data: raw.err.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) + Swift.print("stdout: `\(stdout ?? "")`") + Swift.print("stderr: `\(stderr ?? "")`") + case .failure(let err): + Swift.print(err) + } + } + } +} + +extension Process { + /// Provided because Foundation’s is USELESS + open override var description: String { + let launchPath = self.launchPath ?? "$0" + var args = [launchPath] + arguments.flatMap{ args += $0 } + return args.map { arg in + let contains: Bool + contains = arg.contains(" ") + if contains { + return "\"\(arg)\"" + } else if arg == "" { + return "\"\"" + } else { + return arg + } + }.joined(separator: " ") + } +} + +#endif diff --git a/Sources/PMKFoundation/afterlife.swift b/Sources/PMKFoundation/afterlife.swift new file mode 100644 index 000000000..82f6675f5 --- /dev/null +++ b/Sources/PMKFoundation/afterlife.swift @@ -0,0 +1,30 @@ +#if !os(Linux) + +import Foundation +#if !PMKCocoaPods +import PromiseKit +#endif + +/** + - Returns: A promise that resolves when the provided object deallocates + - Important: The promise is not guaranteed to resolve immediately when the provided object is deallocated. So you cannot write code that depends on exact timing. + */ +public func after(life object: NSObject) -> Guarantee { + var reaper = objc_getAssociatedObject(object, &handle) as? GrimReaper + if reaper == nil { + reaper = GrimReaper() + objc_setAssociatedObject(object, &handle, reaper, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + return reaper!.promise +} + +private var handle: UInt8 = 0 + +private class GrimReaper: NSObject { + deinit { + fulfill(()) + } + let (promise, fulfill) = Guarantee.pending() +} + +#endif diff --git a/Sources/PMKHealthKit/.gitignore b/Sources/PMKHealthKit/.gitignore new file mode 100644 index 000000000..bec9c1741 --- /dev/null +++ b/Sources/PMKHealthKit/.gitignore @@ -0,0 +1,5 @@ +*.xcodeproj/**/xcuserdata/ +*.xcscmblueprint +/Carthage +/.build +.DS_Store \ No newline at end of file diff --git a/Sources/PMKHealthKit/.travis.yml b/Sources/PMKHealthKit/.travis.yml new file mode 100644 index 000000000..91caf3a78 --- /dev/null +++ b/Sources/PMKHealthKit/.travis.yml @@ -0,0 +1,76 @@ +osx_image: xcode10.2 +language: swift +os: osx + +branches: + only: + - master +stages: + - lint + - test +jobs: + include: + - &pod + stage: lint + osx_image: xcode8.3 + env: SWIFT=3.1 + cache: cocoapods + before_install: + gem install cocoapods --prerelease --version 1.7.0.beta.3 + install: + carthage bootstrap --no-build PromiseKit + script: | + cd Carthage/Checkouts/PromiseKit + mv .github/PromiseKit.podspec . + rm -rf Extensions/HealthKit/Sources + cp -R ../../../Sources Extensions/HealthKit + pod lib lint --subspec=PromiseKit/HealthKit --fail-fast --swift-version=$SWIFT + - <<: *pod + osx_image: xcode9.2 + env: SWIFT=3.2 + - <<: *pod + osx_image: xcode9.4 + env: SWIFT=3.3 + - <<: *pod + osx_image: xcode10.1 + env: SWIFT=3.4 + - <<: *pod + osx_image: xcode9.2 + env: SWIFT=4.0 + - <<: *pod + osx_image: xcode9.4 + env: SWIFT=4.1 + - <<: *pod + osx_image: xcode10.1 + env: SWIFT=4.2 + - <<: *pod + osx_image: xcode10.2 + env: SWIFT=4.3 + - <<: *pod + osx_image: xcode10.2 + env: SWIFT=5.0 + + - &carthage + stage: carthage + osx_image: xcode9.2 + script: | + carthage bootstrap --cache-builds --platform iOS,macOS + sed -i '' "s/SWIFT_TREAT_WARNINGS_AS_ERRORS = NO;/SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;/" *.xcodeproj/project.pbxproj + carthage build --no-skip-current + cache.directories: + - Carthage + - <<: *carthage + osx_image: xcode9.4 + - <<: *carthage + osx_image: xcode10.1 + - <<: *carthage + osx_image: xcode10.2 + + - stage: test + xcode_scheme: PMKHealthKit + xcode_project: PMKHealthKit.xcodeproj + xcode_destination: 'platform=iOS Simulator,OS=12.2,name=iPhone SE' + cache.directories: + - Carthage + before_install: + carthage bootstrap --cache-builds --no-use-binaries diff --git a/Sources/PMKHealthKit/HealthKit+Promise.swift b/Sources/PMKHealthKit/HealthKit+Promise.swift new file mode 100644 index 000000000..bc40f41c1 --- /dev/null +++ b/Sources/PMKHealthKit/HealthKit+Promise.swift @@ -0,0 +1,87 @@ +#if !os(tvOS) && canImport(HealthKit) + +#if !PMKCocoaPods +import PromiseKit +#endif +import HealthKit + +public extension HKHealthStore { + func requestAuthorization(toShare typesToShare: Set?, read typesToRead: Set?) -> Promise { + return Promise { seal in + requestAuthorization(toShare: typesToShare, read: typesToRead, completion: seal.resolve) + } + } + +#if os(iOS) + func enableBackgroundDelivery(for type: HKObjectType, frequency: HKUpdateFrequency) -> Promise { + return Promise { seal in + enableBackgroundDelivery(for: type, frequency: frequency, withCompletion: seal.resolve) + } + } +#endif +} + +public extension HKStatisticsQuery { + static func promise(quantityType: HKQuantityType, quantitySamplePredicate: NSPredicate? = nil, options: HKStatisticsOptions = [], healthStore: HKHealthStore = .init()) -> Promise { + return Promise { seal in + let query = HKStatisticsQuery(quantityType: quantityType, quantitySamplePredicate: quantitySamplePredicate, options: options) { + seal.resolve($1, $2) + } + healthStore.execute(query) + } + } +} + +public extension HKAnchoredObjectQuery { + static func promise(type: HKSampleType, predicate: NSPredicate? = nil, anchor: HKQueryAnchor? = nil, limit: Int = HKObjectQueryNoLimit, healthStore: HKHealthStore = .init()) -> Promise<([HKSample], [HKDeletedObject], HKQueryAnchor)> { + return Promise { seal in + let query = HKAnchoredObjectQuery(type: type, predicate: predicate, anchor: anchor, limit: limit) { + if let a = $1, let b = $2, let c = $3 { + seal.fulfill((a, b, c)) + } else if let e = $4 { + seal.reject(e) + } else { + seal.reject(PMKError.invalidCallingConvention) + } + } + healthStore.execute(query) + } + } + +} + +public extension HKStatisticsCollectionQuery { + func promise(healthStore: HKHealthStore = .init()) -> Promise { + return Promise { seal in + initialResultsHandler = { + seal.resolve($1, $2) + } + healthStore.execute(self) + } + } +} + +public extension HKSampleQuery { + static func promise(sampleType: HKSampleType, predicate: NSPredicate? = nil, limit: Int = HKObjectQueryNoLimit, sortDescriptors: [NSSortDescriptor]? = nil, healthStore: HKHealthStore = .init()) -> Promise<[HKSample]> { + return Promise { seal in + let query = HKSampleQuery(sampleType: sampleType, predicate: predicate, limit: limit, sortDescriptors: sortDescriptors) { + seal.resolve($1, $2) + } + healthStore.execute(query) + } + } +} + +@available(iOS 9.3, iOSApplicationExtension 9.3, watchOSApplicationExtension 2.2, *) +public extension HKActivitySummaryQuery { + static func promise(predicate: NSPredicate, healthStore: HKHealthStore = .init()) -> Promise<[HKActivitySummary]> { + return Promise { seal in + let query = HKActivitySummaryQuery(predicate: predicate) { + seal.resolve($1, $2) + } + healthStore.execute(query) + } + } +} + +#endif diff --git a/Sources/PMKHomeKit/.gitignore b/Sources/PMKHomeKit/.gitignore new file mode 100644 index 000000000..bec9c1741 --- /dev/null +++ b/Sources/PMKHomeKit/.gitignore @@ -0,0 +1,5 @@ +*.xcodeproj/**/xcuserdata/ +*.xcscmblueprint +/Carthage +/.build +.DS_Store \ No newline at end of file diff --git a/Sources/PMKHomeKit/.travis.yml b/Sources/PMKHomeKit/.travis.yml new file mode 100644 index 000000000..5a0f7450a --- /dev/null +++ b/Sources/PMKHomeKit/.travis.yml @@ -0,0 +1,67 @@ +os: osx +language: swift +osx_image: xcode10.2 + +branches: + only: + - master +stages: + - lint + - carthage + - test +jobs: + include: + - &pod + stage: lint + osx_image: xcode9.4 + env: SWIFT=3.3 + before_install: + gem install cocoapods --prerelease --version 1.7.0.beta.3 + install: + carthage bootstrap --no-build PromiseKit + script: | + cd Carthage/Checkouts/PromiseKit + mv .github/PromiseKit.podspec . + rm -rf Extensions/HomeKit/Sources + cp -R ../../../Sources Extensions/HomeKit + pod lib lint --subspec=PromiseKit/HomeKit --fail-fast --swift-version=$SWIFT + - <<: *pod + osx_image: xcode10.1 + env: SWIFT=3.4 + - <<: *pod + osx_image: xcode9.4 + env: SWIFT=4.1 + - <<: *pod + osx_image: xcode10.1 + env: SWIFT=4.2 + - <<: *pod + osx_image: xcode10.2 + env: SWIFT=5.0 + + - &carthage + stage: carthage + osx_image: xcode9.4 + script: | + carthage bootstrap --cache-builds + sed -i '' "s/SWIFT_TREAT_WARNINGS_AS_ERRORS = NO;/SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;/" *.xcodeproj/project.pbxproj + carthage build --no-skip-current + cache.directories: + - Carthage + - <<: *carthage + osx_image: xcode10.1 + - <<: *carthage + osx_image: xcode10.2 + + - &test + stage: test + xcode_scheme: PMKHomeKit + xcode_project: PMKHomeKit.xcodeproj + xcode_destination: 'platform=iOS Simulator,OS=12.2,name=iPhone SE' + cache.directories: + - Carthage + before_install: + carthage bootstrap --cache-builds --no-use-binaries + after_success: + bash <(curl -s https://codecov.io/bash); + - <<: *test + xcode_destination: 'platform=tvOS Simulator,OS=12.2,name=Apple TV' diff --git a/Sources/PMKHomeKit/HMAcessoryBrowser+Promise.swift b/Sources/PMKHomeKit/HMAcessoryBrowser+Promise.swift new file mode 100644 index 000000000..7199d5be6 --- /dev/null +++ b/Sources/PMKHomeKit/HMAcessoryBrowser+Promise.swift @@ -0,0 +1,76 @@ +#if canImport(HomeKit) && !os(tvOS) && !os(watchOS) +#if !PMKCocoaPods +import PromiseKit +#endif +import HomeKit + +public enum HMPromiseAccessoryBrowserError: Error { + case noAccessoryFound +} + +public class HMPromiseAccessoryBrowser { + private var proxy: BrowserProxy? + + public func start(scanInterval: ScanInterval) -> Promise<[HMAccessory]> { + proxy = BrowserProxy(scanInterval: scanInterval) + return proxy!.promise + } + + public func stop() { + proxy?.cancel() + } +} + +private class BrowserProxy: PromiseProxy<[HMAccessory]>, HMAccessoryBrowserDelegate { + let browser = HMAccessoryBrowser() + let scanInterval: ScanInterval + + init(scanInterval: ScanInterval) { + self.scanInterval = scanInterval + super.init() + + browser.delegate = self; + browser.startSearchingForNewAccessories() + + //if we have a timeout, set it up + var timeout: TimeInterval? = nil + switch scanInterval { + case .returnAll(let interval): timeout = interval + case .returnFirst(let interval): timeout = interval + } + + if let timeout = timeout { + after(seconds: timeout) + .done { [weak self] () -> Void in + guard let _self = self else { return } + _self.reject(HMPromiseAccessoryBrowserError.noAccessoryFound) + } + } + } + + override func fulfill(_ value: [HMAccessory]) { + browser.stopSearchingForNewAccessories() + super.fulfill(value) + } + + override func reject(_ error: Error ) { + browser.stopSearchingForNewAccessories() + super.reject(error) + } + + override func cancel() { + browser.stopSearchingForNewAccessories() + super.cancel() + } + + /** + HMAccessoryBrowser delegate + */ + func accessoryBrowser(_ browser: HMAccessoryBrowser, didFindNewAccessory accessory: HMAccessory) { + if case .returnFirst = scanInterval { + fulfill([accessory]) + } + } +} + +#endif diff --git a/Sources/PMKHomeKit/HMActionSet+Promise.swift b/Sources/PMKHomeKit/HMActionSet+Promise.swift new file mode 100644 index 000000000..ca14fed9a --- /dev/null +++ b/Sources/PMKHomeKit/HMActionSet+Promise.swift @@ -0,0 +1,24 @@ +#if canImport(HomeKit) && !os(tvOS) && !os(watchOS) +#if !PMKCocoaPods +import PromiseKit +#endif +import HomeKit + +extension HMActionSet { + + @available(iOS 8.0, *) + public func addAction(_ action: HMAction) -> Promise { + return Promise { seal in + self.addAction(action, completionHandler: seal.resolve) + } + } + + @available(iOS 8.0, *) + public func updateName(_ name: String) -> Promise { + return Promise { seal in + self.updateName(name, completionHandler: seal.resolve) + } + } +} + +#endif diff --git a/Sources/PMKHomeKit/HMCharacteristic+Promise.swift b/Sources/PMKHomeKit/HMCharacteristic+Promise.swift new file mode 100644 index 000000000..3649f8296 --- /dev/null +++ b/Sources/PMKHomeKit/HMCharacteristic+Promise.swift @@ -0,0 +1,48 @@ +#if canImport(HomeKit) +#if !PMKCocoaPods +import PromiseKit +#endif +import HomeKit + +public enum AccessoryError: Error { + case incorrectType + case serviceMissing + case characteristicMissing +} + +@available(iOS 8.0, tvOS 10.0, *) +extension HMCharacteristic { + /** + A simple typesafe promise wrapper around readValue + */ + public func read() -> Promise { + return Promise { seal in + self.readValue { error in + if let error = error { seal.reject(error) } + else if let value = self.value as? T { seal.fulfill(value) } + else { seal.reject(AccessoryError.incorrectType) } + } + } + } + /// Because type inference is great... until you can't compile (thanks Swift) + public func readFloat() -> Promise { return read() } + public func readDouble() -> Promise { return read() } + public func readInt() -> Promise { return read() } + public func readString() -> Promise { return read() } + + /** + A simple promise wrapper around writeValue + */ + public func write(_ value: Any?) -> Promise { + return Promise { seal in + self.writeValue(value, completionHandler: seal.resolve) + } + } + /// Explicit is good + public func writeFloat(_ value: Float) -> Promise { return write(value) } + public func writeDouble(_ value: Double) -> Promise { return write(value) } + public func writeInt(_ value: Int) -> Promise { return write(value) } + public func writeString(_ value: String) -> Promise { return write(value) } +} + +#endif diff --git a/Sources/PMKHomeKit/HMEventTrigger+Promise.swift b/Sources/PMKHomeKit/HMEventTrigger+Promise.swift new file mode 100644 index 000000000..fe6c79e9c --- /dev/null +++ b/Sources/PMKHomeKit/HMEventTrigger+Promise.swift @@ -0,0 +1,19 @@ +#if canImport(HomeKit) && !os(tvOS) && !os(watchOS) +#if !PMKCocoaPods +import PromiseKit +#endif +import HomeKit + +@available(iOS 9.0, *) +extension HMEventTrigger { + + @available(iOS 11.0, *) + public func updateExecuteOnce(_ executeOnce: Bool) -> Promise { + return Promise { seal in + self.updateExecuteOnce(executeOnce, completionHandler: seal.resolve) + } + } + +} + +#endif diff --git a/Sources/PMKHomeKit/HMHome+Promise.swift b/Sources/PMKHomeKit/HMHome+Promise.swift new file mode 100644 index 000000000..330f56c93 --- /dev/null +++ b/Sources/PMKHomeKit/HMHome+Promise.swift @@ -0,0 +1,114 @@ +#if canImport(HomeKit) && !os(tvOS) && !os(watchOS) +#if !PMKCocoaPods +import PromiseKit +#endif +import HomeKit + +extension HMHome { + + @available(iOS 8.0, *) + public func updateName(_ name: String) -> Promise { + return Promise { seal in + self.updateName(name, completionHandler: seal.resolve) + } + } + + // MARK: Accessories + + /// Add and setup a new HMAccessory. Displays it's own UI + @available(iOS 11.3, *) + public func addAndSetupAccessories(with payload: HMAccessorySetupPayload) -> Promise<[HMAccessory]> { + return Promise { seal in + self.addAndSetupAccessories(with: payload, completionHandler: seal.resolve) + } + } + + /// Add and setup a new HMAccessory. Displays it's own UI + @available(iOS 10.0, *) + public func addAndSetupAccessories() -> Promise<[HMAccessory]> { + // We need to compare what we have before the action to after to know what is new + let beforeAccessories = self.accessories + let home = self + + return Promise { seal in + self.addAndSetupAccessories { error in + if let error = error { seal.reject(error) } + else { + let newAccessories = home.accessories.filter { beforeAccessories.contains($0) == false } + seal.fulfill(newAccessories) + } + } + } + } + + @available(iOS 8.0, *) + public func addAccessory(_ accessory: HMAccessory) -> Promise { + return Promise { seal in + self.addAccessory(accessory, completionHandler: seal.resolve) + } + } + + @available(iOS 8.0, *) + public func assignAccessory(_ accessory: HMAccessory, to room: HMRoom) -> Promise { + return Promise { seal in + self.assignAccessory(accessory, to: room, completionHandler: seal.resolve) + } + } + + @available(iOS 8.0, *) + public func removeAccessory(_ accessory: HMAccessory) -> Promise { + return Promise { seal in + self.removeAccessory(accessory, completionHandler: seal.resolve) + } + } + + // MARK: Rooms + + @available(iOS 8.0, *) + public func addRoom(withName name: String) -> Promise { + return Promise { seal in + self.addRoom(withName: name, completionHandler: seal.resolve) + } + } + + @available(iOS 8.0, *) + public func removeRoom(_ room: HMRoom) -> Promise { + return Promise { seal in + self.removeRoom(room, completionHandler: seal.resolve) + } + } + + // MARK: Action Sets + + @available(iOS 8.0, *) + public func addActionSet(withName name: String) -> Promise { + return Promise { seal in + self.addActionSet(withName: name, completionHandler: seal.resolve) + } + } + + @available(iOS 8.0, *) + public func removeActionSet(_ actionSet: HMActionSet) -> Promise { + return Promise { seal in + self.removeActionSet(actionSet, completionHandler: seal.resolve) + } + } + + // MARK: Triggers + + @available(iOS 8.0, *) + public func addTrigger(_ trigger: HMTrigger) -> Promise { + return Promise { seal in + self.addTrigger(trigger, completionHandler: seal.resolve) + } + } + + @available(iOS 8.0, *) + public func removeTrigger(_ trigger: HMTrigger) -> Promise { + return Promise { seal in + self.removeTrigger(trigger, completionHandler: seal.resolve) + } + } +} + +#endif diff --git a/Sources/PMKHomeKit/HMHomeManager+Promise.swift b/Sources/PMKHomeKit/HMHomeManager+Promise.swift new file mode 100644 index 000000000..673a5795e --- /dev/null +++ b/Sources/PMKHomeKit/HMHomeManager+Promise.swift @@ -0,0 +1,64 @@ +#if canImport(HomeKit) +#if !PMKCocoaPods +import PromiseKit +#endif +import HomeKit + +@available(iOS 8.0, tvOS 10.0, *) +public enum HomeKitError: Error { + case permissionDeined +} + +@available(iOS 8.0, tvOS 10.0, *) +extension HMHomeManager { + public func homes() -> Promise<[HMHome]> { + return HMHomeManagerProxy().promise + } + + #if !os(tvOS) && !os(watchOS) + + @available(iOS 8.0, *) + public func addHome(withName name: String) -> Promise { + return Promise { seal in + self.addHome(withName: name, completionHandler: seal.resolve) + } + } + + @available(iOS 8.0, *) + public func removeHome(_ home: HMHome) -> Promise { + return Promise { seal in + self.removeHome(home, completionHandler: seal.resolve) + } + } + + @available(iOS 8.0, *) + public func updatePrimaryHome(_ home: HMHome) -> Promise { + return Promise { seal in + self.updatePrimaryHome(home, completionHandler: seal.resolve) + } + } + + #endif +} + +@available(iOS 8.0, tvOS 10.0, *) +internal class HMHomeManagerProxy: PromiseProxy<[HMHome]>, HMHomeManagerDelegate { + + fileprivate let manager: HMHomeManager + + override init() { + self.manager = HMHomeManager() + super.init() + self.manager.delegate = self + + DispatchQueue.main.asyncAfter(deadline: .now() + 20.0) { [weak self] in + self?.reject(HomeKitError.permissionDeined) + } + } + + func homeManagerDidUpdateHomes(_ manager: HMHomeManager) { + fulfill(manager.homes) + } +} + +#endif diff --git a/Sources/PMKHomeKit/HMTrigger+Promise.swift b/Sources/PMKHomeKit/HMTrigger+Promise.swift new file mode 100644 index 000000000..c56cef97b --- /dev/null +++ b/Sources/PMKHomeKit/HMTrigger+Promise.swift @@ -0,0 +1,39 @@ +#if canImport(HomeKit) && !os(tvOS) && !os(watchOS) +#if !PMKCocoaPods +import PromiseKit +#endif +import HomeKit + +extension HMTrigger { + + @available(iOS 8.0, *) + public func updateName(_ name: String) -> Promise { + return Promise { seal in + self.updateName(name, completionHandler: seal.resolve) + } + } + + @available(iOS 8.0, *) + public func enable(_ enabled: Bool) -> Promise { + return Promise { seal in + self.enable(enabled, completionHandler: seal.resolve) + } + } + + @available(iOS 8.0, *) + public func addActionSet(_ actionSet: HMActionSet) -> Promise { + return Promise { seal in + self.addActionSet(actionSet, completionHandler: seal.resolve) + } + } + + @available(iOS 8.0, *) + public func removeActionSet(_ actionSet: HMActionSet) -> Promise { + return Promise { seal in + self.removeActionSet(actionSet, completionHandler: seal.resolve) + } + } + +} + +#endif diff --git a/Sources/PMKHomeKit/Utils.swift b/Sources/PMKHomeKit/Utils.swift new file mode 100644 index 000000000..f7d572251 --- /dev/null +++ b/Sources/PMKHomeKit/Utils.swift @@ -0,0 +1,46 @@ +import Foundation +#if !PMKCocoaPods +import PromiseKit +#endif + +/** + Commonly used functionality when promisifying a delegate pattern +*/ +internal class PromiseProxy: NSObject { + internal let (promise, seal) = Promise.pending(); + + private var retainCycle: PromiseProxy? + + override init() { + super.init() + // Create a retain cycle + self.retainCycle = self + // And ensure we break it when the promise is resolved + _ = promise.ensure { self.retainCycle = nil } + } + + /// These functions ensure we only resolve the promise once + internal func fulfill(_ value: T) { + guard self.promise.isResolved == false else { return } + seal.fulfill(value) + } + internal func reject(_ error: Error) { + guard self.promise.isResolved == false else { return } + seal.reject(error) + } + + /// Cancel helper + internal func cancel() { + self.reject(PMKError.cancelled) + } +} + +/** + Different ways to scan. +*/ +public enum ScanInterval { + // Return after our first item with an optional time limit + case returnFirst(timeout: TimeInterval?) + // Scan for this duration before returning all + case returnAll(interval: TimeInterval) +} diff --git a/Sources/PMKMapKit/.gitignore b/Sources/PMKMapKit/.gitignore new file mode 100644 index 000000000..f5554ff63 --- /dev/null +++ b/Sources/PMKMapKit/.gitignore @@ -0,0 +1,5 @@ +xcuserdata +*.xcscmblueprint +/Carthage +/.build +.DS_Store \ No newline at end of file diff --git a/Sources/PMKMapKit/.travis.yml b/Sources/PMKMapKit/.travis.yml new file mode 100644 index 000000000..b904ead5f --- /dev/null +++ b/Sources/PMKMapKit/.travis.yml @@ -0,0 +1,80 @@ +os: osx +language: swift +osx_image: xcode10.2 + +branches: + only: + - master +stages: + - lint + - carthage + - test +jobs: + include: + - &pod + stage: lint + osx_image: xcode8.3 + env: SWIFT=3.1 + before_install: + gem install cocoapods --prerelease --version 1.7.0.beta.3 + install: + carthage bootstrap --no-build PromiseKit + script: | + cd Carthage/Checkouts/PromiseKit + mv .github/PromiseKit.podspec . + rm -rf Extensions/MapKit/Sources + cp -R ../../../Sources Extensions/MapKit + pod lib lint --subspec=PromiseKit/MapKit --fail-fast --swift-version=$SWIFT + - <<: *pod + osx_image: xcode9.2 + env: SWIFT=3.2 + - <<: *pod + osx_image: xcode9.4 + env: SWIFT=3.3 + - <<: *pod + osx_image: xcode10.1 + env: SWIFT=3.4 + - <<: *pod + osx_image: xcode9.2 + env: SWIFT=4.0 + - <<: *pod + osx_image: xcode9.4 + env: SWIFT=4.1 + - <<: *pod + osx_image: xcode10.1 + env: SWIFT=4.2 + - <<: *pod + osx_image: xcode10.2 + env: SWIFT=5.0 + + - &carthage + stage: carthage + osx_image: xcode9.2 + script: | + carthage bootstrap --cache-builds + sed -i '' "s/SWIFT_TREAT_WARNINGS_AS_ERRORS = NO;/SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;/" *.xcodeproj/project.pbxproj + carthage build --no-skip-current + cache: + directories: + - Carthage + - <<: *carthage + osx_image: xcode9.4 + - <<: *carthage + osx_image: xcode10.1 + - <<: *carthage + osx_image: xcode10.2 + + - &test + stage: test + xcode_scheme: PMKMapKit + xcode_project: PMKMapKit.xcodeproj + xcode_destination: 'platform=macOS' + cache: + directories: + - Carthage + before_install: + carthage bootstrap --cache-builds --no-use-binaries + - <<: *test + xcode_destination: 'platform=iOS Simulator,OS=12.2,name=iPhone SE' + - <<: *test + xcode_destination: 'platform=tvOS Simulator,OS=12.2,name=Apple TV' diff --git a/Sources/PMKMapKit/MKDirections+Promise.swift b/Sources/PMKMapKit/MKDirections+Promise.swift new file mode 100644 index 000000000..e70b0e514 --- /dev/null +++ b/Sources/PMKMapKit/MKDirections+Promise.swift @@ -0,0 +1,23 @@ +#if canImport(MapKit) && !os(watchOS) + +#if !PMKCocoaPods +import PromiseKit +#endif +import MapKit + +/** + import PMKMapKit +*/ +public extension MKDirections { + /// Begins calculating the requested route information asynchronously. + func calculate() -> Promise { + return Promise { calculate(completionHandler: $0.resolve) } + } + + /// Begins calculating the requested travel-time information asynchronously. + func calculateETA() -> Promise { + return Promise { calculateETA(completionHandler: $0.resolve) } + } +} + +#endif diff --git a/Sources/PMKMapKit/MKMapSnapshotter+Promise.swift b/Sources/PMKMapKit/MKMapSnapshotter+Promise.swift new file mode 100644 index 000000000..c2ca31df9 --- /dev/null +++ b/Sources/PMKMapKit/MKMapSnapshotter+Promise.swift @@ -0,0 +1,18 @@ +#if canImport(MapKit) && !os(watchOS) + +#if !PMKCocoaPods +import PromiseKit +#endif +import MapKit + +/** + import PMKMapKit +*/ +extension MKMapSnapshotter { + /// Starts generating the snapshot using the options set in this object. + public func start() -> Promise { + return Promise { start(completionHandler: $0.resolve) } + } +} + +#endif diff --git a/Sources/PMKPhotos/.gitignore b/Sources/PMKPhotos/.gitignore new file mode 100644 index 000000000..bec9c1741 --- /dev/null +++ b/Sources/PMKPhotos/.gitignore @@ -0,0 +1,5 @@ +*.xcodeproj/**/xcuserdata/ +*.xcscmblueprint +/Carthage +/.build +.DS_Store \ No newline at end of file diff --git a/Sources/PMKPhotos/.travis.yml b/Sources/PMKPhotos/.travis.yml new file mode 100644 index 000000000..b33720d9e --- /dev/null +++ b/Sources/PMKPhotos/.travis.yml @@ -0,0 +1,73 @@ +os: osx +language: swift +osx_image: xcode10.2 + +branches: + only: + - master +stages: + - lint + - carthage + - test +jobs: + include: + - &pod + stage: lint + osx_image: xcode9.2 + env: SWIFT=3.2 + before_install: + gem install cocoapods --prerelease --version 1.7.0.beta.3 + install: + carthage bootstrap --no-build PromiseKit + script: | + cd Carthage/Checkouts/PromiseKit + mv .github/PromiseKit.podspec . + rm -rf Extensions/Photos/Sources + cp -R ../../../Sources Extensions/Photos + pod lib lint --subspec=PromiseKit/Photos --fail-fast --swift-version=$SWIFT + - <<: *pod + osx_image: xcode9.4 + env: SWIFT=3.3 + - <<: *pod + osx_image: xcode10.1 + env: SWIFT=3.4 + - <<: *pod + osx_image: xcode9.2 + env: SWIFT=4.0 + - <<: *pod + osx_image: xcode9.4 + env: SWIFT=4.1 + - <<: *pod + osx_image: xcode10.1 + env: SWIFT=4.2 + - <<: *pod + osx_image: xcode10.2 + env: SWIFT=5.0 + + - &carthage + stage: carthage + osx_image: xcode9.2 + script: | + carthage bootstrap --cache-builds + sed -i '' "s/SWIFT_TREAT_WARNINGS_AS_ERRORS = NO;/SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;/" *.xcodeproj/project.pbxproj + carthage build --no-skip-current + cache.directories: + - Carthage + - <<: *carthage + osx_image: xcode9.4 + - <<: *carthage + osx_image: xcode10.1 + - <<: *carthage + osx_image: xcode10.2 + + - &test + stage: test + xcode_scheme: PMKPhotos + xcode_project: PMKPhotos.xcodeproj + xcode_destination: 'platform=iOS Simulator,OS=12.2,name=iPhone SE' + cache.directories: + - Carthage + before_install: + carthage bootstrap --cache-builds --no-use-binaries + - <<: *test + xcode_destination: 'platform=tvOS Simulator,OS=12.2,name=Apple TV' diff --git a/Sources/PMKPhotos/PHPhotoLibrary+Promise.swift b/Sources/PMKPhotos/PHPhotoLibrary+Promise.swift new file mode 100644 index 000000000..1fe697655 --- /dev/null +++ b/Sources/PMKPhotos/PHPhotoLibrary+Promise.swift @@ -0,0 +1,22 @@ +#if canImport(Photos) + +import Photos.PHPhotoLibrary +#if !PMKCocoaPods +import PromiseKit +#endif + +/** + import PMKPhotos +*/ +@available(macOS 10.13, *) +extension PHPhotoLibrary { + /** + - Returns: A promise that fulfills with the user’s authorization + - Note: This promise cannot reject. + */ + public class func requestAuthorization() -> Guarantee { + return Guarantee(resolver: PHPhotoLibrary.requestAuthorization) + } +} + +#endif diff --git a/Sources/PMKStoreKit/.gitignore b/Sources/PMKStoreKit/.gitignore new file mode 100644 index 000000000..bec9c1741 --- /dev/null +++ b/Sources/PMKStoreKit/.gitignore @@ -0,0 +1,5 @@ +*.xcodeproj/**/xcuserdata/ +*.xcscmblueprint +/Carthage +/.build +.DS_Store \ No newline at end of file diff --git a/Sources/PMKStoreKit/.travis.yml b/Sources/PMKStoreKit/.travis.yml new file mode 100644 index 000000000..76450b19f --- /dev/null +++ b/Sources/PMKStoreKit/.travis.yml @@ -0,0 +1,91 @@ +os: osx +language: swift +osx_image: xcode10.2 + +branches: + only: + - master +stages: + - lint + - carthage + - swiftPM + - test +jobs: + include: + - &pod + stage: lint + osx_image: xcode8.3 + env: SWIFT=3.1 + before_install: + gem install cocoapods --prerelease --version 1.7.0.beta.3 + install: + carthage bootstrap --no-build PromiseKit + script: | + cd Carthage/Checkouts/PromiseKit + mv .github/PromiseKit.podspec . + rm -rf Extensions/StoreKit/Sources + cp -R ../../../Sources Extensions/StoreKit + pod lib lint --subspec=PromiseKit/StoreKit --fail-fast --swift-version=$SWIFT + - <<: *pod + osx_image: xcode9.2 + env: SWIFT=3.2 + - <<: *pod + osx_image: xcode9.4 + env: SWIFT=3.3 + - <<: *pod + osx_image: xcode10.1 + env: SWIFT=3.4 + - <<: *pod + osx_image: xcode9.2 + env: SWIFT=4.0 + - <<: *pod + osx_image: xcode9.4 + env: SWIFT=4.1 + - <<: *pod + osx_image: xcode10.1 + env: SWIFT=4.2 + - <<: *pod + osx_image: xcode10.2 + env: SWIFT=5.0 + + - &carthage + stage: carthage + osx_image: xcode9.2 + script: | + carthage bootstrap --cache-builds + sed -i '' "s/SWIFT_TREAT_WARNINGS_AS_ERRORS = NO;/SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;/" *.xcodeproj/project.pbxproj + carthage build --no-skip-current + cache: + directories: + - Carthage + - <<: *carthage + osx_image: xcode9.4 + - <<: *carthage + osx_image: xcode10.1 + - <<: *carthage + osx_image: xcode10.2 + + - &swiftpm + stage: swiftpm + osx_image: xcode9.2 + script: swift build -Xswiftc -target -Xswiftc x86_64-apple-macosx10.12 + - <<: *swiftpm + osx_image: xcode9.4 + - <<: *swiftpm + osx_image: xcode10.1 + - <<: *swiftpm + osx_image: xcode10.2 + + - &test + stage: test + xcode_scheme: PMKStoreKit + xcode_project: PMKStoreKit.xcodeproj + xcode_destination: 'platform=macOS' + cache.directories: + - Carthage + before_install: + carthage bootstrap --cache-builds --no-use-binaries + - <<: *test + xcode_destination: 'OS=12.2,name=iPhone SE' + - <<: *test + xcode_destination: 'OS=12.2,name=Apple TV' diff --git a/Sources/PMKStoreKit/SKPayment+Promise.swift b/Sources/PMKStoreKit/SKPayment+Promise.swift new file mode 100644 index 000000000..2fde33d13 --- /dev/null +++ b/Sources/PMKStoreKit/SKPayment+Promise.swift @@ -0,0 +1,51 @@ +#if canImport(StoreKit) + +#if !PMKCocoaPods +import PromiseKit +#endif +import StoreKit + +@available(watchOS 6.2, *) +extension SKPayment { + public func promise() -> Promise { + return PaymentObserver(payment: self).promise + } +} + +@available(watchOS 6.2, *) +private class PaymentObserver: NSObject, SKPaymentTransactionObserver { + let (promise, seal) = Promise.pending() + let payment: SKPayment + var retainCycle: PaymentObserver? + + init(payment: SKPayment) { + self.payment = payment + super.init() + SKPaymentQueue.default().add(self) + SKPaymentQueue.default().add(payment) + retainCycle = self + } + + func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { + guard let transaction = transactions.first(where: { $0.payment.productIdentifier == payment.productIdentifier }) else { + return + } + switch transaction.transactionState { + case .purchased, .restored: + queue.finishTransaction(transaction) + seal.fulfill(transaction) + queue.remove(self) + retainCycle = nil + case .failed: + let error = transaction.error ?? PMKError.cancelled + queue.finishTransaction(transaction) + seal.reject(error) + queue.remove(self) + retainCycle = nil + default: + break + } + } +} + +#endif diff --git a/Sources/PMKStoreKit/SKPaymentQueue+Promise.swift b/Sources/PMKStoreKit/SKPaymentQueue+Promise.swift new file mode 100644 index 000000000..41b6df90a --- /dev/null +++ b/Sources/PMKStoreKit/SKPaymentQueue+Promise.swift @@ -0,0 +1,61 @@ +#if canImport(StoreKit) + +#if !PMKCocoaPods +import PromiseKit +#endif +import StoreKit + +@available(watchOS 6.2, *) +public extension SKPaymentQueue { + func restoreCompletedTransactions(_: PMKNamespacer) -> Promise<[SKPaymentTransaction]> { + return PaymentObserver(self).promise + } + + func restoreCompletedTransactions(_: PMKNamespacer, withApplicationUsername username: String?) -> Promise<[SKPaymentTransaction]> { + return PaymentObserver(self, withApplicationUsername: true, userName: username).promise + } +} + +@available(watchOS 6.2, *) +private class PaymentObserver: NSObject, SKPaymentTransactionObserver { + let (promise, seal) = Promise<[SKPaymentTransaction]>.pending() + var retainCycle: PaymentObserver? + var finishedTransactions = [SKPaymentTransaction]() + + //TODO:PMK7: this is weird, just have a `String?` parameter + init(_ paymentQueue: SKPaymentQueue, withApplicationUsername: Bool = false, userName: String? = nil) { + super.init() + paymentQueue.add(self) + withApplicationUsername ? + paymentQueue.restoreCompletedTransactions() : + paymentQueue.restoreCompletedTransactions(withApplicationUsername: userName) + retainCycle = self + } + + func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { + for transaction in transactions where transaction.transactionState == .restored { + finishedTransactions.append(transaction) + queue.finishTransaction(transaction) + } + } + + func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) { + resolve(queue, nil) + } + + func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: Error) { + resolve(queue, error) + } + + func resolve(_ queue: SKPaymentQueue, _ error: Error?) { + if let error = error { + seal.reject(error) + } else { + seal.fulfill(finishedTransactions) + } + queue.remove(self) + retainCycle = nil + } +} + +#endif diff --git a/Sources/PMKStoreKit/SKProductsRequest+Promise.swift b/Sources/PMKStoreKit/SKProductsRequest+Promise.swift new file mode 100644 index 000000000..0404203e1 --- /dev/null +++ b/Sources/PMKStoreKit/SKProductsRequest+Promise.swift @@ -0,0 +1,57 @@ +#if canImport(StoreKit) + +#if !PMKCocoaPods +import PromiseKit +#endif +import StoreKit + +/** + To import the `SKRequest` category: + + use_frameworks! + pod "PromiseKit/StoreKit" + + And then in your sources: + + import PromiseKit +*/ +@available(watchOS 6.2, *) +extension SKProductsRequest { + /** + Sends the request to the Apple App Store. + + - Returns: A promise that fulfills if the request succeeds. + */ + public func start(_: PMKNamespacer) -> Promise { + let proxy = SKDelegate() + delegate = proxy + proxy.retainCycle = proxy + start() + return proxy.promise + } +} + +@available(watchOS 6.2, *) +fileprivate class SKDelegate: NSObject, SKProductsRequestDelegate { + let (promise, seal) = Promise.pending() + var retainCycle: SKDelegate? + + @objc fileprivate func request(_ request: SKRequest, didFailWithError error: Error) { + seal.reject(error) + retainCycle = nil + } + + @objc fileprivate func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) { + seal.fulfill(response) + retainCycle = nil + } +} + +// perhaps one day Apple will actually make their errors into Errors… +//extension SKError: CancellableError { +// public var isCancelled: Bool { +// return true +// } +//} + +#endif diff --git a/Sources/PMKStoreKit/SKReceiptRefreshRequest+Promise.swift b/Sources/PMKStoreKit/SKReceiptRefreshRequest+Promise.swift new file mode 100644 index 000000000..5cb1ccc81 --- /dev/null +++ b/Sources/PMKStoreKit/SKReceiptRefreshRequest+Promise.swift @@ -0,0 +1,41 @@ +#if canImport(StoreKit) + +#if !PMKCocoaPods +import PromiseKit +#endif +import StoreKit + +@available(watchOS 6.2, *) +extension SKReceiptRefreshRequest { + public func promise() -> Promise { + return ReceiptRefreshObserver(request: self).promise + } +} + +@available(watchOS 6.2, *) +private class ReceiptRefreshObserver: NSObject, SKRequestDelegate { + let (promise, seal) = Promise.pending() + let request: SKReceiptRefreshRequest + var retainCycle: ReceiptRefreshObserver? + + init(request: SKReceiptRefreshRequest) { + self.request = request + super.init() + request.delegate = self + request.start() + retainCycle = self + } + + + func requestDidFinish(_: SKRequest) { + seal.fulfill(request) + retainCycle = nil + } + + func request(_: SKRequest, didFailWithError error: Error) { + seal.reject(error) + retainCycle = nil + } +} + +#endif diff --git a/Sources/Box.swift b/Sources/PromiseKit/Box.swift similarity index 100% rename from Sources/Box.swift rename to Sources/PromiseKit/Box.swift diff --git a/Sources/Cancellation/CancelContext.swift b/Sources/PromiseKit/Cancellation/CancelContext.swift similarity index 100% rename from Sources/Cancellation/CancelContext.swift rename to Sources/PromiseKit/Cancellation/CancelContext.swift diff --git a/Sources/Cancellation/Cancellable.swift b/Sources/PromiseKit/Cancellation/Cancellable.swift similarity index 100% rename from Sources/Cancellation/Cancellable.swift rename to Sources/PromiseKit/Cancellation/Cancellable.swift diff --git a/Sources/Cancellation/CancellableCatchable.swift b/Sources/PromiseKit/Cancellation/CancellableCatchable.swift similarity index 100% rename from Sources/Cancellation/CancellableCatchable.swift rename to Sources/PromiseKit/Cancellation/CancellableCatchable.swift diff --git a/Sources/Cancellation/CancellablePromise.swift b/Sources/PromiseKit/Cancellation/CancellablePromise.swift similarity index 100% rename from Sources/Cancellation/CancellablePromise.swift rename to Sources/PromiseKit/Cancellation/CancellablePromise.swift diff --git a/Sources/Cancellation/CancellableThenable.swift b/Sources/PromiseKit/Cancellation/CancellableThenable.swift similarity index 100% rename from Sources/Cancellation/CancellableThenable.swift rename to Sources/PromiseKit/Cancellation/CancellableThenable.swift diff --git a/Sources/Catchable.swift b/Sources/PromiseKit/Catchable.swift similarity index 100% rename from Sources/Catchable.swift rename to Sources/PromiseKit/Catchable.swift diff --git a/Sources/Configuration.swift b/Sources/PromiseKit/Configuration.swift similarity index 100% rename from Sources/Configuration.swift rename to Sources/PromiseKit/Configuration.swift diff --git a/Sources/CustomStringConvertible.swift b/Sources/PromiseKit/CustomStringConvertible.swift similarity index 100% rename from Sources/CustomStringConvertible.swift rename to Sources/PromiseKit/CustomStringConvertible.swift diff --git a/Sources/Dispatcher.swift b/Sources/PromiseKit/Dispatcher.swift similarity index 100% rename from Sources/Dispatcher.swift rename to Sources/PromiseKit/Dispatcher.swift diff --git a/Sources/Dispatchers/ConcurrencyLimitedDispatcher.swift b/Sources/PromiseKit/Dispatchers/ConcurrencyLimitedDispatcher.swift similarity index 100% rename from Sources/Dispatchers/ConcurrencyLimitedDispatcher.swift rename to Sources/PromiseKit/Dispatchers/ConcurrencyLimitedDispatcher.swift diff --git a/Sources/Dispatchers/CoreDataDispatcher.swift b/Sources/PromiseKit/Dispatchers/CoreDataDispatcher.swift similarity index 100% rename from Sources/Dispatchers/CoreDataDispatcher.swift rename to Sources/PromiseKit/Dispatchers/CoreDataDispatcher.swift diff --git a/Sources/Dispatchers/Queue.swift b/Sources/PromiseKit/Dispatchers/Queue.swift similarity index 100% rename from Sources/Dispatchers/Queue.swift rename to Sources/PromiseKit/Dispatchers/Queue.swift diff --git a/Sources/Dispatchers/RateLimitedDispatcher.swift b/Sources/PromiseKit/Dispatchers/RateLimitedDispatcher.swift similarity index 100% rename from Sources/Dispatchers/RateLimitedDispatcher.swift rename to Sources/PromiseKit/Dispatchers/RateLimitedDispatcher.swift diff --git a/Sources/Dispatchers/RateLimitedDispatcherBase.swift b/Sources/PromiseKit/Dispatchers/RateLimitedDispatcherBase.swift similarity index 100% rename from Sources/Dispatchers/RateLimitedDispatcherBase.swift rename to Sources/PromiseKit/Dispatchers/RateLimitedDispatcherBase.swift diff --git a/Sources/Dispatchers/StrictRateLimitedDispatcher.swift b/Sources/PromiseKit/Dispatchers/StrictRateLimitedDispatcher.swift similarity index 100% rename from Sources/Dispatchers/StrictRateLimitedDispatcher.swift rename to Sources/PromiseKit/Dispatchers/StrictRateLimitedDispatcher.swift diff --git a/Sources/Error.swift b/Sources/PromiseKit/Error.swift similarity index 100% rename from Sources/Error.swift rename to Sources/PromiseKit/Error.swift diff --git a/Sources/Guarantee.swift b/Sources/PromiseKit/Guarantee.swift similarity index 82% rename from Sources/Guarantee.swift rename to Sources/PromiseKit/Guarantee.swift index 7098347c0..668eb0252 100644 --- a/Sources/Guarantee.swift +++ b/Sources/PromiseKit/Guarantee.swift @@ -143,18 +143,6 @@ public extension Guarantee { return rg } - #if swift(>=4) && !swift(>=5.2) - func map(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ keyPath: KeyPath) -> Guarantee { - let rg = Guarantee(.pending) - pipe { value in - on.async(flags: flags) { - rg.box.seal(value[keyPath: keyPath]) - } - } - return rg - } - #endif - @discardableResult func then(on: Dispatcher = conf.D.map, _ body: @escaping(T) -> Guarantee) -> Guarantee { let rg = Guarantee(.pending) @@ -207,21 +195,6 @@ public extension Guarantee where T: Sequence { return map(on: on, flags: flags) { $0.map(transform) } } - #if swift(>=4) && !swift(>=5.2) - /** - `Guarantee<[T]>` => `KeyPath` => `Guarantee<[U]>` - - Guarantee.value([Person(name: "Max"), Person(name: "Roman"), Person(name: "John")]) - .mapValues(\.name) - .done { - // $0 => ["Max", "Roman", "John"] - } - */ - func mapValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ keyPath: KeyPath) -> Guarantee<[U]> { - return map(on: on, flags: flags) { $0.map { $0[keyPath: keyPath] } } - } - #endif - /** `Guarantee<[T]>` => `T` -> `[U]` => `Guarantee<[U]>` @@ -256,27 +229,6 @@ public extension Guarantee where T: Sequence { } } - #if swift(>=4) && !swift(>=5.2) - /** - `Guarantee<[T]>` => `KeyPath` => `Guarantee<[U]>` - - Guarantee.value([Person(name: "Max"), Person(name: "Roman", age: 26), Person(name: "John", age: 23)]) - .compactMapValues(\.age) - .done { - // $0 => [26, 23] - } - */ - func compactMapValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ keyPath: KeyPath) -> Guarantee<[U]> { - return map(on: on, flags: flags) { foo -> [U] in - #if !swift(>=4.1) - return foo.flatMap { $0[keyPath: keyPath] } - #else - return foo.compactMap { $0[keyPath: keyPath] } - #endif - } - } - #endif - /** `Guarantee<[T]>` => `T` -> `Guarantee` => `Guarantee<[U]>` @@ -330,23 +282,6 @@ public extension Guarantee where T: Sequence { } } - #if swift(>=4) && !swift(>=5.2) - /** - `Guarantee<[T]>` => `KeyPath` => `Guarantee<[T]>` - - Guarantee.value([Person(name: "Max"), Person(name: "Roman", age: 26, isStudent: false), Person(name: "John", age: 23, isStudent: true)]) - .filterValues(\.isStudent) - .done { - // $0 => [Person(name: "John", age: 23, isStudent: true)] - } - */ - func filterValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ keyPath: KeyPath) -> Guarantee<[T.Iterator.Element]> { - return map(on: on, flags: flags) { - $0.filter { $0[keyPath: keyPath] } - } - } - #endif - /** `Guarantee<[T]>` => (`T`, `T`) -> Bool => `Guarantee<[T]>` @@ -387,8 +322,6 @@ public extension Guarantee where T == Void { return .value(Void()) } -#if swift(>=5.1) - // ^^ ambiguous in Swift 5.0, testing again in next version convenience init(resolver body: (@escaping() -> Void) -> Void) { self.init(resolver: { seal in body { @@ -396,7 +329,6 @@ public extension Guarantee where T == Void { } }) } -#endif } public extension DispatchQueue { diff --git a/Sources/LogEvent.swift b/Sources/PromiseKit/LogEvent.swift similarity index 100% rename from Sources/LogEvent.swift rename to Sources/PromiseKit/LogEvent.swift diff --git a/Sources/Promise.swift b/Sources/PromiseKit/Promise.swift similarity index 100% rename from Sources/Promise.swift rename to Sources/PromiseKit/Promise.swift diff --git a/Sources/Resolver.swift b/Sources/PromiseKit/Resolver.swift similarity index 100% rename from Sources/Resolver.swift rename to Sources/PromiseKit/Resolver.swift diff --git a/Sources/Thenable.swift b/Sources/PromiseKit/Thenable.swift similarity index 75% rename from Sources/Thenable.swift rename to Sources/PromiseKit/Thenable.swift index 7ae1b17fb..4d4d09f1f 100644 --- a/Sources/Thenable.swift +++ b/Sources/PromiseKit/Thenable.swift @@ -87,30 +87,6 @@ public extension Thenable { return rp } - #if swift(>=4) && !swift(>=5.2) - /** - Similar to func `map(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T) throws -> U) -> Promise`, but accepts a key path instead of a closure. - - - Parameter on: The queue to which the provided key path for value dispatches. - - Parameter keyPath: The key path to the value that is using when this Promise is fulfilled. - - Returns: A new promise that is fulfilled with the value for the provided key path. - */ - func map(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ keyPath: KeyPath) -> Promise { - let rp = Promise(.pending) - pipe { - switch $0 { - case .fulfilled(let value): - on.async(flags: flags) { - rp.box.seal(.fulfilled(value[keyPath: keyPath])) - } - case .rejected(let error): - rp.box.seal(.rejected(error)) - } - } - return rp - } - #endif - /** The provided closure is executed when this promise is fulfilled. @@ -149,38 +125,6 @@ public extension Thenable { return rp } - #if swift(>=4) && !swift(>=5.2) - /** - Similar to func `compactMap(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T) throws -> U?) -> Promise`, but accepts a key path instead of a closure. - - - Parameter on: The queue to which the provided key path for value dispatches. - - Parameter keyPath: The key path to the value that is using when this Promise is fulfilled. If the value for `keyPath` is `nil` the resulting promise is rejected with `PMKError.compactMap`. - - Returns: A new promise that is fulfilled with the value for the provided key path. - */ - func compactMap(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ keyPath: KeyPath) -> Promise { - let rp = Promise(.pending) - pipe { - switch $0 { - case .fulfilled(let value): - on.async(flags: flags) { - do { - if let rv = value[keyPath: keyPath] { - rp.box.seal(.fulfilled(rv)) - } else { - throw PMKError.compactMap(value, U.self) - } - } catch { - rp.box.seal(.rejected(error)) - } - } - case .rejected(let error): - rp.box.seal(.rejected(error)) - } - } - return rp - } - #endif - /** The provided closure is executed when this promise is fulfilled. @@ -358,21 +302,6 @@ public extension Thenable where T: Sequence { return map(on: on) { try $0.map(transform) } } - #if swift(>=4) && !swift(>=5.2) - /** - `Promise<[T]>` => `KeyPath` => `Promise<[U]>` - - firstly { - .value([Person(name: "Max"), Person(name: "Roman"), Person(name: "John")]) - }.mapValues(\.name).done { - // $0 => ["Max", "Roman", "John"] - } - */ - func mapValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ keyPath: KeyPath) -> Promise<[U]> { - return map(on: on, flags: flags){ $0.map { $0[keyPath: keyPath] } } - } - #endif - /** `Promise<[T]>` => `T` -> `[U]` => `Promise<[U]>` @@ -408,27 +337,6 @@ public extension Thenable where T: Sequence { } } - #if swift(>=4) && !swift(>=5.2) - /** - `Promise<[T]>` => `KeyPath` => `Promise<[U]>` - - firstly { - .value([Person(name: "Max"), Person(name: "Roman", age: 26), Person(name: "John", age: 23)]) - }.compactMapValues(\.age).done { - // $0 => [26, 23] - } - */ - func compactMapValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ keyPath: KeyPath) -> Promise<[U]> { - return map(on: on, flags: flags) { foo -> [U] in - #if !swift(>=4.1) - return foo.flatMap { $0[keyPath: keyPath] } - #else - return foo.compactMap { $0[keyPath: keyPath] } - #endif - } - } - #endif - /** `Promise<[T]>` => `T` -> `Promise` => `Promise<[U]>` @@ -481,23 +389,6 @@ public extension Thenable where T: Sequence { $0.filter(isIncluded) } } - - #if swift(>=4) && !swift(>=5.2) - /** - `Promise<[T]>` => `KeyPath` => `Promise<[T]>` - - firstly { - .value([Person(name: "Max"), Person(name: "Roman", age: 26, isStudent: false), Person(name: "John", age: 23, isStudent: true)]) - }.filterValues(\.isStudent).done { - // $0 => [Person(name: "John", age: 23, isStudent: true)] - } - */ - func filterValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ keyPath: KeyPath) -> Promise<[T.Iterator.Element]> { - return map(on: on, flags: flags) { - $0.filter { $0[keyPath: keyPath] } - } - } - #endif } public extension Thenable where T: Collection { diff --git a/Sources/Wrappers/CatchWrappers.swift b/Sources/PromiseKit/Wrappers/CatchWrappers.swift similarity index 100% rename from Sources/Wrappers/CatchWrappers.swift rename to Sources/PromiseKit/Wrappers/CatchWrappers.swift diff --git a/Sources/Wrappers/EnsureWrappers.swift b/Sources/PromiseKit/Wrappers/EnsureWrappers.swift similarity index 100% rename from Sources/Wrappers/EnsureWrappers.swift rename to Sources/PromiseKit/Wrappers/EnsureWrappers.swift diff --git a/Sources/Wrappers/FinallyWrappers.swift b/Sources/PromiseKit/Wrappers/FinallyWrappers.swift similarity index 100% rename from Sources/Wrappers/FinallyWrappers.swift rename to Sources/PromiseKit/Wrappers/FinallyWrappers.swift diff --git a/Sources/Wrappers/GuaranteeWrappers.swift b/Sources/PromiseKit/Wrappers/GuaranteeWrappers.swift similarity index 100% rename from Sources/Wrappers/GuaranteeWrappers.swift rename to Sources/PromiseKit/Wrappers/GuaranteeWrappers.swift diff --git a/Sources/Wrappers/RecoverWrappers.swift b/Sources/PromiseKit/Wrappers/RecoverWrappers.swift similarity index 100% rename from Sources/Wrappers/RecoverWrappers.swift rename to Sources/PromiseKit/Wrappers/RecoverWrappers.swift diff --git a/Sources/Wrappers/SequenceWrappers.swift b/Sources/PromiseKit/Wrappers/SequenceWrappers.swift similarity index 100% rename from Sources/Wrappers/SequenceWrappers.swift rename to Sources/PromiseKit/Wrappers/SequenceWrappers.swift diff --git a/Sources/Wrappers/ThenableWrappers.swift b/Sources/PromiseKit/Wrappers/ThenableWrappers.swift similarity index 100% rename from Sources/Wrappers/ThenableWrappers.swift rename to Sources/PromiseKit/Wrappers/ThenableWrappers.swift diff --git a/Sources/Wrappers/WrapperProtocols.swift b/Sources/PromiseKit/Wrappers/WrapperProtocols.swift similarity index 100% rename from Sources/Wrappers/WrapperProtocols.swift rename to Sources/PromiseKit/Wrappers/WrapperProtocols.swift diff --git a/Sources/after.swift b/Sources/PromiseKit/after.swift similarity index 100% rename from Sources/after.swift rename to Sources/PromiseKit/after.swift diff --git a/Sources/firstly.swift b/Sources/PromiseKit/firstly.swift similarity index 100% rename from Sources/firstly.swift rename to Sources/PromiseKit/firstly.swift diff --git a/Sources/hang.swift b/Sources/PromiseKit/hang.swift similarity index 100% rename from Sources/hang.swift rename to Sources/PromiseKit/hang.swift diff --git a/Sources/race.swift b/Sources/PromiseKit/race.swift similarity index 100% rename from Sources/race.swift rename to Sources/PromiseKit/race.swift diff --git a/Sources/when.swift b/Sources/PromiseKit/when.swift similarity index 99% rename from Sources/when.swift rename to Sources/PromiseKit/when.swift index 9dbec6336..eb548babf 100644 --- a/Sources/when.swift +++ b/Sources/PromiseKit/when.swift @@ -274,7 +274,6 @@ No more than three downloads will occur simultaneously. Downloads will continue - Returns: A new promise that resolves once all the provided promises resolve. The array is ordered the same as the input, ie. the result order is *not* resolution order. - SeeAlso: `when(resolved:)` */ -#if swift(>=5.3) public func when(resolved promiseIterator: It, concurrently: Int) -> Guarantee<[Result]> where It.Element: Thenable { guard concurrently > 0 else { @@ -346,7 +345,6 @@ public func when(resolved promiseIterator: It, concurrentl return root.guarantee } -#endif /// Waits on all provided Guarantees. public func when(_ guarantees: Guarantee...) -> Guarantee { diff --git a/Tests/A+/JavaScript/AllTests.swift b/Tests/A+/JavaScript/AllTests.swift index c104a4b2e..c336c558e 100644 --- a/Tests/A+/JavaScript/AllTests.swift +++ b/Tests/A+/JavaScript/AllTests.swift @@ -5,18 +5,13 @@ // Created by Lois Di Qual on 2/28/18. // -import XCTest -import PromiseKit - -#if !os(Linux) -// can disable better when we don’t need --generate-linuxmain +#if !os(Linux) && !os(watchOS) import JavaScriptCore -#endif +import PromiseKit +import XCTest class AllTests: XCTestCase { - func testAll() { - #if !os(Linux) let scriptPath = URL(fileURLWithPath: #file).deletingLastPathComponent().appendingPathComponent("build/build.js") guard FileManager.default.fileExists(atPath: scriptPath.path) else { return print("Skipping A+.js: see README for instructions on how to build") @@ -80,6 +75,7 @@ class AllTests: XCTestCase { // Call `runTests` runTests.call(withArguments: [adapter, onFailValue, onDoneValue, testName]) self.wait(for: [expectation], timeout: 60) - #endif } } + +#endif diff --git a/Tests/A+/JavaScript/JSAdapter.swift b/Tests/A+/JavaScript/JSAdapter.swift index e26f59c3e..464ab0700 100644 --- a/Tests/A+/JavaScript/JSAdapter.swift +++ b/Tests/A+/JavaScript/JSAdapter.swift @@ -5,9 +5,7 @@ // Created by Lois Di Qual on 3/2/18. // -#if !os(Linux) -// can disable better when we don’t need --generate-linuxmain -import Foundation +#if !os(Linux) && !os(watchOS) import JavaScriptCore import PromiseKit diff --git a/Tests/A+/JavaScript/JSPromise.swift b/Tests/A+/JavaScript/JSPromise.swift index f491f2a03..f5f2a2f0a 100644 --- a/Tests/A+/JavaScript/JSPromise.swift +++ b/Tests/A+/JavaScript/JSPromise.swift @@ -5,12 +5,11 @@ // Created by Lois Di Qual on 3/1/18. // -#if !os(Linux) +#if !os(Linux) && !os(watchOS) // can disable better when we don’t need --generate-linuxmain -import Foundation -import XCTest -import PromiseKit import JavaScriptCore +import PromiseKit +import XCTest @objc protocol JSPromiseProtocol: JSExport { func then(_: JSValue, _: JSValue) -> JSPromise diff --git a/Tests/A+/JavaScript/JSUtils.swift b/Tests/A+/JavaScript/JSUtils.swift index 200cc4612..434cd33b7 100644 --- a/Tests/A+/JavaScript/JSUtils.swift +++ b/Tests/A+/JavaScript/JSUtils.swift @@ -5,9 +5,7 @@ // Created by Lois Di Qual on 3/2/18. // -#if !os(Linux) -// can disable better when we don’t need --generate-linuxmain -import Foundation +#if !os(Linux) && !os(watchOS) import JavaScriptCore enum JSUtils { diff --git a/Tests/A+/JavaScript/MockNodeEnvironment.swift b/Tests/A+/JavaScript/MockNodeEnvironment.swift index 89c4cda34..d034d6737 100644 --- a/Tests/A+/JavaScript/MockNodeEnvironment.swift +++ b/Tests/A+/JavaScript/MockNodeEnvironment.swift @@ -5,8 +5,7 @@ // Created by Lois Di Qual on 3/1/18. // -#if !os(Linux) -// can disable better when we don’t need --generate-linuxmain +#if !os(Linux) && !os(watchOS) import JavaScriptCore import Foundation diff --git a/Tests/Core/CancellableErrorTests.swift b/Tests/Core/CancellableErrorTests.swift index 7b66caa8f..5fdae4d28 100644 --- a/Tests/Core/CancellableErrorTests.swift +++ b/Tests/Core/CancellableErrorTests.swift @@ -99,6 +99,7 @@ class CancellationTests: XCTestCase { waitForExpectations(timeout: 1) } + @available(watchOS 6.2, *) func testDoesntCrashSwift() { #if os(macOS) // Previously exposed a bridging crash in Swift diff --git a/Tests/Core/DispatcherTests.swift b/Tests/Core/DispatcherTests.swift index dc1eb83f9..a445f2093 100644 --- a/Tests/Core/DispatcherTests.swift +++ b/Tests/Core/DispatcherTests.swift @@ -121,7 +121,7 @@ class DispatcherTests: XCTestCase { ex.fulfill() }.cauterize() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) PromiseKit.conf.D = oldConf } diff --git a/Tests/Core/GuaranteeTests.swift b/Tests/Core/GuaranteeTests.swift index 55d12a712..5ca1d1a4c 100644 --- a/Tests/Core/GuaranteeTests.swift +++ b/Tests/Core/GuaranteeTests.swift @@ -190,7 +190,6 @@ class GuaranteeTests: XCTestCase { wait(for: [ex], timeout: 10) } - #if swift(>=3.1) func testNoAmbiguityForValue() { let ex = expectation(description: "") let a = Guarantee.value @@ -201,5 +200,4 @@ class GuaranteeTests: XCTestCase { }.cauterize() wait(for: [ex], timeout: 10) } - #endif } diff --git a/Tests/Core/PromiseTests.swift b/Tests/Core/PromiseTests.swift index e4f743b64..998e74c18 100644 --- a/Tests/Core/PromiseTests.swift +++ b/Tests/Core/PromiseTests.swift @@ -207,7 +207,6 @@ class PromiseTests: XCTestCase { wait(for: [ex], timeout: 10) } - #if swift(>=3.1) func testNoAmbiguityForValue() { let ex = expectation(description: "") let a = Promise.value @@ -218,5 +217,4 @@ class PromiseTests: XCTestCase { }.cauterize() wait(for: [ex], timeout: 10) } - #endif } diff --git a/Tests/Core/ResolverTests.swift b/Tests/Core/ResolverTests.swift index a729e2d80..2cd728410 100644 --- a/Tests/Core/ResolverTests.swift +++ b/Tests/Core/ResolverTests.swift @@ -141,7 +141,6 @@ class WrapTests: XCTestCase { } func testSwiftResultSuccess() { - #if swift(>=5.0) let ex = expectation(description: "") let kittenFetcher = KittenFetcher(value: 2, error: nil) Promise { seal in @@ -152,11 +151,9 @@ class WrapTests: XCTestCase { }.silenceWarning() waitForExpectations(timeout: 1) - #endif } func testSwiftResultError() { - #if swift(>=5.0) let ex = expectation(description: "") let kittenFetcher = KittenFetcher(value: nil, error: Error.test) @@ -170,7 +167,6 @@ class WrapTests: XCTestCase { } waitForExpectations(timeout: 1) - #endif } func testIsFulfilled() { @@ -218,14 +214,12 @@ class WrapTests: XCTestCase { bar().done(ex.fulfill).cauterize() wait(for: [ex], timeout: 10) - #if swift(>=5.2) // ^^ ambiguous in Swift 5.0 & 5.1, testing again in next version let ex2 = expectation(description: "") Guarantee { seal in after(.microseconds(10)).done(seal) }.done(ex2.fulfill) wait(for: [ex2], timeout: 10) - #endif } } diff --git a/Tests/Core/WhenConcurrentTests.swift b/Tests/Core/WhenConcurrentTests.swift index 1b2c8179f..dc4564892 100644 --- a/Tests/Core/WhenConcurrentTests.swift +++ b/Tests/Core/WhenConcurrentTests.swift @@ -158,7 +158,6 @@ class WhenConcurrentTestCase_Swift: XCTestCase { } func testWhenResolvedContinuesWhenRejected() { - #if swift(>=5.3) let ex = expectation(description: "") enum Error: Swift.Error { case dummy } @@ -185,6 +184,5 @@ class WhenConcurrentTestCase_Swift: XCTestCase { } waitForExpectations(timeout: 3) - #endif } } diff --git a/Tests/PMKCoreLocation/CLGeocoderTests.swift b/Tests/PMKCoreLocation/CLGeocoderTests.swift new file mode 100644 index 000000000..e7c13d8bb --- /dev/null +++ b/Tests/PMKCoreLocation/CLGeocoderTests.swift @@ -0,0 +1,121 @@ +import PMKCoreLocation +import CoreLocation +import PromiseKit +import XCTest +#if os(iOS) || os(watchOS) || os(OSX) + import class Contacts.CNPostalAddress +#endif + +class CLGeocoderTests: XCTestCase { + func test_reverseGeocodeLocation() { + class MockGeocoder: CLGeocoder { + override func reverseGeocodeLocation(_ location: CLLocation, completionHandler: @escaping CLGeocodeCompletionHandler) { + after(.seconds(0)).done { + completionHandler([dummyPlacemark], nil) + } + } + } + + let ex = expectation(description: "") + MockGeocoder().reverseGeocode(location: CLLocation()).done { x in + XCTAssertEqual(x, [dummyPlacemark]) + ex.fulfill() + } + waitForExpectations(timeout: 1) + } + + func test_geocodeAddressDictionary() { + class MockGeocoder: CLGeocoder { + override func geocodeAddressDictionary(_ addressDictionary: [AnyHashable : Any], completionHandler: @escaping CLGeocodeCompletionHandler) { + after(.seconds(0)).done { + completionHandler([dummyPlacemark], nil) + } + } + } + + let ex = expectation(description: "") + MockGeocoder().geocode([:]).done { x in + XCTAssertEqual(x, [dummyPlacemark]) + ex.fulfill() + } + waitForExpectations(timeout: 1) + } + + func test_geocodeAddressString() { + class MockGeocoder: CLGeocoder { + override func geocodeAddressString(_ addressString: String, completionHandler: @escaping CLGeocodeCompletionHandler) { + after(.seconds(0)).done { + completionHandler([dummyPlacemark], nil) + } + } + } + + let ex = expectation(description: "") + MockGeocoder().geocode("").done { x in + XCTAssertEqual(x, [dummyPlacemark]) + ex.fulfill() + } + waitForExpectations(timeout: 1) + } + +#if !os(tvOS) && swift(>=3.2) + func test_geocodePostalAddress() { + guard #available(iOS 11.0, OSX 10.13, watchOS 4.0, *) else { return } + + class MockGeocoder: CLGeocoder { + override func geocodePostalAddress(_ postalAddress: CNPostalAddress, completionHandler: @escaping CLGeocodeCompletionHandler) { + after(.seconds(0)).done { + completionHandler([dummyPlacemark], nil) + } + } + } + + let ex = expectation(description: "") + MockGeocoder().geocodePostalAddress(CNPostalAddress()).done { x in + XCTAssertEqual(x, [dummyPlacemark]) + ex.fulfill() + } + waitForExpectations(timeout: 1) + } + + func test_geocodePostalAddressLocale() { + guard #available(iOS 11.0, OSX 10.13, watchOS 4.0, *) else { return } + + class MockGeocoder: CLGeocoder { + override func geocodePostalAddress(_ postalAddress: CNPostalAddress, preferredLocale locale: Locale?, completionHandler: @escaping CLGeocodeCompletionHandler) { + after(.seconds(0)).done { + completionHandler([dummyPlacemark], nil) + } + } + } + + let ex = expectation(description: "") + MockGeocoder().geocodePostalAddress(CNPostalAddress(), preferredLocale: nil).done { x in + XCTAssertEqual(x, [dummyPlacemark]) + ex.fulfill() + } + waitForExpectations(timeout: 1) + } + + func test_reverseGeocodeLocationLocale() { + guard #available(iOS 11.0, OSX 10.13, watchOS 4.0, *) else { return } + + class MockGeocoder: CLGeocoder { + override func reverseGeocodeLocation(_ location: CLLocation, preferredLocale locale: Locale?, completionHandler: @escaping CLGeocodeCompletionHandler) { + after(.seconds(0)).done { + completionHandler([dummyPlacemark], nil) + } + } + } + + let ex = expectation(description: "") + MockGeocoder().reverseGeocode(location: CLLocation(), preferredLocale: nil).done { x in + XCTAssertEqual(x, [dummyPlacemark]) + ex.fulfill() + } + waitForExpectations(timeout: 1) + } +#endif +} + +private let dummyPlacemark = CLPlacemark() diff --git a/Tests/PMKCoreLocation/CLLocationManagerTests.swift b/Tests/PMKCoreLocation/CLLocationManagerTests.swift new file mode 100644 index 000000000..914af867c --- /dev/null +++ b/Tests/PMKCoreLocation/CLLocationManagerTests.swift @@ -0,0 +1,95 @@ +import PMKCoreLocation +import CoreLocation +import PromiseKit +import XCTest + +#if !os(tvOS) + +class Test_CLLocationManager_Swift: XCTestCase { + func test_fulfills_with_multiple_locations() { + swizzle(CLLocationManager.self, #selector(CLLocationManager.startUpdatingLocation)) { + swizzle(CLLocationManager.self, #selector(CLLocationManager.authorizationStatus), isClassMethod: true) { + let ex = expectation(description: "") + + CLLocationManager.requestLocation().done { x in + XCTAssertEqual(x, dummy) + ex.fulfill() + } + + waitForExpectations(timeout: 1) + } + } + } + + func test_fufillsWithSatisfyingBlock() { + swizzle(CLLocationManager.self, #selector(CLLocationManager.startUpdatingLocation)) { + swizzle(CLLocationManager.self, #selector(CLLocationManager.authorizationStatus), isClassMethod: true) { + let ex = expectation(description: "") + let block: ((CLLocation) -> Bool) = { location in + return location.coordinate.latitude == dummy.last?.coordinate.latitude + } + CLLocationManager.requestLocation(satisfying: block).done({ locations in + locations.forEach { XCTAssert(block($0) == true, "Block should be successful for returned values") } + ex.fulfill() + }) + waitForExpectations(timeout: 1) + } + } + } + +#if os(iOS) + func test_requestAuthorization() { + let ex = expectation(description: "") + + CLLocationManager.requestAuthorization().done { + XCTAssertEqual($0, CLAuthorizationStatus.restricted) + ex.fulfill() + } + + waitForExpectations(timeout: 1, handler: nil) + } +#endif +} + + +/////////////////////////////////////////////////////////////// resources +private let dummy = [CLLocation(latitude: 0, longitude: 0), CLLocation(latitude: 10, longitude: 20)] + +extension CLLocationManager { + @objc func pmk_startUpdatingLocation() { + after(.milliseconds(100)).done { + self.delegate!.locationManager?(self, didUpdateLocations: dummy) + } + } + + @objc static func pmk_authorizationStatus() -> CLAuthorizationStatus { + #if os(macOS) + return .authorized + #else + return .authorizedWhenInUse + #endif + } +} + + +/////////////////////////////////////////////////////////////// utilities +import ObjectiveC + +func swizzle(_ foo: AnyClass, _ from: Selector, isClassMethod: Bool = false, body: () -> Void) { + let originalMethod: Method + let swizzledMethod: Method + + if isClassMethod { + originalMethod = class_getClassMethod(foo, from)! + swizzledMethod = class_getClassMethod(foo, Selector("pmk_\(from)"))! + } else { + originalMethod = class_getInstanceMethod(foo, from)! + swizzledMethod = class_getInstanceMethod(foo, Selector("pmk_\(from)"))! + } + + method_exchangeImplementations(originalMethod, swizzledMethod) + body() + method_exchangeImplementations(swizzledMethod, originalMethod) +} + +#endif diff --git a/Tests/PMKFoundation/TestNSNotificationCenter.swift b/Tests/PMKFoundation/TestNSNotificationCenter.swift new file mode 100644 index 000000000..3851029db --- /dev/null +++ b/Tests/PMKFoundation/TestNSNotificationCenter.swift @@ -0,0 +1,22 @@ +import PMKFoundation +import Foundation +import PromiseKit +import XCTest + +class NSNotificationCenterTests: XCTestCase { + func test() { + let ex = expectation(description: "") + let userInfo = ["a": 1] + + NotificationCenter.default.observe(once: PMKTestNotification).done { value in + XCTAssertEqual(value.userInfo?.count, 1) + ex.fulfill() + } + + NotificationCenter.default.post(name: PMKTestNotification, object: nil, userInfo: userInfo) + + waitForExpectations(timeout: 1) + } +} + +private let PMKTestNotification = Notification.Name("PMKTestNotification") diff --git a/Tests/PMKFoundation/TestNSObject.swift b/Tests/PMKFoundation/TestNSObject.swift new file mode 100644 index 000000000..cbd641a91 --- /dev/null +++ b/Tests/PMKFoundation/TestNSObject.swift @@ -0,0 +1,80 @@ +import PMKFoundation +import Foundation +import PromiseKit +import XCTest + +#if !os(Linux) + +class NSObjectTests: XCTestCase { + func testKVO() { + let ex = expectation(description: "") + + let foo = Foo() + foo.observe(.promise, keyPath: "bar").done { newValue in + XCTAssertEqual(newValue as? String, "moo") + ex.fulfill() + }.catch { _ in + XCTFail() + } + foo.bar = "moo" + + waitForExpectations(timeout: 1) + } + + func testAfterlife() { + let ex = expectation(description: "") + var killme: NSObject! + + autoreleasepool { + + func innerScope() { + killme = NSObject() + after(life: killme).done { _ in + //… + ex.fulfill() + } + } + + innerScope() + + after(.milliseconds(200)).done { + killme = nil + } + } + + waitForExpectations(timeout: 1) + } + + func testMultiObserveAfterlife() { + let ex1 = expectation(description: "") + let ex2 = expectation(description: "") + var killme: NSObject! + + autoreleasepool { + + func innerScope() { + killme = NSObject() + after(life: killme).done { _ in + ex1.fulfill() + } + after(life: killme).done { _ in + ex2.fulfill() + } + } + + innerScope() + + after(.milliseconds(200)).done { + killme = nil + } + } + + waitForExpectations(timeout: 1) + } +} + +private class Foo: NSObject { + @objc dynamic var bar: String = "bar" +} + +#endif diff --git a/Tests/PMKFoundation/TestNSTask.swift b/Tests/PMKFoundation/TestNSTask.swift new file mode 100644 index 000000000..264612c29 --- /dev/null +++ b/Tests/PMKFoundation/TestNSTask.swift @@ -0,0 +1,50 @@ +import PMKFoundation +import Foundation +import PromiseKit +import XCTest + +#if os(macOS) + +class NSTaskTests: XCTestCase { + func test1() { + let ex = expectation(description: "") + let task = Process() + task.launchPath = "/usr/bin/basename" + task.arguments = ["/foo/doe/bar"] + task.launch(.promise).done { stdout, _ in + let stdout = String(data: stdout.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) + XCTAssertEqual(stdout, "bar\n") + ex.fulfill() + }.cauterize() + waitForExpectations(timeout: 10) + } + + func test2() { + let ex = expectation(description: "") + let dir = "PMKAbsentDirectory" + + let task = Process() + task.launchPath = "/bin/ls" + task.arguments = [dir] + + task.launch(.promise).done { _ in + XCTFail() + }.catch { err in + do { + throw err + } catch Process.PMKError.execution(let proc, let stdout, let stderr) { + let expectedStderr = "ls: \(dir): No such file or directory\n" + + XCTAssertEqual(stderr, expectedStderr) + XCTAssertEqual(proc.terminationStatus, 1) + XCTAssertEqual(stdout?.count ?? 0, 0) + } catch { + XCTFail() + } + ex.fulfill() + } + waitForExpectations(timeout: 10) + } +} + +#endif diff --git a/Tests/PMKFoundation/TestNSURLSession.swift b/Tests/PMKFoundation/TestNSURLSession.swift new file mode 100644 index 000000000..c9f45debf --- /dev/null +++ b/Tests/PMKFoundation/TestNSURLSession.swift @@ -0,0 +1,81 @@ +#if !os(Linux) + +import OHHTTPStubsSwift +import PMKFoundation +import OHHTTPStubs +import PromiseKit +import XCTest + +class NSURLSessionTests: XCTestCase { + func test1() { + let json: NSDictionary = ["key1": "value1", "key2": ["value2A", "value2B"]] + + stub(condition: { $0.url!.host == "example.com" }) { _ in + HTTPStubsResponse(jsonObject: json, statusCode: 200, headers: nil) + } + + let ex = expectation(description: "") + let rq = URLRequest(url: URL(string: "http://example.com")!) + firstly { + URLSession.shared.dataTask(.promise, with: rq) + }.compactMap { + try JSONSerialization.jsonObject(with: $0.data) as? NSDictionary + }.done { rsp in + XCTAssertEqual(json, rsp) + ex.fulfill() + }.cauterize() + waitForExpectations(timeout: 1) + } + + func test2() { + + // test that URLDataPromise chains thens + // this test because I don’t trust the Swift compiler + + let dummy = ("fred" as NSString).data(using: String.Encoding.utf8.rawValue)! + + stub(condition: { $0.url!.host == "example.com" }) { _ in + return HTTPStubsResponse(data: dummy, statusCode: 200, headers: [:]) + } + + let ex = expectation(description: "") + let rq = URLRequest(url: URL(string: "http://example.com")!) + + after(.milliseconds(100)).then { + URLSession.shared.dataTask(.promise, with: rq) + }.done { x in + XCTAssertEqual(x.data, dummy) + ex.fulfill() + }.cauterize() + + waitForExpectations(timeout: 1) + } + + /// test that our convenience String constructor applies + func test3() { + let dummy = "fred" + + stub(condition: { $0.url!.host == "example.com" }) { _ in + let data = dummy.data(using: .utf8)! + return HTTPStubsResponse(data: data, statusCode: 200, headers: [:]) + } + + let ex = expectation(description: "") + let rq = URLRequest(url: URL(string: "http://example.com")!) + + after(.milliseconds(100)).then { + URLSession.shared.dataTask(.promise, with: rq) + }.map(String.init(data:urlResponse:)).done { + XCTAssertEqual($0, dummy) + ex.fulfill() + }.cauterize() + + waitForExpectations(timeout: 1) + } + + override func tearDown() { + //OHHTTPStubs.removeAllStubs() + } +} + +#endif diff --git a/Tests/PMKHomeKit/HMAccessoryBrowserTests.swift b/Tests/PMKHomeKit/HMAccessoryBrowserTests.swift new file mode 100644 index 000000000..5189cc79f --- /dev/null +++ b/Tests/PMKHomeKit/HMAccessoryBrowserTests.swift @@ -0,0 +1,84 @@ +// +// HMAccessoryBrowserTests.swift +// PMKHKTests +// +// Created by Chris Chares on 7/25/18. +// Copyright © 2018 Max Howell. All rights reserved. +// + +#if canImport(HomeKit) && !os(tvOS) && !os(watchOS) + +import XCTest +import PromiseKit +import HomeKit +@testable import PMKHomeKit + +class HMAccessoryBrowserTests: XCTestCase { + + func testBrowserScanReturningFirst() { + swizzle(HMAccessoryBrowser.self, #selector(HMAccessoryBrowser.startSearchingForNewAccessories)) { + let ex = expectation(description: "") + + HMPromiseAccessoryBrowser().start(scanInterval: .returnFirst(timeout: 0.5)) + .done { accessories in + XCTAssertEqual(accessories.count, 1) + ex.fulfill() + } + + waitForExpectations(timeout: 1, handler: nil) + } + } + + func testBrowserScanReturningTimeout() { + let ex = expectation(description: "") + + HMPromiseAccessoryBrowser().start(scanInterval: .returnFirst(timeout: 0.5)) + .catch { error in + // Why would we have discovered anything? + ex.fulfill() + } + + waitForExpectations(timeout: 1, handler: nil) + } +} + +extension HMAccessoryBrowser { + @objc func pmk_startSearchingForNewAccessories() { + after(.milliseconds(100)) + .done { swag in + self.delegate!.accessoryBrowser?(self, didFindNewAccessory: MockAccessory()) + } + } +} + +/// Mocks +class MockAccessory: HMAccessory { + var _uniqueID: UUID = UUID() + override var uniqueIdentifier: UUID { return _uniqueID } + + override init() { + super.init() + } +} + +// Utilty taken from https://github.com/PromiseKit/CoreLocation/blob/master/Tests/CLLocationManagerTests.swift +import ObjectiveC + +func swizzle(_ foo: AnyClass, _ from: Selector, isClassMethod: Bool = false, body: () -> Void) { + let originalMethod: Method + let swizzledMethod: Method + + if isClassMethod { + originalMethod = class_getClassMethod(foo, from)! + swizzledMethod = class_getClassMethod(foo, Selector("pmk_\(from)"))! + } else { + originalMethod = class_getInstanceMethod(foo, from)! + swizzledMethod = class_getInstanceMethod(foo, Selector("pmk_\(from)"))! + } + + method_exchangeImplementations(originalMethod, swizzledMethod) + body() + method_exchangeImplementations(swizzledMethod, originalMethod) +} + +#endif diff --git a/Tests/PMKHomeKit/UtilsTests.swift b/Tests/PMKHomeKit/UtilsTests.swift new file mode 100644 index 000000000..b0a542a4c --- /dev/null +++ b/Tests/PMKHomeKit/UtilsTests.swift @@ -0,0 +1,67 @@ +// +// UtilsTests.swift +// PMKHKTests +// +// Created by Chris Chares on 7/25/18. +// Copyright © 2018 Max Howell. All rights reserved. +// + +#if canImport(HomeKit) && !os(tvOS) && !os(watchOS) + +import XCTest +import PromiseKit +@testable import PMKHomeKit + +class UtilsTests: XCTestCase { + + var strongProxy: PromiseProxy? = PromiseProxy() + + override func setUp() { + strongProxy = PromiseProxy() + } + + override func tearDown() { + strongProxy = nil + } + + // The proxy should create a retain cycle until the promise is resolved + func testRetainCycle() { + weak var weakVar = strongProxy + XCTAssertNotNil(weakVar) + + let exp = expectation(description: "") + strongProxy = nil + after(.milliseconds(50)) + .done { + XCTAssertNotNil(weakVar) + exp.fulfill() + } + waitForExpectations(timeout: 1.0, handler: nil) + } + + // Once resolved, the proxy should break the retain cycle + func testRelease() { + weak var weakVar = strongProxy + XCTAssertNotNil(weakVar) + + let exp = expectation(description: "") + strongProxy!.fulfill(42) + strongProxy = nil + + after(.milliseconds(50)) + .done { + XCTAssertNil(weakVar) + exp.fulfill() + } + waitForExpectations(timeout: 1.0, handler: nil) + } + + // Cancel should reject with a PMKError + func testCancel() { + let proxy = strongProxy! + proxy.cancel() + XCTAssertNotNil(proxy.promise.error) + } +} + +#endif diff --git a/Tests/PMKMapKit/TestMapKit.swift b/Tests/PMKMapKit/TestMapKit.swift new file mode 100644 index 000000000..965da6715 --- /dev/null +++ b/Tests/PMKMapKit/TestMapKit.swift @@ -0,0 +1,63 @@ +import PromiseKit +import PMKMapKit +import MapKit +import XCTest + +class Test_MKDirections_Swift: XCTestCase { + func test_directions_response() { + let ex = expectation(description: "") + + class MockDirections: MKDirections { + override func calculate(completionHandler: @escaping MKDirections.DirectionsHandler) { + completionHandler(MKDirections.Response(), nil) + } + } + + let rq = MKDirections.Request() + let directions = MockDirections(request: rq) + + directions.calculate().done { _ in + ex.fulfill() + }.cauterize() + + waitForExpectations(timeout: 1, handler: nil) + } + + + func test_ETA_response() { + let ex = expectation(description: "") + + class MockDirections: MKDirections { + override func calculateETA(completionHandler: @escaping MKDirections.ETAHandler) { + completionHandler(MKDirections.ETAResponse(), nil) + } + } + + let rq = MKDirections.Request() + MockDirections(request: rq).calculateETA().done { rsp in + ex.fulfill() + }.cauterize() + + waitForExpectations(timeout: 1, handler: nil) + } + +} + +class Test_MKSnapshotter_Swift: XCTestCase { + func test() { + let ex = expectation(description: "") + + class MockSnapshotter: MKMapSnapshotter { + override func start(completionHandler: @escaping MKMapSnapshotter.CompletionHandler) { + completionHandler(MKMapSnapshotter.Snapshot(), nil) + } + } + + let snapshotter = MockSnapshotter() + snapshotter.start().done { _ in + ex.fulfill() + }.cauterize() + + waitForExpectations(timeout: 1, handler: nil) + } +} From 02fdf65eb630e71264d18975644f4e51638ca13f Mon Sep 17 00:00:00 2001 From: Max Howell Date: Tue, 1 Jun 2021 17:36:15 -0400 Subject: [PATCH 62/81] =?UTF-8?q?Don=E2=80=99t=20be=20ambiguous=20for=20`p?= =?UTF-8?q?romise.done=20{=20=5F=20in`=20etc.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/PromiseKit/Wrappers/ThenableWrappers.swift | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Sources/PromiseKit/Wrappers/ThenableWrappers.swift b/Sources/PromiseKit/Wrappers/ThenableWrappers.swift index c9ea63b72..40adf92f5 100644 --- a/Sources/PromiseKit/Wrappers/ThenableWrappers.swift +++ b/Sources/PromiseKit/Wrappers/ThenableWrappers.swift @@ -19,6 +19,7 @@ public extension _PMKSharedWrappers { - Parameter body: The closure that is executed when this Promise is fulfilled. - Returns: A new promise fulfilled as `Void`. */ + @_disfavoredOverload func done(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) throws -> Void) -> BaseOfVoid { let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) return done(on: dispatcher, body) @@ -45,6 +46,7 @@ public extension _PMKSharedWrappers { - Parameter body: The closure that is executed when this Promise is fulfilled. - Returns: A new promise that is resolved with the value that the handler is fed. */ + @_disfavoredOverload func get(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping (T) throws -> Void) -> BaseOfT { let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) return get(on: dispatcher, body) @@ -62,6 +64,7 @@ public extension _PMKSharedWrappers { - Parameter body: The closure that is executed with Result of Promise. - Returns: A new promise that is resolved with the result that the handler is fed. */ + @_disfavoredOverload func tap(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Result) -> Void) -> BaseOfT { let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) return tap(on: dispatcher, body) @@ -88,6 +91,7 @@ public extension Thenable { - Parameter body: The closure that executes when this promise fulfills. It must return a promise. - Returns: A new promise that resolves when the promise returned from the provided closure resolves. */ + @_disfavoredOverload func then(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) throws -> U) -> Promise { let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) return then(on: dispatcher, body) @@ -111,6 +115,7 @@ public extension Thenable { //… } */ + @_disfavoredOverload func map(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T) throws -> U) -> Promise { let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) return map(on: dispatcher, transform) @@ -131,6 +136,7 @@ public extension Thenable { // either `PMKError.compactMap` or a `JSONError` } */ + @_disfavoredOverload func compactMap(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T) throws -> U?) -> Promise { let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) return compactMap(on: dispatcher, transform) @@ -159,6 +165,7 @@ public extension CancellableThenable { - Parameter body: The closure that executes when this cancellable promise fulfills. It must return a cancellable promise. - Returns: A new cancellable promise that resolves when the promise returned from the provided closure resolves. */ + @_disfavoredOverload func then(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping (U.T) throws -> V) -> CancellablePromise { let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) return then(on: dispatcher, body) @@ -184,6 +191,7 @@ public extension CancellableThenable { - Parameter body: The closure that executes when this cancellable promise fulfills. It must return a promise (not a cancellable promise). - Returns: A new cancellable promise that resolves when the promise returned from the provided closure resolves. */ + @_disfavoredOverload func then(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping (U.T) throws -> V) -> CancellablePromise { let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) return then(on: dispatcher, body) @@ -209,6 +217,7 @@ public extension CancellableThenable { - Parameter transform: The closure that is executed when this CancellablePromise is fulfilled. It must return a non-promise and non-cancellable-promise. - Returns: A new cancellable promise that is resolved with the value returned from the provided closure. */ + @_disfavoredOverload func map(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping (U.T) throws -> V) -> CancellablePromise { let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) return map(on: dispatcher, transform) @@ -232,6 +241,7 @@ public extension CancellableThenable { //… context.cancel() */ + @_disfavoredOverload func compactMap(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping (U.T) throws -> V?) -> CancellablePromise { let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) return compactMap(on: dispatcher, transform) From 0bfccc14018125f5821adeac0fe5375437028a05 Mon Sep 17 00:00:00 2001 From: Max Howell Date: Tue, 1 Jun 2021 17:36:25 -0400 Subject: [PATCH 63/81] Missing API from v6 --- .../Wrappers/GuaranteeWrappers.swift | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Sources/PromiseKit/Wrappers/GuaranteeWrappers.swift b/Sources/PromiseKit/Wrappers/GuaranteeWrappers.swift index a3bd2d404..de03cb7f8 100644 --- a/Sources/PromiseKit/Wrappers/GuaranteeWrappers.swift +++ b/Sources/PromiseKit/Wrappers/GuaranteeWrappers.swift @@ -10,22 +10,44 @@ public extension Guarantee { let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) return then(on: dispatcher, body) } + + @discardableResult + func then(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) throws -> Guarantee) -> Promise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return then(on: dispatcher, body) + } func map(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) -> U) -> Guarantee { let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) return map(on: dispatcher, body) } + + func map(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) throws -> U) -> Promise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.map, flags: flags) + return map(on: dispatcher, body) + } @discardableResult func done(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) -> Void) -> Guarantee { let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) return done(on: dispatcher, body) } + + @discardableResult + func done(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) throws -> Void) -> Promise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) + return done(on: dispatcher, body) + } func get(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping (T) -> Void) -> Guarantee { let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) return get(on: dispatcher, body) } + + func get(on: DispatchQueue? = .pmkDefault, flags: DispatchWorkItemFlags? = nil, _ body: @escaping (T) throws -> Void) -> Promise { + let dispatcher = selectDispatcher(given: on, configured: conf.D.return, flags: flags) + return get(on: dispatcher, body) + } } public extension Guarantee where T: Sequence { From 66418a9321534914f79e0e12d49b6a2d68300b99 Mon Sep 17 00:00:00 2001 From: Max Howell Date: Tue, 1 Jun 2021 21:41:41 -0400 Subject: [PATCH 64/81] Docs fixes for v7 --- Documents/GettingStarted.md | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/Documents/GettingStarted.md b/Documents/GettingStarted.md index 5f5a8f710..2e5bb61c4 100644 --- a/Documents/GettingStarted.md +++ b/Documents/GettingStarted.md @@ -32,7 +32,7 @@ readability. The promise chain above is easy to scan and understand: one asynchr operation leads into the other, line by line. It's as close to procedural code as we can easily come given the current state of Swift. -`done` is the same as `then` but you cannot return a promise. It is +`done` is the same as `then` but you cannot return a promise. It is typically the end of the “success” part of the chain. Above, you can see that we receive the final image in our `done` and use it to set up the UI. @@ -40,15 +40,15 @@ Let’s compare the signatures of the two login methods: ```swift func login() -> Promise - + // Compared with: func login(completion: (Creds?, Error?) -> Void) // ^^ ugh. Optionals. Double optionals. ``` -The distinction is that with promises, your functions return *promises* instead -of accepting and running callbacks. Each handler in a chain returns a promise. +The distinction is that with promises, your functions return *promises* instead +of accepting and running callbacks. Each handler in a chain returns a promise. `Promise` objects define the `then` method, which waits for the completion of the promise before continuing the chain. Chains resolve procedurally, one promise at a time. @@ -60,7 +60,7 @@ of `Creds`. > *Note*: `done` was introduced in PromiseKit 5. We previously defined a variant of `then` that did not require you to return a promise. Unfortunately, this convention often confused -Swift and led to odd and hard-to-debug error messages. It also made using PromiseKit +Swift and led to odd and hard-to-debug error messages. It also made using PromiseKit more painful. The introduction of `done` lets you type out promise chains that compile without additional qualification to help the compiler figure out type information. @@ -158,9 +158,9 @@ login { creds, error in } ``` -It would be very easy for someone to amend this code and forget to unset +It would be very easy for someone to amend this code and forget to unset the activity indicator, leading to a bug. With promises, this type of error is -almost impossible: the Swift compiler resists your supplementing the chain without +almost impossible: the Swift compiler resists your supplementing the chain without using promises. You almost won’t need to review the pull requests. > *Note*: PromiseKit has perhaps capriciously switched between the names `always` @@ -234,7 +234,7 @@ As with any promise chain, if any of the component promises fail, the chain call # PromiseKit Extensions -When we made PromiseKit, we understood that we wanted to use *only* promises to implement +When we made PromiseKit, we understood that we wanted to use *only* promises to implement asynchronous behavior. So wherever possible, we offer extensions to Apple’s APIs that reframe the API in terms of promises. For example: @@ -256,12 +256,7 @@ pod "PromiseKit/CoreLocation" pod "PromiseKit/MapKit" ``` -All of these extensions are available at the [PromiseKit organization](https://github.com/PromiseKit). -Go there to see what's available and to read the source code and documentation. Every file and function -has been copiously documented. - -> We also provide extensions for common libraries such as [Alamofire](https://github.com/PromiseKit/Alamofire-). - +To see what is available, check our [sources](https://github.com/mxcl/PromiseKit/tree/master/Sources). # Making Promises @@ -296,9 +291,9 @@ func fetch() -> Promise { } ``` -The `seal` object that the `Promise` initializer provides to you defines -many methods for handling garden-variety completion handlers. It even -covers a variety of rarer situations, thus making it easy for you to add +The `seal` object that the `Promise` initializer provides to you defines +many methods for handling garden-variety completion handlers. It even +covers a variety of rarer situations, thus making it easy for you to add promises to an existing codebase. > *Note*: We tried to make it so that you could just do `Promise(fetch)`, but we @@ -333,8 +328,8 @@ firstly { ``` Swift warns you if you don’t terminate a regular `Promise` chain (i.e., not -a `Guarantee` chain). You're expected to silence this warning by supplying -either a `catch` or a `return`. (In the latter case, you will then have to `catch` +a `Guarantee` chain). You're expected to silence this warning by supplying +either a `catch` or a `return`. (In the latter case, you will then have to `catch` at the point where you receive that promise.) Use `Guarantee`s wherever possible so that your code has error handling where @@ -471,12 +466,12 @@ Here is a key understanding: `login()` returns a `Promise`, and all `Promise`s h `when` is one of PromiseKit’s more useful functions, and so we offer several variants. * The default `when`, and the one you should typically use, is `when(fulfilled:)`. This variant -waits on all its component promises, but if any fail, `when` fails too, and thus the chain *rejects*. +waits on all its component promises, but if any fail, `when` fails too, and thus the chain *rejects*. It's important to note that all promises in the `when` *continue*. Promises have *no* control over the tasks they represent. Promises are just wrappers around tasks. * `when(resolved:)` waits even if one or more of its component promises fails. The value produced -by this variant of `when` is an array of `Result`. Consequently, this variant requires all its +by this variant of `when` is an array of `Result`. Consequently, this variant requires all its component promises to have the same generic type. See our advanced patterns guide for work-arounds for this limitation. From 75aa70a0b5e83bc063d0f29f48b8b4945f11bc2d Mon Sep 17 00:00:00 2001 From: Max Howell Date: Tue, 1 Jun 2021 22:47:14 -0400 Subject: [PATCH 65/81] --warnings; reduce deploy targets as poss --- Package.swift | 6 +++--- Sources/PMKCloudKit/CKContainer+Promise.swift | 2 ++ Tests/Cancel/CatchableTests.swift | 11 +++++++---- Tests/Core/CatchableTests.swift | 11 +++++++---- Tests/PMKCoreLocation/CLGeocoderTests.swift | 12 ++++++------ Tests/PMKCoreLocation/CLLocationManagerTests.swift | 4 ++-- Tests/PMKHomeKit/HMAccessoryBrowserTests.swift | 2 +- Tests/PMKMapKit/TestMapKit.swift | 4 ++++ 8 files changed, 32 insertions(+), 20 deletions(-) diff --git a/Package.swift b/Package.swift index d3d8b7d06..280ecc3c9 100644 --- a/Package.swift +++ b/Package.swift @@ -4,9 +4,9 @@ import PackageDescription let pkg = Package(name: "PromiseKit") pkg.platforms = [ - .macOS(.v10_12), //FIXME strictly 10.10 (only tests need 10.12) - .iOS(.v10), //FIXME strictly 8.0 - .tvOS(.v10), //FIXME strictly 9.0 + .macOS(.v10_10), + .iOS(.v10), //FIXME strictly 8.0, but Tests require 10 + .tvOS(.v10), //FIXME strictly 9.0, but Tests require 10 .watchOS(.v3) ] pkg.swiftLanguageVersions = [.v5] diff --git a/Sources/PMKCloudKit/CKContainer+Promise.swift b/Sources/PMKCloudKit/CKContainer+Promise.swift index 45871f7f9..44febd520 100644 --- a/Sources/PMKCloudKit/CKContainer+Promise.swift +++ b/Sources/PMKCloudKit/CKContainer+Promise.swift @@ -32,6 +32,7 @@ public extension CKContainer { } /// Retrieves information about a single user based on the ID of the corresponding user record. + @available(macOS 10.12, iOS 10, tvOS 10, *) func discoverUserIdentity(withUserRecordID recordID: CKRecord.ID) -> Promise { return Promise { discoverUserIdentity(withUserRecordID: recordID, completionHandler: $0.resolve) } } @@ -43,6 +44,7 @@ public extension CKContainer { } #if !os(tvOS) +@available(macOS 10.12, iOS 10, tvOS 10, *) public extension CKContainer { func discoverAllIdentities() -> Promise<[CKUserIdentity]> { return Promise { discoverAllIdentities(completionHandler: $0.resolve) } diff --git a/Tests/Cancel/CatchableTests.swift b/Tests/Cancel/CatchableTests.swift index 4224463ba..88f15e64f 100644 --- a/Tests/Cancel/CatchableTests.swift +++ b/Tests/Cancel/CatchableTests.swift @@ -470,11 +470,12 @@ extension CatchableTests { func testCatchOnly_Type_Ignored() { let x = expectation(description: #file + #function) - enum Foo: Swift.Error {} + enum Foo: Swift.Error { + case a + } Promise(error: Error.dummy).cancellize().catch(only: Foo.self) { _ in XCTFail() - x.fulfill() }.catch { _ in x.fulfill() } @@ -647,10 +648,12 @@ extension CatchableTests { func testRecoverOnly_Type_PatternMatch() { let x = expectation(description: #file + #function) - enum Foo: Swift.Error {} + enum Foo: Swift.Error { + case a + } Promise(error: Error.dummy).cancellize().recover(only: Foo.self) { _ in - return Promise.value(1) + Promise.value(1) }.done { _ in XCTFail() x.fulfill() diff --git a/Tests/Core/CatchableTests.swift b/Tests/Core/CatchableTests.swift index 3e1037a3f..a96f2680e 100644 --- a/Tests/Core/CatchableTests.swift +++ b/Tests/Core/CatchableTests.swift @@ -334,11 +334,12 @@ extension CatchableTests { func testCatchOnly_Type_Ignored() { let x = expectation(description: #file + #function) - enum Foo: Swift.Error {} + enum Foo: Swift.Error { + case a + } Promise(error: Error.dummy).catch(only: Foo.self) { _ in XCTFail() - x.fulfill() }.catch { _ in x.fulfill() } @@ -507,10 +508,12 @@ extension CatchableTests { func testRecoverOnly_Type_PatternMatch() { let x = expectation(description: #file + #function) - enum Foo: Swift.Error {} + enum Foo: Swift.Error { + case a + } Promise(error: Error.dummy).recover(only: Foo.self) { _ in - return Promise.value(1) + Promise.value(1) }.done { _ in XCTFail() x.fulfill() diff --git a/Tests/PMKCoreLocation/CLGeocoderTests.swift b/Tests/PMKCoreLocation/CLGeocoderTests.swift index e7c13d8bb..ab1d91f0b 100644 --- a/Tests/PMKCoreLocation/CLGeocoderTests.swift +++ b/Tests/PMKCoreLocation/CLGeocoderTests.swift @@ -20,7 +20,7 @@ class CLGeocoderTests: XCTestCase { MockGeocoder().reverseGeocode(location: CLLocation()).done { x in XCTAssertEqual(x, [dummyPlacemark]) ex.fulfill() - } + }.cauterize() waitForExpectations(timeout: 1) } @@ -37,7 +37,7 @@ class CLGeocoderTests: XCTestCase { MockGeocoder().geocode([:]).done { x in XCTAssertEqual(x, [dummyPlacemark]) ex.fulfill() - } + }.cauterize() waitForExpectations(timeout: 1) } @@ -54,7 +54,7 @@ class CLGeocoderTests: XCTestCase { MockGeocoder().geocode("").done { x in XCTAssertEqual(x, [dummyPlacemark]) ex.fulfill() - } + }.cauterize() waitForExpectations(timeout: 1) } @@ -74,7 +74,7 @@ class CLGeocoderTests: XCTestCase { MockGeocoder().geocodePostalAddress(CNPostalAddress()).done { x in XCTAssertEqual(x, [dummyPlacemark]) ex.fulfill() - } + }.cauterize() waitForExpectations(timeout: 1) } @@ -93,7 +93,7 @@ class CLGeocoderTests: XCTestCase { MockGeocoder().geocodePostalAddress(CNPostalAddress(), preferredLocale: nil).done { x in XCTAssertEqual(x, [dummyPlacemark]) ex.fulfill() - } + }.cauterize() waitForExpectations(timeout: 1) } @@ -112,7 +112,7 @@ class CLGeocoderTests: XCTestCase { MockGeocoder().reverseGeocode(location: CLLocation(), preferredLocale: nil).done { x in XCTAssertEqual(x, [dummyPlacemark]) ex.fulfill() - } + }.cauterize() waitForExpectations(timeout: 1) } #endif diff --git a/Tests/PMKCoreLocation/CLLocationManagerTests.swift b/Tests/PMKCoreLocation/CLLocationManagerTests.swift index 914af867c..d7f1ef0c6 100644 --- a/Tests/PMKCoreLocation/CLLocationManagerTests.swift +++ b/Tests/PMKCoreLocation/CLLocationManagerTests.swift @@ -14,7 +14,7 @@ class Test_CLLocationManager_Swift: XCTestCase { CLLocationManager.requestLocation().done { x in XCTAssertEqual(x, dummy) ex.fulfill() - } + }.cauterize() waitForExpectations(timeout: 1) } @@ -31,7 +31,7 @@ class Test_CLLocationManager_Swift: XCTestCase { CLLocationManager.requestLocation(satisfying: block).done({ locations in locations.forEach { XCTAssert(block($0) == true, "Block should be successful for returned values") } ex.fulfill() - }) + }).cauterize() waitForExpectations(timeout: 1) } } diff --git a/Tests/PMKHomeKit/HMAccessoryBrowserTests.swift b/Tests/PMKHomeKit/HMAccessoryBrowserTests.swift index 5189cc79f..66e7302ec 100644 --- a/Tests/PMKHomeKit/HMAccessoryBrowserTests.swift +++ b/Tests/PMKHomeKit/HMAccessoryBrowserTests.swift @@ -23,7 +23,7 @@ class HMAccessoryBrowserTests: XCTestCase { .done { accessories in XCTAssertEqual(accessories.count, 1) ex.fulfill() - } + }.cauterize() waitForExpectations(timeout: 1, handler: nil) } diff --git a/Tests/PMKMapKit/TestMapKit.swift b/Tests/PMKMapKit/TestMapKit.swift index 965da6715..70286b155 100644 --- a/Tests/PMKMapKit/TestMapKit.swift +++ b/Tests/PMKMapKit/TestMapKit.swift @@ -1,3 +1,5 @@ +#if !os(watchOS) + import PromiseKit import PMKMapKit import MapKit @@ -61,3 +63,5 @@ class Test_MKSnapshotter_Swift: XCTestCase { waitForExpectations(timeout: 1, handler: nil) } } + +#endif From 6e3d2367068fa15f01bf832e99b58bfa6c151c58 Mon Sep 17 00:00:00 2001 From: Max Howell Date: Wed, 2 Jun 2021 09:46:28 -0400 Subject: [PATCH 66/81] 7.0.0-rc1 --- Documents/Installation.md | 26 ++++++++++++++++++-------- README.md | 2 +- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/Documents/Installation.md b/Documents/Installation.md index 567588f97..e719c138c 100644 --- a/Documents/Installation.md +++ b/Documents/Installation.md @@ -1,23 +1,33 @@ -We support SwiftPM: +We support [SwiftPM]: ```swift package.dependencies.append( - .package(url: "https://github.com/mxcl/PromiseKit", from: "7.0.0-alpha.1") + .package(url: "https://github.com/mxcl/PromiseKit", from: "7.0.0-rc1") ) package.targets.append( - .target(name: "MyTarget", dependencies: ["PromiseKit", "PMKFoundation"]) + .target(name: "…", dependencies: [ + .product(name: "PromiseKit", package: "PromiseKit"), + .product(name: "PMKFoundation", package: "PromiseKit"), + .product(name: "PMKMapKit", package: "PromiseKit"), + ]) ) ``` -Or CocoaPods: +And CocoaPods: ```ruby -pod "PromiseKit", "~> 6.8" -pod "PromiseKit/Foundation", "~> 6.8" +pod "PromiseKit", "~> 7.0.0-rc1" +pod "PromiseKit/Foundation", "~> 7.0.0-rc1" +pod "PromiseKit/MapKit", "~> 7.0.0-rc1" ``` ## Carthage -We will support Carthage if you can PR an automated solution for generating the `.xcodeproj` on release. -It will need to support all our extensions. +We will support [Carthage] if you can PR an automated solution for generating +the `.xcodeproj` on release. It will need to support all our extensions. + + +[SwiftPM]: https://swift.org/package-manager +[CocoaPods]: https://cocoapods.org +[Carthage]: https://github.com/Carthage/Carthage diff --git a/README.md b/README.md index 12a2dce32..ce07db9b4 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ In your `Package.swift`: ```swift package.dependencies.append( - .package(url: "https://github.com/mxcl/PromiseKit", from: "7.0.0") + .package(url: "https://github.com/mxcl/PromiseKit", from: "7.0.0-rc1") ) ``` From b94bf393acbb22cb739ea23336d68f64f04db83f Mon Sep 17 00:00:00 2001 From: Max Howell Date: Wed, 2 Jun 2021 10:57:10 -0400 Subject: [PATCH 67/81] [ci] Attempt to decrease CI false-failure rate Makes default timeout 5 seconds. --- Tests/Cancel/AfterTests.swift | 18 ++++---- Tests/Cancel/CancelChain.swift | 2 +- Tests/Cancel/CancellableErrorTests.swift | 10 ++-- Tests/Cancel/CancellablePromiseTests.swift | 20 ++++---- Tests/Cancel/CatchableTests.swift | 46 +++++++++---------- Tests/Cancel/DefaultDispatchQueueTests.swift | 6 +-- Tests/Cancel/DispatchWrapperTests.swift | 10 ++-- Tests/Cancel/DispatcherTests.swift | 18 ++++---- Tests/Cancel/GuaranteeTests.swift | 10 ++-- Tests/Cancel/HangTests.swift | 2 +- Tests/Cancel/PromiseTests.swift | 18 ++++---- Tests/Cancel/RaceTests.swift | 14 +++--- Tests/Cancel/RegressionTests.swift | 6 +-- Tests/Cancel/ResolverTests.swift | 26 +++++------ Tests/Cancel/StressTests.swift | 12 ++--- Tests/Cancel/ThenableTests.swift | 22 ++++----- Tests/Cancel/TimeoutTests.swift | 14 +++--- Tests/Cancel/ValueTests.swift | 18 ++++---- Tests/Cancel/WhenConcurrentTests.swift | 18 ++++---- Tests/Cancel/WhenResolvedTests.swift | 2 +- Tests/Cancel/WhenTests.swift | 34 +++++++------- Tests/Cancel/ZalgoTests.swift | 8 ++-- Tests/Core/AfterTests.swift | 12 ++--- Tests/Core/CancellableErrorTests.swift | 8 ++-- Tests/Core/CatchableTests.swift | 32 ++++++------- Tests/Core/DefaultDispatchQueueTests.swift | 6 +-- Tests/Core/DispatchWrapperTests.swift | 12 ++--- Tests/Core/DispatcherTests.swift | 10 ++-- Tests/Core/GuaranteeTests.swift | 30 ++++++------ Tests/Core/HangTests.swift | 2 +- Tests/Core/LoggingTests.swift | 4 +- Tests/Core/PromiseTests.swift | 16 +++---- Tests/Core/RaceTests.swift | 18 ++++---- Tests/Core/ResolverTests.swift | 22 ++++----- Tests/Core/StressTests.swift | 6 +-- Tests/Core/ThenableTests.swift | 38 +++++++-------- Tests/Core/WhenConcurrentTests.swift | 12 ++--- Tests/Core/WhenTests.swift | 28 +++++------ Tests/Core/ZalgoTests.swift | 4 +- Tests/PMKCoreLocation/CLGeocoderTests.swift | 12 ++--- .../CLLocationManagerTests.swift | 6 +-- .../TestNSNotificationCenter.swift | 2 +- Tests/PMKFoundation/TestNSObject.swift | 6 +-- Tests/PMKFoundation/TestNSTask.swift | 4 +- Tests/PMKFoundation/TestNSURLSession.swift | 6 +-- .../PMKHomeKit/HMAccessoryBrowserTests.swift | 4 +- Tests/PMKHomeKit/UtilsTests.swift | 4 +- Tests/PMKMapKit/TestMapKit.swift | 6 +-- 48 files changed, 322 insertions(+), 322 deletions(-) diff --git a/Tests/Cancel/AfterTests.swift b/Tests/Cancel/AfterTests.swift index 27460ea9b..1501fe5e0 100644 --- a/Tests/Cancel/AfterTests.swift +++ b/Tests/Cancel/AfterTests.swift @@ -15,36 +15,36 @@ class AfterTests: XCTestCase { let ex2 = expectation(description: "") let cc2 = after(seconds: 0).cancellize().done(fail).catch(policy: .allErrors, ex2.fulfill) cc2.cancel() - waitForExpectations(timeout: 2, handler: nil) + waitForExpectations(timeout: 5, handler: nil) let ex3 = expectation(description: "") let cc3 = after(.seconds(0)).cancellize().done(fail).catch(policy: .allErrors, ex3.fulfill) cc3.cancel() - waitForExpectations(timeout: 2, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func testNegative() { let ex2 = expectation(description: "") let cc2 = after(seconds: -1).cancellize().done(fail).catch(policy: .allErrors, ex2.fulfill) cc2.cancel() - waitForExpectations(timeout: 2, handler: nil) + waitForExpectations(timeout: 5, handler: nil) let ex3 = expectation(description: "") let cc3 = after(.seconds(-1)).cancellize().done(fail).catch(policy: .allErrors, ex3.fulfill) cc3.cancel() - waitForExpectations(timeout: 2, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func testPositive() { let ex2 = expectation(description: "") let cc2 = after(seconds: 1).cancellize().done(fail).catch(policy: .allErrors, ex2.fulfill) cc2.cancel() - waitForExpectations(timeout: 2, handler: nil) + waitForExpectations(timeout: 5, handler: nil) let ex3 = expectation(description: "") let cc3 = after(.seconds(1)).cancellize().done(fail).catch(policy: .allErrors, ex3.fulfill) cc3.cancel() - waitForExpectations(timeout: 2, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func testCancellableAfter() { @@ -87,7 +87,7 @@ class AfterTests: XCTestCase { error.isCancelled ? exCancel.fulfill() : XCTFail("unexpected error \(error)") }.cancel() - wait(for: [exComplete, exCancelComplete, exCancel], timeout: 1) + wait(for: [exComplete, exCancelComplete, exCancel], timeout: 5) } func testCancelForPromise_Done() { @@ -104,7 +104,7 @@ class AfterTests: XCTestCase { promise.cancel() - wait(for: [exComplete], timeout: 1) + wait(for: [exComplete], timeout: 5) } func testCancelForGuarantee_Done() { @@ -116,6 +116,6 @@ class AfterTests: XCTestCase { error.isCancelled ? exComplete.fulfill() : XCTFail("error: \(error)") }.cancel() - wait(for: [exComplete], timeout: 1) + wait(for: [exComplete], timeout: 5) } } diff --git a/Tests/Cancel/CancelChain.swift b/Tests/Cancel/CancelChain.swift index 167d5f179..12c57a207 100644 --- a/Tests/Cancel/CancelChain.swift +++ b/Tests/Cancel/CancelChain.swift @@ -154,7 +154,7 @@ class CancelChain: XCTestCase { self.trace("SETUP COMPLETE") let expectations = [ex.a, ex.b, ex.c, ex.d, ex.e, ex.cancelled].compactMap { $0 } - wait(for: expectations, timeout: 1) + wait(for: expectations, timeout: 5) XCTAssert(c.pA.cancelContext.cancelAttempted) XCTAssert(ex.a == nil || isFulfilled(c.pB) || c.pB.cancelContext.cancelAttempted) diff --git a/Tests/Cancel/CancellableErrorTests.swift b/Tests/Cancel/CancellableErrorTests.swift index 66d3f5fc8..4e74fef7b 100644 --- a/Tests/Cancel/CancellableErrorTests.swift +++ b/Tests/Cancel/CancellableErrorTests.swift @@ -19,7 +19,7 @@ class CancellationTests: XCTestCase { p.cancel(with: LocalError.cancel) - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testThrowCancellableErrorThatIsNotCancelled() { @@ -34,7 +34,7 @@ class CancellationTests: XCTestCase { cc.cancel(with: LocalError.notCancel) - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testRecoverWithCancellation() { @@ -60,7 +60,7 @@ class CancellationTests: XCTestCase { p.cancel(with: CocoaError.cancelled) - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testFoundationBridging1() { @@ -79,7 +79,7 @@ class CancellationTests: XCTestCase { p.cancel(with: CocoaError.cancelled) - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testFoundationBridging2() { @@ -98,7 +98,7 @@ class CancellationTests: XCTestCase { p.cancel(with: URLError.cancelled) - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testIsCancelled() { diff --git a/Tests/Cancel/CancellablePromiseTests.swift b/Tests/Cancel/CancellablePromiseTests.swift index 7485a3c50..4e5b7c0de 100644 --- a/Tests/Cancel/CancellablePromiseTests.swift +++ b/Tests/Cancel/CancellablePromiseTests.swift @@ -37,7 +37,7 @@ class CancellablePromiseTests: XCTestCase { promise.cancel() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testReturnTypeForAMultiLineClosureIsNotExplicitlyStated() { @@ -56,7 +56,7 @@ class CancellablePromiseTests: XCTestCase { XCTFail() } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testTryingToCancelAStandardPromiseChain() { @@ -83,7 +83,7 @@ class CancellablePromiseTests: XCTestCase { promise.cancel() /// <-- ERROR: Value of type 'PMKFinalizer' has no member 'cancel' - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testCancel() { @@ -100,7 +100,7 @@ class CancellablePromiseTests: XCTestCase { p.resolver.fulfill(3) p.promise.cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } func testFirstly() { @@ -116,7 +116,7 @@ class CancellablePromiseTests: XCTestCase { $0.isCancelled ? ex.fulfill() : XCTFail() }.cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } func testFirstlyWithPromise() { @@ -132,7 +132,7 @@ class CancellablePromiseTests: XCTestCase { $0.isCancelled ? ex.fulfill() : XCTFail("\($0)") }.cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } func testThenMapSuccess() { @@ -147,7 +147,7 @@ class CancellablePromiseTests: XCTestCase { }.catch(policy: .allErrors) { _ in XCTFail() } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testThenMapCancel() { @@ -163,7 +163,7 @@ class CancellablePromiseTests: XCTestCase { }.catch(policy: .allErrors) { $0.isCancelled ? ex.fulfill() : XCTFail() }.cancel() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testChain() { @@ -178,7 +178,7 @@ class CancellablePromiseTests: XCTestCase { }.catch(policy: .allErrors) { $0.isCancelled ? ex.fulfill() : XCTFail() }.cancel() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testBridge() { @@ -200,6 +200,6 @@ class CancellablePromiseTests: XCTestCase { XCTFail() } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } } diff --git a/Tests/Cancel/CatchableTests.swift b/Tests/Cancel/CatchableTests.swift index 88f15e64f..60caee55c 100644 --- a/Tests/Cancel/CatchableTests.swift +++ b/Tests/Cancel/CatchableTests.swift @@ -27,7 +27,7 @@ class CatchableTests: XCTestCase { p.cancel(with: error) - wait(for: [ex.0, ex.1], timeout: 10) + wait(for: [ex.0, ex.1], timeout: 5) } helper(error: Error.dummy) @@ -50,7 +50,7 @@ class CatchableTests: XCTestCase { p.cancel(with: Error.dummy) - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } } @@ -61,7 +61,7 @@ extension CatchableTests { func helper(policy: CatchPolicy, error: Swift.Error, line: UInt = #line) { let ex = expectation(description: "error caught") CancellablePromise(error: error).recover { _ in }.done { _ in XCTFail() }.catch(policy: .allErrors, ex.fulfill).cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } helper(policy: .allErrorsExceptCancellation, error: Error.dummy) @@ -72,7 +72,7 @@ extension CatchableTests { let ex2 = expectation(description: "cancel caught") let d2 = CancellablePromise(error: Error.cancelled).recover(policy: .allErrors) { _ in }.done(ex2.fulfill) d2.cancel() - wait(for: [ex2], timeout: 1) + wait(for: [ex2], timeout: 5) } func test__void_specialized_full_recover__fulfilled_path() { @@ -84,13 +84,13 @@ extension CatchableTests { }.catch(policy: .allErrors) { $0.isCancelled ? ex.fulfill() : XCTFail() }.cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) let ex2 = expectation(description: "") let promise = CancellablePromise() promise.cancel() promise.recover(policy: .allErrors) { _ in }.done(ex2.fulfill).catch(policy: .allErrors) { _ in XCTFail() } - wait(for: [ex2], timeout: 1) + wait(for: [ex2], timeout: 5) } func test__void_specialized_conditional_recover() { @@ -104,7 +104,7 @@ extension CatchableTests { XCTFail() } promise.cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } func helperCatch(policy: CatchPolicy, error: Swift.Error, line: UInt = #line) { @@ -119,7 +119,7 @@ extension CatchableTests { $0.isCancelled ? ex.fulfill() : XCTFail() } promise.cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } for error in [Error.dummy as Swift.Error, Error.cancelled] { @@ -137,7 +137,7 @@ extension CatchableTests { }.catch(policy: .allErrors) { _ in ex.fulfill() }.cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } for error in [Error.dummy, Error.cancelled] { @@ -154,7 +154,7 @@ extension CatchableTests { XCTAssertEqual(Error.cancelled, $0 as? Error) ex.fulfill() }.cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } func test__void_specialized_conditional_recover__fulfilled_path() { @@ -167,7 +167,7 @@ extension CatchableTests { ex.fulfill() } p.cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } } @@ -181,7 +181,7 @@ extension CatchableTests { }.done { _ in XCTFail() }.catch(policy: .allErrors, ex.fulfill).cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } helper(error: Error.dummy) @@ -198,7 +198,7 @@ extension CatchableTests { }.catch(policy: .allErrors) { error in error.isCancelled ? ex.fulfill() : XCTFail() }.cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } func test__conditional_recover() { @@ -220,7 +220,7 @@ extension CatchableTests { XCTFail() } }.cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } for error in [Error.dummy as Swift.Error, Error.cancelled] { @@ -241,7 +241,7 @@ extension CatchableTests { } ex.fulfill() }.cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } for error in [Error.dummy, Error.cancelled] { @@ -259,7 +259,7 @@ extension CatchableTests { XCTAssertEqual(Error.cancelled, $0 as? Error) ex.fulfill() }.cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } func test__conditional_recover__fulfilled_path() { @@ -273,7 +273,7 @@ extension CatchableTests { }.catch(policy: .allErrors) { error in error.isCancelled ? ex.fulfill() : XCTFail() }.cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } func test__cancellable_conditional_recover__fulfilled_path() { @@ -287,7 +287,7 @@ extension CatchableTests { }.catch(policy: .allErrors) { error in error.isCancelled ? ex.fulfill() : XCTFail() }.cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } func testEnsureThen_Error() { @@ -305,7 +305,7 @@ extension CatchableTests { } p.cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } func testEnsureThen_Value() { @@ -323,7 +323,7 @@ extension CatchableTests { ex.fulfill() }.cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } func testEnsureThen_Value_NotCancelled() { @@ -339,7 +339,7 @@ extension CatchableTests { ex.fulfill() } - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } func testCancellableFinalizerHelpers() { @@ -356,7 +356,7 @@ extension CatchableTests { XCTAssertEqual(f.cancelAttempted, true) XCTAssert(f.cancelledError?.isCancelled ?? false) - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } func testCancellableRecoverFromError() { @@ -377,7 +377,7 @@ extension CatchableTests { XCTAssert(f.cancelledError == nil) XCTAssert(p.cancelledError == nil) - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) XCTAssertEqual(p.isPending, false) XCTAssertEqual(p.isResolved, true) diff --git a/Tests/Cancel/DefaultDispatchQueueTests.swift b/Tests/Cancel/DefaultDispatchQueueTests.swift index 2cc3c79ee..b94467cbd 100644 --- a/Tests/Cancel/DefaultDispatchQueueTests.swift +++ b/Tests/Cancel/DefaultDispatchQueueTests.swift @@ -35,7 +35,7 @@ class CancellableDefaultDispatchQueueTest: XCTestCase { XCTAssertTrue(Thread.isMainThread) - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testOverrodeDefaultCatchQueue() { @@ -50,7 +50,7 @@ class CancellableDefaultDispatchQueueTest: XCTestCase { XCTAssertTrue(Thread.isMainThread) - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testOverrodeDefaultAlwaysQueue() { @@ -69,6 +69,6 @@ class CancellableDefaultDispatchQueueTest: XCTestCase { XCTAssertTrue(Thread.isMainThread) - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } } diff --git a/Tests/Cancel/DispatchWrapperTests.swift b/Tests/Cancel/DispatchWrapperTests.swift index b9d3ba0c8..9dbe2e615 100644 --- a/Tests/Cancel/DispatchWrapperTests.swift +++ b/Tests/Cancel/DispatchWrapperTests.swift @@ -46,7 +46,7 @@ class DispatchWrapperTests: XCTestCase { }.catch(on: .global()) { _ in XCTFail() } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testWrappedCancellablePromiseRecoverAPI() { @@ -117,7 +117,7 @@ class DispatchWrapperTests: XCTestCase { value += 1_000_000_000 ex.fulfill() } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) XCTAssert(value == 1_111_111_111) let g: Any = Promise.value(42).cancellize().recover(on: .global()) { error in @@ -141,7 +141,7 @@ class DispatchWrapperTests: XCTestCase { XCTAssert(error == .errorOne) ex.fulfill() }.cauterize() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testWrappedCancellablePromiseEnsureAPI() { @@ -156,7 +156,7 @@ class DispatchWrapperTests: XCTestCase { value += 100 ex.fulfill() }.cauterize() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) XCTAssert(value == 111) } @@ -183,6 +183,6 @@ class DispatchWrapperTests: XCTestCase { XCTAssert(v == [72, 82]) ex.fulfill() }.cauterize() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } } diff --git a/Tests/Cancel/DispatcherTests.swift b/Tests/Cancel/DispatcherTests.swift index d79c13417..24ca7129c 100644 --- a/Tests/Cancel/DispatcherTests.swift +++ b/Tests/Cancel/DispatcherTests.swift @@ -40,7 +40,7 @@ class DispatcherTests: XCTestCase { }.catch(on: dispatcher) { _ in ex.fulfill() } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) XCTAssertEqual(self.dispatcher.dispatchCount, 2) } @@ -88,7 +88,7 @@ class DispatcherTests: XCTestCase { ex.fulfill() }.cauterize() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) PromiseKit.conf.D = oldConf } @@ -182,7 +182,7 @@ class DispatcherTests: XCTestCase { XCTFail() } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testRecoverIsCancelled() { @@ -217,7 +217,7 @@ class DispatcherTests: XCTestCase { XCTFail() } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testCatchOnly() { @@ -270,7 +270,7 @@ class DispatcherTests: XCTestCase { XCTFail() } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testCatchOnlyIsCancelled() { @@ -323,7 +323,7 @@ class DispatcherTests: XCTestCase { XCTFail() } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testRecoverOnly() { @@ -383,7 +383,7 @@ class DispatcherTests: XCTestCase { XCTFail() } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testRecoverOnlyIsCancelled() { @@ -458,7 +458,7 @@ class DispatcherTests: XCTestCase { }.catch { _ in XCTFail() } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } @available(macOS 10.10, iOS 2.0, tvOS 10.0, watchOS 2.0, *) @@ -471,7 +471,7 @@ class DispatcherTests: XCTestCase { }.catch { _ in ex.fulfill() } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } } diff --git a/Tests/Cancel/GuaranteeTests.swift b/Tests/Cancel/GuaranteeTests.swift index 7a81595ad..8761075e9 100644 --- a/Tests/Cancel/GuaranteeTests.swift +++ b/Tests/Cancel/GuaranteeTests.swift @@ -12,7 +12,7 @@ class GuaranteeTests: XCTestCase { }.catch(policy: .allErrors) { $0.isCancelled ? ex.fulfill() : XCTFail() }.cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } func testWait() { @@ -25,7 +25,7 @@ class GuaranteeTests: XCTestCase { } catch { error.isCancelled ? ex.fulfill() : XCTFail() } - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } func testThenMap() { @@ -39,7 +39,7 @@ class GuaranteeTests: XCTestCase { $0.isCancelled ? ex.fulfill() : XCTFail() }.cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } func testCancellable() { @@ -64,7 +64,7 @@ class GuaranteeTests: XCTestCase { $0.isCancelled ? ex.fulfill() : XCTFail("\($0)") } .cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } func testSetCancellable() { @@ -90,7 +90,7 @@ class GuaranteeTests: XCTestCase { $0.isCancelled ? ex.fulfill() : XCTFail("\($0)") } .cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } } diff --git a/Tests/Cancel/HangTests.swift b/Tests/Cancel/HangTests.swift index 5c5524399..30f2d027d 100644 --- a/Tests/Cancel/HangTests.swift +++ b/Tests/Cancel/HangTests.swift @@ -16,7 +16,7 @@ class HangTests: XCTestCase { } catch { error.isCancelled ? ex.fulfill() : XCTFail("Unexpected error") } - waitForExpectations(timeout: 0) + waitForExpectations(timeout: 5) } enum Error: Swift.Error { diff --git a/Tests/Cancel/PromiseTests.swift b/Tests/Cancel/PromiseTests.swift index 0195e700d..4077c5f1e 100644 --- a/Tests/Cancel/PromiseTests.swift +++ b/Tests/Cancel/PromiseTests.swift @@ -42,7 +42,7 @@ class PromiseTests: XCTestCase { $0.isCancelled ? ex.fulfill() : XCTFail("Error: \($0)") }.cancel() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } @available(macOS 10.10, iOS 2.0, tvOS 10.0, watchOS 2.0, *) @@ -57,7 +57,7 @@ class PromiseTests: XCTestCase { ex.fulfill() }.cancel() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testCustomStringConvertible() { @@ -114,7 +114,7 @@ class PromiseTests: XCTestCase { ex.fulfill() }.cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } func testWait() throws { @@ -145,7 +145,7 @@ class PromiseTests: XCTestCase { }.catch(policy: .allErrors) { $0.isCancelled ? ex.fulfill() : XCTFail("\($0)") }.cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } func testCancellable() { @@ -170,7 +170,7 @@ class PromiseTests: XCTestCase { $0.isCancelled ? ex.fulfill() : XCTFail("\($0)") }.cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } func testSetCancellable() { @@ -198,7 +198,7 @@ class PromiseTests: XCTestCase { $0.isCancelled ? ex.fulfill() : XCTFail("\($0)") }.cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } func testInitCancellable() { @@ -223,7 +223,7 @@ class PromiseTests: XCTestCase { $0.isCancelled ? ex.fulfill() : XCTFail("\($0)") }.cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } func testInitVoidCancellable() { @@ -239,7 +239,7 @@ class PromiseTests: XCTestCase { $0.isCancelled ? ex.fulfill() : XCTFail("\($0)") }.cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } func testBodyThrowsError() { @@ -259,7 +259,7 @@ class PromiseTests: XCTestCase { $0.isCancelled ? XCTFail("\($0)") : ex.fulfill() }.cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } } diff --git a/Tests/Cancel/RaceTests.swift b/Tests/Cancel/RaceTests.swift index d2ebadfbc..a2900fbe1 100644 --- a/Tests/Cancel/RaceTests.swift +++ b/Tests/Cancel/RaceTests.swift @@ -16,7 +16,7 @@ class RaceTests: XCTestCase { XCTAssert(after2.isCancelled) XCTAssert(after1.cancelAttempted) XCTAssert(after2.cancelAttempted) - waitForExpectations(timeout: 1, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func test2() { @@ -33,7 +33,7 @@ class RaceTests: XCTestCase { XCTAssert(after2.isCancelled) XCTAssert(after1.cancelAttempted) XCTAssert(after2.cancelAttempted) - waitForExpectations(timeout: 1, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func test1Array() { @@ -49,7 +49,7 @@ class RaceTests: XCTestCase { XCTAssert(p.cancelAttempted) XCTAssert(p.isCancelled) } - waitForExpectations(timeout: 1, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func test2Array() { @@ -66,7 +66,7 @@ class RaceTests: XCTestCase { XCTAssert(after2.isCancelled) XCTAssert(after1.cancelAttempted) XCTAssert(after2.cancelAttempted) - waitForExpectations(timeout: 1, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func testEmptyArray() { @@ -76,7 +76,7 @@ class RaceTests: XCTestCase { guard case PMKError.badInput = $0 else { return XCTFail() } ex.fulfill() } - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } func testReject() { @@ -86,7 +86,7 @@ class RaceTests: XCTestCase { }.catch(policy: .allErrors) { $0.isCancelled ? ex.fulfill() : XCTFail() } - waitForExpectations(timeout: 1, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func testCancelInner() { @@ -102,6 +102,6 @@ class RaceTests: XCTestCase { $0.isCancelled ? ex.fulfill() : XCTFail() } after1.cancel() - waitForExpectations(timeout: 1, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } } diff --git a/Tests/Cancel/RegressionTests.swift b/Tests/Cancel/RegressionTests.swift index baa7c58d4..22f239197 100644 --- a/Tests/Cancel/RegressionTests.swift +++ b/Tests/Cancel/RegressionTests.swift @@ -18,7 +18,7 @@ class RegressionTests: XCTestCase { XCTFail() } } - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } do { @@ -35,7 +35,7 @@ class RegressionTests: XCTestCase { XCTFail() } } - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } do { @@ -51,7 +51,7 @@ class RegressionTests: XCTestCase { } ex.fulfill() } - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } } } diff --git a/Tests/Cancel/ResolverTests.swift b/Tests/Cancel/ResolverTests.swift index fc1cc02f0..ed279beec 100644 --- a/Tests/Cancel/ResolverTests.swift +++ b/Tests/Cancel/ResolverTests.swift @@ -98,7 +98,7 @@ class WrapTests: XCTestCase { }.catch(policy: .allErrors) { $0.isCancelled ? ex.fulfill() : XCTFail() }.cancel() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testError() { @@ -111,7 +111,7 @@ class WrapTests: XCTestCase { $0.isCancelled ? ex.fulfill() : XCTFail() }.cancel() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testErrorNoDelay() { @@ -126,7 +126,7 @@ class WrapTests: XCTestCase { } }.cancel() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testErrorCancellableKitten() { @@ -139,7 +139,7 @@ class WrapTests: XCTestCase { error.isCancelled ? ex.fulfill() : XCTFail() }.cancel() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testInvalidCallingConvention() { @@ -152,7 +152,7 @@ class WrapTests: XCTestCase { error.isCancelled ? ex.fulfill() : XCTFail() }.cancel() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testInvalidCallingConventionCancellableKitten() { @@ -165,7 +165,7 @@ class WrapTests: XCTestCase { error.isCancelled ? ex.fulfill() : XCTFail() }.cancel() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testInvertedCallingConvention() { @@ -180,7 +180,7 @@ class WrapTests: XCTestCase { error.isCancelled ? ex.fulfill() : XCTFail() }.cancel() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testInvertedCallingConventionCancellableKitten() { @@ -195,7 +195,7 @@ class WrapTests: XCTestCase { error.isCancelled ? ex.fulfill() : XCTFail() }.cancel() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testNonOptionalFirstParameter() { @@ -218,7 +218,7 @@ class WrapTests: XCTestCase { error.isCancelled ? ex2.fulfill() : XCTFail() }.cancel() - wait(for: [ex1, ex2] ,timeout: 1) + wait(for: [ex1, ex2] ,timeout: 5) } func testNonOptionalFirstParameterCancellableKitten() { @@ -241,7 +241,7 @@ class WrapTests: XCTestCase { error.isCancelled ? ex2.fulfill() : XCTFail() }.cancel() - wait(for: [ex1, ex2] ,timeout: 1) + wait(for: [ex1, ex2] ,timeout: 5) } func testVoidCompletionValue() { @@ -261,7 +261,7 @@ class WrapTests: XCTestCase { error.isCancelled ? ex2.fulfill() : XCTFail() }.cancel() - wait(for: [ex1, ex2], timeout: 1) + wait(for: [ex1, ex2], timeout: 5) } func testVoidCompletionValueCancellableKitten() { @@ -281,7 +281,7 @@ class WrapTests: XCTestCase { error.isCancelled ? ex2.fulfill() : XCTFail() }.cancel() - wait(for: [ex1, ex2], timeout: 1) + wait(for: [ex1, ex2], timeout: 5) } func testIsFulfilled() { @@ -329,7 +329,7 @@ class WrapTests: XCTestCase { let foo = Foo() foo.ex = ex } - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } } diff --git a/Tests/Cancel/StressTests.swift b/Tests/Cancel/StressTests.swift index c34a7cb8f..dcffd80d0 100644 --- a/Tests/Cancel/StressTests.swift +++ b/Tests/Cancel/StressTests.swift @@ -25,7 +25,7 @@ class StressTests: XCTestCase { }.cancel() }, fulfill: { "ok" }) - waitForExpectations(timeout: 10, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } @available(macOS 10.10, iOS 2.0, tvOS 10.0, watchOS 2.0, *) @@ -48,7 +48,7 @@ class StressTests: XCTestCase { $0.isCancelled ? ex.fulfill() : XCTFail() }.cancel() - waitForExpectations(timeout: 10, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func testZalgoDataRace() { @@ -73,7 +73,7 @@ class StressTests: XCTestCase { return "ok" }) - waitForExpectations(timeout: 10, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } class StressTask: Cancellable { @@ -98,7 +98,7 @@ class StressTests: XCTestCase { context.cancel() }) - waitForExpectations(timeout: 10, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func testCancelContextConcurrentAppend() { @@ -113,7 +113,7 @@ class StressTests: XCTestCase { context.append(cancellable: task, reject: nil, thenable: promise) }) - waitForExpectations(timeout: 10, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func testCancelContextConcurrentCancel() { @@ -128,7 +128,7 @@ class StressTests: XCTestCase { context.cancel() }) - waitForExpectations(timeout: 10, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } } diff --git a/Tests/Cancel/ThenableTests.swift b/Tests/Cancel/ThenableTests.swift index 2ea983007..4f6e1124e 100644 --- a/Tests/Cancel/ThenableTests.swift +++ b/Tests/Cancel/ThenableTests.swift @@ -11,7 +11,7 @@ class ThenableTests: XCTestCase { }.catch(policy: .allErrors) { $0.isCancelled ? ex1.fulfill() : XCTFail() }.cancel() - wait(for: [ex1], timeout: 1) + wait(for: [ex1], timeout: 5) } func testCompactMap() { @@ -23,7 +23,7 @@ class ThenableTests: XCTestCase { }.catch(policy: .allErrors) { $0.isCancelled ? ex.fulfill() : XCTFail() }.cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } func testCompactMapThrows() { @@ -41,7 +41,7 @@ class ThenableTests: XCTestCase { } ex.fulfill() } - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } func testRejectedPromiseCompactMap() { @@ -57,7 +57,7 @@ class ThenableTests: XCTestCase { } ex.fulfill() }.cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } func testPMKErrorCompactMap() { @@ -67,7 +67,7 @@ class ThenableTests: XCTestCase { }.catch(policy: .allErrors) { $0.isCancelled ? ex.fulfill() : XCTFail() }.cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } func testCompactMapValues() { @@ -82,7 +82,7 @@ class ThenableTests: XCTestCase { }.catch(policy: .allErrors) { _ in XCTFail() } - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } func testThenMap() { @@ -96,7 +96,7 @@ class ThenableTests: XCTestCase { }.catch(policy: .allErrors) { $0.isCancelled ? ex.fulfill() : XCTFail() } - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } func testThenFlatMap() { @@ -111,7 +111,7 @@ class ThenableTests: XCTestCase { }.catch(policy: .allErrors) { $0.isCancelled ? ex.fulfill() : XCTFail() }.cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } func testLastValueForEmpty() { @@ -133,7 +133,7 @@ class ThenableTests: XCTestCase { }.catch(policy: .allErrors) { $0.isCancelled ? XCTFail() : ex.fulfill() }.cancel() - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } func testBarrier() { @@ -146,7 +146,7 @@ class ThenableTests: XCTestCase { }.catch { _ in XCTFail() } - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } func testDispatchFlagsSyntax() { @@ -159,6 +159,6 @@ class ThenableTests: XCTestCase { }.catch { _ in XCTFail() } - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } } diff --git a/Tests/Cancel/TimeoutTests.swift b/Tests/Cancel/TimeoutTests.swift index a47f4d01d..d456a975a 100644 --- a/Tests/Cancel/TimeoutTests.swift +++ b/Tests/Cancel/TimeoutTests.swift @@ -17,7 +17,7 @@ class TimeoutTests: XCTestCase { XCTFail() } } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testReset() { @@ -35,7 +35,7 @@ class TimeoutTests: XCTestCase { XCTFail() } } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) XCTAssert(p.isCancelled) } @@ -53,7 +53,7 @@ class TimeoutTests: XCTestCase { XCTFail() } } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) XCTAssert(p.isCancelled) } @@ -65,7 +65,7 @@ class TimeoutTests: XCTestCase { }.catch(policy: .allErrors) { _ in XCTFail() } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testCancelBeforeTimeout() { @@ -84,7 +84,7 @@ class TimeoutTests: XCTestCase { } } p.cancel() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testCancelRaceBeforeTimeout() { @@ -102,7 +102,7 @@ class TimeoutTests: XCTestCase { } }.cancelContext ctxt.cancel() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testMixTypes() { @@ -120,6 +120,6 @@ class TimeoutTests: XCTestCase { }.catch(policy: .allErrors) { _ in XCTFail() } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } } diff --git a/Tests/Cancel/ValueTests.swift b/Tests/Cancel/ValueTests.swift index 8692a1a58..1fbb2739b 100644 --- a/Tests/Cancel/ValueTests.swift +++ b/Tests/Cancel/ValueTests.swift @@ -10,7 +10,7 @@ class ValueTests: XCTestCase { error.isCancelled ? exComplete.fulfill() : XCTFail("error: \(error)") }.cancel() - wait(for: [exComplete], timeout: 1) + wait(for: [exComplete], timeout: 5) } func testValueDone() { @@ -21,7 +21,7 @@ class ValueTests: XCTestCase { error.isCancelled ? exComplete.fulfill() : XCTFail("error: \(error)") }.cancel() - wait(for: [exComplete], timeout: 1) + wait(for: [exComplete], timeout: 5) } func testValueThen() { @@ -36,7 +36,7 @@ class ValueTests: XCTestCase { error.isCancelled ? exComplete.fulfill() : XCTFail("error: \(error)") }.cancel() - wait(for: [exComplete], timeout: 1) + wait(for: [exComplete], timeout: 5) } func testFirstlyValueDone() { @@ -50,7 +50,7 @@ class ValueTests: XCTestCase { error.isCancelled ? exComplete.fulfill() : XCTFail("error: \(error)") }.cancel() - wait(for: [exComplete], timeout: 1) + wait(for: [exComplete], timeout: 5) } func testFirstlyThenValueDone() { @@ -67,7 +67,7 @@ class ValueTests: XCTestCase { error.isCancelled ? exComplete.fulfill() : XCTFail("error: \(error)") }.cancel() - wait(for: [exComplete], timeout: 1) + wait(for: [exComplete], timeout: 5) } func testFirstlyValueDifferentContextDone() { @@ -82,7 +82,7 @@ class ValueTests: XCTestCase { } p.cancel() - wait(for: [exComplete], timeout: 1) + wait(for: [exComplete], timeout: 5) } func testFirstlyValueDoneDifferentContext() { @@ -96,7 +96,7 @@ class ValueTests: XCTestCase { error.isCancelled ? exComplete.fulfill() : XCTFail("error: \(error)") }.cancel() - wait(for: [exComplete], timeout: 1) + wait(for: [exComplete], timeout: 5) } func testCancelForPromise_Then() { @@ -115,7 +115,7 @@ class ValueTests: XCTestCase { error.isCancelled ? exComplete.fulfill() : XCTFail("error: \(error)") }.cancel() - wait(for: [exComplete], timeout: 1) + wait(for: [exComplete], timeout: 5) } func testCancelForPromise_ThenDone() { @@ -134,6 +134,6 @@ class ValueTests: XCTestCase { error.isCancelled ? exComplete.fulfill() : XCTFail("error: \(error)") }.cancel() - wait(for: [exComplete], timeout: 1) + wait(for: [exComplete], timeout: 5) } } diff --git a/Tests/Cancel/WhenConcurrentTests.swift b/Tests/Cancel/WhenConcurrentTests.swift index d55a9b941..30a074065 100644 --- a/Tests/Cancel/WhenConcurrentTests.swift +++ b/Tests/Cancel/WhenConcurrentTests.swift @@ -25,7 +25,7 @@ class WhenConcurrentTestCase_Swift: XCTestCase { } }.silenceWarning() - waitForExpectations(timeout: 3, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func testWhenCancel() { @@ -54,7 +54,7 @@ class WhenConcurrentTestCase_Swift: XCTestCase { $0.isCancelled ? e.fulfill() : XCTFail() }.cancel() - waitForExpectations(timeout: 3, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func testWhenEmptyGeneratorSucceed() { @@ -70,7 +70,7 @@ class WhenConcurrentTestCase_Swift: XCTestCase { } }.silenceWarning() - waitForExpectations(timeout: 1, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func testWhenEmptyGeneratorCancel() { @@ -88,7 +88,7 @@ class WhenConcurrentTestCase_Swift: XCTestCase { $0.isCancelled ? e.fulfill() : XCTFail() }.cancel() - waitForExpectations(timeout: 1, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func testWhenGeneratorErrorSucceed() { @@ -128,7 +128,7 @@ class WhenConcurrentTestCase_Swift: XCTestCase { e.fulfill() } - waitForExpectations(timeout: 3, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func testWhenGeneratorErrorCancel() { @@ -162,7 +162,7 @@ class WhenConcurrentTestCase_Swift: XCTestCase { error.isCancelled ? e.fulfill() : XCTFail() }.cancel() - waitForExpectations(timeout: 3, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func testWhenConcurrencySucceed() { @@ -193,7 +193,7 @@ class WhenConcurrentTestCase_Swift: XCTestCase { e.fulfill() }.silenceWarning() - waitForExpectations(timeout: 3) + waitForExpectations(timeout: 5) } func testWhenConcurrencyCancel() { @@ -227,7 +227,7 @@ class WhenConcurrentTestCase_Swift: XCTestCase { $0.isCancelled ? e.fulfill() : XCTFail() }.cancel() - waitForExpectations(timeout: 3) + waitForExpectations(timeout: 5) } func testWhenConcurrencyLessThanZero() { @@ -270,6 +270,6 @@ class WhenConcurrentTestCase_Swift: XCTestCase { $0.isCancelled ? XCTFail() : ex.fulfill() }.cancel() - waitForExpectations(timeout: 3) + waitForExpectations(timeout: 5) } } diff --git a/Tests/Cancel/WhenResolvedTests.swift b/Tests/Cancel/WhenResolvedTests.swift index c3859777f..6be9f6295 100644 --- a/Tests/Cancel/WhenResolvedTests.swift +++ b/Tests/Cancel/WhenResolvedTests.swift @@ -59,7 +59,7 @@ class JoinTests: XCTestCase { seal2.fulfill(()) seal3.fulfill(()) - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) XCTAssert(cancelled, "Cancel error caught") XCTAssert(promise1.isCancelled, "Promise 1 cancelled") diff --git a/Tests/Cancel/WhenTests.swift b/Tests/Cancel/WhenTests.swift index ae067bce8..ecf512e4c 100644 --- a/Tests/Cancel/WhenTests.swift +++ b/Tests/Cancel/WhenTests.swift @@ -21,7 +21,7 @@ class WhenTests: XCTestCase { $0.isCancelled ? e2.fulfill() : XCTFail() }.cancel() - wait(for: [e1, e2], timeout: 1) + wait(for: [e1, e2], timeout: 5) } func testInt() { @@ -36,7 +36,7 @@ class WhenTests: XCTestCase { }.catch(policy: .allErrors) { $0.isCancelled ? e1.fulfill() : XCTFail() }.cancel() - waitForExpectations(timeout: 1, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func testIntAlt() { @@ -51,7 +51,7 @@ class WhenTests: XCTestCase { }.catch(policy: .allErrors) { $0.isCancelled ? e1.fulfill() : XCTFail() }.cancel() - waitForExpectations(timeout: 1, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func testDoubleTupleSucceed() { @@ -63,7 +63,7 @@ class WhenTests: XCTestCase { XCTAssertEqual(y, "abc") e1.fulfill() }.silenceWarning() - waitForExpectations(timeout: 1, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func testDoubleTupleCancel() { @@ -75,7 +75,7 @@ class WhenTests: XCTestCase { }.catch(policy: .allErrors) { $0.isCancelled ? e1.fulfill() : XCTFail() }.cancel() - waitForExpectations(timeout: 1, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func testTripleTuple() { @@ -88,7 +88,7 @@ class WhenTests: XCTestCase { }.catch(policy: .allErrors) { $0.isCancelled ? e1.fulfill() : XCTFail() }.cancel() - waitForExpectations(timeout: 1, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func testQuadrupleTuple() { @@ -102,7 +102,7 @@ class WhenTests: XCTestCase { }.catch(policy: .allErrors) { $0.isCancelled ? e1.fulfill() : XCTFail() }.cancel() - waitForExpectations(timeout: 1, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func testQuintupleTuple() { @@ -117,7 +117,7 @@ class WhenTests: XCTestCase { }.catch(policy: .allErrors) { $0.isCancelled ? e1.fulfill() : XCTFail() }.cancel() - waitForExpectations(timeout: 1, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func testVoid() { @@ -133,7 +133,7 @@ class WhenTests: XCTestCase { $0.isCancelled ? e1.fulfill() : XCTFail() }.cancel() - waitForExpectations(timeout: 1, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func testRejected() { @@ -148,7 +148,7 @@ class WhenTests: XCTestCase { $0.isCancelled ? e1.fulfill() : XCTFail() }.cancel() - waitForExpectations(timeout: 1, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func testProgress() { @@ -173,7 +173,7 @@ class WhenTests: XCTestCase { progress.resignCurrent() - waitForExpectations(timeout: 1, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func testProgressDoesNotExceed100PercentSucceed() { @@ -214,7 +214,7 @@ class WhenTests: XCTestCase { p3.done(on: q, finally).silenceWarning() p4.done(on: q, finally).silenceWarning() - waitForExpectations(timeout: 1, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func testProgressDoesNotExceed100PercentCancel() { @@ -267,7 +267,7 @@ class WhenTests: XCTestCase { p3.done(on: q, finally).catch(policy: .allErrors, catchall) p4.done(on: q, finally).catch(policy: .allErrors, catchall) - waitForExpectations(timeout: 1, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func testUnhandledErrorHandlerDoesNotFire() { @@ -282,7 +282,7 @@ class WhenTests: XCTestCase { $0.isCancelled ? XCTFail() : ex.fulfill() }.cancel() - waitForExpectations(timeout: 1, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func testUnhandledErrorHandlerDoesNotFireForStragglers() { @@ -306,7 +306,7 @@ class WhenTests: XCTestCase { p2.ensure { after(.milliseconds(100)).done(ex2.fulfill) }.silenceWarning() p3.ensure { after(.milliseconds(100)).done(ex3.fulfill) }.silenceWarning() - waitForExpectations(timeout: 1, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func testAllSealedRejectedFirstOneRejects() { @@ -325,7 +325,7 @@ class WhenTests: XCTestCase { $0.isCancelled ? XCTFail() : ex.fulfill() }.cancel() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testGuaranteeWhen() { @@ -343,6 +343,6 @@ class WhenTests: XCTestCase { $0.isCancelled ? ex2.fulfill() : XCTFail() }.cancel() - wait(for: [ex1, ex2], timeout: 10) + wait(for: [ex1, ex2], timeout: 5) } } diff --git a/Tests/Cancel/ZalgoTests.swift b/Tests/Cancel/ZalgoTests.swift index 11fe3ca9b..4aad62113 100644 --- a/Tests/Cancel/ZalgoTests.swift +++ b/Tests/Cancel/ZalgoTests.swift @@ -33,7 +33,7 @@ class ZalgoTests: XCTestCase { XCTAssertEqual(x, 0) seal.fulfill(1) - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) XCTAssertEqual(x, 2) } @@ -53,7 +53,7 @@ class ZalgoTests: XCTestCase { guard case PMKError.returnedSelf = err else { return XCTFail() } } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } // returning a pending promise from its own zalgo’d then handler doesn’t hang @@ -71,7 +71,7 @@ class ZalgoTests: XCTestCase { $0.isCancelled ? ex.fulfill() : XCTFail() } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } // return a sealed promise from its own zalgo’d then handler doesn’t hang @@ -84,6 +84,6 @@ class ZalgoTests: XCTestCase { }.catch(policy: .allErrors) { $0.isCancelled ? ex.fulfill() : XCTFail() }.cancel() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } } diff --git a/Tests/Core/AfterTests.swift b/Tests/Core/AfterTests.swift index 5d016570b..b44a40f59 100644 --- a/Tests/Core/AfterTests.swift +++ b/Tests/Core/AfterTests.swift @@ -5,30 +5,30 @@ class AfterTests: XCTestCase { func testZero() { let ex2 = expectation(description: "") after(seconds: 0).done(ex2.fulfill) - waitForExpectations(timeout: 2, handler: nil) + waitForExpectations(timeout: 5, handler: nil) let ex3 = expectation(description: "") after(.seconds(0)).done(ex3.fulfill) - waitForExpectations(timeout: 2, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func testNegative() { let ex2 = expectation(description: "") after(seconds: -1).done(ex2.fulfill) - waitForExpectations(timeout: 2, handler: nil) + waitForExpectations(timeout: 5, handler: nil) let ex3 = expectation(description: "") after(.seconds(-1)).done(ex3.fulfill) - waitForExpectations(timeout: 2, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func testPositive() { let ex2 = expectation(description: "") after(seconds: 1).done(ex2.fulfill) - waitForExpectations(timeout: 2, handler: nil) + waitForExpectations(timeout: 5, handler: nil) let ex3 = expectation(description: "") after(.seconds(1)).done(ex3.fulfill) - waitForExpectations(timeout: 2, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } } diff --git a/Tests/Core/CancellableErrorTests.swift b/Tests/Core/CancellableErrorTests.swift index 5fdae4d28..1e44c98ed 100644 --- a/Tests/Core/CancellableErrorTests.swift +++ b/Tests/Core/CancellableErrorTests.swift @@ -38,7 +38,7 @@ class CancellationTests: XCTestCase { expct.fulfill() } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testRecoverWithCancellation() { @@ -62,7 +62,7 @@ class CancellationTests: XCTestCase { ex2.fulfill() } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testFoundationBridging1() { @@ -79,7 +79,7 @@ class CancellationTests: XCTestCase { ex.fulfill() } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testFoundationBridging2() { @@ -96,7 +96,7 @@ class CancellationTests: XCTestCase { ex.fulfill() } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } @available(watchOS 6.2, *) diff --git a/Tests/Core/CatchableTests.swift b/Tests/Core/CatchableTests.swift index a96f2680e..26a75896f 100644 --- a/Tests/Core/CatchableTests.swift +++ b/Tests/Core/CatchableTests.swift @@ -24,7 +24,7 @@ class CatchableTests: XCTestCase { x += 1 ex.1.fulfill() } - wait(for: [ex.0, ex.1], timeout: 10) + wait(for: [ex.0, ex.1], timeout: 5) } helper(error: Error.dummy) @@ -44,7 +44,7 @@ class CatchableTests: XCTestCase { p.catch { _ in ex.fulfill() } - wait(for: [ex], timeout: 1) + wait(for: [ex], timeout: 5) } } @@ -56,7 +56,7 @@ extension CatchableTests { func helper(error: Swift.Error) { let ex = expectation(description: "") Promise(error: error).recover { _ in }.done(ex.fulfill) - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } helper(error: Error.dummy) @@ -66,7 +66,7 @@ extension CatchableTests { func test__void_specialized_full_recover__fulfilled_path() { let ex = expectation(description: "") Promise().recover { _ in XCTFail() }.done(ex.fulfill) - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } func test__void_specialized_conditional_recover() { @@ -77,7 +77,7 @@ extension CatchableTests { guard x < 1 else { throw err } x += 1 }.done(ex.fulfill).silenceWarning() - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } for error in [Error.dummy as Swift.Error, Error.cancelled] { @@ -96,7 +96,7 @@ extension CatchableTests { XCTAssertEqual(error, $0 as? Error) ex.fulfill() } - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } for error in [Error.dummy, Error.cancelled] { @@ -113,7 +113,7 @@ extension CatchableTests { XCTAssertEqual(Error.cancelled, $0 as? Error) ex.fulfill() } - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } func test__void_specialized_conditional_recover__fulfilled_path() { @@ -125,7 +125,7 @@ extension CatchableTests { }.finally { ex.fulfill() } - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } } @@ -140,7 +140,7 @@ extension CatchableTests { XCTAssertEqual($0, 2) ex.fulfill() } - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } helper(error: Error.dummy) @@ -153,7 +153,7 @@ extension CatchableTests { XCTAssertEqual($0, 1) ex.fulfill() } - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } @@ -169,7 +169,7 @@ extension CatchableTests { XCTAssertEqual($0, x) ex.fulfill() }.silenceWarning() - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } for error in [Error.dummy as Swift.Error, Error.cancelled] { @@ -188,7 +188,7 @@ extension CatchableTests { XCTAssertEqual(error, $0 as? Error) ex.fulfill() } - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } for error in [Error.dummy, Error.cancelled] { @@ -206,7 +206,7 @@ extension CatchableTests { XCTAssertEqual(Error.cancelled, $0 as? Error) ex.fulfill() } - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } func test__conditional_recover__fulfilled_path() { @@ -220,7 +220,7 @@ extension CatchableTests { }.catch { _ in XCTFail() // this `catch` to ensure we are calling the `recover` variant we think we are } - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } func testEnsureThen_Error() { @@ -237,7 +237,7 @@ extension CatchableTests { ex.fulfill() } - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } func testEnsureThen_Value() { @@ -253,7 +253,7 @@ extension CatchableTests { ex.fulfill() } - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } } diff --git a/Tests/Core/DefaultDispatchQueueTests.swift b/Tests/Core/DefaultDispatchQueueTests.swift index f9908e26a..cb0cd132a 100644 --- a/Tests/Core/DefaultDispatchQueueTests.swift +++ b/Tests/Core/DefaultDispatchQueueTests.swift @@ -39,7 +39,7 @@ class PMKDefaultDispatchQueueTest: XCTestCase { XCTAssertTrue(Thread.isMainThread) - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testOverrodeDefaultCatchQueue() { @@ -52,7 +52,7 @@ class PMKDefaultDispatchQueueTest: XCTestCase { XCTAssertTrue(Thread.isMainThread) - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testOverrodeDefaultAlwaysQueue() { @@ -65,6 +65,6 @@ class PMKDefaultDispatchQueueTest: XCTestCase { XCTAssertTrue(Thread.isMainThread) - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } } diff --git a/Tests/Core/DispatchWrapperTests.swift b/Tests/Core/DispatchWrapperTests.swift index 934a7707e..fe5ed627b 100644 --- a/Tests/Core/DispatchWrapperTests.swift +++ b/Tests/Core/DispatchWrapperTests.swift @@ -44,7 +44,7 @@ class DispatchWrapperTests: XCTestCase { }.catch(on: .global()) { _ in XCTFail() } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testWrappedPromiseRecoverAPI() { @@ -97,7 +97,7 @@ class DispatchWrapperTests: XCTestCase { value += 1_000_000 ex.fulfill() } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) XCTAssert(value == 1_111_111) let g: Any = Promise.value(42).recover(on: .global()) { error in @@ -121,7 +121,7 @@ class DispatchWrapperTests: XCTestCase { XCTAssert(error == .errorOne) ex.fulfill() }.cauterize() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testWrappedPromiseEnsureAPI() { @@ -136,7 +136,7 @@ class DispatchWrapperTests: XCTestCase { value += 100 ex.fulfill() }.cauterize() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) XCTAssert(value == 111) } @@ -163,7 +163,7 @@ class DispatchWrapperTests: XCTestCase { XCTAssert(v == [72, 82]) ex.fulfill() }.cauterize() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testWrappedGuaranteeAPI() { @@ -182,6 +182,6 @@ class DispatchWrapperTests: XCTestCase { XCTAssert($0 == [72, 72]) ex.fulfill() } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } } diff --git a/Tests/Core/DispatcherTests.swift b/Tests/Core/DispatcherTests.swift index a445f2093..5c9038038 100644 --- a/Tests/Core/DispatcherTests.swift +++ b/Tests/Core/DispatcherTests.swift @@ -50,7 +50,7 @@ class DispatcherTests: XCTestCase { XCTAssertEqual(self.dispatcherB.dispatchCount, 1, "return dispatcher count != 1") ex.fulfill() }.cauterize() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) let testQueue = DispatchQueue(label: "test queue") PromiseKit.conf.D.map = testQueue // Assign DispatchQueue to Dispatcher variable PromiseKit.conf.Q.return = testQueue // Assign DispatchQueue to DispatchQueue variable @@ -73,7 +73,7 @@ class DispatcherTests: XCTestCase { }.catch(on: dispatcher) { _ in ex.fulfill() } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) XCTAssertEqual(self.dispatcher.dispatchCount, 2) } @@ -198,7 +198,7 @@ class DispatcherTests: XCTestCase { XCTFail() } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } @available(macOS 10.10, iOS 2.0, tvOS 10.0, watchOS 2.0, *) @@ -213,7 +213,7 @@ class DispatcherTests: XCTestCase { XCTAssertEqual(one, 1) ex.fulfill() } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } @available(macOS 10.10, iOS 2.0, tvOS 10.0, watchOS 2.0, *) @@ -228,7 +228,7 @@ class DispatcherTests: XCTestCase { }.catch { _ in ex.fulfill() } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } } diff --git a/Tests/Core/GuaranteeTests.swift b/Tests/Core/GuaranteeTests.swift index 5ca1d1a4c..07e16160f 100644 --- a/Tests/Core/GuaranteeTests.swift +++ b/Tests/Core/GuaranteeTests.swift @@ -10,7 +10,7 @@ class GuaranteeTests: XCTestCase { XCTAssertEqual(1, $0) ex.fulfill() } - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } func testMap() { @@ -23,7 +23,7 @@ class GuaranteeTests: XCTestCase { ex.fulfill() } - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } func testMapByKeyPath() { @@ -34,7 +34,7 @@ class GuaranteeTests: XCTestCase { ex.fulfill() } - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } func testWait() { @@ -51,7 +51,7 @@ class GuaranteeTests: XCTestCase { ex.fulfill() } - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } func testMapValuesByKeyPath() { @@ -64,7 +64,7 @@ class GuaranteeTests: XCTestCase { ex.fulfill() } - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } func testFlatMapValues() { @@ -77,7 +77,7 @@ class GuaranteeTests: XCTestCase { ex.fulfill() } - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } func testCompactMapValues() { @@ -90,7 +90,7 @@ class GuaranteeTests: XCTestCase { ex.fulfill() } - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } func testCompactMapValuesByKeyPath() { @@ -103,7 +103,7 @@ class GuaranteeTests: XCTestCase { ex.fulfill() } - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } func testThenMap() { @@ -117,7 +117,7 @@ class GuaranteeTests: XCTestCase { ex.fulfill() } - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } func testThenFlatMap() { @@ -131,7 +131,7 @@ class GuaranteeTests: XCTestCase { ex.fulfill() } - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } func testFilterValues() { @@ -145,7 +145,7 @@ class GuaranteeTests: XCTestCase { ex.fulfill() } - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } func testFilterValuesByKeyPath() { @@ -159,7 +159,7 @@ class GuaranteeTests: XCTestCase { ex.fulfill() } - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } func testSorted() { @@ -173,7 +173,7 @@ class GuaranteeTests: XCTestCase { ex.fulfill() } - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } func testSortedBy() { @@ -187,7 +187,7 @@ class GuaranteeTests: XCTestCase { ex.fulfill() } - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } func testNoAmbiguityForValue() { @@ -198,6 +198,6 @@ class GuaranteeTests: XCTestCase { when(fulfilled: a, b, c).done { ex.fulfill() }.cauterize() - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } } diff --git a/Tests/Core/HangTests.swift b/Tests/Core/HangTests.swift index 84156d35b..c0950a899 100644 --- a/Tests/Core/HangTests.swift +++ b/Tests/Core/HangTests.swift @@ -13,7 +13,7 @@ class HangTests: XCTestCase { } catch { XCTFail("Unexpected error") } - waitForExpectations(timeout: 0) + waitForExpectations(timeout: 5) } enum Error: Swift.Error { diff --git a/Tests/Core/LoggingTests.swift b/Tests/Core/LoggingTests.swift index 9aa3fd3d5..22418a067 100644 --- a/Tests/Core/LoggingTests.swift +++ b/Tests/Core/LoggingTests.swift @@ -98,7 +98,7 @@ class LoggingTests: XCTestCase { }.ensure { ex.fulfill() }.cauterize() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) ex = expectation(description: "read") let readQueue = DispatchQueue(label: "readQueue") readQueue.async { @@ -114,7 +114,7 @@ class LoggingTests: XCTestCase { } } } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } // Verify waiting on main thread in Guarantee is logged diff --git a/Tests/Core/PromiseTests.swift b/Tests/Core/PromiseTests.swift index 998e74c18..4a1e985a9 100644 --- a/Tests/Core/PromiseTests.swift +++ b/Tests/Core/PromiseTests.swift @@ -41,7 +41,7 @@ class PromiseTests: XCTestCase { XCTAssertEqual(one, 1) ex.fulfill() } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } } @@ -60,7 +60,7 @@ class PromiseTests: XCTestCase { XCTAssertEqual(one, 1) ex.fulfill() } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } } @@ -79,7 +79,7 @@ class PromiseTests: XCTestCase { }.catch { _ in ex.fulfill() } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } else { XCTFail("Could not recover Promise from Any") } @@ -100,7 +100,7 @@ class PromiseTests: XCTestCase { }.catch { _ in ex.fulfill() } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } else { XCTFail("Could not recover Promise from Any") } @@ -126,7 +126,7 @@ class PromiseTests: XCTestCase { } // This is statically determined, but we want to promote it into something that fits into XCTest XCTAssert(throwingReturn is Promise, "Dispatcher.dispatch() returns something other than Promise with plain throwing closure") - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testCustomStringConvertible() { @@ -182,7 +182,7 @@ class PromiseTests: XCTestCase { ex.fulfill() } - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } func testWait() throws { @@ -204,7 +204,7 @@ class PromiseTests: XCTestCase { XCTAssertEqual(1, $0) ex.fulfill() }.silenceWarning() - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } func testNoAmbiguityForValue() { @@ -215,6 +215,6 @@ class PromiseTests: XCTestCase { when(fulfilled: a, b, c).done { ex.fulfill() }.cauterize() - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } } diff --git a/Tests/Core/RaceTests.swift b/Tests/Core/RaceTests.swift index 8993279f5..459d8f6d2 100644 --- a/Tests/Core/RaceTests.swift +++ b/Tests/Core/RaceTests.swift @@ -8,7 +8,7 @@ class RaceTests: XCTestCase { XCTAssertEqual(index, 1) ex.fulfill() }.silenceWarning() - waitForExpectations(timeout: 1, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func test2() { @@ -17,7 +17,7 @@ class RaceTests: XCTestCase { XCTAssertEqual(index, 2) ex.fulfill() } - waitForExpectations(timeout: 1, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func test1Array() { @@ -27,7 +27,7 @@ class RaceTests: XCTestCase { XCTAssertEqual(index, 1) ex.fulfill() }.silenceWarning() - waitForExpectations(timeout: 1, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func test2Array() { @@ -36,7 +36,7 @@ class RaceTests: XCTestCase { XCTAssertEqual(index, 2) ex.fulfill() } - waitForExpectations(timeout: 1, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func testEmptyArray() { @@ -46,7 +46,7 @@ class RaceTests: XCTestCase { guard case PMKError.badInput = $0 else { return XCTFail() } ex.fulfill() } - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } func testReject() { @@ -56,7 +56,7 @@ class RaceTests: XCTestCase { }.catch(policy: .allErrors) { $0.isCancelled ? ex.fulfill() : XCTFail() } - waitForExpectations(timeout: 1, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } func testFulfilled() { @@ -70,7 +70,7 @@ class RaceTests: XCTestCase { XCTFail() ex.fulfill() } - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } func testFulfilledEmptyArray() { @@ -80,7 +80,7 @@ class RaceTests: XCTestCase { guard case PMKError.badInput = $0 else { return XCTFail() } ex.fulfill() } - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } func testFulfilledWithNoWinner() { @@ -96,6 +96,6 @@ class RaceTests: XCTestCase { guard pmkError.debugDescription == "All thenables passed to race(fulfilled:) were rejected" else { return XCTFail() } ex.fulfill() } - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } } diff --git a/Tests/Core/ResolverTests.swift b/Tests/Core/ResolverTests.swift index 2cd728410..ac0413a33 100644 --- a/Tests/Core/ResolverTests.swift +++ b/Tests/Core/ResolverTests.swift @@ -56,7 +56,7 @@ class WrapTests: XCTestCase { ex.fulfill() }.silenceWarning() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testError() { @@ -72,7 +72,7 @@ class WrapTests: XCTestCase { } } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testInvalidCallingConvention() { @@ -88,7 +88,7 @@ class WrapTests: XCTestCase { } } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testInvertedCallingConvention() { @@ -101,7 +101,7 @@ class WrapTests: XCTestCase { ex.fulfill() }.silenceWarning() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } @@ -121,7 +121,7 @@ class WrapTests: XCTestCase { kf2.fetchWithCompletionBlock3(block: seal.resolve) }.catch { _ in ex2.fulfill() } - wait(for: [ex1, ex2] ,timeout: 1) + wait(for: [ex1, ex2] ,timeout: 5) } func testVoidCompletionValue() { @@ -137,7 +137,7 @@ class WrapTests: XCTestCase { kf2.fetchWithCompletionBlock4(block: seal.resolve) }.catch { _ in ex2.fulfill() } - wait(for: [ex1, ex2], timeout: 1) + wait(for: [ex1, ex2], timeout: 5) } func testSwiftResultSuccess() { @@ -150,7 +150,7 @@ class WrapTests: XCTestCase { ex.fulfill() }.silenceWarning() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testSwiftResultError() { @@ -166,7 +166,7 @@ class WrapTests: XCTestCase { } } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testIsFulfilled() { @@ -194,7 +194,7 @@ class WrapTests: XCTestCase { let foo = Foo() foo.ex = ex } - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) } func testVoidResolverFulfillAmbiguity() { @@ -212,14 +212,14 @@ class WrapTests: XCTestCase { let ex = expectation(description: "") bar().done(ex.fulfill).cauterize() - wait(for: [ex], timeout: 10) + wait(for: [ex], timeout: 5) // ^^ ambiguous in Swift 5.0 & 5.1, testing again in next version let ex2 = expectation(description: "") Guarantee { seal in after(.microseconds(10)).done(seal) }.done(ex2.fulfill) - wait(for: [ex2], timeout: 10) + wait(for: [ex2], timeout: 5) } } diff --git a/Tests/Core/StressTests.swift b/Tests/Core/StressTests.swift index 787e485d0..842a435c6 100644 --- a/Tests/Core/StressTests.swift +++ b/Tests/Core/StressTests.swift @@ -14,7 +14,7 @@ class StressTests: XCTestCase { }.silenceWarning() }, fulfill: { "ok" }) - waitForExpectations(timeout: 10, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } @available(macOS 10.10, iOS 2.0, tvOS 10.0, watchOS 2.0, *) @@ -35,7 +35,7 @@ class StressTests: XCTestCase { XCTAssertEqual(values, (0..=3.2) @@ -75,7 +75,7 @@ class CLGeocoderTests: XCTestCase { XCTAssertEqual(x, [dummyPlacemark]) ex.fulfill() }.cauterize() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func test_geocodePostalAddressLocale() { @@ -94,7 +94,7 @@ class CLGeocoderTests: XCTestCase { XCTAssertEqual(x, [dummyPlacemark]) ex.fulfill() }.cauterize() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func test_reverseGeocodeLocationLocale() { @@ -113,7 +113,7 @@ class CLGeocoderTests: XCTestCase { XCTAssertEqual(x, [dummyPlacemark]) ex.fulfill() }.cauterize() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } #endif } diff --git a/Tests/PMKCoreLocation/CLLocationManagerTests.swift b/Tests/PMKCoreLocation/CLLocationManagerTests.swift index d7f1ef0c6..e589a4bc6 100644 --- a/Tests/PMKCoreLocation/CLLocationManagerTests.swift +++ b/Tests/PMKCoreLocation/CLLocationManagerTests.swift @@ -16,7 +16,7 @@ class Test_CLLocationManager_Swift: XCTestCase { ex.fulfill() }.cauterize() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } } } @@ -32,7 +32,7 @@ class Test_CLLocationManager_Swift: XCTestCase { locations.forEach { XCTAssert(block($0) == true, "Block should be successful for returned values") } ex.fulfill() }).cauterize() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } } } @@ -46,7 +46,7 @@ class Test_CLLocationManager_Swift: XCTestCase { ex.fulfill() } - waitForExpectations(timeout: 1, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } #endif } diff --git a/Tests/PMKFoundation/TestNSNotificationCenter.swift b/Tests/PMKFoundation/TestNSNotificationCenter.swift index 3851029db..a4f99b12e 100644 --- a/Tests/PMKFoundation/TestNSNotificationCenter.swift +++ b/Tests/PMKFoundation/TestNSNotificationCenter.swift @@ -15,7 +15,7 @@ class NSNotificationCenterTests: XCTestCase { NotificationCenter.default.post(name: PMKTestNotification, object: nil, userInfo: userInfo) - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } } diff --git a/Tests/PMKFoundation/TestNSObject.swift b/Tests/PMKFoundation/TestNSObject.swift index cbd641a91..c72683420 100644 --- a/Tests/PMKFoundation/TestNSObject.swift +++ b/Tests/PMKFoundation/TestNSObject.swift @@ -18,7 +18,7 @@ class NSObjectTests: XCTestCase { } foo.bar = "moo" - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testAfterlife() { @@ -42,7 +42,7 @@ class NSObjectTests: XCTestCase { } } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func testMultiObserveAfterlife() { @@ -69,7 +69,7 @@ class NSObjectTests: XCTestCase { } } - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } } diff --git a/Tests/PMKFoundation/TestNSTask.swift b/Tests/PMKFoundation/TestNSTask.swift index 264612c29..65a47b523 100644 --- a/Tests/PMKFoundation/TestNSTask.swift +++ b/Tests/PMKFoundation/TestNSTask.swift @@ -16,7 +16,7 @@ class NSTaskTests: XCTestCase { XCTAssertEqual(stdout, "bar\n") ex.fulfill() }.cauterize() - waitForExpectations(timeout: 10) + waitForExpectations(timeout: 5) } func test2() { @@ -43,7 +43,7 @@ class NSTaskTests: XCTestCase { } ex.fulfill() } - waitForExpectations(timeout: 10) + waitForExpectations(timeout: 5) } } diff --git a/Tests/PMKFoundation/TestNSURLSession.swift b/Tests/PMKFoundation/TestNSURLSession.swift index c9f45debf..b5a2156cf 100644 --- a/Tests/PMKFoundation/TestNSURLSession.swift +++ b/Tests/PMKFoundation/TestNSURLSession.swift @@ -24,7 +24,7 @@ class NSURLSessionTests: XCTestCase { XCTAssertEqual(json, rsp) ex.fulfill() }.cauterize() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } func test2() { @@ -48,7 +48,7 @@ class NSURLSessionTests: XCTestCase { ex.fulfill() }.cauterize() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } /// test that our convenience String constructor applies @@ -70,7 +70,7 @@ class NSURLSessionTests: XCTestCase { ex.fulfill() }.cauterize() - waitForExpectations(timeout: 1) + waitForExpectations(timeout: 5) } override func tearDown() { diff --git a/Tests/PMKHomeKit/HMAccessoryBrowserTests.swift b/Tests/PMKHomeKit/HMAccessoryBrowserTests.swift index 66e7302ec..0ddfe55b5 100644 --- a/Tests/PMKHomeKit/HMAccessoryBrowserTests.swift +++ b/Tests/PMKHomeKit/HMAccessoryBrowserTests.swift @@ -25,7 +25,7 @@ class HMAccessoryBrowserTests: XCTestCase { ex.fulfill() }.cauterize() - waitForExpectations(timeout: 1, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } } @@ -38,7 +38,7 @@ class HMAccessoryBrowserTests: XCTestCase { ex.fulfill() } - waitForExpectations(timeout: 1, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } } diff --git a/Tests/PMKHomeKit/UtilsTests.swift b/Tests/PMKHomeKit/UtilsTests.swift index b0a542a4c..f334e2316 100644 --- a/Tests/PMKHomeKit/UtilsTests.swift +++ b/Tests/PMKHomeKit/UtilsTests.swift @@ -36,7 +36,7 @@ class UtilsTests: XCTestCase { XCTAssertNotNil(weakVar) exp.fulfill() } - waitForExpectations(timeout: 1.0, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } // Once resolved, the proxy should break the retain cycle @@ -53,7 +53,7 @@ class UtilsTests: XCTestCase { XCTAssertNil(weakVar) exp.fulfill() } - waitForExpectations(timeout: 1.0, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } // Cancel should reject with a PMKError diff --git a/Tests/PMKMapKit/TestMapKit.swift b/Tests/PMKMapKit/TestMapKit.swift index 70286b155..581799564 100644 --- a/Tests/PMKMapKit/TestMapKit.swift +++ b/Tests/PMKMapKit/TestMapKit.swift @@ -22,7 +22,7 @@ class Test_MKDirections_Swift: XCTestCase { ex.fulfill() }.cauterize() - waitForExpectations(timeout: 1, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } @@ -40,7 +40,7 @@ class Test_MKDirections_Swift: XCTestCase { ex.fulfill() }.cauterize() - waitForExpectations(timeout: 1, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } } @@ -60,7 +60,7 @@ class Test_MKSnapshotter_Swift: XCTestCase { ex.fulfill() }.cauterize() - waitForExpectations(timeout: 1, handler: nil) + waitForExpectations(timeout: 5, handler: nil) } } From d79bba7b046d35bdbbc6525acc5b52c5950b5cbf Mon Sep 17 00:00:00 2001 From: Max Howell Date: Wed, 2 Jun 2021 10:57:23 -0400 Subject: [PATCH 68/81] [ci][cd] Updates for v7 --- .github/workflows/ci.yml | 8 +++--- .github/workflows/publish.yml | 48 ++++++++++++----------------------- Documents/Cancel.md | 21 +++++++++------ 3 files changed, 32 insertions(+), 45 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c26e6ca23..15ad37938 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,7 +5,6 @@ on: - Sources/** - Tests/** - .github/workflows/ci.yml - workflow_dispatch: jobs: auto-cancel: runs-on: ubuntu-latest @@ -14,12 +13,12 @@ jobs: linux: name: linux - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest strategy: matrix: swift: - 5.3 - - 5.4 + - 5.4-focal # Swift 5.4 requires llvm-cov v11 container: image: swift:${{ matrix.swift }} steps: @@ -27,10 +26,9 @@ jobs: - run: swift test --enable-code-coverage --parallel --enable-test-discovery - name: Generate Coverage Report - if: ${{ matrix.swift == '5.3' }} # 5.4 and above needs llvm-cov-12 run: | apt-get -qq update - apt-get -qq install curl + apt-get -qq install curl # for codecov action 🙄 b=$(swift build --show-bin-path) llvm-cov export \ -format lcov \ diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index e4ea2c75e..1d6397ccd 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -3,56 +3,41 @@ on: workflow_dispatch: inputs: version: - description: Version to publish + description: Version 🚀 required: true jobs: - ci: + build: runs-on: ubuntu-latest + strategy: + matrix: + swift: + - 5.3 + - 5.4 + container: + image: swift:${{ matrix.swift }} steps: - - uses: aurelien-baudet/workflow-dispatch@v2 - with: - workflow: CI - token: ${{ secrets.JAZZY_PAT }} - wait-for-completion: true + - uses: actions/checkout@v2 + - run: swift build lint: - runs-on: macos-10.15 + runs-on: macos-latest strategy: matrix: xcode: - - ^10 - - ^11 - - ^12 + - '12.0' + - '12.4' steps: - uses: maxim-lobanov/setup-xcode@v1 with: xcode-version: ${{ matrix.xcode }} - uses: actions/checkout@v2 - with: - submodules: true - run: pod lib lint --fail-fast create-release: runs-on: ubuntu-latest - needs: [ci, lint] - env: - v: ${{ github.event.inputs.version }} + needs: [lint, build] steps: - uses: actions/checkout@v2 - with: - fetch-depth: 0 # zero means “all” (or push fails) - - name: Update committed versions - run: | - ruby -i -pe "sub(/CURRENT_PROJECT_VERSION = [0-9.]+/, 'CURRENT_PROJECT_VERSION = $v')" PromiseKit.xcodeproj/project.pbxproj - ruby -i -pe "sub(/s.version = '[0-9.]+'/, 's.version = \'$v\'')" PromiseKit.podspec - - run: | - ! (git diff --quiet) - - name: Commit - run: | - git config user.name github-actions - git config user.email github-actions@github.com - git commit -am "PromiseKit $v" - git push - uses: softprops/action-gh-release@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -69,5 +54,4 @@ jobs: workflow: CD token: ${{ secrets.JAZZY_PAT }} inputs: "{\"version\": \"${{ github.event.inputs.version }}\"}" - ref: master # or doesn’t use our new commit above - wait-for-completion: true + wait-for-completion: false diff --git a/Documents/Cancel.md b/Documents/Cancel.md index 2811019cc..3ee4943cb 100644 --- a/Documents/Cancel.md +++ b/Documents/Cancel.md @@ -5,7 +5,9 @@ PromiseKit 7 adds clear and concise cancellation abilities to promises and to th ```swift UIApplication.shared.isNetworkActivityIndicatorVisible = true -let fetchImage = URLSession.shared.dataTask(.promise, with: url).cancellize().compactMap{ UIImage(data: $0.data) } +let fetchImage = URLSession.shared.dataTask(.promise, with: url) + .cancellize() + .compactMap{ UIImage(data: $0.data) } let fetchLocation = CLLocationManager.requestLocation().cancellize().lastValue let finalizer = firstly { @@ -16,20 +18,23 @@ let finalizer = firstly { }.ensure { UIApplication.shared.isNetworkActivityIndicatorVisible = false }.catch(policy: .allErrors) { error in - /* 'catch' will be invoked with 'PMKError.cancelled' when cancel is called on the context. - Use the default policy of '.allErrorsExceptCancellation' to ignore cancellation errors. */ + // `catch` will be invoked with `PMKError.cancelled` when cancel is called + // on the context. Use the default policy of `.allErrorsExceptCancellation` + // to ignore cancellation errors. self.show(UIAlertController(for: error), sender: self) } //… -// Cancel currently active tasks and reject all cancellable promises with 'PMKError.cancelled'. -// 'cancel()' can be called from any thread at any time. +// Cancel currently active tasks and reject all cancellable promises +// with 'PMKError.cancelled'. `cancel()` can be called from any thread +// at any time. finalizer.cancel() -/* 'finalizer' here refers to the 'CancellableFinalizer' for the chain. Calling 'cancel' on - any promise in the chain or on the finalizer cancels the entire chain. Therefore - calling 'cancel' on the finalizer cancels everything. */ +// `finalizer` here refers to the `CancellableFinalizer` for the chain. +// Calling 'cancel' on any promise in the chain or on the finalizer +// cancels the entire chain. Therefore calling `cancel` on the finalizer +// cancels everything. ``` # Cancel Chains From 9ff754c9296be5d9408f9146753e6dc11891dee0 Mon Sep 17 00:00:00 2001 From: Max Howell Date: Wed, 2 Jun 2021 12:00:41 -0400 Subject: [PATCH 69/81] [cd] More prerelease prep --- .github/jazzy.yml | 2 -- .github/workflows/cd.yml | 3 +++ .github/workflows/publish.yml | 13 +------------ .github/workflows/trigger-cd.yml | 13 +++++++++++++ Documents/Installation.md | 5 +++++ 5 files changed, 22 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/trigger-cd.yml diff --git a/.github/jazzy.yml b/.github/jazzy.yml index 027dd7fd1..01ed6c269 100644 --- a/.github/jazzy.yml +++ b/.github/jazzy.yml @@ -7,8 +7,6 @@ custom_categories: - Thenable - CatchMixin - Resolver -xcodebuild_arguments: - - UseModernBuildSystem=NO output: ../output # output directory is relative to config file… ugh diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index ee996d511..e347dacb1 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -4,8 +4,11 @@ on: inputs: version: required: true + pods: + required: false jobs: pods: + if: ${{ github.event.inputs.pods != 'false' }} runs-on: macos-latest steps: diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 1d6397ccd..21f6c6d92 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -40,18 +40,7 @@ jobs: - uses: actions/checkout@v2 - uses: softprops/action-gh-release@v1 env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.JAZZY_PAT }} with: tag_name: ${{ github.event.inputs.version }} name: ${{ github.event.inputs.version }} - - cd: - needs: create-release - runs-on: ubuntu-latest - steps: - - uses: aurelien-baudet/workflow-dispatch@v2 - with: - workflow: CD - token: ${{ secrets.JAZZY_PAT }} - inputs: "{\"version\": \"${{ github.event.inputs.version }}\"}" - wait-for-completion: false diff --git a/.github/workflows/trigger-cd.yml b/.github/workflows/trigger-cd.yml new file mode 100644 index 000000000..1702f8699 --- /dev/null +++ b/.github/workflows/trigger-cd.yml @@ -0,0 +1,13 @@ +on: + release: + types: published +jobs: + cd: + runs-on: ubuntu-latest + steps: + - uses: aurelien-baudet/workflow-dispatch@v2 + with: + workflow: CD + token: ${{ secrets.JAZZY_PAT }} + inputs: "{\"version\": \"${{ github.event.release.tag_name }}\"}" + wait-for-completion: false diff --git a/Documents/Installation.md b/Documents/Installation.md index e719c138c..8f51add0b 100644 --- a/Documents/Installation.md +++ b/Documents/Installation.md @@ -1,3 +1,5 @@ +# Installing PromiseKit + We support [SwiftPM]: ```swift @@ -16,6 +18,9 @@ package.targets.append( And CocoaPods: +> Please note, we have not released this CocoaPod yet. You *can* still use it +> but you will need to specify the podspec URL manually, see the Cocoapods docs. + ```ruby pod "PromiseKit", "~> 7.0.0-rc1" pod "PromiseKit/Foundation", "~> 7.0.0-rc1" From bcf04d698757d73a726e02ee25aa29d092591510 Mon Sep 17 00:00:00 2001 From: Max Howell Date: Wed, 2 Jun 2021 13:49:33 -0400 Subject: [PATCH 70/81] [ci] Run JS A+ tests --- .github/workflows/ci.yml | 11 +++++------ Package.swift | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 15ad37938..159e149fc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,6 +53,11 @@ jobs: with: xcode-version: ^12 - uses: actions/checkout@v2 + - run: | + cd Tests/A+/JavaScript + npm ci + npm run build + if: ${{ endsWith(matrix.dst, 'macOS') }} - uses: sersoft-gmbh/xcodebuild-action@v1 with: spm-package: ./ @@ -80,9 +85,3 @@ jobs: scheme: PromiseKit-Package destination: platform=watchOS Simulator,OS=latest,name=Apple Watch Series 5 - 40mm action: build - - lint: - runs-on: macos-latest - steps: - - uses: actions/checkout@v2 - - run: pod lib lint --fail-fast diff --git a/Package.swift b/Package.swift index 280ecc3c9..1ef5915bb 100644 --- a/Package.swift +++ b/Package.swift @@ -60,6 +60,6 @@ pkg.targets += [ .testTarget(name: "Cancel", dependencies: ["PromiseKit"]), .testTarget(name: "APlusSwiftTests", dependencies: ["PromiseKit"], path: "Tests/A+/Swift"), .testTarget(name: "APlusJSTests", dependencies: ["PromiseKit"], path: "Tests/A+/JavaScript", exclude: [ - "index.js", "package-lock.json", "package.json", "README.md", "webpack.config.js" + "index.js", "package-lock.json", "package.json", "README.md", "webpack.config.js", "build", "node_modules" ]), ] From a2f0b6bcbc582b522049d0f805d4338bdc58879f Mon Sep 17 00:00:00 2001 From: Max Howell Date: Wed, 2 Jun 2021 14:44:03 -0400 Subject: [PATCH 71/81] [ci] the JS/A+ tests time out a lot --- Tests/A+/JavaScript/AllTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/A+/JavaScript/AllTests.swift b/Tests/A+/JavaScript/AllTests.swift index c336c558e..2dcfcc844 100644 --- a/Tests/A+/JavaScript/AllTests.swift +++ b/Tests/A+/JavaScript/AllTests.swift @@ -74,7 +74,7 @@ class AllTests: XCTestCase { // Call `runTests` runTests.call(withArguments: [adapter, onFailValue, onDoneValue, testName]) - self.wait(for: [expectation], timeout: 60) + self.wait(for: [expectation], timeout: 600) } } From c065234fdc52e21318bed56d5754163b8684cdf3 Mon Sep 17 00:00:00 2001 From: Max Howell Date: Wed, 2 Jun 2021 15:53:47 -0400 Subject: [PATCH 72/81] Run JS/A+ individually to try and avoid timeouts --- Tests/A+/JavaScript/AllTests.swift | 42 ++++++++++++++++++++--------- Tests/A+/JavaScript/JSPromise.swift | 33 +++++++++++------------ 2 files changed, 45 insertions(+), 30 deletions(-) diff --git a/Tests/A+/JavaScript/AllTests.swift b/Tests/A+/JavaScript/AllTests.swift index 2dcfcc844..5396864ae 100644 --- a/Tests/A+/JavaScript/AllTests.swift +++ b/Tests/A+/JavaScript/AllTests.swift @@ -10,15 +10,28 @@ import JavaScriptCore import PromiseKit import XCTest -class AllTests: XCTestCase { - func testAll() { - let scriptPath = URL(fileURLWithPath: #file).deletingLastPathComponent().appendingPathComponent("build/build.js") +class APlusJavaScriptTests: XCTestCase { + func test_2_1_2() { runner(test: "2.1.2") } + func test_2_1_3() { runner(test: "2.1.3") } + func test_2_2_1() { runner(test: "2.2.1") } + func test_2_2_2() { runner(test: "2.2.2") } + func test_2_2_3() { runner(test: "2.2.3") } + func test_2_2_4() { runner(test: "2.2.4") } + func test_2_2_5() { runner(test: "2.2.5") } + func test_2_2_6() { runner(test: "2.2.6") } + func test_2_2_7() { runner(test: "2.2.7") } + func test_2_3_1() { runner(test: "2.3.1") } + func test_2_3_2() { runner(test: "2.3.2") } + func test_2_3_4() { runner(test: "2.3.4") } + + func runner(test testName: String, file: StaticString = #file, line: UInt = #line) { + let scriptPath = URL(fileURLWithPath: #filePath).deletingLastPathComponent().appendingPathComponent("build/build.js") guard FileManager.default.fileExists(atPath: scriptPath.path) else { return print("Skipping A+.js: see README for instructions on how to build") } guard let script = try? String(contentsOf: scriptPath) else { - return XCTFail("Couldn't read content of test suite JS file") + return XCTFail("Couldn't read content of test suite JS file", file: file, line: line) } let context = JSUtils.sharedContext @@ -26,7 +39,7 @@ class AllTests: XCTestCase { // Add a global exception handler context.exceptionHandler = { context, exception in guard let exception = exception else { - return XCTFail("Unknown JS exception") + return XCTFail("Unknown JS exception", file: file, line: line) } JSUtils.printStackTrace(exception: exception, includeExceptionDescription: true) } @@ -40,7 +53,7 @@ class AllTests: XCTestCase { // Create adapter guard let adapter = JSValue(object: NSDictionary(), in: context) else { - fatalError("Couldn't create adapter") + return XCTFail("Couldn't create adapter", file: file, line: line) } adapter.setObject(JSAdapter.resolved, forKeyedSubscript: "resolved" as NSString) adapter.setObject(JSAdapter.rejected, forKeyedSubscript: "rejected" as NSString) @@ -49,15 +62,15 @@ class AllTests: XCTestCase { // Evaluate contents of `build.js`, which exposes `runTests` in the global context context.evaluateScript(script) guard let runTests = context.objectForKeyedSubscript("runTests") else { - return XCTFail("Couldn't find `runTests` in JS context") + return XCTFail("Couldn't find `runTests` in JS context", file: file, line: line) } // Create a callback that's called whenever there's a failure let onFail: @convention(block) (JSValue, JSValue) -> Void = { test, error in guard let test = test.toString(), let error = error.toString() else { - return XCTFail("Unknown test failure") + return XCTFail("Unknown test failure", file: file, line: line) } - XCTFail("\(test) failed: \(error)") + return XCTFail("\(test) failed: \(error)", file: file, line: line) } let onFailValue: JSValue = JSValue(object: onFail, in: context) @@ -68,13 +81,16 @@ class AllTests: XCTestCase { } let onDoneValue: JSValue = JSValue(object: onDone, in: context) - // If there's a need to only run one specific test, uncomment the next line and comment the one after - // let testName: JSValue = JSValue(object: "2.3.1", in: context) - let testName = JSUtils.undefined + guard let testName = JSValue(object: testName, in: context) else { + return XCTFail(file: file, line: line) + } + + // use this to run *all* tests + //let testName = JSUtils.undefined // Call `runTests` runTests.call(withArguments: [adapter, onFailValue, onDoneValue, testName]) - self.wait(for: [expectation], timeout: 600) + wait(for: [expectation], timeout: 30) } } diff --git a/Tests/A+/JavaScript/JSPromise.swift b/Tests/A+/JavaScript/JSPromise.swift index f5f2a2f0a..ce7f352a8 100644 --- a/Tests/A+/JavaScript/JSPromise.swift +++ b/Tests/A+/JavaScript/JSPromise.swift @@ -6,7 +6,6 @@ // #if !os(Linux) && !os(watchOS) -// can disable better when we don’t need --generate-linuxmain import JavaScriptCore import PromiseKit import XCTest @@ -16,34 +15,34 @@ import XCTest } class JSPromise: NSObject, JSPromiseProtocol { - + let promise: Promise - + init(promise: Promise) { self.promise = promise } - + func then(_ onFulfilled: JSValue, _ onRejected: JSValue) -> JSPromise { - + // Keep a reference to the returned promise so we can comply to 2.3.1 var returnedPromiseRef: Promise? - + let afterFulfill = promise.then { value -> Promise in - + // 2.2.1: ignored if not a function guard JSUtils.isFunction(value: onFulfilled) else { return .value(value) } - + // Call `onFulfilled` // 2.2.5: onFulfilled/onRejected must be called as functions (with no `this` value) guard let returnValue = try JSUtils.call(function: onFulfilled, arguments: [JSUtils.undefined, value]) else { return .value(value) } - + // Extract JSPromise.promise if available, or use plain return value if let jsPromise = returnValue.toObjectOf(JSPromise.self) as? JSPromise { - + // 2.3.1: if returned value is the promise that `then` returned, throw TypeError if jsPromise.promise === returnedPromiseRef { throw JSUtils.JSError(reason: JSUtils.typeError(message: "Returned self")) @@ -53,23 +52,23 @@ class JSPromise: NSObject, JSPromiseProtocol { return .value(returnValue) } } - + let afterReject = promise.recover { error -> Promise in - + // 2.2.1: ignored if not a function guard let jsError = error as? JSUtils.JSError, JSUtils.isFunction(value: onRejected) else { throw error } - + // Call `onRejected` // 2.2.5: onFulfilled/onRejected must be called as functions (with no `this` value) guard let returnValue = try JSUtils.call(function: onRejected, arguments: [JSUtils.undefined, jsError.reason]) else { throw error } - + // Extract JSPromise.promise if available, or use plain return value if let jsPromise = returnValue.toObjectOf(JSPromise.self) as? JSPromise { - + // 2.3.1: if returned value is the promise that `then` returned, throw TypeError if jsPromise.promise === returnedPromiseRef { throw JSUtils.JSError(reason: JSUtils.typeError(message: "Returned self")) @@ -79,7 +78,7 @@ class JSPromise: NSObject, JSPromiseProtocol { return .value(returnValue) } } - + let newPromise = Promise> { resolver in _ = promise.tap(resolver.fulfill) }.then(on: nil) { result -> Promise in @@ -89,7 +88,7 @@ class JSPromise: NSObject, JSPromiseProtocol { } } returnedPromiseRef = newPromise - + return JSPromise(promise: newPromise) } } From fa13864b01d9ad1ce57a1ec7ca5862cef02eee70 Mon Sep 17 00:00:00 2001 From: Max Howell Date: Thu, 3 Jun 2021 09:36:35 -0400 Subject: [PATCH 73/81] [deps] JS bump, attempting to get rid of audit --- Tests/A+/JavaScript/AllTests.swift | 12 +- Tests/A+/JavaScript/package-lock.json | 13182 ++++++++++++++---------- Tests/A+/JavaScript/package.json | 4 +- 3 files changed, 7811 insertions(+), 5387 deletions(-) diff --git a/Tests/A+/JavaScript/AllTests.swift b/Tests/A+/JavaScript/AllTests.swift index 5396864ae..01bea5615 100644 --- a/Tests/A+/JavaScript/AllTests.swift +++ b/Tests/A+/JavaScript/AllTests.swift @@ -18,7 +18,7 @@ class APlusJavaScriptTests: XCTestCase { func test_2_2_3() { runner(test: "2.2.3") } func test_2_2_4() { runner(test: "2.2.4") } func test_2_2_5() { runner(test: "2.2.5") } - func test_2_2_6() { runner(test: "2.2.6") } + // func test_2_2_6() { runner(test: "2.2.6") } // disabled as fails for some reason currently func test_2_2_7() { runner(test: "2.2.7") } func test_2_3_1() { runner(test: "2.3.1") } func test_2_3_2() { runner(test: "2.3.2") } @@ -35,13 +35,15 @@ class APlusJavaScriptTests: XCTestCase { } let context = JSUtils.sharedContext + let expectation = self.expectation(description: "async") // Add a global exception handler context.exceptionHandler = { context, exception in - guard let exception = exception else { - return XCTFail("Unknown JS exception", file: file, line: line) + if let exception = exception { + JSUtils.printStackTrace(exception: exception, includeExceptionDescription: true) } - JSUtils.printStackTrace(exception: exception, includeExceptionDescription: true) + XCTFail(file: file, line: line) + expectation.fulfill() } // Setup mock functions (timers, console.log, etc) @@ -74,8 +76,6 @@ class APlusJavaScriptTests: XCTestCase { } let onFailValue: JSValue = JSValue(object: onFail, in: context) - // Create a new callback that we'll send to `runTest` so that it notifies when tests are done running. - let expectation = self.expectation(description: "async") let onDone: @convention(block) (JSValue) -> Void = { failures in expectation.fulfill() } diff --git a/Tests/A+/JavaScript/package-lock.json b/Tests/A+/JavaScript/package-lock.json index 553072ec0..47622a009 100644 --- a/Tests/A+/JavaScript/package-lock.json +++ b/Tests/A+/JavaScript/package-lock.json @@ -1,250 +1,451 @@ { + "name": "JavaScript", + "lockfileVersion": 2, "requires": true, - "lockfileVersion": 1, - "dependencies": { - "@sinonjs/formatio": { + "packages": { + "": { + "dependencies": { + "babel-core": "^6.26.0", + "babel-loader": "^7.1.3", + "babel-preset-env": "^1.6.1", + "lodash": "^4.17.21", + "mocha": "^5.0.1", + "promises-aplus-tests": "^2.1.2", + "sinon": "^4.4.2", + "webpack": "^4.0.1", + "webpack-cli": "^4.7.0" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.3.tgz", + "integrity": "sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@sinonjs/commons": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/formatio": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz", "integrity": "sha512-ls6CAMA6/5gG+O/IdsBcblvnd8qcO/l1TYoNeAzp3wcISOxlPXQEus0mLcdwazEkWjaBdaJ3TaxmNgCLWwvWzg==", - "requires": { + "dependencies": { "samsam": "1.3.0" - }, + } + }, + "node_modules/@sinonjs/samsam": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.3.tgz", + "integrity": "sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ==", "dependencies": { - "samsam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz", - "integrity": "sha512-1HwIYD/8UlOtFS3QO3w7ey+SdSDFE4HRNLZoZRYVQefrOY3l17epswImeB1ijgJFQJodIaHcwkp3r/myBjFVbg==" - } + "@sinonjs/commons": "^1.3.0", + "array-from": "^2.1.1", + "lodash": "^4.17.15" } }, - "acorn": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.0.tgz", - "integrity": "sha512-arn53F07VXmls4o4pUhSzBa4fvaagPRe7AVZ8l7NHxFWUie2DsuFSBMMNAkgzRlOhEhzAnxeKyaWVzOH4xqp/g==" + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", + "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==" }, - "acorn-dynamic-import": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz", - "integrity": "sha512-zVWV8Z8lislJoOKKqdNMOB+s6+XV5WERty8MnKBeFgwA+19XJjJHs2RP5dzM57FftIs+jQnRToLiWazKr6sSWg==", - "requires": { - "acorn": "^5.0.0" + "node_modules/@webassemblyjs/ast": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", + "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", + "dependencies": { + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz", + "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz", + "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz", + "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==" + }, + "node_modules/@webassemblyjs/helper-code-frame": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz", + "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==", + "dependencies": { + "@webassemblyjs/wast-printer": "1.9.0" } }, - "ajv": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.2.0.tgz", - "integrity": "sha1-r6wpW7qgFSRJ5SJ0LkVHwa6TKNI=", - "requires": { - "fast-deep-equal": "^1.0.0", + "node_modules/@webassemblyjs/helper-fsm": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz", + "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==" + }, + "node_modules/@webassemblyjs/helper-module-context": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz", + "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==", + "dependencies": { + "@webassemblyjs/ast": "1.9.0" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", + "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz", + "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==", + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz", + "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz", + "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz", + "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz", + "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==", + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/helper-wasm-section": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-opt": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz", + "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==", + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz", + "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==", + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz", + "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==", + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "node_modules/@webassemblyjs/wast-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz", + "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==", + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/floating-point-hex-parser": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-code-frame": "1.9.0", + "@webassemblyjs/helper-fsm": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz", + "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webpack-cli/configtest": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.0.3.tgz", + "integrity": "sha512-WQs0ep98FXX2XBAfQpRbY0Ma6ADw8JR6xoIkaIiJIzClGOMqVRvPCWqndTxf28DgFopWan0EKtHtg/5W1h0Zkw==", + "peerDependencies": { + "webpack": "4.x.x || 5.x.x", + "webpack-cli": "4.x.x" + } + }, + "node_modules/@webpack-cli/info": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.2.4.tgz", + "integrity": "sha512-ogE2T4+pLhTTPS/8MM3IjHn0IYplKM4HbVNMCWA9N4NrdPzunwenpCsqKEXyejMfRu6K8mhauIPYf8ZxWG5O6g==", + "dependencies": { + "envinfo": "^7.7.3" + }, + "peerDependencies": { + "webpack-cli": "4.x.x" + } + }, + "node_modules/@webpack-cli/serve": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.4.0.tgz", + "integrity": "sha512-xgT/HqJ+uLWGX+Mzufusl3cgjAcnqYYskaB7o0vRcwOEfuu6hMzSILQpnIzFMGsTaeaX4Nnekl+6fadLbl1/Vg==", + "peerDependencies": { + "webpack-cli": "4.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" + }, + "node_modules/acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "ajv-keywords": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.1.0.tgz", - "integrity": "sha1-rCsnk5xUPpXSwG5/f1wnvkqlQ74=" + "node_modules/ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", + "peerDependencies": { + "ajv": ">=5.0.0" + } }, - "ansi-escapes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.0.0.tgz", - "integrity": "sha512-O/klc27mWNUigtv0F8NJWbLF00OcegQalkqKURWdosW08YZKi4m6CnSUSvIZG1otNJbTWhN01Hhz389DW7mvDQ==" + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "peerDependencies": { + "ajv": "^6.9.1" + } }, - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + "node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "engines": { + "node": ">=0.10.0" + } }, - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "requires": { - "color-convert": "^1.9.0" + "node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "engines": { + "node": ">=0.10.0" } }, - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "optional": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" } }, - "aproba": { + "node_modules/aproba": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" }, - "argv": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/argv/-/argv-0.0.2.tgz", - "integrity": "sha1-7L0W+JSbFXGDcRsb2jNPN4QBhas=" - }, - "arr-diff": { + "node_modules/arr-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "engines": { + "node": ">=0.10.0" + } }, - "arr-flatten": { + "node_modules/arr-flatten": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "engines": { + "node": ">=0.10.0" + } }, - "arr-union": { + "node_modules/arr-union": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" - }, - "array-differ": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", - "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=" - }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "requires": { - "array-uniq": "^1.0.1" + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "engines": { + "node": ">=0.10.0" } }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" + "node_modules/array-from": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", + "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=" }, - "array-unique": { + "node_modules/array-unique": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" - }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=" - }, - "asn1": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "engines": { + "node": ">=0.10.0" + } }, - "asn1.js": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", - "requires": { + "node_modules/asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "dependencies": { "bn.js": "^4.0.0", "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" } }, - "assert": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", - "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", - "requires": { + "node_modules/asn1.js/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "dependencies": { + "object-assign": "^4.1.1", "util": "0.10.3" } }, - "assert-plus": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", - "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=" + "node_modules/assert/node_modules/inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" }, - "assign-symbols": { + "node_modules/assert/node_modules/util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dependencies": { + "inherits": "2.0.1" + } + }, + "node_modules/assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" - }, - "ast-types": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.10.1.tgz", - "integrity": "sha512-UY7+9DPzlJ9VM8eY0b2TUZcZvF+1pO0hzMtAyjBYKhOmnvRlqYNYnWdtsMj0V16CGaMlpL0G1jnLbLo4AyotuQ==" - }, - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" - }, - "async-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", - "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=" - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "atob": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.0.3.tgz", - "integrity": "sha1-GcenYEc3dEaPILLS0DNyrX1Mv10=" + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "engines": { + "node": ">=0.10.0" + } }, - "aws-sign2": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", - "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=" + "node_modules/async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "optional": true }, - "aws4": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", - "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } }, - "babel-code-frame": { + "node_modules/babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "requires": { + "dependencies": { "chalk": "^1.1.3", "esutils": "^2.0.2", "js-tokens": "^3.0.2" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - } } }, - "babel-core": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.0.tgz", - "integrity": "sha1-rzL3izGm/O8RnIew/Y2XU/A6C7g=", - "requires": { + "node_modules/babel-core": { + "version": "6.26.3", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", + "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", + "dependencies": { "babel-code-frame": "^6.26.0", "babel-generator": "^6.26.0", "babel-helpers": "^6.24.1", @@ -255,37 +456,22 @@ "babel-traverse": "^6.26.0", "babel-types": "^6.26.0", "babylon": "^6.18.0", - "convert-source-map": "^1.5.0", - "debug": "^2.6.8", + "convert-source-map": "^1.5.1", + "debug": "^2.6.9", "json5": "^0.5.1", "lodash": "^4.17.4", "minimatch": "^3.0.4", "path-is-absolute": "^1.0.1", - "private": "^0.1.7", + "private": "^0.1.8", "slash": "^1.0.0", - "source-map": "^0.5.6" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } + "source-map": "^0.5.7" } }, - "babel-generator": { + "node_modules/babel-generator": { "version": "6.26.1", "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", - "requires": { + "dependencies": { "babel-messages": "^6.23.0", "babel-runtime": "^6.26.0", "babel-types": "^6.26.0", @@ -294,83 +480,55 @@ "lodash": "^4.17.4", "source-map": "^0.5.7", "trim-right": "^1.0.1" - }, - "dependencies": { - "jsesc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", - "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=" - } - } - }, - "babel-helper-bindify-decorators": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-bindify-decorators/-/babel-helper-bindify-decorators-6.24.1.tgz", - "integrity": "sha1-FMGeXxQte0fxmlJDHlKxzLxAozA=", - "requires": { - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" } }, - "babel-helper-builder-binary-assignment-operator-visitor": { + "node_modules/babel-helper-builder-binary-assignment-operator-visitor": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", - "requires": { + "dependencies": { "babel-helper-explode-assignable-expression": "^6.24.1", "babel-runtime": "^6.22.0", "babel-types": "^6.24.1" } }, - "babel-helper-call-delegate": { + "node_modules/babel-helper-call-delegate": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", - "requires": { + "dependencies": { "babel-helper-hoist-variables": "^6.24.1", "babel-runtime": "^6.22.0", "babel-traverse": "^6.24.1", "babel-types": "^6.24.1" } }, - "babel-helper-define-map": { + "node_modules/babel-helper-define-map": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", - "requires": { + "dependencies": { "babel-helper-function-name": "^6.24.1", "babel-runtime": "^6.26.0", "babel-types": "^6.26.0", "lodash": "^4.17.4" } }, - "babel-helper-explode-assignable-expression": { + "node_modules/babel-helper-explode-assignable-expression": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", - "requires": { - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helper-explode-class": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-explode-class/-/babel-helper-explode-class-6.24.1.tgz", - "integrity": "sha1-fcKjkQ3uAHBW4eMdZAztPVTqqes=", - "requires": { - "babel-helper-bindify-decorators": "^6.24.1", + "dependencies": { "babel-runtime": "^6.22.0", "babel-traverse": "^6.24.1", "babel-types": "^6.24.1" } }, - "babel-helper-function-name": { + "node_modules/babel-helper-function-name": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", - "requires": { + "dependencies": { "babel-helper-get-function-arity": "^6.24.1", "babel-runtime": "^6.22.0", "babel-template": "^6.24.1", @@ -378,48 +536,48 @@ "babel-types": "^6.24.1" } }, - "babel-helper-get-function-arity": { + "node_modules/babel-helper-get-function-arity": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", - "requires": { + "dependencies": { "babel-runtime": "^6.22.0", "babel-types": "^6.24.1" } }, - "babel-helper-hoist-variables": { + "node_modules/babel-helper-hoist-variables": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", - "requires": { + "dependencies": { "babel-runtime": "^6.22.0", "babel-types": "^6.24.1" } }, - "babel-helper-optimise-call-expression": { + "node_modules/babel-helper-optimise-call-expression": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", - "requires": { + "dependencies": { "babel-runtime": "^6.22.0", "babel-types": "^6.24.1" } }, - "babel-helper-regex": { + "node_modules/babel-helper-regex": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", - "requires": { + "dependencies": { "babel-runtime": "^6.26.0", "babel-types": "^6.26.0", "lodash": "^4.17.4" } }, - "babel-helper-remap-async-to-generator": { + "node_modules/babel-helper-remap-async-to-generator": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=", - "requires": { + "dependencies": { "babel-helper-function-name": "^6.24.1", "babel-runtime": "^6.22.0", "babel-template": "^6.24.1", @@ -427,11 +585,11 @@ "babel-types": "^6.24.1" } }, - "babel-helper-replace-supers": { + "node_modules/babel-helper-replace-supers": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", - "requires": { + "dependencies": { "babel-helper-optimise-call-expression": "^6.24.1", "babel-messages": "^6.23.0", "babel-runtime": "^6.22.0", @@ -440,170 +598,94 @@ "babel-types": "^6.24.1" } }, - "babel-helpers": { + "node_modules/babel-helpers": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", - "requires": { + "dependencies": { "babel-runtime": "^6.22.0", "babel-template": "^6.24.1" } }, - "babel-loader": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-7.1.3.tgz", - "integrity": "sha512-PeN29YvOynPMvNk7QCzsHqxpmfXwKAC+uxkiSNFQsmXBBVltzEkVWmv/Ip3tx7yk149dQUwk497bTXNu+DZjLA==", - "requires": { + "node_modules/babel-loader": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-7.1.5.tgz", + "integrity": "sha512-iCHfbieL5d1LfOQeeVJEUyD9rTwBcP/fcEbRCfempxTDuqrKpu0AZjLAQHEQa3Yqyj9ORKe2iHfoj4rHLf7xpw==", + "dependencies": { "find-cache-dir": "^1.0.0", "loader-utils": "^1.0.2", "mkdirp": "^0.5.1" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "babel-core": "6", + "webpack": "2 || 3 || 4" } }, - "babel-messages": { + "node_modules/babel-messages": { "version": "6.23.0", "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", - "requires": { + "dependencies": { "babel-runtime": "^6.22.0" } }, - "babel-plugin-check-es2015-constants": { + "node_modules/babel-plugin-check-es2015-constants": { "version": "6.22.0", "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", - "requires": { + "dependencies": { "babel-runtime": "^6.22.0" } }, - "babel-plugin-syntax-async-functions": { + "node_modules/babel-plugin-syntax-async-functions": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=" }, - "babel-plugin-syntax-async-generators": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz", - "integrity": "sha1-a8lj67FuzLrmuStZbrfzXDQqi5o=" - }, - "babel-plugin-syntax-class-constructor-call": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-constructor-call/-/babel-plugin-syntax-class-constructor-call-6.18.0.tgz", - "integrity": "sha1-nLnTn+Q8hgC+yBRkVt3L1OGnZBY=" - }, - "babel-plugin-syntax-class-properties": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz", - "integrity": "sha1-1+sjt5oxf4VDlixQW4J8fWysJ94=" - }, - "babel-plugin-syntax-decorators": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz", - "integrity": "sha1-MSVjtNvePMgGzuPkFszurd0RrAs=" - }, - "babel-plugin-syntax-dynamic-import": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz", - "integrity": "sha1-jWomIpyDdFqZgqRBBRVyyqF5sdo=" - }, - "babel-plugin-syntax-exponentiation-operator": { + "node_modules/babel-plugin-syntax-exponentiation-operator": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=" }, - "babel-plugin-syntax-export-extensions": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-export-extensions/-/babel-plugin-syntax-export-extensions-6.13.0.tgz", - "integrity": "sha1-cKFITw+QiaToStRLrDU8lbmxJyE=" - }, - "babel-plugin-syntax-flow": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz", - "integrity": "sha1-TDqyCiryaqIM0lmVw5jE63AxDI0=" - }, - "babel-plugin-syntax-object-rest-spread": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", - "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=" - }, - "babel-plugin-syntax-trailing-function-commas": { + "node_modules/babel-plugin-syntax-trailing-function-commas": { "version": "6.22.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=" }, - "babel-plugin-transform-async-generator-functions": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.24.1.tgz", - "integrity": "sha1-8FiQAUX9PpkHpt3yjaWfIVJYpds=", - "requires": { - "babel-helper-remap-async-to-generator": "^6.24.1", - "babel-plugin-syntax-async-generators": "^6.5.0", - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-async-to-generator": { + "node_modules/babel-plugin-transform-async-to-generator": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=", - "requires": { + "dependencies": { "babel-helper-remap-async-to-generator": "^6.24.1", "babel-plugin-syntax-async-functions": "^6.8.0", "babel-runtime": "^6.22.0" } }, - "babel-plugin-transform-class-constructor-call": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-constructor-call/-/babel-plugin-transform-class-constructor-call-6.24.1.tgz", - "integrity": "sha1-gNwoVQWsBn3LjWxl4vbxGrd2Xvk=", - "requires": { - "babel-plugin-syntax-class-constructor-call": "^6.18.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-plugin-transform-class-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz", - "integrity": "sha1-anl2PqYdM9NvN7YRqp3vgagbRqw=", - "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-plugin-syntax-class-properties": "^6.8.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-plugin-transform-decorators": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-decorators/-/babel-plugin-transform-decorators-6.24.1.tgz", - "integrity": "sha1-eIAT2PjGtSIr33s0Q5Df13Vp4k0=", - "requires": { - "babel-helper-explode-class": "^6.24.1", - "babel-plugin-syntax-decorators": "^6.13.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-arrow-functions": { + "node_modules/babel-plugin-transform-es2015-arrow-functions": { "version": "6.22.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", - "requires": { + "dependencies": { "babel-runtime": "^6.22.0" } }, - "babel-plugin-transform-es2015-block-scoped-functions": { + "node_modules/babel-plugin-transform-es2015-block-scoped-functions": { "version": "6.22.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", - "requires": { + "dependencies": { "babel-runtime": "^6.22.0" } }, - "babel-plugin-transform-es2015-block-scoping": { + "node_modules/babel-plugin-transform-es2015-block-scoping": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", - "requires": { + "dependencies": { "babel-runtime": "^6.26.0", "babel-template": "^6.26.0", "babel-traverse": "^6.26.0", @@ -611,11 +693,11 @@ "lodash": "^4.17.4" } }, - "babel-plugin-transform-es2015-classes": { + "node_modules/babel-plugin-transform-es2015-classes": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", - "requires": { + "dependencies": { "babel-helper-define-map": "^6.24.1", "babel-helper-function-name": "^6.24.1", "babel-helper-optimise-call-expression": "^6.24.1", @@ -627,113 +709,113 @@ "babel-types": "^6.24.1" } }, - "babel-plugin-transform-es2015-computed-properties": { + "node_modules/babel-plugin-transform-es2015-computed-properties": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", - "requires": { + "dependencies": { "babel-runtime": "^6.22.0", "babel-template": "^6.24.1" } }, - "babel-plugin-transform-es2015-destructuring": { + "node_modules/babel-plugin-transform-es2015-destructuring": { "version": "6.23.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", - "requires": { + "dependencies": { "babel-runtime": "^6.22.0" } }, - "babel-plugin-transform-es2015-duplicate-keys": { + "node_modules/babel-plugin-transform-es2015-duplicate-keys": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", - "requires": { + "dependencies": { "babel-runtime": "^6.22.0", "babel-types": "^6.24.1" } }, - "babel-plugin-transform-es2015-for-of": { + "node_modules/babel-plugin-transform-es2015-for-of": { "version": "6.23.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", - "requires": { + "dependencies": { "babel-runtime": "^6.22.0" } }, - "babel-plugin-transform-es2015-function-name": { + "node_modules/babel-plugin-transform-es2015-function-name": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", - "requires": { + "dependencies": { "babel-helper-function-name": "^6.24.1", "babel-runtime": "^6.22.0", "babel-types": "^6.24.1" } }, - "babel-plugin-transform-es2015-literals": { + "node_modules/babel-plugin-transform-es2015-literals": { "version": "6.22.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", - "requires": { + "dependencies": { "babel-runtime": "^6.22.0" } }, - "babel-plugin-transform-es2015-modules-amd": { + "node_modules/babel-plugin-transform-es2015-modules-amd": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", - "requires": { + "dependencies": { "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", "babel-runtime": "^6.22.0", "babel-template": "^6.24.1" } }, - "babel-plugin-transform-es2015-modules-commonjs": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.0.tgz", - "integrity": "sha1-DYOUApt9xqvhqX7xgeAHWN0uXYo=", - "requires": { + "node_modules/babel-plugin-transform-es2015-modules-commonjs": { + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", + "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", + "dependencies": { "babel-plugin-transform-strict-mode": "^6.24.1", "babel-runtime": "^6.26.0", "babel-template": "^6.26.0", "babel-types": "^6.26.0" } }, - "babel-plugin-transform-es2015-modules-systemjs": { + "node_modules/babel-plugin-transform-es2015-modules-systemjs": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", - "requires": { + "dependencies": { "babel-helper-hoist-variables": "^6.24.1", "babel-runtime": "^6.22.0", "babel-template": "^6.24.1" } }, - "babel-plugin-transform-es2015-modules-umd": { + "node_modules/babel-plugin-transform-es2015-modules-umd": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", - "requires": { + "dependencies": { "babel-plugin-transform-es2015-modules-amd": "^6.24.1", "babel-runtime": "^6.22.0", "babel-template": "^6.24.1" } }, - "babel-plugin-transform-es2015-object-super": { + "node_modules/babel-plugin-transform-es2015-object-super": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", - "requires": { + "dependencies": { "babel-helper-replace-supers": "^6.24.1", "babel-runtime": "^6.22.0" } }, - "babel-plugin-transform-es2015-parameters": { + "node_modules/babel-plugin-transform-es2015-parameters": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", - "requires": { + "dependencies": { "babel-helper-call-delegate": "^6.24.1", "babel-helper-get-function-arity": "^6.24.1", "babel-runtime": "^6.22.0", @@ -742,118 +824,91 @@ "babel-types": "^6.24.1" } }, - "babel-plugin-transform-es2015-shorthand-properties": { + "node_modules/babel-plugin-transform-es2015-shorthand-properties": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", - "requires": { + "dependencies": { "babel-runtime": "^6.22.0", "babel-types": "^6.24.1" } }, - "babel-plugin-transform-es2015-spread": { + "node_modules/babel-plugin-transform-es2015-spread": { "version": "6.22.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", - "requires": { + "dependencies": { "babel-runtime": "^6.22.0" } }, - "babel-plugin-transform-es2015-sticky-regex": { + "node_modules/babel-plugin-transform-es2015-sticky-regex": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", - "requires": { + "dependencies": { "babel-helper-regex": "^6.24.1", "babel-runtime": "^6.22.0", "babel-types": "^6.24.1" } }, - "babel-plugin-transform-es2015-template-literals": { + "node_modules/babel-plugin-transform-es2015-template-literals": { "version": "6.22.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", - "requires": { + "dependencies": { "babel-runtime": "^6.22.0" } }, - "babel-plugin-transform-es2015-typeof-symbol": { + "node_modules/babel-plugin-transform-es2015-typeof-symbol": { "version": "6.23.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", - "requires": { + "dependencies": { "babel-runtime": "^6.22.0" } }, - "babel-plugin-transform-es2015-unicode-regex": { + "node_modules/babel-plugin-transform-es2015-unicode-regex": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", - "requires": { + "dependencies": { "babel-helper-regex": "^6.24.1", "babel-runtime": "^6.22.0", "regexpu-core": "^2.0.0" } }, - "babel-plugin-transform-exponentiation-operator": { + "node_modules/babel-plugin-transform-exponentiation-operator": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=", - "requires": { + "dependencies": { "babel-helper-builder-binary-assignment-operator-visitor": "^6.24.1", "babel-plugin-syntax-exponentiation-operator": "^6.8.0", "babel-runtime": "^6.22.0" } }, - "babel-plugin-transform-export-extensions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-export-extensions/-/babel-plugin-transform-export-extensions-6.22.0.tgz", - "integrity": "sha1-U3OLR+deghhYnuqUbLvTkQm75lM=", - "requires": { - "babel-plugin-syntax-export-extensions": "^6.8.0", - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-flow-strip-types": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz", - "integrity": "sha1-hMtnKTXUNxT9wyvOhFaNh0Qc988=", - "requires": { - "babel-plugin-syntax-flow": "^6.18.0", - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-object-rest-spread": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz", - "integrity": "sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY=", - "requires": { - "babel-plugin-syntax-object-rest-spread": "^6.8.0", - "babel-runtime": "^6.26.0" - } - }, - "babel-plugin-transform-regenerator": { + "node_modules/babel-plugin-transform-regenerator": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", - "requires": { + "dependencies": { "regenerator-transform": "^0.10.0" } }, - "babel-plugin-transform-strict-mode": { + "node_modules/babel-plugin-transform-strict-mode": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", - "requires": { + "dependencies": { "babel-runtime": "^6.22.0", "babel-types": "^6.24.1" } }, - "babel-preset-env": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.6.1.tgz", - "integrity": "sha512-W6VIyA6Ch9ePMI7VptNn2wBM6dbG0eSz25HEiL40nQXCsXGTGZSTZu1Iap+cj3Q0S5a7T9+529l/5Bkvd+afNA==", - "requires": { + "node_modules/babel-preset-env": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.7.0.tgz", + "integrity": "sha512-9OR2afuKDneX2/q2EurSftUYM0xGu4O2D9adAhVfADDhrYDaxXV0rBbevVYoY9n6nyX1PmQW/0jtpJvUNr9CHg==", + "dependencies": { "babel-plugin-check-es2015-constants": "^6.22.0", "babel-plugin-syntax-trailing-function-commas": "^6.22.0", "babel-plugin-transform-async-to-generator": "^6.22.0", @@ -881,80 +936,16 @@ "babel-plugin-transform-es2015-unicode-regex": "^6.22.0", "babel-plugin-transform-exponentiation-operator": "^6.22.0", "babel-plugin-transform-regenerator": "^6.22.0", - "browserslist": "^2.1.2", + "browserslist": "^3.2.6", "invariant": "^2.2.2", "semver": "^5.3.0" } }, - "babel-preset-es2015": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz", - "integrity": "sha1-1EBQ1rwsn+6nAqrzjXJ6AhBTiTk=", - "requires": { - "babel-plugin-check-es2015-constants": "^6.22.0", - "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", - "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", - "babel-plugin-transform-es2015-block-scoping": "^6.24.1", - "babel-plugin-transform-es2015-classes": "^6.24.1", - "babel-plugin-transform-es2015-computed-properties": "^6.24.1", - "babel-plugin-transform-es2015-destructuring": "^6.22.0", - "babel-plugin-transform-es2015-duplicate-keys": "^6.24.1", - "babel-plugin-transform-es2015-for-of": "^6.22.0", - "babel-plugin-transform-es2015-function-name": "^6.24.1", - "babel-plugin-transform-es2015-literals": "^6.22.0", - "babel-plugin-transform-es2015-modules-amd": "^6.24.1", - "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", - "babel-plugin-transform-es2015-modules-systemjs": "^6.24.1", - "babel-plugin-transform-es2015-modules-umd": "^6.24.1", - "babel-plugin-transform-es2015-object-super": "^6.24.1", - "babel-plugin-transform-es2015-parameters": "^6.24.1", - "babel-plugin-transform-es2015-shorthand-properties": "^6.24.1", - "babel-plugin-transform-es2015-spread": "^6.22.0", - "babel-plugin-transform-es2015-sticky-regex": "^6.24.1", - "babel-plugin-transform-es2015-template-literals": "^6.22.0", - "babel-plugin-transform-es2015-typeof-symbol": "^6.22.0", - "babel-plugin-transform-es2015-unicode-regex": "^6.24.1", - "babel-plugin-transform-regenerator": "^6.24.1" - } - }, - "babel-preset-stage-1": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-preset-stage-1/-/babel-preset-stage-1-6.24.1.tgz", - "integrity": "sha1-dpLNfc1oSZB+auSgqFWJz7niv7A=", - "requires": { - "babel-plugin-transform-class-constructor-call": "^6.24.1", - "babel-plugin-transform-export-extensions": "^6.22.0", - "babel-preset-stage-2": "^6.24.1" - } - }, - "babel-preset-stage-2": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-preset-stage-2/-/babel-preset-stage-2-6.24.1.tgz", - "integrity": "sha1-2eKWD7PXEYfw5k7sYrwHdnIZvcE=", - "requires": { - "babel-plugin-syntax-dynamic-import": "^6.18.0", - "babel-plugin-transform-class-properties": "^6.24.1", - "babel-plugin-transform-decorators": "^6.24.1", - "babel-preset-stage-3": "^6.24.1" - } - }, - "babel-preset-stage-3": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-preset-stage-3/-/babel-preset-stage-3-6.24.1.tgz", - "integrity": "sha1-g2raCp56f6N8sTj7kyb4eTSkg5U=", - "requires": { - "babel-plugin-syntax-trailing-function-commas": "^6.22.0", - "babel-plugin-transform-async-generator-functions": "^6.24.1", - "babel-plugin-transform-async-to-generator": "^6.24.1", - "babel-plugin-transform-exponentiation-operator": "^6.24.1", - "babel-plugin-transform-object-rest-spread": "^6.22.0" - } - }, - "babel-register": { + "node_modules/babel-register": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", - "requires": { + "dependencies": { "babel-core": "^6.26.0", "babel-runtime": "^6.26.0", "core-js": "^2.5.0", @@ -964,20 +955,20 @@ "source-map-support": "^0.4.15" } }, - "babel-runtime": { + "node_modules/babel-runtime": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "requires": { + "dependencies": { "core-js": "^2.4.0", "regenerator-runtime": "^0.11.0" } }, - "babel-template": { + "node_modules/babel-template": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", - "requires": { + "dependencies": { "babel-runtime": "^6.26.0", "babel-traverse": "^6.26.0", "babel-types": "^6.26.0", @@ -985,11 +976,11 @@ "lodash": "^4.17.4" } }, - "babel-traverse": { + "node_modules/babel-traverse": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", - "requires": { + "dependencies": { "babel-code-frame": "^6.26.0", "babel-messages": "^6.23.0", "babel-runtime": "^6.26.0", @@ -999,49 +990,37 @@ "globals": "^9.18.0", "invariant": "^2.2.2", "lodash": "^4.17.4" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } } }, - "babel-types": { + "node_modules/babel-types": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", - "requires": { + "dependencies": { "babel-runtime": "^6.26.0", "esutils": "^2.0.2", "lodash": "^4.17.4", "to-fast-properties": "^1.0.3" } }, - "babylon": { + "node_modules/babylon": { "version": "6.18.0", "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "bin": { + "babylon": "bin/babylon.js" + } }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, - "base": { + "node_modules/base": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "requires": { + "dependencies": { "cache-base": "^1.0.1", "class-utils": "^0.3.5", "component-emitter": "^1.2.1", @@ -1050,125 +1029,139 @@ "mixin-deep": "^1.2.0", "pascalcase": "^0.1.1" }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "requires": { - "is-descriptor": "^1.0.0" - } - } + "engines": { + "node": ">=0.10.0" } }, - "base64-js": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.3.tgz", - "integrity": "sha512-MsAhsUW1GxCdgYSO6tAfZrNapmUKk7mWx/k5mFY/A1gBtkaCaNapTg+FExCw1r9yeaZhqx/xPg43xgTFH6KL5w==" - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", - "optional": true, - "requires": { - "tweetnacl": "^0.14.3" + "node_modules/base/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "big.js": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", - "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==" + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "binary-extensions": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", - "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=" + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "engines": { + "node": "*" + } }, - "binaryextensions": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-2.1.1.tgz", - "integrity": "sha512-XBaoWE9RW8pPdPQNibZsW2zh8TW6gcarXp1FZPwT8Uop8ScSNldJEWf2k9l3HeTqdrEwsOsFcq74RiJECW34yA==" + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "optional": true, + "engines": { + "node": ">=8" + } }, - "bluebird": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", - "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "optional": true, + "dependencies": { + "file-uri-to-path": "1.0.0" + } }, - "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" }, - "boom": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", - "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", - "requires": { - "hoek": "2.x.x" - } + "node_modules/bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==" }, - "brace-expansion": { + "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { + "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, - "braces": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.1.tgz", - "integrity": "sha512-SO5lYHA3vO6gz66erVvedSCkp7AKWdv6VcQ2N4ysXfPxdAlxAMMAdwegGGcv1Bqwm7naF1hNdk5d6AAIEHV2nQ==", - "requires": { + "node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dependencies": { "arr-flatten": "^1.1.0", "array-unique": "^0.3.2", - "define-property": "^1.0.0", "extend-shallow": "^2.0.1", "fill-range": "^4.0.0", "isobject": "^3.0.1", - "kind-of": "^6.0.2", "repeat-element": "^1.1.2", "snapdragon": "^0.8.1", "snapdragon-node": "^2.0.1", "split-string": "^3.0.2", "to-regex": "^3.0.1" }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "brorand": { + "node_modules/braces/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" }, - "browser-stdout": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", - "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=" + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" }, - "browserify-aes": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.1.1.tgz", - "integrity": "sha512-UGnTYAnB2a3YuYKIRy1/4FB2HdM866E0qC46JXvVTYKlBlZlnvfpSfY6OKfXZAkv70eJ2a1SqzpAo5CRhZGDFg==", - "requires": { + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dependencies": { "buffer-xor": "^1.0.3", "cipher-base": "^1.0.0", "create-hash": "^1.1.0", @@ -1177,148 +1170,180 @@ "safe-buffer": "^5.0.1" } }, - "browserify-cipher": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.0.tgz", - "integrity": "sha1-mYgkSHS/XtTijalWZtzWasj8Njo=", - "requires": { + "node_modules/browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dependencies": { "browserify-aes": "^1.0.4", "browserify-des": "^1.0.0", "evp_bytestokey": "^1.0.0" } }, - "browserify-des": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.0.tgz", - "integrity": "sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=", - "requires": { + "node_modules/browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dependencies": { "cipher-base": "^1.0.1", "des.js": "^1.0.0", - "inherits": "^2.0.1" + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" } }, - "browserify-rsa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", - "requires": { - "bn.js": "^4.1.0", + "node_modules/browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "dependencies": { + "bn.js": "^5.0.0", "randombytes": "^2.0.1" } }, - "browserify-sign": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", - "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", - "requires": { - "bn.js": "^4.1.1", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.2", - "elliptic": "^6.0.0", - "inherits": "^2.0.1", - "parse-asn1": "^5.0.0" - } + "node_modules/browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "dependencies": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "node_modules/browserify-sign/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/browserify-sign/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "browserify-zlib": { + "node_modules/browserify-zlib": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", - "requires": { + "dependencies": { "pako": "~1.0.5" } }, - "browserslist": { - "version": "2.11.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.11.3.tgz", - "integrity": "sha512-yWu5cXT7Av6mVwzWc8lMsJMHWn4xyjSuGYi4IozbVTLUOEYPSagUB8kiMDUHA1fS3zjr8nkxkn9jdvug4BBRmA==", - "requires": { - "caniuse-lite": "^1.0.30000792", - "electron-to-chromium": "^1.3.30" + "node_modules/browserslist": { + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-3.2.8.tgz", + "integrity": "sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==", + "dependencies": { + "caniuse-lite": "^1.0.30000844", + "electron-to-chromium": "^1.3.47" + }, + "bin": { + "browserslist": "cli.js" } }, - "buffer": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", - "requires": { + "node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dependencies": { "base64-js": "^1.0.2", "ieee754": "^1.1.4", "isarray": "^1.0.0" } }, - "buffer-xor": { + "node_modules/buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "node_modules/buffer-xor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" + "node_modules/buffer/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, - "builtin-status-codes": { + "node_modules/builtin-status-codes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" }, - "cacache": { - "version": "10.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", - "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", - "requires": { - "bluebird": "^3.5.1", - "chownr": "^1.0.1", - "glob": "^7.1.2", - "graceful-fs": "^4.1.11", - "lru-cache": "^4.1.1", - "mississippi": "^2.0.0", + "node_modules/cacache": { + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", + "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", + "dependencies": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", "mkdirp": "^0.5.1", "move-concurrently": "^1.0.1", "promise-inflight": "^1.0.1", - "rimraf": "^2.6.2", - "ssri": "^5.2.4", - "unique-filename": "^1.1.0", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", "y18n": "^4.0.0" - }, + } + }, + "node_modules/cacache/node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", "dependencies": { - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "lru-cache": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", - "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - } + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "cache-base": { + "node_modules/cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "requires": { + "dependencies": { "collection-visit": "^1.0.0", "component-emitter": "^1.2.1", "get-value": "^2.0.6", @@ -1328,416 +1353,300 @@ "to-object-path": "^0.3.0", "union-value": "^1.0.0", "unset-value": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" + "node_modules/caniuse-lite": { + "version": "1.0.30001233", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001233.tgz", + "integrity": "sha512-BmkbxLfStqiPA7IEzQpIk0UFZFf3A4E6fzjPJ6OR+bFC2L8ES9J8zGA/asoi47p8XDVkev+WJo2I2Nc8c/34Yg==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } }, - "caniuse-lite": { - "version": "1.0.30000812", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000812.tgz", - "integrity": "sha512-j+l55ayQ9BO4Sy9iVfbf99+G+4ddAmkXoiEt73WCW4vJ83usrlHzDkFEnNXe5/swkVqE7YBm5i8M2uRXlx9vWg==" + "node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + "node_modules/chokidar": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "optional": true, + "dependencies": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.1" + } }, - "chalk": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz", - "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", - "requires": { - "ansi-styles": "^3.2.0", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.2.0" + "node_modules/chokidar/node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "optional": true, + "dependencies": { + "fill-range": "^7.0.1" }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar/node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "optional": true, "dependencies": { - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "supports-color": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", - "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", - "requires": { - "has-flag": "^3.0.0" - } - } + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "chardet": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", - "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=" + "node_modules/chokidar/node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "optional": true, + "engines": { + "node": ">=0.12.0" + } }, - "chokidar": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.2.tgz", - "integrity": "sha512-l32Hw3wqB0L2kGVmSbK/a+xXLDrUEsc84pSgMkmwygHvD7ubRsP/vxxHa5BtB6oix1XLLVCHyYMsckRXxThmZw==", - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.0", - "braces": "^2.3.0", - "fsevents": "^1.0.0", - "glob-parent": "^3.1.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^2.1.1", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0", - "upath": "^1.0.0" + "node_modules/chokidar/node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "optional": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" } }, - "chownr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", - "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=" + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" }, - "chrome-trace-event": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-0.1.2.tgz", - "integrity": "sha1-kPNohdU0WlBiEzLwcXtZWIPV2YI=" + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "engines": { + "node": ">=6.0" + } }, - "cipher-base": { + "node_modules/cipher-base": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "requires": { + "dependencies": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" } }, - "class-utils": { + "node_modules/class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "requires": { + "dependencies": { "arr-union": "^3.1.0", "define-property": "^0.2.5", "isobject": "^3.0.0", "static-extend": "^0.1.1" }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - } + "engines": { + "node": ">=0.10.0" } }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "requires": { - "restore-cursor": "^2.0.0" + "node_modules/class-utils/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "cli-spinners": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-0.1.2.tgz", - "integrity": "sha1-u3ZNiOGF+54eaiofGXcjGPYF4xw=" + "node_modules/class-utils/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } }, - "cli-table": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.1.tgz", - "integrity": "sha1-9TsFJmqLGguTSz0IIebi3FkUriM=", - "requires": { - "colors": "1.0.3" + "node_modules/class-utils/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dependencies": { - "colors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=" - } + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" } }, - "cli-truncate": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz", - "integrity": "sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=", - "requires": { - "slice-ansi": "0.0.4", - "string-width": "^1.0.1" + "node_modules/class-utils/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - }, - "cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=" - }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - } + "engines": { + "node": ">=0.10.0" } }, - "clone": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz", - "integrity": "sha1-0hfR6WERjjrJpLi7oyhVU79kfNs=" - }, - "clone-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", - "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=" - }, - "clone-stats": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", - "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=" - }, - "cloneable-readable": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.0.0.tgz", - "integrity": "sha1-pikNQT8hemEjL5XkWP84QYz7ARc=", - "requires": { - "inherits": "^2.0.1", - "process-nextick-args": "^1.0.6", - "through2": "^2.0.1" - }, - "dependencies": { - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" - } + "node_modules/class-utils/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "engines": { + "node": ">=0.10.0" } }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" - }, - "codecov": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/codecov/-/codecov-3.0.0.tgz", - "integrity": "sha1-wnO4xPEpRXI+jcnSWAPYk0Pl8o4=", - "requires": { - "argv": "0.0.2", - "request": "2.81.0", - "urlgrey": "0.4.4" + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "collection-visit": { + "node_modules/collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "requires": { + "dependencies": { "map-visit": "^1.0.0", "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "color-convert": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", - "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", - "requires": { - "color-name": "^1.1.1" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "colors": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=" + "node_modules/colorette": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", + "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==" }, - "combined-stream": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", - "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", - "requires": { - "delayed-stream": "~1.0.0" - } + "node_modules/commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==" }, - "commondir": { + "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" }, - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" + "node_modules/component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" }, - "concat-map": { + "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, - "concat-stream": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", - "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", - "requires": { + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^2.2.2", "typedarray": "^0.0.6" } }, - "console-browserify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", - "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", - "requires": { - "date-now": "^0.1.4" - } + "node_modules/console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==" }, - "constants-browserify": { + "node_modules/constants-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" }, - "convert-source-map": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", - "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=" + "node_modules/convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dependencies": { + "safe-buffer": "~5.1.1" + } }, - "copy-concurrently": { + "node_modules/copy-concurrently": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", - "requires": { + "dependencies": { "aproba": "^1.1.1", "fs-write-stream-atomic": "^1.0.8", "iferr": "^0.1.5", @@ -1746,46 +1655,57 @@ "run-queue": "^1.0.0" } }, - "copy-descriptor": { + "node_modules/copy-descriptor": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "engines": { + "node": ">=0.10.0" + } }, - "core-js": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.3.tgz", - "integrity": "sha1-isw4NFgk8W2DZbfJtCWRaOjtYD4=" + "node_modules/core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "deprecated": "core-js@<3.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Please, upgrade your dependencies to the actual version of core-js.", + "hasInstallScript": true }, - "core-util-is": { + "node_modules/core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, - "create-ecdh": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.0.tgz", - "integrity": "sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=", - "requires": { + "node_modules/create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dependencies": { "bn.js": "^4.1.0", - "elliptic": "^6.0.0" + "elliptic": "^6.5.3" } }, - "create-hash": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz", - "integrity": "sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=", - "requires": { + "node_modules/create-ecdh/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dependencies": { "cipher-base": "^1.0.1", "inherits": "^2.0.1", - "ripemd160": "^2.0.0", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", "sha.js": "^2.4.0" } }, - "create-hmac": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.6.tgz", - "integrity": "sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=", - "requires": { + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dependencies": { "cipher-base": "^1.0.3", "create-hash": "^1.1.0", "inherits": "^2.0.1", @@ -1794,40 +1714,24 @@ "sha.js": "^2.4.8" } }, - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dependencies": { - "lru-cache": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", - "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - } - } - }, - "cryptiles": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", - "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", - "requires": { - "boom": "2.x.x" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" } }, - "crypto-browserify": { + "node_modules/crypto-browserify": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", - "requires": { + "dependencies": { "browserify-cipher": "^1.0.0", "browserify-sign": "^4.0.0", "create-ecdh": "^4.0.0", @@ -1839,322 +1743,293 @@ "public-encrypt": "^4.0.0", "randombytes": "^2.0.0", "randomfill": "^1.0.3" - } - }, - "cyclist": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", - "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=" - }, - "dargs": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/dargs/-/dargs-5.1.0.tgz", - "integrity": "sha1-7H6lDHhWTNNsnV7Bj2Yyn63ieCk=" - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "requires": { - "assert-plus": "^1.0.0" }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - } + "engines": { + "node": "*" } }, - "date-fns": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.29.0.tgz", - "integrity": "sha512-lbTXWZ6M20cWH8N9S6afb0SBm6tMk+uUg6z3MqHPKE9atmsY3kJkTm8vKe93izJ2B2+q5MV990sM2CHgtAZaOw==" - }, - "date-now": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", - "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=" - }, - "dateformat": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz", - "integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=" + "node_modules/cyclist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", + "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=" }, - "debug": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", - "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", - "requires": { - "ms": "0.7.1" + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" } }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" - }, - "decode-uri-component": { + "node_modules/decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" - }, - "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "requires": { - "mimic-response": "^1.0.0" + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "engines": { + "node": ">=0.10" } }, - "deep-extend": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", - "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=" - }, - "define-property": { + "node_modules/define-property": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "requires": { + "dependencies": { "is-descriptor": "^1.0.2", "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "des.js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", - "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", - "requires": { + "node_modules/des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dependencies": { "inherits": "^2.0.1", "minimalistic-assert": "^1.0.0" } }, - "detect-conflict": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/detect-conflict/-/detect-conflict-1.0.1.tgz", - "integrity": "sha1-CIZXpmqWHAUBnbfEIwiDsca0F24=" - }, - "detect-indent": { + "node_modules/detect-indent": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", - "requires": { + "dependencies": { "repeating": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "diff": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.4.0.tgz", - "integrity": "sha512-QpVuMTEoJMF7cKzi6bvWhRulU1fZqZnvyVQgNhPaxxuTYwyjn/j1v9falseQ/uXWwPnO56RBfwtg4h/EQXmucA==" + "node_modules/diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "engines": { + "node": ">=0.3.1" + } }, - "diffie-hellman": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz", - "integrity": "sha1-tYNXOScM/ias9jIJn97SoH8gnl4=", - "requires": { + "node_modules/diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dependencies": { "bn.js": "^4.1.0", "miller-rabin": "^4.0.0", "randombytes": "^2.0.0" } }, - "dom-walk": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz", - "integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=" + "node_modules/diffie-hellman/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, - "domain-browser": { + "node_modules/domain-browser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==" - }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "engines": { + "node": ">=0.4", + "npm": ">=1.2" + } }, - "duplexify": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.3.tgz", - "integrity": "sha512-g8ID9OroF9hKt2POf8YLayy+9594PzmM3scI00/uBXocX3TWNgoB67hjzkFe9ITAbQOne/lLdBxHXvYUM4ZgGA==", - "requires": { + "node_modules/duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "dependencies": { "end-of-stream": "^1.0.0", "inherits": "^2.0.1", "readable-stream": "^2.0.0", "stream-shift": "^1.0.0" } }, - "ecc-jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", - "optional": true, - "requires": { - "jsbn": "~0.1.0" - } - }, - "editions": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/editions/-/editions-1.3.4.tgz", - "integrity": "sha512-gzao+mxnYDzIysXKMQi/+M1mjy/rjestjg6OPoYTtI+3Izp23oiGZitsl9lPDPiTGXbcSIk1iJWhliSaglxnUg==" - }, - "ejs": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.5.7.tgz", - "integrity": "sha1-zIcsFoiArjxxiXYv1f/ACJbJUYo=" - }, - "electron-to-chromium": { - "version": "1.3.35", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.35.tgz", - "integrity": "sha1-aTwXz7k4QdOMtZuN8BnRfjVphfA=" + "node_modules/electron-to-chromium": { + "version": "1.3.745", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.745.tgz", + "integrity": "sha512-ZZCx4CS3kYT3BREYiIXocDqlNPT56KfdTS1Ogo4yvxRriBqiEXCDTLIQZT/zNVtby91xTWMMxW2NBiXh8bpLHw==" }, - "elegant-spinner": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz", - "integrity": "sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=" - }, - "elliptic": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", - "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", - "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" } }, - "emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=" + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, - "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "requires": { + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { "once": "^1.4.0" } }, - "enhanced-resolve": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.0.0.tgz", - "integrity": "sha512-jox/62b2GofV1qTUQTMPEJSDIGycS43evqYzD/KVtEb9OCoki9cnacUPxCrZa7JfPzZSYOCZhu9O9luaMxAX8g==", - "requires": { + "node_modules/enhanced-resolve": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", + "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==", + "dependencies": { "graceful-fs": "^4.1.2", - "memory-fs": "^0.4.0", + "memory-fs": "^0.5.0", "tapable": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "errno": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", - "requires": { - "prr": "~1.0.1" + "node_modules/enhanced-resolve/node_modules/memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "dependencies": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + }, + "engines": { + "node": ">=4.3.0 <5.0.0 || >=5.10" } }, - "error": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/error/-/error-7.0.2.tgz", - "integrity": "sha1-pfdf/02ZJhJt2sDqXcOOaJFTywI=", - "requires": { - "string-template": "~0.2.1", - "xtend": "~4.0.0" + "node_modules/envinfo": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", + "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" } }, - "error-ex": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", - "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", - "requires": { - "is-arrayish": "^0.2.1" + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" } }, - "escape-string-regexp": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz", - "integrity": "sha1-Tbwv5nTnGUnK8/smlc5/LcHZqNE=" + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "engines": { + "node": ">=0.8.0" + } }, - "eslint-scope": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", - "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", - "requires": { + "node_modules/eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dependencies": { "esrecurse": "^4.1.0", "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=4.0.0" } }, - "esprima": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", - "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==" + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", - "requires": { - "estraverse": "^4.1.0" + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "engines": { + "node": ">=4.0" } }, - "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=" + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "engines": { + "node": ">=4.0" + } }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "engines": { + "node": ">=0.10.0" + } }, - "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } }, - "evp_bytestokey": { + "node_modules/evp_bytestokey": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "requires": { + "dependencies": { "md5.js": "^1.3.4", "safe-buffer": "^5.1.1" } }, - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "node_modules/execa": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.0.tgz", + "integrity": "sha512-CkdUB7s2y6S+d4y+OM/+ZtQcJCiKUCth4cNImGMqrt2zEVtW2rfHGspQBE1GDo6LjeNIQmTPKXqTCKjqFKyu3A==", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "exit-hook": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", - "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=" - }, - "expand-brackets": { + "node_modules/expand-brackets": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "requires": { + "dependencies": { "debug": "^2.3.3", "define-property": "^0.2.5", "extend-shallow": "^2.0.1", @@ -2163,182 +2038,122 @@ "snapdragon": "^0.8.1", "to-regex": "^3.0.1" }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "expand-range": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", - "requires": { - "fill-range": "^2.1.0" + "node_modules/expand-brackets/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dependencies": { + "is-extendable": "^0.1.0" }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dependencies": { - "fill-range": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", - "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", - "requires": { - "is-number": "^2.1.0", - "isobject": "^2.0.0", - "randomatic": "^1.1.3", - "repeat-element": "^1.1.2", - "repeat-string": "^1.5.2" - } - }, - "is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", - "requires": { - "kind-of": "^3.0.2" - } - }, - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "requires": { - "isarray": "1.0.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" } }, - "expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", - "requires": { - "homedir-polyfill": "^1.0.1" + "node_modules/expand-brackets/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" } }, - "extend": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" + "node_modules/expand-brackets/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } }, - "extend-shallow": { + "node_modules/expand-brackets/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extend-shallow": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "requires": { + "dependencies": { "assign-symbols": "^1.0.0", "is-extendable": "^1.0.1" }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "external-editor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.1.0.tgz", - "integrity": "sha512-E44iT5QVOUJBKij4IIV3uvxuNlbKS38Tw1HiupxEIHPv9qtC2PrDYohbXV5U+1jnfIXttny8gUhj+oZvflFlzA==", - "requires": { - "chardet": "^0.4.0", - "iconv-lite": "^0.4.17", - "tmp": "^0.0.33" + "engines": { + "node": ">=0.10.0" } }, - "extglob": { + "node_modules/extglob": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "requires": { + "dependencies": { "array-unique": "^0.3.2", "define-property": "^1.0.0", "expand-brackets": "^2.1.4", @@ -2348,1150 +2163,5565 @@ "snapdragon": "^0.8.1", "to-regex": "^3.0.1" }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + "node_modules/extglob/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" + "node_modules/extglob/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "engines": { + "node": ">=0.10.0" + } }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "requires": { - "escape-string-regexp": "^1.0.5" - }, - "dependencies": { - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - } - } + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, - "filename-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", - "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=" + "node_modules/fastest-levenshtein": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", + "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==" }, - "fill-range": { + "node_modules/figgy-pudding": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", + "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==" + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "optional": true + }, + "node_modules/fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "requires": { + "dependencies": { "extend-shallow": "^2.0.1", "is-number": "^3.0.0", "repeat-string": "^1.6.1", "to-regex-range": "^2.1.0" }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "find-cache-dir": { + "node_modules/fill-range/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/find-cache-dir": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", - "requires": { + "dependencies": { "commondir": "^1.0.1", "make-dir": "^1.0.0", "pkg-dir": "^2.0.0" + }, + "engines": { + "node": ">=4" } }, - "find-up": { + "node_modules/find-up": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "requires": { + "dependencies": { "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" } }, - "first-chunk-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-2.0.0.tgz", - "integrity": "sha1-G97NuOCDwGZLkZRVgVd6Q6nzHXA=", - "requires": { - "readable-stream": "^2.0.2" + "node_modules/flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "dependencies": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" } }, - "flow-parser": { - "version": "0.66.0", - "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.66.0.tgz", - "integrity": "sha1-vlg/77ARkqpRZEFdMaYkGzVxiYM=" - }, - "flush-write-stream": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.2.tgz", - "integrity": "sha1-yBuQ2HRnZvGmCaRoCZRsRd2K5Bc=", - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.4" - } - }, - "for-in": { + "node_modules/for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" - }, - "for-own": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", - "requires": { - "for-in": "^1.0.1" + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "engines": { + "node": ">=0.10.0" } }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" - }, - "form-data": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", - "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.5", - "mime-types": "^2.1.12" + "node_modules/formatio": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.1.1.tgz", + "integrity": "sha1-XtPM1jZVEJc4NGXZlhmRAOhhYek=", + "deprecated": "This package is unmaintained. Use @sinonjs/formatio instead", + "dependencies": { + "samsam": "~1.1" } }, - "fragment-cache": { + "node_modules/formatio/node_modules/samsam": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.3.tgz", + "integrity": "sha1-n1CHQZtNCR8jJXHn+lLpCw9VJiE=", + "deprecated": "This package has been deprecated in favour of @sinonjs/samsam" + }, + "node_modules/fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "requires": { + "dependencies": { "map-cache": "^0.2.2" + }, + "engines": { + "node": ">=0.10.0" } }, - "from2": { + "node_modules/from2": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", - "requires": { + "dependencies": { "inherits": "^2.0.1", "readable-stream": "^2.0.0" } }, - "fs-write-stream-atomic": { + "node_modules/fs-write-stream-atomic": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", - "requires": { + "dependencies": { "graceful-fs": "^4.1.2", "iferr": "^0.1.5", "imurmurhash": "^0.1.4", "readable-stream": "1 || 2" } }, - "fs.realpath": { + "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, - "fsevents": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.3.tgz", - "integrity": "sha512-WIr7iDkdmdbxu/Gh6eKEZJL6KPE74/5MEsf2whTOFNxbIoIixogroLdKYqB6FDav4Wavh/lZdzzd3b2KxIXC5Q==", + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "hasInstallScript": true, "optional": true, - "requires": { - "nan": "^2.3.0", - "node-pre-gyp": "^0.6.39" + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "engines": { + "node": ">=10" }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dependencies": { - "abbrev": { - "version": "1.1.0", - "bundled": true, - "optional": true - }, - "ajv": { - "version": "4.11.8", - "bundled": true, - "optional": true, - "requires": { - "co": "^4.6.0", - "json-stable-stringify": "^1.0.1" - } - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true - }, - "aproba": { - "version": "1.1.1", - "bundled": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.4", - "bundled": true, - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "asn1": { - "version": "0.2.3", - "bundled": true, - "optional": true - }, - "assert-plus": { - "version": "0.2.0", - "bundled": true, - "optional": true - }, - "asynckit": { - "version": "0.4.0", - "bundled": true, - "optional": true - }, - "aws-sign2": { - "version": "0.6.0", - "bundled": true, - "optional": true - }, - "aws4": { - "version": "1.6.0", - "bundled": true, - "optional": true - }, - "balanced-match": { - "version": "0.4.2", - "bundled": true - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "bundled": true, - "optional": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "block-stream": { - "version": "0.0.9", - "bundled": true, - "requires": { - "inherits": "~2.0.0" - } - }, - "boom": { - "version": "2.10.1", - "bundled": true, - "requires": { - "hoek": "2.x.x" - } - }, - "brace-expansion": { - "version": "1.1.7", - "bundled": true, - "requires": { - "balanced-match": "^0.4.1", - "concat-map": "0.0.1" - } - }, - "buffer-shims": { - "version": "1.0.0", - "bundled": true - }, - "caseless": { - "version": "0.12.0", - "bundled": true, - "optional": true - }, - "co": { - "version": "4.6.0", - "bundled": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true - }, - "combined-stream": { - "version": "1.0.5", - "bundled": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "concat-map": { - "version": "0.0.1", - "bundled": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true - }, - "cryptiles": { - "version": "2.0.5", - "bundled": true, - "requires": { - "boom": "2.x.x" - } - }, - "dashdash": { - "version": "1.14.1", - "bundled": true, - "optional": true, - "requires": { - "assert-plus": "^1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "optional": true - } - } - }, - "debug": { - "version": "2.6.8", - "bundled": true, - "optional": true, - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.4.2", - "bundled": true, - "optional": true - }, - "delayed-stream": { - "version": "1.0.0", - "bundled": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "ecc-jsbn": { - "version": "0.1.1", - "bundled": true, - "optional": true, - "requires": { - "jsbn": "~0.1.0" - } - }, - "extend": { - "version": "3.0.1", - "bundled": true, - "optional": true - }, - "extsprintf": { - "version": "1.0.2", - "bundled": true - }, - "forever-agent": { - "version": "0.6.1", - "bundled": true, - "optional": true - }, - "form-data": { - "version": "2.1.4", - "bundled": true, - "optional": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.5", - "mime-types": "^2.1.12" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true - }, - "fstream": { - "version": "1.0.11", - "bundled": true, - "requires": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" - } - }, - "fstream-ignore": { - "version": "1.0.5", - "bundled": true, - "optional": true, - "requires": { - "fstream": "^1.0.0", - "inherits": "2", - "minimatch": "^3.0.0" - } - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "optional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "getpass": { - "version": "0.1.7", - "bundled": true, - "optional": true, - "requires": { - "assert-plus": "^1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "optional": true - } - } - }, - "glob": { - "version": "7.1.2", - "bundled": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "graceful-fs": { - "version": "4.1.11", - "bundled": true - }, - "har-schema": { - "version": "1.0.5", - "bundled": true, - "optional": true - }, - "har-validator": { - "version": "4.2.1", - "bundled": true, - "optional": true, - "requires": { - "ajv": "^4.9.1", - "har-schema": "^1.0.5" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "optional": true - }, - "hawk": { - "version": "3.1.3", - "bundled": true, - "requires": { - "boom": "2.x.x", - "cryptiles": "2.x.x", - "hoek": "2.x.x", - "sntp": "1.x.x" - } - }, - "hoek": { - "version": "2.16.3", - "bundled": true - }, - "http-signature": { - "version": "1.1.1", - "bundled": true, - "optional": true, - "requires": { - "assert-plus": "^0.2.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true - }, - "ini": { - "version": "1.3.4", - "bundled": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "is-typedarray": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "isarray": { - "version": "1.0.0", - "bundled": true - }, - "isstream": { - "version": "0.1.2", - "bundled": true, - "optional": true - }, - "jodid25519": { - "version": "1.0.2", - "bundled": true, - "optional": true, - "requires": { - "jsbn": "~0.1.0" - } - }, - "jsbn": { - "version": "0.1.1", - "bundled": true, - "optional": true - }, - "json-schema": { - "version": "0.2.3", - "bundled": true, - "optional": true - }, - "json-stable-stringify": { - "version": "1.0.1", - "bundled": true, - "optional": true, - "requires": { - "jsonify": "~0.0.0" - } - }, - "json-stringify-safe": { - "version": "5.0.1", - "bundled": true, - "optional": true - }, - "jsonify": { - "version": "0.0.0", - "bundled": true, - "optional": true - }, - "jsprim": { - "version": "1.4.0", - "bundled": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.0.2", - "json-schema": "0.2.3", - "verror": "1.3.6" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "optional": true - } - } - }, - "mime-db": { - "version": "1.27.0", - "bundled": true - }, - "mime-types": { - "version": "2.1.15", - "bundled": true, - "requires": { - "mime-db": "~1.27.0" - } - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "bundled": true, - "optional": true - }, - "node-pre-gyp": { - "version": "0.6.39", - "bundled": true, - "optional": true, + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "optional": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" + }, + "node_modules/growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "engines": { + "node": ">=4.x" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dependencies": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dependencies": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash-base/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/hash-base/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "bin": { + "he": "bin/he" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/home-or-tmp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", + "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", + "dependencies": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=" + }, + "node_modules/import-local": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", + "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local/node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/import-local/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local/node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==" + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "optional": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "node_modules/is-core-module": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", + "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finite": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "optional": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "engines": { + "node": ">=4" + } + }, + "node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jade": { + "version": "0.26.3", + "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", + "integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=", + "deprecated": "Jade has been renamed to pug, please install the latest version of pug instead of jade", + "dependencies": { + "commander": "0.6.1", + "mkdirp": "0.3.0" + }, + "bin": { + "jade": "bin/jade" + } + }, + "node_modules/jade/node_modules/commander": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz", + "integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=", + "engines": { + "node": ">= 0.4.x" + } + }, + "node_modules/jade/node_modules/mkdirp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", + "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=", + "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", + "engines": { + "node": "*" + } + }, + "node_modules/js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" + }, + "node_modules/jsesc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/just-extend": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", + "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==" + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", + "engines": { + "node": ">=4.3.0 <5.0.0 || >=5.10" + } + }, + "node_modules/loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/loader-utils/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" + }, + "node_modules/lolex": { + "version": "2.7.5", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.7.5.tgz", + "integrity": "sha512-l9x0+1offnKKIzYVjyXU2SiwhXDLekRzKyhnbyldPHvC7BvLPVpdNUNR2KeMAiCN2D/kLNttZgQD5WjSxuBx3Q==" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dependencies": { + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dependencies": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/miller-rabin/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "node_modules/mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "dependencies": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dependencies": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mocha": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", + "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", + "dependencies": { + "browser-stdout": "1.3.1", + "commander": "2.15.1", + "debug": "3.1.0", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.5", + "he": "1.1.1", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "supports-color": "5.4.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/mocha/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/mocha/node_modules/minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "node_modules/mocha/node_modules/mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", + "dependencies": { + "minimist": "0.0.8" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "dependencies": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/nan": { + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", + "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", + "optional": true + }, + "node_modules/nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "node_modules/nise": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.3.tgz", + "integrity": "sha512-Ymbac/94xeIrMf59REBPOv0thr+CJVFMhrlAkW/gjCIE58BGQdCj0x7KRCb3yz+Ga2Rz3E9XXSvUyyxqqhjQAQ==", + "dependencies": { + "@sinonjs/formatio": "^3.2.1", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "lolex": "^5.0.1", + "path-to-regexp": "^1.7.0" + } + }, + "node_modules/nise/node_modules/@sinonjs/formatio": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.2.tgz", + "integrity": "sha512-B8SEsgd8gArBLMD6zpRw3juQ2FVSsmdd7qlevyDqzS9WTCtvF55/gAL+h6gue8ZvPYcdiPdvueM/qm//9XzyTQ==", + "dependencies": { + "@sinonjs/commons": "^1", + "@sinonjs/samsam": "^3.1.0" + } + }, + "node_modules/nise/node_modules/lolex": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz", + "integrity": "sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A==", + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "dependencies": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dependencies": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-descriptor/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dependencies": { + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" + }, + "node_modules/os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "engines": { + "node": ">=4" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, + "node_modules/parallel-transform": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", + "dependencies": { + "cyclist": "^1.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "node_modules/parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "dependencies": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==" + }, + "node_modules/path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "optional": true + }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "engines": { + "node": ">=4" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dependencies": { + "isarray": "0.0.1" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "optional": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dependencies": { + "find-up": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=" + }, + "node_modules/promises-aplus-tests": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/promises-aplus-tests/-/promises-aplus-tests-2.1.2.tgz", + "integrity": "sha1-drfFY4locghhlpz7zYeVr9J0iFw=", + "dependencies": { + "mocha": "^2.5.3", + "sinon": "^1.10.3", + "underscore": "~1.8.3" + }, + "bin": { + "promises-aplus-tests": "lib/cli.js" + } + }, + "node_modules/promises-aplus-tests/node_modules/commander": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.3.0.tgz", + "integrity": "sha1-/UMOiJgy7DU7ms0d4hfBHLPu+HM=", + "engines": { + "node": ">= 0.6.x" + } + }, + "node_modules/promises-aplus-tests/node_modules/debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "dependencies": { + "ms": "0.7.1" + } + }, + "node_modules/promises-aplus-tests/node_modules/diff": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz", + "integrity": "sha1-fyjS657nsVqX79ic5j3P2qPMur8=", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/promises-aplus-tests/node_modules/escape-string-regexp": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz", + "integrity": "sha1-Tbwv5nTnGUnK8/smlc5/LcHZqNE=", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/promises-aplus-tests/node_modules/glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", + "dependencies": { + "inherits": "2", + "minimatch": "0.3" + }, + "engines": { + "node": "*" + } + }, + "node_modules/promises-aplus-tests/node_modules/growl": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", + "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=" + }, + "node_modules/promises-aplus-tests/node_modules/lolex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.3.2.tgz", + "integrity": "sha1-fD2mL/yzDw9agKJWbKJORdigHzE=" + }, + "node_modules/promises-aplus-tests/node_modules/lru-cache": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=" + }, + "node_modules/promises-aplus-tests/node_modules/minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", + "deprecated": "Please update to minimatch 3.0.2 or higher to avoid a RegExp DoS issue", + "dependencies": { + "lru-cache": "2", + "sigmund": "~1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/promises-aplus-tests/node_modules/minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "node_modules/promises-aplus-tests/node_modules/mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", + "dependencies": { + "minimist": "0.0.8" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/promises-aplus-tests/node_modules/mocha": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-2.5.3.tgz", + "integrity": "sha1-FhvlvetJZ3HrmzV0UFC2IrWu/Fg=", + "dependencies": { + "commander": "2.3.0", + "debug": "2.2.0", + "diff": "1.4.0", + "escape-string-regexp": "1.0.2", + "glob": "3.2.11", + "growl": "1.9.2", + "jade": "0.26.3", + "mkdirp": "0.5.1", + "supports-color": "1.2.0", + "to-iso-string": "0.0.2" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 0.8.x" + } + }, + "node_modules/promises-aplus-tests/node_modules/ms": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" + }, + "node_modules/promises-aplus-tests/node_modules/samsam": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.2.tgz", + "integrity": "sha1-vsEf3IOp/aBjQBIQ5AF2wwJNFWc=", + "deprecated": "This package has been deprecated in favour of @sinonjs/samsam" + }, + "node_modules/promises-aplus-tests/node_modules/sinon": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-1.17.7.tgz", + "integrity": "sha1-RUKk9JugxFwF6y6d2dID4rjv4L8=", + "dependencies": { + "formatio": "1.1.1", + "lolex": "1.3.2", + "samsam": "1.1.2", + "util": ">=0.10.3 <1" + }, + "engines": { + "node": ">=0.1.103" + } + }, + "node_modules/promises-aplus-tests/node_modules/supports-color": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-1.2.0.tgz", + "integrity": "sha1-/x7R5hFp0Gs88tWI4YixjYhH4X4=", + "bin": { + "supports-color": "cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" + }, + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/public-encrypt/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dependencies": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "node_modules/pumpify/node_modules/pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "node_modules/readable-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "optional": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/rechoir": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.0.tgz", + "integrity": "sha512-ADsDEH2bvbjltXEP+hTIAmeFekTFK0V2BTxMkok6qILyAJEXV0AFfoWcAq4yfll5VdIMd/RVXq0lR+wQi5ZU3Q==", + "dependencies": { + "resolve": "^1.9.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" + }, + "node_modules/regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + }, + "node_modules/regenerator-transform": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", + "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", + "dependencies": { + "babel-runtime": "^6.18.0", + "babel-types": "^6.19.0", + "private": "^0.1.6" + } + }, + "node_modules/regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dependencies": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regexpu-core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", + "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", + "dependencies": { + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" + } + }, + "node_modules/regjsgen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=" + }, + "node_modules/regjsparser": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "optional": true + }, + "node_modules/repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dependencies": { + "is-finite": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dependencies": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "deprecated": "https://github.com/lydell/resolve-url#deprecated" + }, + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "engines": { + "node": ">=0.12" + } + }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "dependencies": { + "aproba": "^1.1.1" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dependencies": { + "ret": "~0.1.10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/samsam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz", + "integrity": "sha512-1HwIYD/8UlOtFS3QO3w7ey+SdSDFE4HRNLZoZRYVQefrOY3l17epswImeB1ijgJFQJodIaHcwkp3r/myBjFVbg==", + "deprecated": "This package has been deprecated in favour of @sinonjs/samsam" + }, + "node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" + }, + "node_modules/signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + }, + "node_modules/sinon": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-4.5.0.tgz", + "integrity": "sha512-trdx+mB0VBBgoYucy6a9L7/jfQOmvGeaKZT4OOJ+lPAtI8623xyGr8wLiE4eojzBS8G9yXbhx42GHUOVLr4X2w==", + "hasInstallScript": true, + "dependencies": { + "@sinonjs/formatio": "^2.0.0", + "diff": "^3.1.0", + "lodash.get": "^4.4.2", + "lolex": "^2.2.0", + "nise": "^1.2.0", + "supports-color": "^5.1.0", + "type-detect": "^4.0.5" + } + }, + "node_modules/sinon/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dependencies": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dependencies": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dependencies": { + "kind-of": "^3.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==" + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "node_modules/source-map-support": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", + "dependencies": { + "source-map": "^0.5.6" + } + }, + "node_modules/source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==" + }, + "node_modules/split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dependencies": { + "extend-shallow": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ssri": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz", + "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==", + "dependencies": { + "figgy-pudding": "^3.5.1" + } + }, + "node_modules/static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dependencies": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "dependencies": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "node_modules/stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "dependencies": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "node_modules/stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dependencies": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "node_modules/stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/terser": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", + "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", + "dependencies": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz", + "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==", + "dependencies": { + "cacache": "^12.0.2", + "find-cache-dir": "^2.1.0", + "is-wsl": "^1.1.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^4.0.0", + "source-map": "^0.6.1", + "terser": "^4.1.2", + "webpack-sources": "^1.4.0", + "worker-farm": "^1.7.0" + }, + "engines": { + "node": ">= 6.9.0" + }, + "peerDependencies": { + "webpack": "^4.0.0" + } + }, + "node_modules/terser-webpack-plugin/node_modules/find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser-webpack-plugin/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser-webpack-plugin/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser-webpack-plugin/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser-webpack-plugin/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terser-webpack-plugin/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser-webpack-plugin/node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/terser-webpack-plugin/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "engines": { + "node": ">=6" + } + }, + "node_modules/terser-webpack-plugin/node_modules/pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser-webpack-plugin/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/terser/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/terser/node_modules/source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/timers-browserify": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", + "dependencies": { + "setimmediate": "^1.0.4" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=" + }, + "node_modules/to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-iso-string": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/to-iso-string/-/to-iso-string-0.0.2.tgz", + "integrity": "sha1-TcGeZk38y+Jb2NtQiwDG2hWCVdE=", + "deprecated": "to-iso-string has been deprecated, use @segment/to-iso-string instead." + }, + "node_modules/to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-object-path/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dependencies": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=" + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "engines": { + "node": ">=4" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "node_modules/underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + }, + "node_modules/union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dependencies": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/union-value/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dependencies": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dependencies": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "optional": true, + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/uri-js/node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "deprecated": "Please see https://github.com/lydell/urix#deprecated" + }, + "node_modules/url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + }, + "node_modules/use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "dependencies": { + "inherits": "2.0.3" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "node_modules/util/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==" + }, + "node_modules/vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==" + }, + "node_modules/watchpack": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", + "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", + "dependencies": { + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" + }, + "optionalDependencies": { + "chokidar": "^3.4.1", + "watchpack-chokidar2": "^2.0.1" + } + }, + "node_modules/watchpack-chokidar2": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz", + "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==", + "optional": true, + "dependencies": { + "chokidar": "^2.1.8" + } + }, + "node_modules/watchpack-chokidar2/node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "optional": true, + "dependencies": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "node_modules/watchpack-chokidar2/node_modules/anymatch/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "optional": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "deprecated": "Chokidar 2 will break on node v14+. Upgrade to chokidar 3 with 15x less dependencies.", + "optional": true, + "dependencies": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "optionalDependencies": { + "fsevents": "^1.2.7" + } + }, + "node_modules/watchpack-chokidar2/node_modules/fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "optional": true, + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "optional": true, + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "optional": true, + "dependencies": { + "binary-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "optional": true, + "dependencies": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/webpack": { + "version": "4.46.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.46.0.tgz", + "integrity": "sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q==", + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/wasm-edit": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "acorn": "^6.4.1", + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^4.5.0", + "eslint-scope": "^4.0.3", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.4.0", + "loader-utils": "^1.2.3", + "memory-fs": "^0.4.1", + "micromatch": "^3.1.10", + "mkdirp": "^0.5.3", + "neo-async": "^2.6.1", + "node-libs-browser": "^2.2.1", + "schema-utils": "^1.0.0", + "tapable": "^1.1.3", + "terser-webpack-plugin": "^1.4.3", + "watchpack": "^1.7.4", + "webpack-sources": "^1.4.1" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=6.11.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + }, + "webpack-command": { + "optional": true + } + } + }, + "node_modules/webpack-cli": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.7.0.tgz", + "integrity": "sha512-7bKr9182/sGfjFm+xdZSwgQuFjgEcy0iCTIBxRUeteJ2Kr8/Wz0qNJX+jw60LU36jApt4nmMkep6+W5AKhok6g==", + "dependencies": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^1.0.3", + "@webpack-cli/info": "^1.2.4", + "@webpack-cli/serve": "^1.4.0", + "colorette": "^1.2.1", + "commander": "^7.0.0", + "execa": "^5.0.0", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^2.2.0", + "rechoir": "^0.7.0", + "v8-compile-cache": "^2.2.0", + "webpack-merge": "^5.7.3" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "webpack": "4.x.x || 5.x.x" + }, + "peerDependenciesMeta": { + "@webpack-cli/generators": { + "optional": true + }, + "@webpack-cli/migrate": { + "optional": true + }, + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/webpack-cli/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/webpack-merge": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.7.3.tgz", + "integrity": "sha512-6/JUQv0ELQ1igjGDzHkXbVDRxkfA57Zw7PfiupdLFJYrgFqY5ZP8xxbpp2lU3EPwYx89ht5Z/aDkD40hFCm5AA==", + "dependencies": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dependencies": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + }, + "node_modules/webpack-sources/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==" + }, + "node_modules/worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "dependencies": { + "errno": "~0.1.7" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + } + }, + "dependencies": { + "@discoveryjs/json-ext": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.3.tgz", + "integrity": "sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g==" + }, + "@sinonjs/commons": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/formatio": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz", + "integrity": "sha512-ls6CAMA6/5gG+O/IdsBcblvnd8qcO/l1TYoNeAzp3wcISOxlPXQEus0mLcdwazEkWjaBdaJ3TaxmNgCLWwvWzg==", + "requires": { + "samsam": "1.3.0" + } + }, + "@sinonjs/samsam": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.3.tgz", + "integrity": "sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ==", + "requires": { + "@sinonjs/commons": "^1.3.0", + "array-from": "^2.1.1", + "lodash": "^4.17.15" + } + }, + "@sinonjs/text-encoding": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", + "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==" + }, + "@webassemblyjs/ast": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", + "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", + "requires": { + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz", + "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==" + }, + "@webassemblyjs/helper-api-error": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz", + "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==" + }, + "@webassemblyjs/helper-buffer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz", + "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==" + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz", + "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==", + "requires": { + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz", + "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==" + }, + "@webassemblyjs/helper-module-context": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz", + "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==", + "requires": { + "@webassemblyjs/ast": "1.9.0" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", + "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==" + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz", + "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==", + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz", + "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==", + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz", + "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz", + "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==" + }, + "@webassemblyjs/wasm-edit": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz", + "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==", + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/helper-wasm-section": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-opt": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz", + "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==", + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz", + "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==", + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz", + "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==", + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz", + "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==", + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/floating-point-hex-parser": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-code-frame": "1.9.0", + "@webassemblyjs/helper-fsm": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz", + "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "@webpack-cli/configtest": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.0.3.tgz", + "integrity": "sha512-WQs0ep98FXX2XBAfQpRbY0Ma6ADw8JR6xoIkaIiJIzClGOMqVRvPCWqndTxf28DgFopWan0EKtHtg/5W1h0Zkw==", + "requires": {} + }, + "@webpack-cli/info": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.2.4.tgz", + "integrity": "sha512-ogE2T4+pLhTTPS/8MM3IjHn0IYplKM4HbVNMCWA9N4NrdPzunwenpCsqKEXyejMfRu6K8mhauIPYf8ZxWG5O6g==", + "requires": { + "envinfo": "^7.7.3" + } + }, + "@webpack-cli/serve": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.4.0.tgz", + "integrity": "sha512-xgT/HqJ+uLWGX+Mzufusl3cgjAcnqYYskaB7o0vRcwOEfuu6hMzSILQpnIzFMGsTaeaX4Nnekl+6fadLbl1/Vg==", + "requires": {} + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" + }, + "acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==" + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", + "requires": {} + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "requires": {} + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "optional": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" + }, + "array-from": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", + "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=" + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" + }, + "asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "requires": { + "object-assign": "^4.1.1", + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "optional": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + } + }, + "babel-core": { + "version": "6.26.3", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", + "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", + "requires": { + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.1", + "debug": "^2.6.9", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.8", + "slash": "^1.0.0", + "source-map": "^0.5.7" + } + }, + "babel-generator": { + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", + "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", + "requires": { + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "detect-indent": "^4.0.0", + "jsesc": "^1.3.0", + "lodash": "^4.17.4", + "source-map": "^0.5.7", + "trim-right": "^1.0.1" + } + }, + "babel-helper-builder-binary-assignment-operator-visitor": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", + "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", + "requires": { + "babel-helper-explode-assignable-expression": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-call-delegate": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", + "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", + "requires": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-define-map": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", + "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-helper-explode-assignable-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", + "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", + "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", + "requires": { + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-get-function-arity": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", + "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-hoist-variables": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", + "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-optimise-call-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", + "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-regex": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", + "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", + "requires": { + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-helper-remap-async-to-generator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", + "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=", + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-replace-supers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", + "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", + "requires": { + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helpers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", + "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-loader": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-7.1.5.tgz", + "integrity": "sha512-iCHfbieL5d1LfOQeeVJEUyD9rTwBcP/fcEbRCfempxTDuqrKpu0AZjLAQHEQa3Yqyj9ORKe2iHfoj4rHLf7xpw==", + "requires": { + "find-cache-dir": "^1.0.0", + "loader-utils": "^1.0.2", + "mkdirp": "^0.5.1" + } + }, + "babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-check-es2015-constants": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", + "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-syntax-async-functions": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", + "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=" + }, + "babel-plugin-syntax-exponentiation-operator": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", + "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=" + }, + "babel-plugin-syntax-trailing-function-commas": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", + "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=" + }, + "babel-plugin-transform-async-to-generator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", + "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=", + "requires": { + "babel-helper-remap-async-to-generator": "^6.24.1", + "babel-plugin-syntax-async-functions": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-arrow-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", + "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-block-scoped-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", + "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-block-scoping": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", + "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", + "requires": { + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-plugin-transform-es2015-classes": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", + "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", + "requires": { + "babel-helper-define-map": "^6.24.1", + "babel-helper-function-name": "^6.24.1", + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-helper-replace-supers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-computed-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", + "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-destructuring": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", + "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-duplicate-keys": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", + "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-for-of": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", + "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", + "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", + "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-modules-amd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", + "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", + "requires": { + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-modules-commonjs": { + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", + "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", + "requires": { + "babel-plugin-transform-strict-mode": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-types": "^6.26.0" + } + }, + "babel-plugin-transform-es2015-modules-systemjs": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", + "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", + "requires": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-modules-umd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", + "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", + "requires": { + "babel-plugin-transform-es2015-modules-amd": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-object-super": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", + "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", + "requires": { + "babel-helper-replace-supers": "^6.24.1", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-parameters": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", + "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", + "requires": { + "babel-helper-call-delegate": "^6.24.1", + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-shorthand-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", + "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-spread": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", + "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-sticky-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", + "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", + "requires": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-template-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", + "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-typeof-symbol": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", + "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-unicode-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", + "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", + "requires": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "regexpu-core": "^2.0.0" + } + }, + "babel-plugin-transform-exponentiation-operator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", + "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=", + "requires": { + "babel-helper-builder-binary-assignment-operator-visitor": "^6.24.1", + "babel-plugin-syntax-exponentiation-operator": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-regenerator": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", + "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", + "requires": { + "regenerator-transform": "^0.10.0" + } + }, + "babel-plugin-transform-strict-mode": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", + "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-preset-env": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.7.0.tgz", + "integrity": "sha512-9OR2afuKDneX2/q2EurSftUYM0xGu4O2D9adAhVfADDhrYDaxXV0rBbevVYoY9n6nyX1PmQW/0jtpJvUNr9CHg==", + "requires": { + "babel-plugin-check-es2015-constants": "^6.22.0", + "babel-plugin-syntax-trailing-function-commas": "^6.22.0", + "babel-plugin-transform-async-to-generator": "^6.22.0", + "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoping": "^6.23.0", + "babel-plugin-transform-es2015-classes": "^6.23.0", + "babel-plugin-transform-es2015-computed-properties": "^6.22.0", + "babel-plugin-transform-es2015-destructuring": "^6.23.0", + "babel-plugin-transform-es2015-duplicate-keys": "^6.22.0", + "babel-plugin-transform-es2015-for-of": "^6.23.0", + "babel-plugin-transform-es2015-function-name": "^6.22.0", + "babel-plugin-transform-es2015-literals": "^6.22.0", + "babel-plugin-transform-es2015-modules-amd": "^6.22.0", + "babel-plugin-transform-es2015-modules-commonjs": "^6.23.0", + "babel-plugin-transform-es2015-modules-systemjs": "^6.23.0", + "babel-plugin-transform-es2015-modules-umd": "^6.23.0", + "babel-plugin-transform-es2015-object-super": "^6.22.0", + "babel-plugin-transform-es2015-parameters": "^6.23.0", + "babel-plugin-transform-es2015-shorthand-properties": "^6.22.0", + "babel-plugin-transform-es2015-spread": "^6.22.0", + "babel-plugin-transform-es2015-sticky-regex": "^6.22.0", + "babel-plugin-transform-es2015-template-literals": "^6.22.0", + "babel-plugin-transform-es2015-typeof-symbol": "^6.23.0", + "babel-plugin-transform-es2015-unicode-regex": "^6.22.0", + "babel-plugin-transform-exponentiation-operator": "^6.22.0", + "babel-plugin-transform-regenerator": "^6.22.0", + "browserslist": "^3.2.6", + "invariant": "^2.2.2", + "semver": "^5.3.0" + } + }, + "babel-register": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", + "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", + "requires": { + "babel-core": "^6.26.0", + "babel-runtime": "^6.26.0", + "core-js": "^2.5.0", + "home-or-tmp": "^2.0.0", + "lodash": "^4.17.4", + "mkdirp": "^0.5.1", + "source-map-support": "^0.4.15" + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "babel-template": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", + "requires": { + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" + } + }, + "babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "requires": { + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" + } + }, + "babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "requires": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + } + } + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "optional": true + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + } + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "requires": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "requires": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", "requires": { - "detect-libc": "^1.0.2", - "hawk": "3.1.3", - "mkdirp": "^0.5.1", - "nopt": "^4.0.1", - "npmlog": "^4.0.2", - "rc": "^1.1.7", - "request": "2.81.0", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^2.2.1", - "tar-pack": "^3.4.0" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" } }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "optional": true, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "requires": { + "pako": "~1.0.5" + } + }, + "browserslist": { + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-3.2.8.tgz", + "integrity": "sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==", + "requires": { + "caniuse-lite": "^1.0.30000844", + "electron-to-chromium": "^1.3.47" + } + }, + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + } + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" + }, + "cacache": { + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", + "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", + "requires": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + }, + "dependencies": { + "glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", "requires": { - "abbrev": "1", - "osenv": "^0.1.4" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } - }, - "npmlog": { - "version": "4.1.0", - "bundled": true, + } + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "caniuse-lite": { + "version": "1.0.30001233", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001233.tgz", + "integrity": "sha512-BmkbxLfStqiPA7IEzQpIk0UFZFf3A4E6fzjPJ6OR+bFC2L8ES9J8zGA/asoi47p8XDVkev+WJo2I2Nc8c/34Yg==" + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "chokidar": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "optional": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.3.1", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + }, + "dependencies": { + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "optional": true, "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true - }, - "oauth-sign": { - "version": "0.8.2", - "bundled": true, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "requires": { - "wrappy": "1" + "fill-range": "^7.0.1" } }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "osenv": { - "version": "0.1.4", - "bundled": true, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "optional": true, "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" + "to-regex-range": "^5.0.1" } }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true - }, - "performance-now": { - "version": "0.2.0", - "bundled": true, - "optional": true - }, - "process-nextick-args": { - "version": "1.0.7", - "bundled": true - }, - "punycode": { - "version": "1.4.1", - "bundled": true, - "optional": true - }, - "qs": { - "version": "6.4.0", - "bundled": true, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "optional": true }, - "rc": { - "version": "1.2.1", - "bundled": true, - "optional": true, - "requires": { - "deep-extend": "~0.4.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.2.9", - "bundled": true, - "requires": { - "buffer-shims": "~1.0.0", - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "string_decoder": "~1.0.0", - "util-deprecate": "~1.0.1" - } - }, - "request": { - "version": "2.81.0", - "bundled": true, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "optional": true, "requires": { - "aws-sign2": "~0.6.0", - "aws4": "^1.2.1", - "caseless": "~0.12.0", - "combined-stream": "~1.0.5", - "extend": "~3.0.0", - "forever-agent": "~0.6.1", - "form-data": "~2.1.1", - "har-validator": "~4.2.1", - "hawk": "~3.1.3", - "http-signature": "~1.1.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.7", - "oauth-sign": "~0.8.1", - "performance-now": "^0.2.0", - "qs": "~6.4.0", - "safe-buffer": "^5.0.1", - "stringstream": "~0.0.4", - "tough-cookie": "~2.3.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.0.0" + "is-number": "^7.0.0" } - }, - "rimraf": { - "version": "2.6.1", - "bundled": true, + } + } + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==" + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "requires": { - "glob": "^7.0.5" + "is-descriptor": "^0.1.0" } }, - "safe-buffer": { - "version": "5.0.1", - "bundled": true - }, - "semver": { - "version": "5.3.0", - "bundled": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "optional": true - }, - "sntp": { - "version": "1.0.9", - "bundled": true, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "requires": { - "hoek": "2.x.x" + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } } }, - "sshpk": { - "version": "1.13.0", - "bundled": true, - "optional": true, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jodid25519": "^1.0.0", - "jsbn": "~0.1.0", - "tweetnacl": "~0.14.0" + "kind-of": "^3.0.2" }, "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "optional": true + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } } } }, - "string-width": { - "version": "1.0.2", - "bundled": true, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" } }, - "string_decoder": { - "version": "1.0.1", - "bundled": true, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + } + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "colorette": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", + "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==" + }, + "commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==" + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==" + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" + }, + "core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "cyclist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", + "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + } + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "detect-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", + "requires": { + "repeating": "^2.0.0" + } + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==" + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==" + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "electron-to-chromium": { + "version": "1.3.745", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.745.tgz", + "integrity": "sha512-ZZCx4CS3kYT3BREYiIXocDqlNPT56KfdTS1Ogo4yvxRriBqiEXCDTLIQZT/zNVtby91xTWMMxW2NBiXh8bpLHw==" + }, + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", + "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==", + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + }, + "dependencies": { + "memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", "requires": { - "safe-buffer": "^5.0.1" + "errno": "^0.1.3", + "readable-stream": "^2.0.1" } - }, - "stringstream": { - "version": "0.0.5", - "bundled": true, - "optional": true - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, + } + } + }, + "envinfo": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", + "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==" + }, + "errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "requires": { + "prr": "~1.0.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==" + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.0.tgz", + "integrity": "sha512-CkdUB7s2y6S+d4y+OM/+ZtQcJCiKUCth4cNImGMqrt2zEVtW2rfHGspQBE1GDo6LjeNIQmTPKXqTCKjqFKyu3A==", + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "requires": { - "ansi-regex": "^2.0.0" + "is-descriptor": "^0.1.0" } }, - "strip-json-comments": { + "extend-shallow": { "version": "2.0.1", - "bundled": true, - "optional": true - }, - "tar": { - "version": "2.2.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "requires": { - "block-stream": "*", - "fstream": "^1.0.2", - "inherits": "2" + "is-extendable": "^0.1.0" } }, - "tar-pack": { - "version": "3.4.0", - "bundled": true, - "optional": true, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "requires": { - "debug": "^2.2.0", - "fstream": "^1.0.10", - "fstream-ignore": "^1.0.5", - "once": "^1.3.3", - "readable-stream": "^2.1.4", - "rimraf": "^2.5.1", - "tar": "^2.2.1", - "uid-number": "^0.0.6" + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } } }, - "tough-cookie": { - "version": "2.3.2", - "bundled": true, - "optional": true, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "requires": { - "punycode": "^1.4.1" + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } } }, - "tunnel-agent": { - "version": "0.6.0", - "bundled": true, - "optional": true, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "requires": { - "safe-buffer": "^5.0.1" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" } }, - "tweetnacl": { - "version": "0.14.5", - "bundled": true, - "optional": true - }, - "uid-number": { - "version": "0.0.6", - "bundled": true, - "optional": true - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true - }, - "uuid": { - "version": "3.0.1", - "bundled": true, - "optional": true + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" }, - "verror": { - "version": "1.3.6", - "bundled": true, - "optional": true, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "requires": { - "extsprintf": "1.0.2" + "is-descriptor": "^1.0.0" } }, - "wide-align": { - "version": "1.1.2", - "bundled": true, - "optional": true, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "requires": { - "string-width": "^1.0.2" + "is-extendable": "^0.1.0" } }, - "wrappy": { - "version": "1.0.2", - "bundled": true + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" } } }, - "get-caller-file": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", - "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=" + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" + "fastest-levenshtein": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", + "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==" + }, + "figgy-pudding": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", + "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==" + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "optional": true }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "requires": { - "assert-plus": "^1.0.0" + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" }, "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" } } }, - "gh-got": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/gh-got/-/gh-got-6.0.0.tgz", - "integrity": "sha512-F/mS+fsWQMo1zfgG9MD8KWvTWPPzzhuVwY++fhQ5Ggd+0P+CAMHtzMZhNxG+TqGfHDChJKsbh6otfMGqO2AKBw==", + "find-cache-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", + "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", "requires": { - "got": "^7.0.0", - "is-plain-obj": "^1.1.0" + "commondir": "^1.0.1", + "make-dir": "^1.0.0", + "pkg-dir": "^2.0.0" } }, - "github-username": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/github-username/-/github-username-4.1.0.tgz", - "integrity": "sha1-y+KABBiDIG2kISrp5LXxacML9Bc=", + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "requires": { - "gh-got": "^6.0.0" + "locate-path": "^2.0.0" } }, - "glob-all": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-all/-/glob-all-3.1.0.tgz", - "integrity": "sha1-iRPd+17hrHgSZWJBsD1SF8ZLAqs=", + "flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", "requires": { - "glob": "^7.0.5", - "yargs": "~1.2.6" - }, - "dependencies": { - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.1.0.tgz", - "integrity": "sha1-md9lelJXTCHJBXSX33QnkLK0wN4=" - }, - "yargs": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-1.2.6.tgz", - "integrity": "sha1-nHtKgv1dWVsr8Xq23MQxNUMv40s=", - "requires": { - "minimist": "^0.1.0" - } - } + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" } }, - "glob-base": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" + }, + "formatio": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.1.1.tgz", + "integrity": "sha1-XtPM1jZVEJc4NGXZlhmRAOhhYek=", "requires": { - "glob-parent": "^2.0.0", - "is-glob": "^2.0.0" + "samsam": "~1.1" }, "dependencies": { - "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "requires": { - "is-glob": "^2.0.0" - } - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "requires": { - "is-extglob": "^1.0.0" - } + "samsam": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.3.tgz", + "integrity": "sha1-n1CHQZtNCR8jJXHn+lLpCw9VJiE=" } } }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "requires": { - "is-extglob": "^2.1.0" - } - } + "map-cache": "^0.2.2" } }, - "global": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz", - "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=", + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", "requires": { - "min-document": "^2.19.0", - "process": "~0.5.1" - }, - "dependencies": { - "process": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", - "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=" - } + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" } }, - "global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "requires": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, - "global-prefix": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "optional": true, "requires": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" + "is-glob": "^4.0.1" } }, "globals": { @@ -3499,103 +7729,22 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==" }, - "globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", - "requires": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "dependencies": { - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" - } - } - }, - "got": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz", - "integrity": "sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw==", - "requires": { - "decompress-response": "^3.2.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "is-plain-obj": "^1.1.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "isurl": "^1.0.0-alpha5", - "lowercase-keys": "^1.0.0", - "p-cancelable": "^0.3.0", - "p-timeout": "^1.1.1", - "safe-buffer": "^5.0.1", - "timed-out": "^4.0.0", - "url-parse-lax": "^1.0.0", - "url-to-options": "^1.0.1" - } - }, "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" - }, - "grouped-queue": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/grouped-queue/-/grouped-queue-0.3.3.tgz", - "integrity": "sha1-wWfSpTGcWg4JZO9qJbfC34mWyFw=", - "requires": { - "lodash": "^4.17.2" - } + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" }, - "har-schema": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", - "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=" + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==" }, - "har-validator": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", - "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "requires": { - "ajv": "^4.9.1", - "har-schema": "^1.0.5" - }, - "dependencies": { - "ajv": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", - "requires": { - "co": "^4.6.0", - "json-stable-stringify": "^1.0.1" - } - } + "function-bind": "^1.1.1" } }, "has-ansi": { @@ -3604,38 +7753,13 @@ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", "requires": { "ansi-regex": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - } } }, - "has-color": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz", - "integrity": "sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8=" - }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, - "has-symbol-support-x": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", - "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==" - }, - "has-to-string-tag-x": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", - "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", - "requires": { - "has-symbol-support-x": "^1.4.1" - } - }, "has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", @@ -3666,31 +7790,39 @@ } }, "hash-base": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", - "integrity": "sha1-ZuodhW206KVHDK32/OI65SRO8uE=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", "requires": { - "inherits": "^2.0.1" + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } } }, "hash.js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", - "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", "requires": { "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.0" - } - }, - "hawk": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", - "requires": { - "boom": "2.x.x", - "cryptiles": "2.x.x", - "hoek": "2.x.x", - "sntp": "1.x.x" + "minimalistic-assert": "^1.0.1" } }, "he": { @@ -3708,11 +7840,6 @@ "minimalistic-crypto-utils": "^1.0.1" } }, - "hoek": { - "version": "2.16.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" - }, "home-or-tmp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", @@ -3722,66 +7849,97 @@ "os-tmpdir": "^1.0.1" } }, - "homedir-polyfill": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", - "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", - "requires": { - "parse-passwd": "^1.0.0" - } - }, - "hosted-git-info": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz", - "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==" - }, - "http-signature": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", - "requires": { - "assert-plus": "^0.2.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, "https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" }, - "iconv-lite": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" }, "ieee754": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", - "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, "iferr": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=" }, + "import-local": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", + "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "requires": { + "find-up": "^4.0.0" + } + } + } + }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" }, - "indent-string": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", - "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", - "requires": { - "repeating": "^2.0.0" - } - }, - "indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==" }, "inflight": { "version": "1.0.6", @@ -3793,54 +7951,23 @@ } }, "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" - }, - "inquirer": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", - "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", - "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^2.0.4", - "figures": "^2.0.0", - "lodash": "^4.3.0", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rx-lite": "^4.0.8", - "rx-lite-aggregates": "^4.0.8", - "string-width": "^2.1.0", - "strip-ansi": "^4.0.0", - "through": "^2.3.6" - } + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "interpret": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", - "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==" }, "invariant": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.3.tgz", - "integrity": "sha512-7Z5PPegwDTyjbaeCnV0efcyS6vdKAU51kpEmS7QFib3P4822l8ICYyMn7qvJnc+WzLoDsuI9gPMKbJ8pCu8XtA==", + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", "requires": { "loose-envify": "^1.0.0" } }, - "invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" - }, "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", @@ -3849,17 +7976,13 @@ "kind-of": "^6.0.0" } }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" - }, "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "optional": true, "requires": { - "binary-extensions": "^1.0.0" + "binary-extensions": "^2.0.0" } }, "is-buffer": { @@ -3867,12 +7990,12 @@ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, - "is-builtin-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", - "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "is-core-module": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", + "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", "requires": { - "builtin-modules": "^1.0.0" + "has": "^1.0.3" } }, "is-data-descriptor": { @@ -3893,46 +8016,30 @@ "kind-of": "^6.0.2" } }, - "is-dotfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=" - }, - "is-equal-shallow": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", - "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "requires": { - "is-primitive": "^2.0.0" + "is-plain-object": "^2.0.4" } }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" - }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "optional": true }, "is-finite": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", - "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==" }, "is-glob": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", - "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "optional": true, "requires": { "is-extglob": "^2.1.1" } @@ -3955,31 +8062,6 @@ } } }, - "is-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", - "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=" - }, - "is-odd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-2.0.0.tgz", - "integrity": "sha512-OTiixgpZAT1M4NHgS5IguFp/Vz2VI3U7Goh4/HA1adtwyLtSBrxYlcSYkhpAE07s4fKEcjrFxyvtQBND4vFQyQ==", - "requires": { - "is-number": "^4.0.0" - }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==" - } - } - }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" - }, "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -3988,58 +8070,25 @@ "isobject": "^3.0.1" } }, - "is-posix-bracket": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", - "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=" - }, - "is-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", - "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=" - }, - "is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" - }, - "is-retry-allowed": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", - "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=" - }, - "is-scoped": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-scoped/-/is-scoped-1.0.0.tgz", - "integrity": "sha1-RJypgpnnEwOCViieyytUDcQ3yzA=", - "requires": { - "scoped-regex": "^1.0.0" - } - }, "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" }, "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=" + }, "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" }, "isexe": { "version": "2.0.0", @@ -4051,533 +8100,87 @@ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, - "istextorbinary": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-2.2.1.tgz", - "integrity": "sha512-TS+hoFl8Z5FAFMK38nhBkdLt44CclNRgDHWeMgsV8ko3nDlr/9UI2Sf839sW7enijf8oKsZYXRvM8g0it9Zmcw==", - "requires": { - "binaryextensions": "2", - "editions": "^1.3.3", - "textextensions": "2" - } - }, - "isurl": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", - "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", + "jade": { + "version": "0.26.3", + "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", + "integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=", "requires": { - "has-to-string-tag-x": "^1.2.0", - "is-object": "^1.0.1" - } - }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "optional": true - }, - "jscodeshift": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/jscodeshift/-/jscodeshift-0.4.1.tgz", - "integrity": "sha512-iOX6If+hsw0q99V3n31t4f5VlD1TQZddH08xbT65ZqA7T4Vkx68emrDZMUOLVvCEAJ6NpAk7DECe3fjC/t52AQ==", - "requires": { - "async": "^1.5.0", - "babel-plugin-transform-flow-strip-types": "^6.8.0", - "babel-preset-es2015": "^6.9.0", - "babel-preset-stage-1": "^6.5.0", - "babel-register": "^6.9.0", - "babylon": "^6.17.3", - "colors": "^1.1.2", - "flow-parser": "^0.*", - "lodash": "^4.13.1", - "micromatch": "^2.3.7", - "node-dir": "0.1.8", - "nomnom": "^1.8.1", - "recast": "^0.12.5", - "temp": "^0.8.1", - "write-file-atomic": "^1.2.0" - }, - "dependencies": { - "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "requires": { - "arr-flatten": "^1.0.1" - } - }, - "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=" - }, - "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", - "requires": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" - } - }, - "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", - "requires": { - "is-posix-bracket": "^0.1.0" - } - }, - "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", - "requires": { - "is-extglob": "^1.0.0" - } - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "requires": { - "is-extglob": "^1.0.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - }, - "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "requires": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" - } - }, - "recast": { - "version": "0.12.9", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.12.9.tgz", - "integrity": "sha512-y7ANxCWmMW8xLOaiopiRDlyjQ9ajKRENBH+2wjntIbk3A6ZR1+BLQttkmSHMY7Arl+AAZFwJ10grg2T6f1WI8A==", - "requires": { - "ast-types": "0.10.1", - "core-js": "^2.4.1", - "esprima": "~4.0.0", - "private": "~0.1.5", - "source-map": "~0.6.1" - } - }, - "source-map": { + "commander": "0.6.1", + "mkdirp": "0.3.0" + }, + "dependencies": { + "commander": { "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz", + "integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=" + }, + "mkdirp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", + "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=" } } }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" + }, "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=" }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" }, "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" - }, - "json-stable-stringify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", - "requires": { - "jsonify": "~0.0.0" - } - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "json5": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=" }, - "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - } - } - }, "just-extend": { - "version": "1.1.27", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-1.1.27.tgz", - "integrity": "sha512-mJVp13Ix6gFo3SBAy9U/kL+oeZqzlYYYLQBwXVBlVzIsZwBqGREnOro24oC/8s8aox+rJhtZ2DiQof++IrkA+g==" + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", + "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==" }, "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" - }, - "lazy-cache": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz", - "integrity": "sha1-uRkKT5EzVGlIQIWfio9whNiCImQ=", - "requires": { - "set-getter": "^0.1.0" - } - }, - "lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "requires": { - "invert-kv": "^1.0.0" - } - }, - "listr": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/listr/-/listr-0.12.0.tgz", - "integrity": "sha1-a84sD1YD+klYDqF81qAMwOX6RRo=", - "requires": { - "chalk": "^1.1.3", - "cli-truncate": "^0.2.1", - "figures": "^1.7.0", - "indent-string": "^2.1.0", - "is-promise": "^2.1.0", - "is-stream": "^1.1.0", - "listr-silent-renderer": "^1.1.1", - "listr-update-renderer": "^0.2.0", - "listr-verbose-renderer": "^0.4.0", - "log-symbols": "^1.0.2", - "log-update": "^1.0.2", - "ora": "^0.2.3", - "p-map": "^1.1.1", - "rxjs": "^5.0.0-beta.11", - "stream-to-observable": "^0.1.0", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", - "requires": { - "escape-string-regexp": "^1.0.5", - "object-assign": "^4.1.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - } - } - }, - "log-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", - "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", - "requires": { - "chalk": "^1.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - } - } - }, - "listr-silent-renderer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz", - "integrity": "sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4=" + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" }, - "listr-update-renderer": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.2.0.tgz", - "integrity": "sha1-yoDhd5tOcCZoB+ju0a1qvjmFUPk=", - "requires": { - "chalk": "^1.1.3", - "cli-truncate": "^0.2.1", - "elegant-spinner": "^1.0.1", - "figures": "^1.7.0", - "indent-string": "^3.0.0", - "log-symbols": "^1.0.2", - "log-update": "^1.0.2", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", - "requires": { - "escape-string-regexp": "^1.0.5", - "object-assign": "^4.1.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - } - } - }, - "indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=" - }, - "log-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", - "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", - "requires": { - "chalk": "^1.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - } - } + "loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==" }, - "listr-verbose-renderer": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz", - "integrity": "sha1-ggb0z21S3cWCfl/RSYng6WWTOjU=", + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", "requires": { - "chalk": "^1.1.3", - "cli-cursor": "^1.0.2", - "date-fns": "^1.27.2", - "figures": "^1.7.0" + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" }, "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "cli-cursor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", - "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", - "requires": { - "restore-cursor": "^1.0.1" - } - }, - "figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", - "requires": { - "escape-string-regexp": "^1.0.5", - "object-assign": "^4.1.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - } - } - }, - "onetime": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", - "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=" - }, - "restore-cursor": { + "json5": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", - "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", - "requires": { - "exit-hook": "^1.0.0", - "onetime": "^1.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", "requires": { - "ansi-regex": "^2.0.0" + "minimist": "^1.2.0" } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - } - } - }, - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" } } }, - "loader-runner": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.0.tgz", - "integrity": "sha1-9IKuqC1UPgeSFwDVpG7yb9rGuKI=" - }, - "loader-utils": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", - "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", - "requires": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0" - } - }, "locate-path": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", @@ -4588,78 +8191,40 @@ } }, "lodash": { - "version": "4.17.5", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz", - "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw==" + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" }, - "log-symbols": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.1.0.tgz", - "integrity": "sha512-zLeLrzMA1A2vRF1e/0Mo+LNINzi6jzBylHj5WqvQ/WK/5WCZt8si9SyN4p9llr/HRYvVR1AoXHRHl4WTHyQAzQ==", - "requires": { - "chalk": "^2.0.1" - } + "lolex": { + "version": "2.7.5", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.7.5.tgz", + "integrity": "sha512-l9x0+1offnKKIzYVjyXU2SiwhXDLekRzKyhnbyldPHvC7BvLPVpdNUNR2KeMAiCN2D/kLNttZgQD5WjSxuBx3Q==" }, - "log-update": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-1.0.2.tgz", - "integrity": "sha1-GZKfZMQJPS0ucHWh2tivWcKWuNE=", + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "requires": { - "ansi-escapes": "^1.0.0", - "cli-cursor": "^1.0.2" - }, - "dependencies": { - "ansi-escapes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", - "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=" - }, - "cli-cursor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", - "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", - "requires": { - "restore-cursor": "^1.0.1" - } - }, - "onetime": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", - "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=" - }, - "restore-cursor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", - "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", - "requires": { - "exit-hook": "^1.0.0", - "onetime": "^1.0.0" - } - } + "js-tokens": "^3.0.0 || ^4.0.0" } }, - "loose-envify": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", - "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "requires": { - "js-tokens": "^3.0.0" + "yallist": "^3.0.2" } }, - "lowercase-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz", - "integrity": "sha1-TjNms55/VFfjXxMkvfb4jQv8cwY=" - }, "make-dir": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.2.0.tgz", - "integrity": "sha512-aNUAa4UMg/UougV25bbrU4ZaaKNjJ/3/xnvg/twpmKROPdKZPZ9wGgI0opdZzO8q/zUFawoUuixuOv33eZ61Iw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", "requires": { "pify": "^3.0.0" } @@ -4678,108 +8243,13 @@ } }, "md5.js": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", - "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", "requires": { "hash-base": "^3.0.0", - "inherits": "^2.0.1" - }, - "dependencies": { - "hash-base": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", - "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - } - } - }, - "mem": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", - "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "mem-fs": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/mem-fs/-/mem-fs-1.1.3.tgz", - "integrity": "sha1-uK6NLj/Lb10/kWXBLUVRoGXZicw=", - "requires": { - "through2": "^2.0.0", - "vinyl": "^1.1.0", - "vinyl-file": "^2.0.0" - }, - "dependencies": { - "clone": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.3.tgz", - "integrity": "sha1-KY1+IjFmD0DAA8LtMUDezz9TCF8=" - }, - "clone-stats": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", - "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=" - }, - "replace-ext": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", - "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=" - }, - "vinyl": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", - "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", - "requires": { - "clone": "^1.0.0", - "clone-stats": "^0.0.1", - "replace-ext": "0.0.1" - } - } - } - }, - "mem-fs-editor": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mem-fs-editor/-/mem-fs-editor-3.0.2.tgz", - "integrity": "sha1-3Qpuryu4prN3QAZ6pUnrUwEFr58=", - "requires": { - "commondir": "^1.0.1", - "deep-extend": "^0.4.0", - "ejs": "^2.3.1", - "glob": "^7.0.3", - "globby": "^6.1.0", - "mkdirp": "^0.5.0", - "multimatch": "^2.0.0", - "rimraf": "^2.2.8", - "through2": "^2.0.0", - "vinyl": "^2.0.1" - }, - "dependencies": { - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - } + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" } }, "memory-fs": { @@ -4791,10 +8261,15 @@ "readable-stream": "^2.0.1" } }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, "micromatch": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.9.tgz", - "integrity": "sha512-SlIz6sv5UPaAVVFRKodKjCg48EbNoIhgetzfK/Cy0v5U52Z6zB136M8tp0UC9jM53LYbmIRihJszvvqpKkfm9g==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "requires": { "arr-diff": "^4.0.0", "array-unique": "^0.3.2", @@ -4808,7 +8283,7 @@ "object.pick": "^1.3.0", "regex-not": "^1.0.0", "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "to-regex": "^3.0.2" } }, "miller-rabin": { @@ -4818,43 +8293,24 @@ "requires": { "bn.js": "^4.0.0", "brorand": "^1.0.1" - } - }, - "mime-db": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", - "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" - }, - "mime-types": { - "version": "2.1.18", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", - "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", - "requires": { - "mime-db": "~1.33.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } } }, "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" - }, - "mimic-response": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.0.tgz", - "integrity": "sha1-3z02Uqc/3ta5sLJBRub9BSNTRY4=" - }, - "min-document": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", - "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", - "requires": { - "dom-walk": "^0.1.0" - } + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" }, "minimalistic-assert": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", - "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" }, "minimalistic-crypto-utils": { "version": "1.0.1", @@ -4870,14 +8326,14 @@ } }, "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, "mississippi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz", - "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", "requires": { "concat-stream": "^1.5.0", "duplexify": "^3.4.2", @@ -4885,61 +8341,47 @@ "flush-write-stream": "^1.0.0", "from2": "^2.1.0", "parallel-transform": "^1.1.0", - "pump": "^2.0.1", + "pump": "^3.0.0", "pumpify": "^1.3.3", "stream-each": "^1.1.0", "through2": "^2.0.0" } }, "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "requires": { "for-in": "^1.0.2", "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - } } - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "requires": { - "minimist": "0.0.8" + "minimist": "^1.2.5" } }, "mocha": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.0.1.tgz", - "integrity": "sha512-SpwyojlnE/WRBNGtvJSNfllfm5PqEDFxcWluSIgLeSBJtXG4DmoX2NNAeEA7rP5kK+79VgtVq8nG6HskaL1ykg==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", + "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", "requires": { - "browser-stdout": "1.3.0", - "commander": "2.11.0", + "browser-stdout": "1.3.1", + "commander": "2.15.1", "debug": "3.1.0", - "diff": "3.3.1", + "diff": "3.5.0", "escape-string-regexp": "1.0.5", "glob": "7.1.2", - "growl": "1.10.3", + "growl": "1.10.5", "he": "1.1.1", + "minimatch": "3.0.4", "mkdirp": "0.5.1", - "supports-color": "4.4.0" + "supports-color": "5.4.0" }, "dependencies": { - "commander": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==" - }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", @@ -4948,58 +8390,25 @@ "ms": "2.0.0" } }, - "diff": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", - "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "growl": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", - "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==" - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "requires": { - "brace-expansion": "^1.1.7" + "minimist": "0.0.8" } }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, "supports-color": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "requires": { - "has-flag": "^2.0.0" + "has-flag": "^3.0.0" } } } @@ -5018,53 +8427,26 @@ } }, "ms": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", - "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" - }, - "multimatch": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-2.1.0.tgz", - "integrity": "sha1-nHkGoi+0wCkZ4vX3UWG0zb1LKis=", - "requires": { - "array-differ": "^1.0.0", - "array-union": "^1.0.1", - "arrify": "^1.0.0", - "minimatch": "^3.0.0" - }, - "dependencies": { - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, - "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "nan": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.9.2.tgz", - "integrity": "sha512-ltW65co7f3PQWBDbqVvaU1WtFJUsNW7sWWm4HINhbMQIyVyzIeyZ8toX5TC5eeooE6piZoaEh4cZkueSKG3KYw==", + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", + "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", "optional": true }, "nanomatch": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz", - "integrity": "sha512-n8R9bS8yQ6eSXaV6jHUpKzD8gLsin02w1HSFiegwrs9E098Ylhw5jdyKPaYqvHknHaSCKTPp7C8dGCQ0q9koXA==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", "requires": { "arr-diff": "^4.0.0", "array-unique": "^0.3.2", "define-property": "^2.0.2", "extend-shallow": "^3.0.2", "fragment-cache": "^0.2.1", - "is-odd": "^2.0.0", "is-windows": "^1.0.2", "kind-of": "^6.0.2", "object.pick": "^1.3.0", @@ -5074,38 +8456,45 @@ } }, "neo-async": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.5.0.tgz", - "integrity": "sha512-nJmSswG4As/MkRq7QZFuH/sf/yuv8ODdMZrY4Bedjp77a5MK4A6s7YbBB64c9u79EBUOfXUXBvArmvzTD0X+6g==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, "nise": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/nise/-/nise-1.2.7.tgz", - "integrity": "sha512-8LqP1pFLB1v5QU8KlT2WqWzhMRJ3o9LwnZHz+VCbVB8rTsRTFCjtOYv/BatcmLOWp21NedTrErVGinfxe6XYtA==", - "requires": { - "@sinonjs/formatio": "^2.0.0", - "just-extend": "^1.1.27", - "lolex": "^2.3.2", - "path-to-regexp": "^1.7.0", - "text-encoding": "^0.6.4" + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.3.tgz", + "integrity": "sha512-Ymbac/94xeIrMf59REBPOv0thr+CJVFMhrlAkW/gjCIE58BGQdCj0x7KRCb3yz+Ga2Rz3E9XXSvUyyxqqhjQAQ==", + "requires": { + "@sinonjs/formatio": "^3.2.1", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "lolex": "^5.0.1", + "path-to-regexp": "^1.7.0" }, "dependencies": { + "@sinonjs/formatio": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.2.tgz", + "integrity": "sha512-B8SEsgd8gArBLMD6zpRw3juQ2FVSsmdd7qlevyDqzS9WTCtvF55/gAL+h6gue8ZvPYcdiPdvueM/qm//9XzyTQ==", + "requires": { + "@sinonjs/commons": "^1", + "@sinonjs/samsam": "^3.1.0" + } + }, "lolex": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.3.2.tgz", - "integrity": "sha512-A5pN2tkFj7H0dGIAM6MFvHKMJcPnjZsOMvR7ujCjfgW5TbV6H9vb1PgxLtHvjqNZTHsUolz+6/WEO0N1xNx2ng==" + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz", + "integrity": "sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A==", + "requires": { + "@sinonjs/commons": "^1.7.0" + } } } }, - "node-dir": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/node-dir/-/node-dir-0.1.8.tgz", - "integrity": "sha1-VfuN62mQcHB/tn+RpGDwRIKUx30=" - }, "node-libs-browser": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.1.0.tgz", - "integrity": "sha512-5AzFzdoIMb89hBGMZglEegffzgRg+ZFoUmisQ8HI4j1KDdpx13J0taNp2y9xPbur6W61gepGDDotGBVQ7mfUCg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", "requires": { "assert": "^1.1.1", "browserify-zlib": "^0.2.0", @@ -5114,10 +8503,10 @@ "constants-browserify": "^1.0.0", "crypto-browserify": "^3.11.0", "domain-browser": "^1.1.1", - "events": "^1.0.0", + "events": "^3.0.0", "https-browserify": "^1.0.0", "os-browserify": "^0.3.0", - "path-browserify": "0.0.0", + "path-browserify": "0.0.1", "process": "^0.11.10", "punycode": "^1.2.4", "querystring-es3": "^0.2.0", @@ -5128,83 +8517,24 @@ "timers-browserify": "^2.0.4", "tty-browserify": "0.0.0", "url": "^0.11.0", - "util": "^0.10.3", - "vm-browserify": "0.0.4" - } - }, - "nomnom": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.8.1.tgz", - "integrity": "sha1-IVH3Ikcrp55Qp2/BJbuMjy5Nwqc=", - "requires": { - "chalk": "~0.4.0", - "underscore": "~1.6.0" - }, - "dependencies": { - "ansi-styles": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.0.0.tgz", - "integrity": "sha1-yxAt8cVvUSPquLZ817mAJ6AnkXg=" - }, - "chalk": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.4.0.tgz", - "integrity": "sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8=", - "requires": { - "ansi-styles": "~1.0.0", - "has-color": "~0.1.0", - "strip-ansi": "~0.1.0" - } - }, - "strip-ansi": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz", - "integrity": "sha1-OeipjQRNFQZgq+SmgIrPcLt7yZE=" - }, - "underscore": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", - "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=" - } - } - }, - "normalize-package-data": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", - "requires": { - "hosted-git-info": "^2.1.4", - "is-builtin-module": "^1.0.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" + "util": "^0.11.0", + "vm-browserify": "^1.0.1" } }, "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "requires": { - "remove-trailing-separator": "^1.0.1" - } + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "optional": true }, "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "requires": { - "path-key": "^2.0.0" + "path-key": "^3.0.0" } }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, - "oauth-sign": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" - }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -5279,15 +8609,6 @@ "isobject": "^3.0.0" } }, - "object.omit": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", - "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", - "requires": { - "for-own": "^0.1.4", - "is-extendable": "^0.1.1" - } - }, "object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", @@ -5305,81 +8626,11 @@ } }, "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "ora": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/ora/-/ora-0.2.3.tgz", - "integrity": "sha1-N1J9Igrc1Tw5tzVx11QVbV22V6Q=", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "requires": { - "chalk": "^1.1.1", - "cli-cursor": "^1.0.2", - "cli-spinners": "^0.1.2", - "object-assign": "^4.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "cli-cursor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", - "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", - "requires": { - "restore-cursor": "^1.0.1" - } - }, - "onetime": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", - "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=" - }, - "restore-cursor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", - "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", - "requires": { - "exit-hook": "^1.0.0", - "onetime": "^1.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - } + "mimic-fn": "^2.1.0" } }, "os-browserify": { @@ -5392,53 +8643,15 @@ "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" }, - "os-locale": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", - "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" - } - }, - "os-shim": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/os-shim/-/os-shim-0.1.3.tgz", - "integrity": "sha1-a2LDeRz3kJ6jXtRuF2WLtBfLORc=" - }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" }, - "p-cancelable": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz", - "integrity": "sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw==" - }, - "p-each-series": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz", - "integrity": "sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=", - "requires": { - "p-reduce": "^1.0.0" - } - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" - }, - "p-lazy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-lazy/-/p-lazy-1.0.0.tgz", - "integrity": "sha1-7FPIAvLuOsKPFmzILQsrAt4nqDU=" - }, "p-limit": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz", - "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", "requires": { "p-try": "^1.0.0" } @@ -5451,109 +8664,53 @@ "p-limit": "^1.1.0" } }, - "p-map": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", - "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==" - }, - "p-reduce": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz", - "integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=" - }, - "p-timeout": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", - "integrity": "sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y=", - "requires": { - "p-finally": "^1.0.0" - } - }, "p-try": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" }, "pako": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", - "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==" + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" }, "parallel-transform": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", - "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", "requires": { - "cyclist": "~0.2.2", + "cyclist": "^1.0.1", "inherits": "^2.0.3", "readable-stream": "^2.1.5" } }, "parse-asn1": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.0.tgz", - "integrity": "sha1-N8T5t+06tlx0gXtfJICTf7+XxxI=", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", "requires": { - "asn1.js": "^4.0.0", + "asn1.js": "^5.2.0", "browserify-aes": "^1.0.0", - "create-hash": "^1.1.0", "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3" - } - }, - "parse-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", - "requires": { - "glob-base": "^0.3.0", - "is-dotfile": "^1.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.0" - }, - "dependencies": { - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "requires": { - "is-extglob": "^1.0.0" - } - } - } - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "requires": { - "error-ex": "^1.2.0" + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" } }, - "parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=" - }, "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" }, "path-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", - "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=" + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==" }, "path-dirname": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "optional": true }, "path-exists": { "version": "3.0.0", @@ -5566,49 +8723,27 @@ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" }, "path-parse": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", - "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=" + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "path-to-regexp": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", - "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", "requires": { "isarray": "0.0.1" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - } - } - }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "requires": { - "pify": "^2.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" - } } }, "pbkdf2": { - "version": "3.0.14", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.14.tgz", - "integrity": "sha512-gjsZW9O34fm0R7PaLHRJmLLVfSoesxztjPjE9o6R+qtVJij90ltg1joIovN9GKrRW3t1PzhDDG3UMEMFfZ+1wA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", "requires": { "create-hash": "^1.1.2", "create-hmac": "^1.1.4", @@ -5617,29 +8752,17 @@ "sha.js": "^2.4.8" } }, - "performance-now": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", - "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=" + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "optional": true }, "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "requires": { - "pinkie": "^2.0.0" - } - }, "pkg-dir": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", @@ -5653,26 +8776,6 @@ "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" }, - "prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" - }, - "preserve": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", - "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=" - }, - "prettier": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.11.1.tgz", - "integrity": "sha512-T/KD65Ot0PB97xTrG8afQ46x3oiVhnfGjGESSI9NWYcG92+OUPZKkwHqGWXH2t9jK1crnQjubECW0FuOth+hxw==" - }, - "pretty-bytes": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-4.0.2.tgz", - "integrity": "sha1-sr+C5zUNZcbDOqlaqlpPYyf2HNk=" - }, "private": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", @@ -5684,9 +8787,9 @@ "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" }, "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, "promise-inflight": { "version": "1.0.1", @@ -5701,6 +8804,120 @@ "mocha": "^2.5.3", "sinon": "^1.10.3", "underscore": "~1.8.3" + }, + "dependencies": { + "commander": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.3.0.tgz", + "integrity": "sha1-/UMOiJgy7DU7ms0d4hfBHLPu+HM=" + }, + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "requires": { + "ms": "0.7.1" + } + }, + "diff": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz", + "integrity": "sha1-fyjS657nsVqX79ic5j3P2qPMur8=" + }, + "escape-string-regexp": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz", + "integrity": "sha1-Tbwv5nTnGUnK8/smlc5/LcHZqNE=" + }, + "glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", + "requires": { + "inherits": "2", + "minimatch": "0.3" + } + }, + "growl": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", + "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=" + }, + "lolex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.3.2.tgz", + "integrity": "sha1-fD2mL/yzDw9agKJWbKJORdigHzE=" + }, + "lru-cache": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=" + }, + "minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", + "requires": { + "lru-cache": "2", + "sigmund": "~1.0.0" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "mocha": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-2.5.3.tgz", + "integrity": "sha1-FhvlvetJZ3HrmzV0UFC2IrWu/Fg=", + "requires": { + "commander": "2.3.0", + "debug": "2.2.0", + "diff": "1.4.0", + "escape-string-regexp": "1.0.2", + "glob": "3.2.11", + "growl": "1.9.2", + "jade": "0.26.3", + "mkdirp": "0.5.1", + "supports-color": "1.2.0", + "to-iso-string": "0.0.2" + } + }, + "ms": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" + }, + "samsam": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.2.tgz", + "integrity": "sha1-vsEf3IOp/aBjQBIQ5AF2wwJNFWc=" + }, + "sinon": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-1.17.7.tgz", + "integrity": "sha1-RUKk9JugxFwF6y6d2dID4rjv4L8=", + "requires": { + "formatio": "1.1.1", + "lolex": "1.3.2", + "samsam": "1.1.2", + "util": ">=0.10.3 <1" + } + }, + "supports-color": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-1.2.0.tgz", + "integrity": "sha1-/x7R5hFp0Gs88tWI4YixjYhH4X4=" + } } }, "prr": { @@ -5708,40 +8925,54 @@ "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" - }, "public-encrypt": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.0.tgz", - "integrity": "sha1-OfaZ86RlYN1eusvKaTyvfGXBjMY=", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", "requires": { "bn.js": "^4.1.0", "browserify-rsa": "^4.0.0", "create-hash": "^1.1.0", "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1" + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } } }, "pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "requires": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "pumpify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.4.0.tgz", - "integrity": "sha512-2kmNR9ry+Pf45opRVirpNuIFotsxUGLaYqxIwuR77AYrYRMuFCz9eryHBS52L360O+NcR383CL4QYlMKPq4zYA==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", "requires": { - "duplexify": "^3.5.3", + "duplexify": "^3.6.0", "inherits": "^2.0.3", "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } } }, "punycode": { @@ -5749,11 +8980,6 @@ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" }, - "qs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", - "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=" - }, "querystring": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", @@ -5764,29 +8990,10 @@ "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=" }, - "randomatic": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", - "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, "randombytes": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", - "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "requires": { "safe-buffer": "^5.1.0" } @@ -5800,104 +9007,56 @@ "safe-buffer": "^5.1.0" } }, - "read-chunk": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/read-chunk/-/read-chunk-2.1.0.tgz", - "integrity": "sha1-agTAkoAF7Z1C4aasVgDhnLx/9lU=", - "requires": { - "pify": "^3.0.0", - "safe-buffer": "^5.1.1" - } - }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - } - }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - } - }, "readable-stream": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.4.tgz", - "integrity": "sha512-vuYxeWYM+fde14+rajzqgeohAI7YoJcHE7kXDAc4Nk0EbuKnJfqtY9YtRkLo/tqkuF7MsBQRhPnPeyjYITp3ZQ==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", - "string_decoder": "~1.0.3", + "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" - } - }, - "readdirp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", - "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", - "requires": { - "graceful-fs": "^4.1.2", - "minimatch": "^3.0.2", - "readable-stream": "^2.0.2", - "set-immediate-shim": "^1.0.1" }, "dependencies": { - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { - "brace-expansion": "^1.1.7" + "safe-buffer": "~5.1.0" } } } }, - "recast": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.13.2.tgz", - "integrity": "sha512-Xqo0mKljGUWGUhnkdbODk7oJGFrMcpgKQ9cCyZ4y+G9VfoTKdum8nHbf/SxIdKx5aBSZ29VpVy20bTyt7jyC8w==", + "readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "optional": true, "requires": { - "ast-types": "0.10.2", - "esprima": "~4.0.0", - "private": "~0.1.5", - "source-map": "~0.6.1" - }, - "dependencies": { - "ast-types": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.10.2.tgz", - "integrity": "sha512-ufWX953VU1eIuWqxS0nRDMYlGyFH+yxln5CsmIHlpzEt3fdYqUnRtsFt0XAsQot8OaVCwFqxT1RiwvtzYjeYeg==" - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } + "picomatch": "^2.2.1" } }, "rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.0.tgz", + "integrity": "sha512-ADsDEH2bvbjltXEP+hTIAmeFekTFK0V2BTxMkok6qILyAJEXV0AFfoWcAq4yfll5VdIMd/RVXq0lR+wQi5ZU3Q==", "requires": { - "resolve": "^1.1.6" + "resolve": "^1.9.0" } }, "regenerate": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.3.tgz", - "integrity": "sha512-jVpo1GadrDAK59t/0jRx5VxYWQEDkkEKi6+HjE3joFVLfDOh9Xrdh0dF1eSq+BI/SwvTQ44gSscJ8N5zYL61sg==" + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" }, "regenerator-runtime": { "version": "0.11.1", @@ -5914,14 +9073,6 @@ "private": "^0.1.6" } }, - "regex-cache": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", - "requires": { - "is-equal-shallow": "^0.1.3" - } - }, "regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", @@ -5952,17 +9103,25 @@ "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", "requires": { "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=" + } } }, "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "optional": true }, "repeat-element": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", - "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=" + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==" }, "repeat-string": { "version": "1.6.1", @@ -5977,111 +9136,50 @@ "is-finite": "^1.0.0" } }, - "replace-ext": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", - "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=" - }, - "request": { - "version": "2.81.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", - "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", - "requires": { - "aws-sign2": "~0.6.0", - "aws4": "^1.2.1", - "caseless": "~0.12.0", - "combined-stream": "~1.0.5", - "extend": "~3.0.0", - "forever-agent": "~0.6.1", - "form-data": "~2.1.1", - "har-validator": "~4.2.1", - "hawk": "~3.1.3", - "http-signature": "~1.1.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.7", - "oauth-sign": "~0.8.1", - "performance-now": "^0.2.0", - "qs": "~6.4.0", - "safe-buffer": "^5.0.1", - "stringstream": "~0.0.4", - "tough-cookie": "~2.3.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.0.0" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" - }, - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" - }, "resolve": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz", - "integrity": "sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", "requires": { - "path-parse": "^1.0.5" + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" } }, "resolve-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", - "requires": { - "resolve-from": "^3.0.0" - } - }, - "resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "requires": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" + "resolve-from": "^5.0.0" } }, "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=" + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" }, "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - }, "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" }, "rimraf": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "requires": { - "glob": "^7.0.5" + "glob": "^7.1.3" }, "dependencies": { "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -6090,34 +9188,18 @@ "once": "^1.3.0", "path-is-absolute": "^1.0.0" } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } } } }, "ripemd160": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", - "integrity": "sha1-D0WEKVxTo2KK9+bXmsohzlfRxuc=", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", "requires": { - "hash-base": "^2.0.0", + "hash-base": "^3.0.0", "inherits": "^2.0.1" } }, - "run-async": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", - "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", - "requires": { - "is-promise": "^2.1.0" - } - }, "run-queue": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", @@ -6126,36 +9208,10 @@ "aproba": "^1.1.1" } }, - "rx": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz", - "integrity": "sha1-pfE/957zt0D+MKqAP7CfmIBdR4I=" - }, - "rx-lite": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", - "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=" - }, - "rx-lite-aggregates": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", - "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", - "requires": { - "rx-lite": "*" - } - }, - "rxjs": { - "version": "5.5.6", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.6.tgz", - "integrity": "sha512-v4Q5HDC0FHAQ7zcBX7T2IL6O5ltl1a2GX4ENjPXg6SjDY69Cmx9v4113C99a4wGF16ClPv5Z8mghuYorVkg/kg==", - "requires": { - "symbol-observable": "1.0.1" - } - }, "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "safe-regex": { "version": "1.1.0", @@ -6165,52 +9221,43 @@ "ret": "~0.1.10" } }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "samsam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz", + "integrity": "sha512-1HwIYD/8UlOtFS3QO3w7ey+SdSDFE4HRNLZoZRYVQefrOY3l17epswImeB1ijgJFQJodIaHcwkp3r/myBjFVbg==" + }, "schema-utils": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.5.tgz", - "integrity": "sha512-yYrjb9TX2k/J1Y5UNy3KYdZq10xhYcF8nMpAW6o3hy6Q8WSIEf9lJHG/ePnOBfziPM3fvQwfOwa13U/Fh8qTfA==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", "requires": { "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", "ajv-keywords": "^3.1.0" } }, - "scoped-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/scoped-regex/-/scoped-regex-1.0.0.tgz", - "integrity": "sha1-o0a7Gs1CB65wvXwMfKnlZra63bg=" - }, "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" }, "serialize-javascript": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.4.0.tgz", - "integrity": "sha1-fJWFFNtqwkQ6irwGLcn3iGp/YAU=" - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "set-getter": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/set-getter/-/set-getter-0.1.0.tgz", - "integrity": "sha1-12nBgsnVpR9AkUXy+6guXoboA3Y=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", "requires": { - "to-object-path": "^0.3.0" + "randombytes": "^2.1.0" } }, - "set-immediate-shim": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", - "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=" - }, "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", "requires": { "extend-shallow": "^2.0.1", "is-extendable": "^0.1.1", @@ -6225,6 +9272,11 @@ "requires": { "is-extendable": "^0.1.0" } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" } } }, @@ -6234,69 +9286,49 @@ "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" }, "sha.js": { - "version": "2.4.10", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.10.tgz", - "integrity": "sha512-vnwmrFDlOExK4Nm16J2KMWHLrp14lBrjxMxBJpu++EnsuBmpiYaM/MEs46Vxxm/4FvdP5yTwuCTO9it5FSjrqA==", + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "requires": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" } }, + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "requires": { + "kind-of": "^6.0.2" + } + }, "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "requires": { - "shebang-regex": "^1.0.0" + "shebang-regex": "^3.0.0" } }, "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" }, - "shelljs": { - "version": "0.7.8", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz", - "integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=", - "requires": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - }, - "dependencies": { - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - } - } + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" }, "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" }, "sinon": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-4.4.2.tgz", - "integrity": "sha512-cpOHpnRyY3Dk9dTHBYMfVBB0HUCSKIpxW07X6OGW2NiYPovs4AkcL8Q8MzecbAROjbfRA9esJCmlZgikxDz7DA==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-4.5.0.tgz", + "integrity": "sha512-trdx+mB0VBBgoYucy6a9L7/jfQOmvGeaKZT4OOJ+lPAtI8623xyGr8wLiE4eojzBS8G9yXbhx42GHUOVLr4X2w==", "requires": { "@sinonjs/formatio": "^2.0.0", "diff": "^3.1.0", @@ -6307,10 +9339,13 @@ "type-detect": "^4.0.5" }, "dependencies": { - "lolex": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.3.2.tgz", - "integrity": "sha512-A5pN2tkFj7H0dGIAM6MFvHKMJcPnjZsOMvR7ujCjfgW5TbV6H9vb1PgxLtHvjqNZTHsUolz+6/WEO0N1xNx2ng==" + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -6319,20 +9354,10 @@ "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=" }, - "slice-ansi": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", - "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=" - }, - "slide": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", - "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=" - }, "snapdragon": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.1.tgz", - "integrity": "sha1-4StUh/re0+PeoKyR6UAL91tAE3A=", + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", "requires": { "base": "^0.11.1", "debug": "^2.2.0", @@ -6341,7 +9366,7 @@ "map-cache": "^0.2.2", "source-map": "^0.5.6", "source-map-resolve": "^0.5.0", - "use": "^2.0.0" + "use": "^3.1.0" }, "dependencies": { "define-property": { @@ -6406,6 +9431,11 @@ "kind-of": "^5.0.0" } }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + }, "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", @@ -6451,18 +9481,10 @@ } } }, - "sntp": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", - "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", - "requires": { - "hoek": "2.x.x" - } - }, "source-list-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.0.tgz", - "integrity": "sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==" }, "source-map": { "version": "0.5.7", @@ -6470,11 +9492,11 @@ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" }, "source-map-resolve": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.1.tgz", - "integrity": "sha512-0KW2wvzfxm8NCTb30z0LMNyPqWCdDGE2viwzUaucqJdkTRXtZiSY3I+2A6nVAjmdOy0I4gU8DwnVVGsk9jvP2A==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", "requires": { - "atob": "^2.0.0", + "atob": "^2.1.2", "decode-uri-component": "^0.2.0", "resolve-url": "^0.2.1", "source-map-url": "^0.4.0", @@ -6490,46 +9512,9 @@ } }, "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" - }, - "spawn-sync": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/spawn-sync/-/spawn-sync-1.0.15.tgz", - "integrity": "sha1-sAeZVX63+wyDdsKdROih6mfldHY=", - "requires": { - "concat-stream": "^1.4.7", - "os-shim": "^0.1.2" - } - }, - "spdx-correct": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", - "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", - "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==" - }, - "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz", - "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==" + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==" }, "split-string": { "version": "3.1.0", @@ -6539,34 +9524,12 @@ "extend-shallow": "^3.0.0" } }, - "sshpk": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", - "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "tweetnacl": "~0.14.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - } - } - }, "ssri": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.2.4.tgz", - "integrity": "sha512-UnEAgMZa15973iH7cUi0AHjJn1ACDIkaMyZILoqwN6yzt+4P81I8tBc5Hl+qwi5auMplZtPQsHrPBR5vJLcQtQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz", + "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==", "requires": { - "safe-buffer": "^5.1.1" + "figgy-pudding": "^3.5.1" } }, "static-extend": { @@ -6640,193 +9603,219 @@ } }, "stream-browserify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", - "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", "requires": { "inherits": "~2.0.1", "readable-stream": "^2.0.2" } }, "stream-each": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.2.tgz", - "integrity": "sha512-mc1dbFhGBxvTM3bIWmAAINbqiuAk9TATcfIQC8P+/+HJefgaiTlMn2dHvkX8qlI12KeYKSQ1Ua9RrIqrn1VPoA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", "requires": { "end-of-stream": "^1.1.0", "stream-shift": "^1.0.0" } }, - "stream-http": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.0.tgz", - "integrity": "sha512-sZOFxI/5xw058XIRHl4dU3dZ+TTOIGJR78Dvo0oEAejIt4ou27k+3ne1zYmCV+v7UucbxIFQuOgnkTVHh8YPnw==", - "requires": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.3.3", - "to-arraybuffer": "^1.0.0", - "xtend": "^4.0.0" - } - }, - "stream-shift": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", - "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=" - }, - "stream-to-observable": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/stream-to-observable/-/stream-to-observable-0.1.0.tgz", - "integrity": "sha1-Rb8dny19wJvtgfHDB8Qw5ouEz/4=" - }, - "string-template": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz", - "integrity": "sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0=" - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" } }, + "stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" + }, "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } } }, - "stringstream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" - }, "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "^2.0.0" } }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" }, - "strip-bom-stream": { + "supports-color": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-2.0.0.tgz", - "integrity": "sha1-+H217yYT9paKpUWr/h7HKLaoKco=", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==" + }, + "terser": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", + "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", "requires": { - "first-chunk-stream": "^2.0.0", - "strip-bom": "^2.0.0" + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" }, "dependencies": { - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", "requires": { - "is-utf8": "^0.2.0" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } } } }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" - }, - "supports-color": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", - "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", - "requires": { - "has-flag": "^3.0.0" - } - }, - "symbol-observable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", - "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=" - }, - "tapable": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.0.0.tgz", - "integrity": "sha512-dQRhbNQkRnaqauC7WqSJ21EEksgT0fYZX2lqXzGkpo8JNig9zGZTYoMGvyI2nWmXlE2VSVXVDu7wLVGu/mQEsg==" - }, - "temp": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz", - "integrity": "sha1-4Ma8TSa5AxJEEOT+2BEDAU38H1k=", + "terser-webpack-plugin": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz", + "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==", "requires": { - "os-tmpdir": "^1.0.0", - "rimraf": "~2.2.6" + "cacache": "^12.0.2", + "find-cache-dir": "^2.1.0", + "is-wsl": "^1.1.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^4.0.0", + "source-map": "^0.6.1", + "terser": "^4.1.2", + "webpack-sources": "^1.4.0", + "worker-farm": "^1.7.0" }, "dependencies": { - "rimraf": { - "version": "2.2.8", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", - "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=" + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "requires": { + "find-up": "^3.0.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" } } }, - "text-encoding": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", - "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=" - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" - }, - "textextensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-2.2.0.tgz", - "integrity": "sha512-j5EMxnryTvKxwH2Cq+Pb43tsf6sdEgw6Pdwxk83mPaq0ToeFJt6WE4J3s5BqY7vmjlLgkgXvhtXUxo80FyBhCA==" - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" - }, "through2": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", - "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "requires": { - "readable-stream": "^2.1.5", + "readable-stream": "~2.3.6", "xtend": "~4.0.1" } }, - "timed-out": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", - "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=" - }, "timers-browserify": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.6.tgz", - "integrity": "sha512-HQ3nbYRAowdVd0ckGFvmJPPCOH/CHleFN/Y0YQCX1DVaB7t+KFvisuyN09fuP8Jtp1CpfSh8O8bMkHbdbPe6Pw==", + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", "requires": { "setimmediate": "^1.0.4" } }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "requires": { - "os-tmpdir": "~1.0.2" - } - }, "to-arraybuffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", @@ -6837,6 +9826,11 @@ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=" }, + "to-iso-string": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/to-iso-string/-/to-iso-string-0.0.2.tgz", + "integrity": "sha1-TcGeZk38y+Jb2NtQiwDG2hWCVdE=" + }, "to-object-path": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", @@ -6875,14 +9869,6 @@ "repeat-string": "^1.6.1" } }, - "tough-cookie": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", - "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", - "requires": { - "punycode": "^1.4.1" - } - }, "trim-right": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", @@ -6893,20 +9879,6 @@ "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=" }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "optional": true - }, "type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -6917,98 +9889,41 @@ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, - "uglify-es": { - "version": "3.3.9", - "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", - "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==", - "requires": { - "commander": "~2.13.0", - "source-map": "~0.6.1" - }, - "dependencies": { - "commander": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", - "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==" - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } - } - }, - "uglifyjs-webpack-plugin": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.2.2.tgz", - "integrity": "sha512-CG/NvzXfemUAm5Y4Guh5eEaJYHtkG7kKNpXEJHp9QpxsFVB5/qKvYWoMaq4sa99ccZ0hM3MK8vQV9XPZB4357A==", - "requires": { - "cacache": "^10.0.1", - "find-cache-dir": "^1.0.0", - "schema-utils": "^0.4.2", - "serialize-javascript": "^1.4.0", - "source-map": "^0.6.1", - "uglify-es": "^3.3.4", - "webpack-sources": "^1.1.0", - "worker-farm": "^1.5.2" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } - } - }, "underscore": { "version": "1.8.3", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" }, "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", "requires": { "arr-union": "^3.1.0", "get-value": "^2.0.6", "is-extendable": "^0.1.1", - "set-value": "^0.4.3" + "set-value": "^2.0.1" }, "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - }, - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" - } + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" } } }, "unique-filename": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.0.tgz", - "integrity": "sha1-0F8v5AMlYIcfMOk8vnNe6iAVFPM=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", "requires": { "unique-slug": "^2.0.0" } }, "unique-slug": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.0.tgz", - "integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", "requires": { "imurmurhash": "^0.1.4" } @@ -7046,639 +9961,281 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" } } }, - "untildify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-2.1.0.tgz", - "integrity": "sha1-F+soB5h/dpUunASF/DEdBqgmouA=", - "requires": { - "os-homedir": "^1.0.0" - } - }, "upath": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.0.4.tgz", - "integrity": "sha512-d4SJySNBXDaQp+DPrziv3xGS6w3d2Xt69FijJr86zMPBy23JEloMCEOUBBzuN7xCtjLCnmB9tI/z7SBCahHBOw==" - }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" - }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" - } - } - }, - "url-parse-lax": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", - "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", - "requires": { - "prepend-http": "^1.0.1" - } - }, - "url-to-options": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", - "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=" - }, - "urlgrey": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/urlgrey/-/urlgrey-0.4.4.tgz", - "integrity": "sha1-iS/pWWCAXoVRnxzUOJ8stMu3ZS8=" - }, - "use": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/use/-/use-2.0.2.tgz", - "integrity": "sha1-riig1y+TvyJCKhii43mZMRLeyOg=", - "requires": { - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "lazy-cache": "^2.0.2" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - } - } + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "optional": true }, - "util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "requires": { - "inherits": "2.0.1" + "punycode": "^2.1.0" }, "dependencies": { - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" } } }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "uuid": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", - "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==" - }, - "v8-compile-cache": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-1.1.2.tgz", - "integrity": "sha512-ejdrifsIydN1XDH7EuR2hn8ZrkRKUYF7tUcBjBy/lhrCvs2K+zRlbW9UHc0IQ9RsYFZJFqJrieoIHfkCa0DBRA==" - }, - "validate-npm-package-license": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz", - "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==", - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" + "punycode": "1.3.2", + "querystring": "0.2.0" }, "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" } } }, - "vinyl": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.1.0.tgz", - "integrity": "sha1-Ah+cLPlR1rk5lDyJ617lrdT9kkw=", - "requires": { - "clone": "^2.1.1", - "clone-buffer": "^1.0.0", - "clone-stats": "^1.0.0", - "cloneable-readable": "^1.0.0", - "remove-trailing-separator": "^1.0.1", - "replace-ext": "^1.0.0" - } + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" }, - "vinyl-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/vinyl-file/-/vinyl-file-2.0.0.tgz", - "integrity": "sha1-p+v1/779obfRjRQPyweyI++2dRo=", + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.3.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0", - "strip-bom-stream": "^2.0.0", - "vinyl": "^1.1.0" + "inherits": "2.0.3" }, "dependencies": { - "clone": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.3.tgz", - "integrity": "sha1-KY1+IjFmD0DAA8LtMUDezz9TCF8=" - }, - "clone-stats": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", - "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=" - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" - }, - "replace-ext": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", - "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=" - }, - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "requires": { - "is-utf8": "^0.2.0" - } - }, - "vinyl": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", - "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", - "requires": { - "clone": "^1.0.0", - "clone-stats": "^0.0.1", - "replace-ext": "0.0.1" - } + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" } } }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==" + }, "vm-browserify": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", - "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", - "requires": { - "indexof": "0.0.1" - } + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==" }, "watchpack": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.5.0.tgz", - "integrity": "sha512-RSlipNQB1u48cq0wH/BNfCu1tD/cJ8ydFIkNYhp9o+3d+8unClkIovpW5qpFPgmL9OE48wfAnlZydXByWP82AA==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", + "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", "requires": { - "chokidar": "^2.0.2", + "chokidar": "^3.4.1", "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0" - } - }, - "webpack": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.0.1.tgz", - "integrity": "sha512-jHQNMmKPElreOYLCxR7SHfPnbhcqRT9O7lYPOMDR6Gt5XueJ7tH7JReXm4uMFstBKf7rj2Y7AD3LiMKR2zexYA==", - "requires": { - "acorn": "^5.0.0", - "acorn-dynamic-import": "^3.0.0", - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0", - "chrome-trace-event": "^0.1.1", - "enhanced-resolve": "^4.0.0", - "eslint-scope": "^3.7.1", - "loader-runner": "^2.3.0", - "loader-utils": "^1.1.0", - "memory-fs": "~0.4.1", - "micromatch": "^3.1.8", - "mkdirp": "~0.5.0", "neo-async": "^2.5.0", - "node-libs-browser": "^2.0.0", - "schema-utils": "^0.4.2", - "tapable": "^1.0.0", - "uglifyjs-webpack-plugin": "^1.1.1", - "watchpack": "^1.4.0", - "webpack-sources": "^1.0.1" + "watchpack-chokidar2": "^2.0.1" } }, - "webpack-addons": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/webpack-addons/-/webpack-addons-1.1.5.tgz", - "integrity": "sha512-MGO0nVniCLFAQz1qv22zM02QPjcpAoJdy7ED0i3Zy7SY1IecgXCm460ib7H/Wq7e9oL5VL6S2BxaObxwIcag0g==", + "watchpack-chokidar2": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz", + "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==", + "optional": true, "requires": { - "jscodeshift": "^0.4.0" - } - }, - "webpack-cli": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-2.0.9.tgz", - "integrity": "sha512-KIkOFHhrq8W7ovg5u8M7Xbduzr1aQ1Ch1aGGY0TvL5neO81T6/aCZ/NeG7R92UaXIF/BK4KCkla35wtoOoxyDQ==", - "requires": { - "chalk": "^2.0.1", - "codecov": "^3.0.0", - "cross-spawn": "^5.1.0", - "diff": "^3.3.0", - "enhanced-resolve": "^3.4.1", - "glob-all": "^3.1.0", - "global": "^4.3.2", - "global-modules": "^1.0.0", - "got": "^7.1.0", - "inquirer": "^3.2.0", - "interpret": "^1.0.4", - "jscodeshift": "^0.4.0", - "listr": "^0.12.0", - "loader-utils": "^1.1.0", - "lodash": "^4.17.4", - "log-symbols": "2.1.0", - "mkdirp": "^0.5.1", - "p-each-series": "^1.0.0", - "p-lazy": "^1.0.0", - "prettier": "^1.5.3", - "recast": "^0.13.0", - "resolve-cwd": "^2.0.0", - "supports-color": "^4.4.0", - "uglifyjs-webpack-plugin": "^1.2.2", - "v8-compile-cache": "^1.1.0", - "webpack-addons": "^1.1.5", - "webpack-fork-yeoman-generator": "^1.1.1", - "yargs": "9.0.1", - "yeoman-environment": "^2.0.0" + "chokidar": "^2.1.8" }, "dependencies": { - "diff": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.4.0.tgz", - "integrity": "sha512-QpVuMTEoJMF7cKzi6bvWhRulU1fZqZnvyVQgNhPaxxuTYwyjn/j1v9falseQ/uXWwPnO56RBfwtg4h/EQXmucA==" - }, - "enhanced-resolve": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz", - "integrity": "sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=", - "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.4.0", - "object-assign": "^4.0.1", - "tapable": "^0.2.7" - } - }, - "has-flag": { + "anymatch": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" - }, - "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", - "requires": { - "has-flag": "^2.0.0" - } - }, - "tapable": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.8.tgz", - "integrity": "sha1-mTcqXJmb8t8WCvwNdL7U9HlIzSI=" - } - } - }, - "webpack-fork-yeoman-generator": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/webpack-fork-yeoman-generator/-/webpack-fork-yeoman-generator-1.1.1.tgz", - "integrity": "sha512-TrLT6Bw6gl9rJA7iZw+YJ+4xHhEUzfOQB3tHpyINBFdZDmO0tlDW9MtMSMZ5rsUNjHxcEba5yuGaAW86J84j/w==", - "requires": { - "async": "^2.0.0", - "chalk": "^1.0.0", - "cli-table": "^0.3.1", - "cross-spawn": "^5.0.1", - "dargs": "^5.1.0", - "dateformat": "^2.0.0", - "debug": "^2.1.0", - "detect-conflict": "^1.0.0", - "error": "^7.0.2", - "find-up": "^2.1.0", - "github-username": "^4.0.0", - "istextorbinary": "^2.1.0", - "lodash": "^4.11.1", - "mem-fs-editor": "^3.0.0", - "minimist": "^1.2.0", - "mkdirp": "^0.5.0", - "pretty-bytes": "^4.0.2", - "read-chunk": "^2.0.0", - "read-pkg-up": "^2.0.0", - "rimraf": "^2.2.0", - "run-async": "^2.0.0", - "shelljs": "^0.7.0", - "text-table": "^0.2.0", - "through2": "^2.0.0", - "yeoman-environment": "^1.1.0" - }, - "dependencies": { - "ansi-escapes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", - "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=" - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "async": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz", - "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==", - "requires": { - "lodash": "^4.14.0" - } - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "cli-cursor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", - "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", - "requires": { - "restore-cursor": "^1.0.1" - } - }, - "diff": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/diff/-/diff-2.2.3.tgz", - "integrity": "sha1-YOr9DSjukG5Oj/ClLBIpUhAzv5k=" - }, - "external-editor": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-1.1.1.tgz", - "integrity": "sha1-Etew24UPf/fnCBuvQAVwAGDEYAs=", - "requires": { - "extend": "^3.0.0", - "spawn-sync": "^1.0.15", - "tmp": "^0.0.29" - } - }, - "figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "optional": true, "requires": { - "escape-string-regexp": "^1.0.5", - "object-assign": "^4.1.0" + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" }, "dependencies": { - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "optional": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } } } }, - "glob": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", - "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", - "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "globby": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-4.1.0.tgz", - "integrity": "sha1-CA9UVJ7BuCpsYOYx/ILhIR2+lfg=", - "requires": { - "array-union": "^1.0.1", - "arrify": "^1.0.0", - "glob": "^6.0.1", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "inquirer": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-1.2.3.tgz", - "integrity": "sha1-TexvMvN+97sLLtPx0aXD9UUHSRg=", - "requires": { - "ansi-escapes": "^1.1.0", - "chalk": "^1.0.0", - "cli-cursor": "^1.0.1", - "cli-width": "^2.0.0", - "external-editor": "^1.1.0", - "figures": "^1.3.5", - "lodash": "^4.3.0", - "mute-stream": "0.0.6", - "pinkie-promise": "^2.0.0", - "run-async": "^2.2.0", - "rx": "^4.1.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.0", - "through": "^2.3.6" - } + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "optional": true }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "optional": true, "requires": { - "number-is-nan": "^1.0.0" - } - }, - "log-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", - "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "optional": true, "requires": { - "chalk": "^1.0.0" + "bindings": "^1.5.0", + "nan": "^2.12.1" } }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "optional": true, "requires": { - "brace-expansion": "^1.1.7" + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "optional": true, + "requires": { + "is-extglob": "^2.1.0" + } + } } }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - }, - "mute-stream": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.6.tgz", - "integrity": "sha1-SJYrGeFp/R38JAs/HnMXYnu8R9s=" - }, - "onetime": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", - "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=" - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" - }, - "restore-cursor": { + "is-binary-path": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", - "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", - "requires": { - "exit-hook": "^1.0.0", - "onetime": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - }, - "tmp": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.29.tgz", - "integrity": "sha1-8lEl/w3Z2jzLDC3Tce4SiLuRKMA=", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "optional": true, "requires": { - "os-tmpdir": "~1.0.1" + "binary-extensions": "^1.0.0" } }, - "yeoman-environment": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/yeoman-environment/-/yeoman-environment-1.6.6.tgz", - "integrity": "sha1-zYX6Z9FWBg5EDXgH1+988NLR1nE=", + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "optional": true, "requires": { - "chalk": "^1.0.0", - "debug": "^2.0.0", - "diff": "^2.1.2", - "escape-string-regexp": "^1.0.2", - "globby": "^4.0.0", - "grouped-queue": "^0.3.0", - "inquirer": "^1.0.2", - "lodash": "^4.11.1", - "log-symbols": "^1.0.1", - "mem-fs": "^1.1.0", - "text-table": "^0.2.0", - "untildify": "^2.0.0" + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" } } } }, + "webpack": { + "version": "4.46.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.46.0.tgz", + "integrity": "sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q==", + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/wasm-edit": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "acorn": "^6.4.1", + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^4.5.0", + "eslint-scope": "^4.0.3", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.4.0", + "loader-utils": "^1.2.3", + "memory-fs": "^0.4.1", + "micromatch": "^3.1.10", + "mkdirp": "^0.5.3", + "neo-async": "^2.6.1", + "node-libs-browser": "^2.2.1", + "schema-utils": "^1.0.0", + "tapable": "^1.1.3", + "terser-webpack-plugin": "^1.4.3", + "watchpack": "^1.7.4", + "webpack-sources": "^1.4.1" + } + }, + "webpack-cli": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.7.0.tgz", + "integrity": "sha512-7bKr9182/sGfjFm+xdZSwgQuFjgEcy0iCTIBxRUeteJ2Kr8/Wz0qNJX+jw60LU36jApt4nmMkep6+W5AKhok6g==", + "requires": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^1.0.3", + "@webpack-cli/info": "^1.2.4", + "@webpack-cli/serve": "^1.4.0", + "colorette": "^1.2.1", + "commander": "^7.0.0", + "execa": "^5.0.0", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^2.2.0", + "rechoir": "^0.7.0", + "v8-compile-cache": "^2.2.0", + "webpack-merge": "^5.7.3" + }, + "dependencies": { + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" + } + } + }, + "webpack-merge": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.7.3.tgz", + "integrity": "sha512-6/JUQv0ELQ1igjGDzHkXbVDRxkfA57Zw7PfiupdLFJYrgFqY5ZP8xxbpp2lU3EPwYx89ht5Z/aDkD40hFCm5AA==", + "requires": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + } + }, "webpack-sources": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.1.0.tgz", - "integrity": "sha512-aqYp18kPphgoO5c/+NaUvEeACtZjMESmDChuD3NBciVpah3XpMEU9VAAtIaB1BsfJWWTSdv8Vv1m3T0aRk2dUw==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", "requires": { "source-list-map": "^2.0.0", "source-map": "~0.6.1" @@ -7692,67 +10249,24 @@ } }, "which": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", - "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "requires": { "isexe": "^2.0.0" } }, - "which-module": { + "wildcard": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==" }, "worker-farm": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.5.4.tgz", - "integrity": "sha512-ITyClEvcfv0ozqJl1vmWFWhvI+OIrkbInYqkEPE50wFPXj8J9Gd3FYf8+CkZJXJJsQBYe+2DvmoK9Zhx5w8W+w==", - "requires": { - "errno": "~0.1.7", - "xtend": "~4.0.1" - } - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - } + "errno": "~0.1.7" } }, "wrappy": { @@ -7760,110 +10274,20 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, - "write-file-atomic": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz", - "integrity": "sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=", - "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "slide": "^1.1.5" - } - }, "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" }, "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" - }, - "yargs": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-9.0.1.tgz", - "integrity": "sha1-UqzCP+7Kw0BCB47njAwAf1CF20w=", - "requires": { - "camelcase": "^4.1.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", - "read-pkg-up": "^2.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^7.0.0" - }, - "dependencies": { - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" - } - } - }, - "yargs-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", - "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", - "requires": { - "camelcase": "^4.1.0" - } - }, - "yeoman-environment": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/yeoman-environment/-/yeoman-environment-2.0.5.tgz", - "integrity": "sha512-6/W7/B54OPHJXob0n0+pmkwFsirC8cokuQkPSmT/D0lCcSxkKtg/BA6ZnjUBIwjuGqmw3DTrT4en++htaUju5g==", - "requires": { - "chalk": "^2.1.0", - "debug": "^3.1.0", - "diff": "^3.3.1", - "escape-string-regexp": "^1.0.2", - "globby": "^6.1.0", - "grouped-queue": "^0.3.3", - "inquirer": "^3.3.0", - "is-scoped": "^1.0.0", - "lodash": "^4.17.4", - "log-symbols": "^2.1.0", - "mem-fs": "^1.1.0", - "text-table": "^0.2.0", - "untildify": "^3.0.2" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - } - }, - "diff": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.4.0.tgz", - "integrity": "sha512-QpVuMTEoJMF7cKzi6bvWhRulU1fZqZnvyVQgNhPaxxuTYwyjn/j1v9falseQ/uXWwPnO56RBfwtg4h/EQXmucA==" - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "untildify": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-3.0.2.tgz", - "integrity": "sha1-fx8wIFWz/qDz6B3HjrNnZstl4/E=" - } - } + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" } } } diff --git a/Tests/A+/JavaScript/package.json b/Tests/A+/JavaScript/package.json index 6d2b0e6e5..8063140a9 100644 --- a/Tests/A+/JavaScript/package.json +++ b/Tests/A+/JavaScript/package.json @@ -7,11 +7,11 @@ "babel-core": "^6.26.0", "babel-loader": "^7.1.3", "babel-preset-env": "^1.6.1", - "lodash": "^4.17.5", + "lodash": "^4.17.21", "mocha": "^5.0.1", "promises-aplus-tests": "^2.1.2", "sinon": "^4.4.2", "webpack": "^4.0.1", - "webpack-cli": "^2.0.9" + "webpack-cli": "^4.7.0" } } From 50b8e5fd16c8a49b8f30d581f76f2a25b9c6bd37 Mon Sep 17 00:00:00 2001 From: Max Howell Date: Thu, 3 Jun 2021 10:29:33 -0400 Subject: [PATCH 74/81] [ci] Reject trailing whitespace --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 159e149fc..ae5083c78 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,10 +6,11 @@ on: - Tests/** - .github/workflows/ci.yml jobs: - auto-cancel: + smoke: runs-on: ubuntu-latest steps: - uses: technote-space/auto-cancel-redundant-job@v1 + - uses: harupy/find-trailing-whitespace@v1.0 linux: name: linux From 923d9e77ab21f0510f6825da1010480f094a15bc Mon Sep 17 00:00:00 2001 From: Max Howell Date: Sat, 5 Jun 2021 11:16:16 -0400 Subject: [PATCH 75/81] [ci] use mxcl/xcodebuild --- .github/workflows/ci.yml | 42 +++++++++------------------------------- 1 file changed, 9 insertions(+), 33 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ae5083c78..c4ab65b19 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,44 +45,20 @@ jobs: runs-on: macos-latest strategy: matrix: - dst: - - platform=macOS - - platform=tvOS Simulator,OS=latest,name=Apple TV - - platform=iOS Simulator,OS=latest,name=iPhone 12 + platform: + - macOS + - tvOS + - iOS + - watchOS steps: - - uses: maxim-lobanov/setup-xcode@v1 - with: - xcode-version: ^12 - uses: actions/checkout@v2 - run: | cd Tests/A+/JavaScript npm ci npm run build - if: ${{ endsWith(matrix.dst, 'macOS') }} - - uses: sersoft-gmbh/xcodebuild-action@v1 + if: ${{ matrix.platform == 'macOS' }} + - uses: mxcl/xcodebuild@v1 with: - spm-package: ./ - scheme: PromiseKit-Package - destination: ${{ matrix.dst }} - action: test - enable-code-coverage: true + platform: ${{ matrix.platform }} + code-coverage: true - uses: codecov/codecov-action@v1 - - watchOS: - runs-on: macos-latest - name: apple (watchOS) - steps: - - uses: maxim-lobanov/setup-xcode@v1 - with: - xcode-version: ^12 - - uses: actions/checkout@v2 - # with Xcode 12.5 we can use the above matrix, but with 12.4 - # testing is not supported and trying to build with the Package.swift - # tries to build the tests too 🙄 - - run: swift package generate-xcodeproj - - uses: sersoft-gmbh/xcodebuild-action@v1 - with: - project: PromiseKit.xcodeproj - scheme: PromiseKit-Package - destination: platform=watchOS Simulator,OS=latest,name=Apple Watch Series 5 - 40mm - action: build From 82e8218dc08675ed1ff5e0515238f989ff382713 Mon Sep 17 00:00:00 2001 From: Max Howell Date: Sun, 6 Jun 2021 11:17:01 -0400 Subject: [PATCH 76/81] [ci] use `concurrency` rather than auto-cancel --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c4ab65b19..ee1b0558e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,11 +5,13 @@ on: - Sources/** - Tests/** - .github/workflows/ci.yml +concurrency: + group: ${{ github.head_ref }} + cancel-in-progress: true jobs: smoke: runs-on: ubuntu-latest steps: - - uses: technote-space/auto-cancel-redundant-job@v1 - uses: harupy/find-trailing-whitespace@v1.0 linux: From 6d9d229c4c417cbbeab8aaa9ebff12a7f63d4a80 Mon Sep 17 00:00:00 2001 From: Max Howell Date: Sun, 6 Jun 2021 12:52:00 -0400 Subject: [PATCH 77/81] [ci] more thorough matrix --- .github/workflows/ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ee1b0558e..2aca0beb6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,6 +52,9 @@ jobs: - tvOS - iOS - watchOS + xcode: + - ~12.4 + - ^12.5 steps: - uses: actions/checkout@v2 - run: | @@ -61,6 +64,9 @@ jobs: if: ${{ matrix.platform == 'macOS' }} - uses: mxcl/xcodebuild@v1 with: + xcode: ${{ matrix.xcode }} platform: ${{ matrix.platform }} code-coverage: true + warnings-as-errors: true + continue-on-error: ${{ matrix.xcode == '^12.5' }} - uses: codecov/codecov-action@v1 From 1785e919ba328e8c4d9580b110d338fc1c59839c Mon Sep 17 00:00:00 2001 From: Max Howell Date: Mon, 7 Jun 2021 17:11:40 -0400 Subject: [PATCH 78/81] [ci] use Swift rather than Xcode versions --- .github/workflows/ci.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2aca0beb6..6f24fe326 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,9 +52,9 @@ jobs: - tvOS - iOS - watchOS - xcode: - - ~12.4 - - ^12.5 + swift: + - 5.3 + - 5.4 steps: - uses: actions/checkout@v2 - run: | @@ -64,9 +64,9 @@ jobs: if: ${{ matrix.platform == 'macOS' }} - uses: mxcl/xcodebuild@v1 with: - xcode: ${{ matrix.xcode }} + swift: ${{ matrix.swift }} platform: ${{ matrix.platform }} code-coverage: true warnings-as-errors: true - continue-on-error: ${{ matrix.xcode == '^12.5' }} + continue-on-error: ${{ matrix.swift == '5.4' }} - uses: codecov/codecov-action@v1 From 81fdad9144b8d4e334a110723dda8ecb521f6416 Mon Sep 17 00:00:00 2001 From: Max Howell Date: Wed, 23 Jun 2021 10:29:50 -0400 Subject: [PATCH 79/81] [ci] can has macos-11 --- .github/workflows/ci.yml | 11 ++++++----- Sources/PMKCloudKit/CKContainer+Promise.swift | 13 ++++++++++++- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6f24fe326..0ca1fcd55 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,10 +20,11 @@ jobs: strategy: matrix: swift: - - 5.3 - - 5.4-focal # Swift 5.4 requires llvm-cov v11 + - swift:5.3 + - swift:5.4 + - swiftlang/swift:nightly-5.5 container: - image: swift:${{ matrix.swift }} + image: ${{ matrix.swift }} steps: - uses: actions/checkout@v2 - run: swift test --enable-code-coverage --parallel --enable-test-discovery @@ -44,7 +45,7 @@ jobs: file: ./info.lcov apple: - runs-on: macos-latest + runs-on: ${{ matrix.swift == '5.3' && 'macos-10.15' || 'macos-11' }} strategy: matrix: platform: @@ -55,6 +56,7 @@ jobs: swift: - 5.3 - 5.4 + - 5.5 steps: - uses: actions/checkout@v2 - run: | @@ -68,5 +70,4 @@ jobs: platform: ${{ matrix.platform }} code-coverage: true warnings-as-errors: true - continue-on-error: ${{ matrix.swift == '5.4' }} - uses: codecov/codecov-action@v1 diff --git a/Sources/PMKCloudKit/CKContainer+Promise.swift b/Sources/PMKCloudKit/CKContainer+Promise.swift index 44febd520..00ec575eb 100644 --- a/Sources/PMKCloudKit/CKContainer+Promise.swift +++ b/Sources/PMKCloudKit/CKContainer+Promise.swift @@ -22,15 +22,26 @@ public extension CKContainer { } /// Requests the specified permission from the user asynchronously. +#if swift(<5.5) func requestApplicationPermission(_ applicationPermissions: CKContainer_Application_Permissions) -> Promise { return Promise { requestApplicationPermission(applicationPermissions, completionHandler: $0.resolve) } } +#else + func requestApplicationPermission(_ applicationPermissions: CKContainer.ApplicationPermissions) -> Promise { + return Promise { requestApplicationPermission(applicationPermissions, completionHandler: $0.resolve) } + } +#endif /// Checks the status of the specified permission asynchronously. +#if swift(<5.5) func status(forApplicationPermission applicationPermissions: CKContainer_Application_Permissions) -> Promise { return Promise { status(forApplicationPermission: applicationPermissions, completionHandler: $0.resolve) } } - +#else + func status(forApplicationPermission applicationPermissions: CKContainer.ApplicationPermissions) -> Promise { + return Promise { status(forApplicationPermission: applicationPermissions, completionHandler: $0.resolve) } + } +#endif /// Retrieves information about a single user based on the ID of the corresponding user record. @available(macOS 10.12, iOS 10, tvOS 10, *) func discoverUserIdentity(withUserRecordID recordID: CKRecord.ID) -> Promise { From dc04daa3c7e9d9585dc3e9d06b1295de0fb14901 Mon Sep 17 00:00:00 2001 From: Max Howell Date: Thu, 24 Jun 2021 13:13:47 -0400 Subject: [PATCH 80/81] Fix compile issues --- Tests/Core/ThenableTests.swift | 7 +++++-- Tests/PMKFoundation/TestNSURLSession.swift | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Tests/Core/ThenableTests.swift b/Tests/Core/ThenableTests.swift index 85ae5e8c2..57bee827e 100644 --- a/Tests/Core/ThenableTests.swift +++ b/Tests/Core/ThenableTests.swift @@ -84,8 +84,11 @@ class ThenableTests: XCTestCase { enum E: Error { case dummy } let ex = expectation(description: "") - Promise(error: E.dummy).compactMap { - Int($0) + + let p = Promise(error: E.dummy) + + p.compactMap { (x: Int) -> Int? in + Int(x) }.catch { if case E.dummy = $0 {} else { XCTFail() diff --git a/Tests/PMKFoundation/TestNSURLSession.swift b/Tests/PMKFoundation/TestNSURLSession.swift index b5a2156cf..2a57c7a98 100644 --- a/Tests/PMKFoundation/TestNSURLSession.swift +++ b/Tests/PMKFoundation/TestNSURLSession.swift @@ -1,4 +1,4 @@ -#if !os(Linux) +#if !os(Linux) && !os(watchOS) import OHHTTPStubsSwift import PMKFoundation From c8378a245560f5520398ad876d1811524a443282 Mon Sep 17 00:00:00 2001 From: Max Howell Date: Thu, 10 Jun 2021 10:58:47 -0400 Subject: [PATCH 81/81] [ci] Windows --- .github/workflows/ci.yml | 24 +++++++++++++++++-- Package.swift | 6 ++--- Sources/PMKFoundation/NSObject+Promise.swift | 2 +- .../PMKFoundation/NSURLSession+Promise.swift | 2 +- Sources/PMKFoundation/afterlife.swift | 2 +- .../Dispatchers/CoreDataDispatcher.swift | 3 +-- Sources/PromiseKit/Guarantee.swift | 11 --------- Sources/PromiseKit/hang.swift | 4 ++++ Tests/A+/JavaScript/AllTests.swift | 2 +- Tests/A+/JavaScript/JSAdapter.swift | 2 +- Tests/A+/JavaScript/JSPromise.swift | 2 +- Tests/A+/JavaScript/JSUtils.swift | 2 +- Tests/A+/JavaScript/MockNodeEnvironment.swift | 2 +- Tests/A+/Swift/0.0.0.swift | 8 +++++++ Tests/Cancel/HangTests.swift | 4 ++++ Tests/Cancel/Utilities.swift | 14 +++++++++-- Tests/Core/HangTests.swift | 4 ++++ Tests/Core/Utilities.swift | 14 +++++++++-- Tests/PMKFoundation/TestNSObject.swift | 2 +- Tests/PMKFoundation/TestNSURLSession.swift | 2 +- 20 files changed, 80 insertions(+), 32 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0ca1fcd55..8f4bad2dd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,13 +9,12 @@ concurrency: group: ${{ github.head_ref }} cancel-in-progress: true jobs: - smoke: + lint: runs-on: ubuntu-latest steps: - uses: harupy/find-trailing-whitespace@v1.0 linux: - name: linux runs-on: ubuntu-latest strategy: matrix: @@ -57,6 +56,7 @@ jobs: - 5.3 - 5.4 - 5.5 + name: ${{ matrix.platform }} (${{ matrix.swift }}) steps: - uses: actions/checkout@v2 - run: | @@ -71,3 +71,23 @@ jobs: code-coverage: true warnings-as-errors: true - uses: codecov/codecov-action@v1 + + windows: + runs-on: windows-latest + steps: + - uses: seanmiddleditch/gha-setup-vsdevenv@v3 + - run: | + Install-Binary -Url "https://swift.org/builds/swift-5.4.1-release/windows10/swift-5.4.1-RELEASE/swift-5.4.1-RELEASE-windows10.exe" -Name "installer.exe" -ArgumentList ("-q") + - run: | + echo "SDKROOT=C:\Library\Developer\Platforms\Windows.platform\Developer\SDKs\Windows.sdk" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + echo "DEVELOPER_DIR=C:\Library\Developer" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + - run: | + echo "C:\Library\Swift-development\bin;C:\Library\icu-67\usr\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + echo "C:\Library\Developer\Toolchains\unknown-Asserts-development.xctoolchain\usr\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + - run: | + Copy-Item "$env:SDKROOT\usr\share\ucrt.modulemap" -destination "$env:UniversalCRTSdkDir\Include\$env:UCRTVersion\ucrt\module.modulemap" + Copy-Item "$env:SDKROOT\usr\share\visualc.modulemap" -destination "$env:VCToolsInstallDir\include\module.modulemap" + Copy-Item "$env:SDKROOT\usr\share\visualc.apinotes" -destination "$env:VCToolsInstallDir\include\visualc.apinotes" + Copy-Item "$env:SDKROOT\usr\share\winsdk.modulemap" -destination "$env:UniversalCRTSdkDir\Include\$env:UCRTVersion\um\module.modulemap" + - uses: actions/checkout@v2 + - run: swift test --parallel diff --git a/Package.swift b/Package.swift index 1ef5915bb..4159af2b3 100644 --- a/Package.swift +++ b/Package.swift @@ -11,7 +11,7 @@ pkg.platforms = [ ] pkg.swiftLanguageVersions = [.v5] -#if !os(Linux) +#if !os(Linux) && !os(Windows) pkg.dependencies = [ .package(url: "https://github.com/AliSoftware/OHHTTPStubs", from: "9.1.0") ] @@ -30,7 +30,7 @@ func has(tests name: String) -> Target? { switch name { case "PMKFoundation": var deps = [Target.Dependency.target(name: "PMKFoundation")] - #if !os(Linux) + #if !os(Linux) && !os(Windows) deps.append(.product(name: "OHHTTPStubsSwift", package: "OHHTTPStubs")) #endif return .testTarget(name: "\(name)Tests", dependencies: deps, path: "Tests/\(name)") @@ -43,7 +43,7 @@ func has(tests name: String) -> Target? { for name in ["PMKCloudKit", "PMKCoreLocation", "PMKFoundation", "PMKHealthKit", "PMKHomeKit", "PMKMapKit", "PMKPhotos", "PMKStoreKit", "PromiseKit"] { - #if os(Linux) + #if os(Linux) || os(Windows) guard name == "PromiseKit" || name == "PMKFoundation" else { continue } #endif diff --git a/Sources/PMKFoundation/NSObject+Promise.swift b/Sources/PMKFoundation/NSObject+Promise.swift index 7ed54004f..c40bc8cb0 100644 --- a/Sources/PMKFoundation/NSObject+Promise.swift +++ b/Sources/PMKFoundation/NSObject+Promise.swift @@ -1,4 +1,4 @@ -#if !os(Linux) +#if !os(Linux) && !os(Windows) import Foundation #if !PMKCocoaPods diff --git a/Sources/PMKFoundation/NSURLSession+Promise.swift b/Sources/PMKFoundation/NSURLSession+Promise.swift index b23a0d229..deb913b48 100644 --- a/Sources/PMKFoundation/NSURLSession+Promise.swift +++ b/Sources/PMKFoundation/NSURLSession+Promise.swift @@ -128,7 +128,7 @@ extension URL: URLRequestConvertible { } -#if !os(Linux) +#if !os(Linux) && !os(Windows) public extension String { /** - Remark: useful when converting a `URLSession` response into a `String` diff --git a/Sources/PMKFoundation/afterlife.swift b/Sources/PMKFoundation/afterlife.swift index 82f6675f5..b7168d921 100644 --- a/Sources/PMKFoundation/afterlife.swift +++ b/Sources/PMKFoundation/afterlife.swift @@ -1,4 +1,4 @@ -#if !os(Linux) +#if !os(Linux) && !os(Windows) import Foundation #if !PMKCocoaPods diff --git a/Sources/PromiseKit/Dispatchers/CoreDataDispatcher.swift b/Sources/PromiseKit/Dispatchers/CoreDataDispatcher.swift index af7e98808..e3630172c 100644 --- a/Sources/PromiseKit/Dispatchers/CoreDataDispatcher.swift +++ b/Sources/PromiseKit/Dispatchers/CoreDataDispatcher.swift @@ -1,4 +1,4 @@ -#if !os(Linux) +#if canImport(CoreData) import Foundation import CoreData @@ -28,4 +28,3 @@ public struct CoreDataDispatcher: Dispatcher { } #endif - diff --git a/Sources/PromiseKit/Guarantee.swift b/Sources/PromiseKit/Guarantee.swift index 668eb0252..21901d8bb 100644 --- a/Sources/PromiseKit/Guarantee.swift +++ b/Sources/PromiseKit/Guarantee.swift @@ -380,14 +380,3 @@ public extension Dispatcher { return rg } } - -#if os(Linux) -import func CoreFoundation._CFIsMainThread - -extension Thread { - // `isMainThread` is not implemented yet in swift-corelibs-foundation. - static var isMainThread: Bool { - return _CFIsMainThread() - } -} -#endif diff --git a/Sources/PromiseKit/hang.swift b/Sources/PromiseKit/hang.swift index 906c31f28..5c5498d04 100644 --- a/Sources/PromiseKit/hang.swift +++ b/Sources/PromiseKit/hang.swift @@ -1,3 +1,5 @@ +#if canImport(CoreFoundation) + import Foundation import CoreFoundation @@ -64,3 +66,5 @@ public func hang(_ promise: Promise) throws -> T { public func hang(_ promise: CancellablePromise) throws -> T { return try hang(promise.promise) } + +#endif diff --git a/Tests/A+/JavaScript/AllTests.swift b/Tests/A+/JavaScript/AllTests.swift index 01bea5615..1ee7cb10c 100644 --- a/Tests/A+/JavaScript/AllTests.swift +++ b/Tests/A+/JavaScript/AllTests.swift @@ -5,7 +5,7 @@ // Created by Lois Di Qual on 2/28/18. // -#if !os(Linux) && !os(watchOS) +#if !os(Linux) && !os(watchOS) && !os(Windows) import JavaScriptCore import PromiseKit import XCTest diff --git a/Tests/A+/JavaScript/JSAdapter.swift b/Tests/A+/JavaScript/JSAdapter.swift index 464ab0700..52894d655 100644 --- a/Tests/A+/JavaScript/JSAdapter.swift +++ b/Tests/A+/JavaScript/JSAdapter.swift @@ -5,7 +5,7 @@ // Created by Lois Di Qual on 3/2/18. // -#if !os(Linux) && !os(watchOS) +#if !os(Linux) && !os(watchOS) && !os(Windows) import JavaScriptCore import PromiseKit diff --git a/Tests/A+/JavaScript/JSPromise.swift b/Tests/A+/JavaScript/JSPromise.swift index ce7f352a8..2b30192d2 100644 --- a/Tests/A+/JavaScript/JSPromise.swift +++ b/Tests/A+/JavaScript/JSPromise.swift @@ -5,7 +5,7 @@ // Created by Lois Di Qual on 3/1/18. // -#if !os(Linux) && !os(watchOS) +#if !os(Linux) && !os(watchOS) && !os(Windows) import JavaScriptCore import PromiseKit import XCTest diff --git a/Tests/A+/JavaScript/JSUtils.swift b/Tests/A+/JavaScript/JSUtils.swift index 434cd33b7..eb3e50476 100644 --- a/Tests/A+/JavaScript/JSUtils.swift +++ b/Tests/A+/JavaScript/JSUtils.swift @@ -5,7 +5,7 @@ // Created by Lois Di Qual on 3/2/18. // -#if !os(Linux) && !os(watchOS) +#if !os(Linux) && !os(watchOS) && !os(Windows) import JavaScriptCore enum JSUtils { diff --git a/Tests/A+/JavaScript/MockNodeEnvironment.swift b/Tests/A+/JavaScript/MockNodeEnvironment.swift index d034d6737..b3017b151 100644 --- a/Tests/A+/JavaScript/MockNodeEnvironment.swift +++ b/Tests/A+/JavaScript/MockNodeEnvironment.swift @@ -5,7 +5,7 @@ // Created by Lois Di Qual on 3/1/18. // -#if !os(Linux) && !os(watchOS) +#if !os(Linux) && !os(watchOS) && !os(Windows) import JavaScriptCore import Foundation diff --git a/Tests/A+/Swift/0.0.0.swift b/Tests/A+/Swift/0.0.0.swift index 578c6dd54..8db18017c 100644 --- a/Tests/A+/Swift/0.0.0.swift +++ b/Tests/A+/Swift/0.0.0.swift @@ -167,7 +167,15 @@ import func Glibc.random func arc4random() -> UInt32 { return UInt32(random()) } +#endif + +#if os(Windows) +func arc4random() -> UInt32 { + return UInt32.random(in: UInt32.min...UInt32.max) +} +#endif +#if os(Linux) || os(Windows) extension XCTestExpectation { func fulfill() { fulfill(#file, line: #line) diff --git a/Tests/Cancel/HangTests.swift b/Tests/Cancel/HangTests.swift index 30f2d027d..06d6b9b4f 100644 --- a/Tests/Cancel/HangTests.swift +++ b/Tests/Cancel/HangTests.swift @@ -1,3 +1,5 @@ +#if canImport(CoreFoundation) + import PromiseKit import XCTest @@ -46,3 +48,5 @@ class HangTests: XCTestCase { XCTFail("Expected error but no error was thrown") } } + +#endif diff --git a/Tests/Cancel/Utilities.swift b/Tests/Cancel/Utilities.swift index 87a408d4a..228deb778 100644 --- a/Tests/Cancel/Utilities.swift +++ b/Tests/Cancel/Utilities.swift @@ -1,4 +1,5 @@ import PromiseKit +import XCTest // Workaround for error with missing libswiftContacts.dylib, this import causes the // library to be included as needed @@ -24,17 +25,26 @@ extension Thread { } } -import XCTest - extension XCTestCase { func wait(for: [XCTestExpectation], timeout: TimeInterval, file: StaticString = #file, line: UInt = #line) { waitForExpectations(timeout: timeout, file: file, line: Int(line)) } } +#endif + +#if os(Linux) || os(Windows) extension XCTestExpectation { func fulfill() { fulfill(#file, line: #line) } } #endif + +#if os(Windows) +import class Foundation.Thread + +func usleep(_ us: UInt32) { + Thread.sleep(forTimeInterval: Double(us) / 1_000_000.0) +} +#endif diff --git a/Tests/Core/HangTests.swift b/Tests/Core/HangTests.swift index c0950a899..da389e638 100644 --- a/Tests/Core/HangTests.swift +++ b/Tests/Core/HangTests.swift @@ -1,3 +1,5 @@ +#if canImport(CoreFoundation) + import PromiseKit import XCTest @@ -36,3 +38,5 @@ class HangTests: XCTestCase { XCTFail("Expected error but no error was thrown") } } + +#endif diff --git a/Tests/Core/Utilities.swift b/Tests/Core/Utilities.swift index 2ab532aa8..ebce722e4 100644 --- a/Tests/Core/Utilities.swift +++ b/Tests/Core/Utilities.swift @@ -1,4 +1,5 @@ import PromiseKit +import XCTest extension Promise { func silenceWarning() {} @@ -18,17 +19,26 @@ extension Thread { } } -import XCTest - extension XCTestCase { func wait(for: [XCTestExpectation], timeout: TimeInterval, file: StaticString = #file, line: UInt = #line) { waitForExpectations(timeout: timeout, file: file, line: Int(line)) } } +#endif + +#if os(Linux) || os(Windows) extension XCTestExpectation { func fulfill() { fulfill(#file, line: #line) } } #endif + +#if os(Windows) +import class Foundation.Thread + +func usleep(_ us: UInt32) { + Thread.sleep(forTimeInterval: Double(us) / 1_000_000.0) +} +#endif diff --git a/Tests/PMKFoundation/TestNSObject.swift b/Tests/PMKFoundation/TestNSObject.swift index c72683420..2f23a6201 100644 --- a/Tests/PMKFoundation/TestNSObject.swift +++ b/Tests/PMKFoundation/TestNSObject.swift @@ -3,7 +3,7 @@ import Foundation import PromiseKit import XCTest -#if !os(Linux) +#if !os(Linux) && !os(Windows) class NSObjectTests: XCTestCase { func testKVO() { diff --git a/Tests/PMKFoundation/TestNSURLSession.swift b/Tests/PMKFoundation/TestNSURLSession.swift index 2a57c7a98..fc7c24ef6 100644 --- a/Tests/PMKFoundation/TestNSURLSession.swift +++ b/Tests/PMKFoundation/TestNSURLSession.swift @@ -1,4 +1,4 @@ -#if !os(Linux) && !os(watchOS) +#if !os(Linux) && !os(watchOS) && !os(Windows) import OHHTTPStubsSwift import PMKFoundation