@swiftbrowser/swift 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Package.swift +200 -9
- package/README.md +7 -9
- package/Sources/AVFoundation/AVFoundation.swift +131 -0
- package/Sources/AVFoundation/Asset.swift +487 -0
- package/Sources/AVFoundation/AudioSession.swift +144 -0
- package/Sources/AVFoundation/CMTime.swift +303 -0
- package/Sources/AVFoundation/Capture.swift +688 -0
- package/Sources/AVFoundation/Player.swift +749 -0
- package/Sources/AVFoundation/PlayerView.swift +117 -0
- package/Sources/AVKit/AVKit.swift +132 -0
- package/Sources/Accelerate/Accelerate.swift +122 -0
- package/Sources/AppIntents/AppIntents.swift +144 -0
- package/Sources/AppIntents/Parameters.swift +220 -0
- package/Sources/AppIntents/Shortcuts.swift +159 -0
- package/Sources/AudioToolbox/AudioToolbox.swift +55 -0
- package/Sources/AuthenticationServices/AuthenticationServices.swift +257 -0
- package/Sources/AuthenticationServices/Authorization.swift +251 -0
- package/Sources/Charts/Charts.swift +0 -4
- package/Sources/Combine/AnyCancellable.swift +65 -0
- package/Sources/Combine/AnyPublisher.swift +148 -0
- package/Sources/Combine/AnySubscriber.swift +230 -0
- package/Sources/Combine/Cancellable.swift +35 -0
- package/Sources/Combine/Codable.swift +29 -0
- package/Sources/Combine/CombineIdentifier.swift +45 -0
- package/Sources/Combine/Concurrency/ConcurrencyHelpers.swift +24 -0
- package/Sources/Combine/Concurrency/Future+Concurrency.swift +136 -0
- package/Sources/Combine/Concurrency/GENERATED-Publisher+Concurrency.swift +397 -0
- package/Sources/Combine/CurrentValueSubject.swift +252 -0
- package/Sources/Combine/CustomCombineIdentifierConvertible.swift +28 -0
- package/Sources/Combine/Future.swift +228 -0
- package/Sources/Combine/GENERATED-RootProtocols.swift +426 -0
- package/Sources/Combine/Helpers/ConduitBase.swift +40 -0
- package/Sources/Combine/Helpers/ConduitList.swift +59 -0
- package/Sources/Combine/Helpers/DebugHook.swift +177 -0
- package/Sources/Combine/Helpers/FilterProducer.swift +174 -0
- package/Sources/Combine/Helpers/Locking.swift +30 -0
- package/Sources/Combine/Helpers/PartialCompletion.swift +26 -0
- package/Sources/Combine/Helpers/ReduceProducer.swift +249 -0
- package/Sources/Combine/Helpers/SubjectSubscriber.swift +96 -0
- package/Sources/Combine/Helpers/SubscriberTap.swift +69 -0
- package/Sources/Combine/Helpers/SubscriptionStatus.swift +33 -0
- package/Sources/Combine/Helpers/SubscriptionTap.swift +33 -0
- package/Sources/Combine/Helpers/Utils.swift +30 -0
- package/Sources/Combine/Helpers/Violations.swift +33 -0
- package/Sources/Combine/ImmediateScheduler.swift +178 -0
- package/Sources/Combine/LICENSE +21 -0
- package/Sources/Combine/NotificationPublisher.swift +48 -0
- package/Sources/Combine/ObjectWillChange.swift +46 -0
- package/Sources/Combine/PassthroughSubject.swift +222 -0
- package/Sources/Combine/Publisher+Subscribe.swift +53 -0
- package/Sources/Combine/Publishers/Deferred.swift +48 -0
- package/Sources/Combine/Publishers/Empty.swift +54 -0
- package/Sources/Combine/Publishers/Fail.swift +42 -0
- package/Sources/Combine/Publishers/GENERATED-Publishers.Catch.swift +673 -0
- package/Sources/Combine/Publishers/GENERATED-Publishers.Encode.swift +388 -0
- package/Sources/Combine/Publishers/GENERATED-Publishers.MapKeyPath.swift +384 -0
- package/Sources/Combine/Publishers/Just.swift +313 -0
- package/Sources/Combine/Publishers/Optional.Publisher.swift +315 -0
- package/Sources/Combine/Publishers/Publishers.AllSatisfy.swift +217 -0
- package/Sources/Combine/Publishers/Publishers.AssertNoFailure.swift +158 -0
- package/Sources/Combine/Publishers/Publishers.Autoconnect.swift +193 -0
- package/Sources/Combine/Publishers/Publishers.Breakpoint.swift +231 -0
- package/Sources/Combine/Publishers/Publishers.Buffer.swift +356 -0
- package/Sources/Combine/Publishers/Publishers.Collect.swift +102 -0
- package/Sources/Combine/Publishers/Publishers.CollectByCount.swift +195 -0
- package/Sources/Combine/Publishers/Publishers.CompactMap.swift +241 -0
- package/Sources/Combine/Publishers/Publishers.Comparison.swift +371 -0
- package/Sources/Combine/Publishers/Publishers.Concatenate.swift +474 -0
- package/Sources/Combine/Publishers/Publishers.Contains.swift +293 -0
- package/Sources/Combine/Publishers/Publishers.Count.swift +87 -0
- package/Sources/Combine/Publishers/Publishers.Debounce.swift +332 -0
- package/Sources/Combine/Publishers/Publishers.Delay.swift +269 -0
- package/Sources/Combine/Publishers/Publishers.Drop.swift +155 -0
- package/Sources/Combine/Publishers/Publishers.DropUntilOutput.swift +283 -0
- package/Sources/Combine/Publishers/Publishers.DropWhile.swift +378 -0
- package/Sources/Combine/Publishers/Publishers.Filter.swift +248 -0
- package/Sources/Combine/Publishers/Publishers.First.swift +261 -0
- package/Sources/Combine/Publishers/Publishers.FlatMap.swift +533 -0
- package/Sources/Combine/Publishers/Publishers.HandleEvents.swift +260 -0
- package/Sources/Combine/Publishers/Publishers.IgnoreOutput.swift +109 -0
- package/Sources/Combine/Publishers/Publishers.Last.swift +252 -0
- package/Sources/Combine/Publishers/Publishers.MakeConnectable.swift +82 -0
- package/Sources/Combine/Publishers/Publishers.Map.swift +351 -0
- package/Sources/Combine/Publishers/Publishers.MapError.swift +140 -0
- package/Sources/Combine/Publishers/Publishers.MeasureInterval.swift +187 -0
- package/Sources/Combine/Publishers/Publishers.Multicast.swift +284 -0
- package/Sources/Combine/Publishers/Publishers.Output.swift +241 -0
- package/Sources/Combine/Publishers/Publishers.PrefixUntilOutput.swift +197 -0
- package/Sources/Combine/Publishers/Publishers.PrefixWhile.swift +176 -0
- package/Sources/Combine/Publishers/Publishers.Print.swift +206 -0
- package/Sources/Combine/Publishers/Publishers.ReceiveOn.swift +227 -0
- package/Sources/Combine/Publishers/Publishers.Reduce.swift +204 -0
- package/Sources/Combine/Publishers/Publishers.RemoveDuplicates.swift +264 -0
- package/Sources/Combine/Publishers/Publishers.ReplaceEmpty.swift +201 -0
- package/Sources/Combine/Publishers/Publishers.ReplaceError.swift +211 -0
- package/Sources/Combine/Publishers/Publishers.Retry.swift +274 -0
- package/Sources/Combine/Publishers/Publishers.Scan.swift +331 -0
- package/Sources/Combine/Publishers/Publishers.Sequence.swift +437 -0
- package/Sources/Combine/Publishers/Publishers.SetFailureType.swift +120 -0
- package/Sources/Combine/Publishers/Publishers.Share.swift +104 -0
- package/Sources/Combine/Publishers/Publishers.SubscribeOn.swift +197 -0
- package/Sources/Combine/Publishers/Publishers.SwitchToLatest.swift +444 -0
- package/Sources/Combine/Publishers/Publishers.Throttle.swift +359 -0
- package/Sources/Combine/Publishers/Publishers.Timeout.swift +301 -0
- package/Sources/Combine/Publishers/Publishers.Zip.swift +720 -0
- package/Sources/Combine/Publishers/Publishers.swift +13 -0
- package/Sources/Combine/Publishers/Record.swift +237 -0
- package/Sources/Combine/Publishers/Result.Publisher.swift +337 -0
- package/Sources/Combine/Result.swift +46 -0
- package/Sources/Combine/Scheduler.swift +89 -0
- package/Sources/Combine/Schedulers.swift +211 -0
- package/Sources/Combine/Subject+Void.swift +17 -0
- package/Sources/Combine/Subscriber+Void.swift +20 -0
- package/Sources/Combine/Subscribers/Subscribers.Assign.swift +175 -0
- package/Sources/Combine/Subscribers/Subscribers.Completion.swift +90 -0
- package/Sources/Combine/Subscribers/Subscribers.Demand.swift +476 -0
- package/Sources/Combine/Subscribers/Subscribers.Sink.swift +206 -0
- package/Sources/Combine/Subscribers/Subscribers.swift +9 -0
- package/Sources/Combine/Subscription.swift +22 -0
- package/Sources/Combine/Subscriptions.swift +42 -0
- package/Sources/Combine/SwiftUIObservation.swift +10 -0
- package/Sources/Combine/_Introspection.swift +107 -0
- package/Sources/CommonCrypto/CommonCrypto.swift +80 -0
- package/Sources/Compression/Compression.swift +365 -0
- package/Sources/Compression/Deflate.swift +407 -0
- package/Sources/Compression/Zlib.swift +179 -0
- package/Sources/CoreFoundationTypes/CoreFoundationTypes.swift +101 -0
- package/Sources/CoreGraphics/CoreGraphics.swift +411 -0
- package/Sources/CoreGraphics/ProjectionTransforms.swift +28 -0
- package/Sources/CoreHaptics/CoreHaptics.swift +399 -0
- package/Sources/CoreImage/CIColor.swift +41 -0
- package/Sources/CoreImage/CIContext.swift +31 -0
- package/Sources/CoreImage/CIDetector.swift +57 -0
- package/Sources/CoreImage/CIFilter.swift +243 -0
- package/Sources/CoreImage/CIFilterBuiltins.swift +136 -0
- package/Sources/CoreImage/CIImage.swift +162 -0
- package/Sources/CoreImage/QRCode.swift +393 -0
- package/Sources/CoreLocation/Coordinate.swift +30 -0
- package/Sources/CoreLocation/Location.swift +65 -0
- package/Sources/CoreML/Evaluation.swift +414 -0
- package/Sources/CoreML/FeatureValue.swift +266 -0
- package/Sources/CoreML/Model.swift +269 -0
- package/Sources/CoreML/MultiArray.swift +92 -0
- package/Sources/CoreML/Specification.swift +233 -0
- package/Sources/CoreText/CoreText.swift +64 -0
- package/Sources/CoreTransferable/CoreTransferable.swift +606 -0
- package/Sources/CryptoKit/ChaChaPoly.swift +258 -0
- package/Sources/CryptoKit/CryptoKit.swift +375 -0
- package/Sources/CryptoKit/Curve25519.swift +317 -0
- package/Sources/CryptoKit/P256.swift +321 -0
- package/Sources/FoundationBridge/AppStorage.swift +165 -0
- package/Sources/FoundationBridge/AssetResources.swift +90 -0
- package/Sources/FoundationBridge/AsyncBytes.swift +128 -0
- package/Sources/FoundationBridge/AsyncImage.swift +116 -0
- package/Sources/FoundationBridge/AttributedStrings.swift +16 -0
- package/Sources/{_FoundationCommon → FoundationBridge}/Bundle.swift +41 -4
- package/Sources/FoundationBridge/CombineExports.swift +8 -0
- package/Sources/FoundationBridge/CoreFoundation.swift +77 -0
- package/Sources/FoundationBridge/Dispatch.swift +458 -0
- package/Sources/FoundationBridge/EnvironmentFoundation.swift +41 -0
- package/Sources/FoundationBridge/FileImporter.swift +128 -0
- package/Sources/FoundationBridge/FileSystem.swift +68 -0
- package/Sources/FoundationBridge/FoundationBridge.swift +24 -72
- package/Sources/FoundationBridge/HostResolution.swift +31 -0
- package/Sources/FoundationBridge/ItemProvider.swift +655 -0
- package/Sources/FoundationBridge/KeyValueObserving.swift +138 -0
- package/Sources/FoundationBridge/Links.swift +127 -0
- package/Sources/FoundationBridge/ListFormatter.swift +32 -0
- package/Sources/FoundationBridge/Localization.swift +149 -0
- package/Sources/FoundationBridge/Locks.swift +50 -0
- package/Sources/FoundationBridge/Markdown.swift +527 -0
- package/Sources/FoundationBridge/MeasurementFormat.swift +95 -0
- package/Sources/FoundationBridge/NotificationCenter.swift +154 -0
- package/Sources/FoundationBridge/OpenURLHandlers.swift +60 -0
- package/Sources/FoundationBridge/Operation.swift +195 -0
- package/Sources/FoundationBridge/ProcessInfo.swift +32 -0
- package/Sources/FoundationBridge/RelativeDateTimeFormatter.swift +136 -0
- package/Sources/FoundationBridge/SceneStorage.swift +78 -0
- package/Sources/FoundationBridge/SecurityExports.swift +8 -0
- package/Sources/FoundationBridge/Selector.swift +226 -0
- package/Sources/FoundationBridge/StringComparators.swift +17 -0
- package/Sources/FoundationBridge/Table.swift +211 -0
- package/Sources/FoundationBridge/TextAttributes.swift +42 -0
- package/Sources/FoundationBridge/Timer.swift +6 -1
- package/Sources/FoundationBridge/URLSession.swift +718 -0
- package/Sources/{_FoundationCommon → FoundationBridge}/UserDefaults.swift +17 -4
- package/Sources/FoundationBridge/WebAuthRedirects.swift +227 -0
- package/Sources/FoundationBridge/WebSocket.swift +187 -0
- package/Sources/FoundationModels/FoundationModels.swift +1098 -0
- package/Sources/ImageIO/GIF.swift +72 -0
- package/Sources/ImageIO/ImageIO.swift +248 -0
- package/Sources/ImageIO/ImageTypes.swift +28 -0
- package/Sources/LinkPresentation/LinkPresentation.swift +249 -0
- package/Sources/LocalAuthentication/LAContext.swift +86 -0
- package/Sources/MapKit/Geometry.swift +223 -0
- package/Sources/MapKit/Map.swift +638 -0
- package/Sources/MapKit/MapView.swift +229 -0
- package/Sources/MobileCoreServices/MobileCoreServices.swift +28 -0
- package/Sources/NaturalLanguage/LanguageIdentifier.swift +317 -0
- package/Sources/NaturalLanguage/NaturalLanguage.swift +135 -0
- package/Sources/OSLog/OSLog.swift +6 -0
- package/Sources/Photos/Photos.swift +572 -0
- package/Sources/PhotosUI/PhotosPicker.swift +248 -0
- package/Sources/PhotosUI/PhotosPickerItem.swift +261 -0
- package/Sources/QuickLook/QuickLook.swift +168 -0
- package/Sources/SafariServices/SafariServices.swift +106 -0
- package/Sources/Security/Security.swift +332 -0
- package/Sources/StoreKit/AppStore.swift +207 -0
- package/Sources/StoreKit/Products.swift +280 -0
- package/Sources/StoreKit/RequestReview.swift +28 -0
- package/Sources/StoreKit/StoreKit1.swift +486 -0
- package/Sources/StoreKit/StoreViews.swift +428 -0
- package/Sources/StoreKit/Transactions.swift +197 -0
- package/Sources/SwiftData/Macros.swift +58 -0
- package/Sources/SwiftData/ModelActor.swift +38 -0
- package/Sources/SwiftData/ModelContainer.swift +129 -0
- package/Sources/SwiftData/ModelContext.swift +451 -0
- package/Sources/SwiftData/PersistentModel.swift +238 -0
- package/Sources/SwiftData/Properties.swift +261 -0
- package/Sources/SwiftData/Query.swift +256 -0
- package/Sources/SwiftData/Schema.swift +192 -0
- package/Sources/SwiftData/Store.swift +146 -0
- package/Sources/SwiftData/StringPredicates.swift +125 -0
- package/Sources/SwiftDataMacros/EntryMacro.swift +82 -0
- package/Sources/SwiftDataMacros/GenerableMacro.swift +183 -0
- package/Sources/SwiftDataMacros/ModelActorMacro.swift +46 -0
- package/Sources/SwiftDataMacros/ModelMacro.swift +260 -0
- package/Sources/SwiftDataMacros/Plugin.swift +18 -0
- package/Sources/SwiftDataMacros/PredicateMacro.swift +1244 -0
- package/Sources/SwiftUI/API/Accessibility.swift +55 -6
- package/Sources/SwiftUI/API/AccessibilityExtras.swift +33 -0
- package/Sources/SwiftUI/API/AccessibilityFocus.swift +63 -0
- package/Sources/SwiftUI/API/Alert.swift +1 -1
- package/Sources/SwiftUI/API/AlignmentGuides.swift +65 -0
- package/Sources/SwiftUI/API/Animation.swift +37 -5
- package/Sources/SwiftUI/API/App.swift +31 -1
- package/Sources/SwiftUI/API/BindingCollections.swift +35 -0
- package/Sources/SwiftUI/API/BindingForEach.swift +27 -0
- package/Sources/SwiftUI/API/Bitmaps.swift +254 -0
- package/Sources/SwiftUI/API/ButtonStyles.swift +96 -0
- package/Sources/SwiftUI/API/Canvas.swift +337 -0
- package/Sources/SwiftUI/API/Color.swift +104 -28
- package/Sources/SwiftUI/API/ColorEffects.swift +52 -0
- package/Sources/SwiftUI/API/ColorPickerAndLabeledContent.swift +138 -0
- package/Sources/SwiftUI/API/ColorSpaces.swift +161 -0
- package/Sources/SwiftUI/API/Commands.swift +106 -0
- package/Sources/SwiftUI/API/ContainerBackground.swift +28 -0
- package/Sources/SwiftUI/API/ContentShape.swift +33 -0
- package/Sources/SwiftUI/API/ContentSizeCategory.swift +35 -0
- package/Sources/SwiftUI/API/ContentUnavailable.swift +101 -0
- package/Sources/SwiftUI/API/Controls.swift +59 -27
- package/Sources/SwiftUI/API/CustomLayout.swift +49 -10
- package/Sources/SwiftUI/API/CustomToolbarContent.swift +46 -0
- package/Sources/SwiftUI/API/Decorations.swift +1 -1
- package/Sources/SwiftUI/API/EditMode.swift +25 -82
- package/Sources/SwiftUI/API/Effects.swift +105 -7
- package/Sources/SwiftUI/API/Entry.swift +8 -0
- package/Sources/SwiftUI/API/Environment.swift +93 -9
- package/Sources/SwiftUI/API/EnvironmentSendable.swift +6 -0
- package/Sources/SwiftUI/API/Focus.swift +11 -3
- package/Sources/SwiftUI/API/Gauge.swift +3 -2
- package/Sources/SwiftUI/API/GeometryEffects.swift +232 -0
- package/Sources/SwiftUI/API/GeometryReader.swift +13 -2
- package/Sources/SwiftUI/API/GestureState.swift +65 -0
- package/Sources/SwiftUI/API/Gestures.swift +1 -1
- package/Sources/SwiftUI/API/Glass.swift +127 -0
- package/Sources/SwiftUI/API/Grid.swift +1 -1
- package/Sources/SwiftUI/API/GroupBoxAndControlGroup.swift +130 -0
- package/Sources/SwiftUI/API/KeyPress.swift +54 -0
- package/Sources/SwiftUI/API/LabelStyle.swift +13 -6
- package/Sources/SwiftUI/API/Layout.swift +67 -12
- package/Sources/SwiftUI/API/LazyStacks.swift +49 -0
- package/Sources/SwiftUI/API/LegacyAlert.swift +97 -0
- package/Sources/SwiftUI/API/Lists.swift +61 -6
- package/Sources/SwiftUI/API/Menu.swift +8 -1
- package/Sources/SwiftUI/API/Modifiers.swift +80 -10
- package/Sources/SwiftUI/API/MoreModifiers.swift +100 -0
- package/Sources/SwiftUI/API/Namespace.swift +44 -0
- package/Sources/SwiftUI/API/Navigation.swift +24 -9
- package/Sources/SwiftUI/API/NavigationDestinationBindings.swift +55 -0
- package/Sources/SwiftUI/API/ObservableObject.swift +16 -2
- package/Sources/SwiftUI/API/Path.swift +543 -0
- package/Sources/SwiftUI/API/Phase8Controls.swift +48 -31
- package/Sources/SwiftUI/API/PhaseAnimator.swift +130 -0
- package/Sources/SwiftUI/API/Picker.swift +11 -2
- package/Sources/SwiftUI/API/PointerAndKeyboard.swift +77 -0
- package/Sources/SwiftUI/API/Popover.swift +41 -0
- package/Sources/SwiftUI/API/Preferences.swift +262 -0
- package/Sources/SwiftUI/API/Presentation.swift +17 -4
- package/Sources/SwiftUI/API/PresentationExtras.swift +60 -0
- package/Sources/SwiftUI/API/Primitives.swift +164 -37
- package/Sources/SwiftUI/API/Publishers.swift +4 -4
- package/Sources/SwiftUI/API/Redaction.swift +75 -0
- package/Sources/SwiftUI/API/RowActions.swift +3 -2
- package/Sources/SwiftUI/API/Scenes.swift +131 -0
- package/Sources/SwiftUI/API/ScrollModifiers.swift +132 -0
- package/Sources/SwiftUI/API/ScrollTargets.swift +3 -3
- package/Sources/SwiftUI/API/ScrollViewReader.swift +144 -0
- package/Sources/SwiftUI/API/SectionSupplements.swift +96 -0
- package/Sources/SwiftUI/API/Shape.swift +214 -0
- package/Sources/SwiftUI/API/ShapeStyle.swift +105 -7
- package/Sources/SwiftUI/API/ShapeTrim.swift +78 -0
- package/Sources/SwiftUI/API/Sharing.swift +193 -0
- package/Sources/SwiftUI/API/StackLayouts.swift +199 -0
- package/Sources/SwiftUI/API/State.swift +18 -1
- package/Sources/SwiftUI/API/StatusGaps.swift +121 -0
- package/Sources/SwiftUI/API/SymbolEffects.swift +203 -0
- package/Sources/SwiftUI/API/TabContent.swift +841 -0
- package/Sources/SwiftUI/API/Table.swift +382 -0
- package/Sources/SwiftUI/API/Tabs.swift +148 -4
- package/Sources/SwiftUI/API/Tasks.swift +2 -2
- package/Sources/SwiftUI/API/Text.swift +386 -0
- package/Sources/SwiftUI/API/TextDates.swift +61 -0
- package/Sources/SwiftUI/API/TextDecorations.swift +52 -0
- package/Sources/SwiftUI/API/TextFieldAxis.swift +103 -0
- package/Sources/SwiftUI/API/TextFieldCommit.swift +52 -0
- package/Sources/SwiftUI/API/Toolbar.swift +52 -16
- package/Sources/SwiftUI/API/Transfer.swift +145 -0
- package/Sources/SwiftUI/API/UnevenRoundedRectangle.swift +66 -0
- package/Sources/SwiftUI/API/Video.swift +64 -0
- package/Sources/SwiftUI/API/View.swift +10 -2
- package/Sources/SwiftUI/API/ViewModifier.swift +4 -1
- package/Sources/SwiftUI/API/VisualEffect.swift +74 -1
- package/Sources/SwiftUI/API/Windows.swift +25 -0
- package/Sources/SwiftUI/Charts/Axis.swift +7 -1
- package/Sources/SwiftUI/Charts/AxisFormatsAndRanges.swift +68 -0
- package/Sources/SwiftUI/Charts/Chart.swift +17 -3
- package/Sources/SwiftUI/Charts/ChartProxy.swift +57 -0
- package/Sources/SwiftUI/Localization/Localization.swift +498 -0
- package/Sources/SwiftUI/Localization/PluralRules.swift +127 -0
- package/Sources/SwiftUI/Runtime/Executor.swift +2 -0
- package/Sources/SwiftUI/Runtime/Exports.swift +37 -1
- package/Sources/SwiftUI/Runtime/ImageSource.swift +310 -0
- package/Sources/SwiftUI/Runtime/JSON.swift +76 -4
- package/Sources/SwiftUI/Runtime/LazyRows.swift +99 -0
- package/Sources/SwiftUI/Runtime/Measurements.swift +140 -0
- package/Sources/SwiftUI/Runtime/Ops.swift +58 -12
- package/Sources/SwiftUI/Runtime/Reconciler.swift +10 -1
- package/Sources/SwiftUI/Runtime/Replay.swift +382 -0
- package/Sources/SwiftUI/Runtime/Runtime.swift +977 -198
- package/Sources/SwiftUI/Runtime/Sameness.swift +152 -0
- package/Sources/SwiftUI/Runtime/ViewPath.swift +89 -0
- package/Sources/SwiftUI/Runtime/ViewTraits.swift +175 -0
- package/Sources/SwiftUI/Runtime/WebAuthHeadless.swift +49 -0
- package/Sources/Symbols/Symbols.swift +25 -0
- package/Sources/Translation/Translation.swift +423 -0
- package/Sources/UIKit/Activity.swift +213 -0
- package/Sources/UIKit/Alerts.swift +69 -0
- package/Sources/UIKit/AppDelegate.swift +222 -0
- package/Sources/UIKit/Appearance.swift +84 -0
- package/Sources/UIKit/ColorConversions.swift +70 -0
- package/Sources/UIKit/Controls.swift +225 -0
- package/Sources/UIKit/Device.swift +200 -0
- package/Sources/UIKit/Feedback.swift +65 -0
- package/Sources/UIKit/Font.swift +307 -0
- package/Sources/UIKit/Graphics.swift +204 -0
- package/Sources/UIKit/HostingController.swift +185 -0
- package/Sources/UIKit/Identity.swift +24 -0
- package/Sources/UIKit/Introspectable.swift +510 -0
- package/Sources/UIKit/Pasteboard.swift +363 -0
- package/Sources/UIKit/PhotoAlbum.swift +81 -0
- package/Sources/UIKit/RemoteNotifications.swift +15 -0
- package/Sources/UIKit/Representable.swift +288 -0
- package/Sources/UIKit/SafeArea.swift +12 -0
- package/Sources/UIKit/SceneDelegates.swift +132 -0
- package/Sources/UIKit/Scenes.swift +40 -0
- package/Sources/UIKit/ScrollView.swift +157 -0
- package/Sources/UIKit/ShortcutItems.swift +67 -0
- package/Sources/UIKit/SymbolConfiguration.swift +124 -0
- package/Sources/UIKit/TargetAction.swift +123 -0
- package/Sources/UIKit/TextPaste.swift +161 -0
- package/Sources/UIKit/TextView.swift +538 -0
- package/Sources/UIKit/UIKit.swift +292 -0
- package/Sources/UIKit/UIView.swift +855 -0
- package/Sources/UIKit/ViewTranslation.swift +311 -0
- package/Sources/UniformTypeIdentifiers/UTType.swift +196 -0
- package/Sources/UserNotifications/Content.swift +370 -0
- package/Sources/UserNotifications/NotificationCenter.swift +309 -0
- package/Sources/WeatherKit/WeatherKit.swift +228 -0
- package/Sources/WebKit/WebKit.swift +245 -0
- package/Sources/os/os.swift +376 -0
- package/Sources/zlib/zlib.swift +6 -0
- package/index.d.ts +1 -1
- package/index.mjs +2 -2
- package/manifest.d.mts +2 -0
- package/manifest.mjs +39 -0
- package/package.json +5 -3
- package/sync-sources.mjs +21 -7
- package/Sources/FoundationLite/Common.swift +0 -3
- package/Sources/FoundationLite/FoundationLite.swift +0 -171
- package/Sources/FoundationLite/Interaction.swift +0 -146
- package/Sources/FoundationLite/Timer.swift +0 -48
- package/Sources/_FoundationCommon/_FoundationCommon.swift +0 -10
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
// `UserDefaults` that survives reloads
|
|
1
|
+
// `UserDefaults` that survives reloads (corelibs Foundation's cannot persist on
|
|
2
|
+
// WASI; the CLI's generated file aliases this one in the app). Values live in memory and are mirrored
|
|
2
3
|
// to the host: every change is sent as a `defaults` op (the whole dictionary
|
|
3
4
|
// as JSON), which the browser stores per app in localStorage and hands back at
|
|
4
5
|
// the next launch through the `SB_DEFAULTS` environment variable. Headless
|
|
@@ -8,10 +9,11 @@
|
|
|
8
9
|
// travel as whole numbers (and come back as Int), `Data` as `{"$data": base64}`,
|
|
9
10
|
// `Date` as `{"$date": seconds}`, `URL` as `{"$url": string}`.
|
|
10
11
|
|
|
11
|
-
import
|
|
12
|
+
import Foundation
|
|
12
13
|
import SwiftUI
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
/// Equal and hashed by identity, as an `NSObject` is on Apple platforms.
|
|
16
|
+
public final class UserDefaults: Hashable, @unchecked Sendable {
|
|
15
17
|
nonisolated(unsafe) public static let standard = UserDefaults(persistent: true)
|
|
16
18
|
|
|
17
19
|
private var storage: [String: Any] = [:]
|
|
@@ -32,6 +34,8 @@ public final class UserDefaults: @unchecked Sendable {
|
|
|
32
34
|
// MARK: Reading
|
|
33
35
|
|
|
34
36
|
public func object(forKey key: String) -> Any? { storage[key] ?? registered[key] }
|
|
37
|
+
/// Key-value coding's read, which answers the default for the key.
|
|
38
|
+
public func value(forKey key: String) -> Any? { object(forKey: key) }
|
|
35
39
|
public func string(forKey key: String) -> String? {
|
|
36
40
|
switch object(forKey: key) {
|
|
37
41
|
case let s as String: s
|
|
@@ -89,6 +93,7 @@ public final class UserDefaults: @unchecked Sendable {
|
|
|
89
93
|
public func set(_ value: Any?, forKey key: String) {
|
|
90
94
|
if let value { storage[key] = value } else { storage[key] = nil }
|
|
91
95
|
persist()
|
|
96
|
+
_DefaultsKey.named(key).changed()
|
|
92
97
|
}
|
|
93
98
|
public func set(_ value: Int, forKey key: String) { set(value as Any, forKey: key) }
|
|
94
99
|
public func set(_ value: Double, forKey key: String) { set(value as Any, forKey: key) }
|
|
@@ -99,7 +104,10 @@ public final class UserDefaults: @unchecked Sendable {
|
|
|
99
104
|
|
|
100
105
|
/// Fallback values, read when nothing was set; never persisted.
|
|
101
106
|
public func register(defaults: [String: Any]) {
|
|
102
|
-
for (key, value) in defaults {
|
|
107
|
+
for (key, value) in defaults {
|
|
108
|
+
registered[key] = value
|
|
109
|
+
_DefaultsKey.named(key).changed()
|
|
110
|
+
}
|
|
103
111
|
}
|
|
104
112
|
public func dictionaryRepresentation() -> [String: Any] {
|
|
105
113
|
var all = registered
|
|
@@ -109,6 +117,7 @@ public final class UserDefaults: @unchecked Sendable {
|
|
|
109
117
|
public func removePersistentDomain(forName domainName: String) {
|
|
110
118
|
storage.removeAll()
|
|
111
119
|
persist()
|
|
120
|
+
for key in _DefaultsKey.all { key.changed() }
|
|
112
121
|
}
|
|
113
122
|
@discardableResult public func synchronize() -> Bool { true }
|
|
114
123
|
|
|
@@ -126,6 +135,9 @@ public final class UserDefaults: @unchecked Sendable {
|
|
|
126
135
|
let json = _json
|
|
127
136
|
MainActor.assumeIsolated { _Runtime.shared.storeDefaults(json) }
|
|
128
137
|
}
|
|
138
|
+
|
|
139
|
+
public static func == (lhs: UserDefaults, rhs: UserDefaults) -> Bool { lhs === rhs }
|
|
140
|
+
public func hash(into hasher: inout Hasher) { hasher.combine(ObjectIdentifier(self)) }
|
|
129
141
|
}
|
|
130
142
|
|
|
131
143
|
/// A defaults value as JSON; nil for a type the store cannot carry.
|
|
@@ -156,6 +168,7 @@ func _defaultsValue(fromJSON json: _JSON) -> Any? {
|
|
|
156
168
|
case .bool(let b): return b
|
|
157
169
|
case .string(let s): return s
|
|
158
170
|
case .number(let n): return n == n.rounded() && abs(n) < 9_007_199_254_740_992 ? Int(n) as Any : n
|
|
171
|
+
case .bytes(let bytes): return Data(bytes)
|
|
159
172
|
case .array(let items): return items.compactMap(_defaultsValue(fromJSON:))
|
|
160
173
|
case .object(let fields):
|
|
161
174
|
if fields.count == 1 {
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
// OAuth redirects to the app's own URL scheme, in the browser. An iOS app
|
|
2
|
+
// signs in by registering `myapp://…` as its OAuth redirect URI and letting
|
|
3
|
+
// `ASWebAuthenticationSession` catch the provider's redirect to it. A web
|
|
4
|
+
// page cannot receive a redirect to a custom scheme: the browser would hand
|
|
5
|
+
// it to another app, or fail. So the page serves a callback page at its own
|
|
6
|
+
// origin (Web/public/app/oauth-callback.html), and wherever one of the app's
|
|
7
|
+
// schemes appears as an OAuth redirect, the shim puts that page's URL in its
|
|
8
|
+
// place, with the original scheme (and what followed it) in the query so the
|
|
9
|
+
// app's URL can be rebuilt:
|
|
10
|
+
//
|
|
11
|
+
// icecubesapp:// → <page>/app/oauth-callback.html?sb_scheme=icecubesapp
|
|
12
|
+
// myapp://auth/done?x=1 → <page>/app/oauth-callback.html?sb_scheme=myapp&sb_target=%2F%2Fauth%2Fdone%3Fx%3D1
|
|
13
|
+
//
|
|
14
|
+
// The swap is narrow. Only the app's own schemes: those it declares
|
|
15
|
+
// (CFBundleURLTypes in its Info.plist; the CLI hands them to the page, and
|
|
16
|
+
// the page to the app as `SB_URL_SCHEMES`), plus the `callbackURLScheme` of
|
|
17
|
+
// a session once it starts. Only the redirect parameters, `redirect_uri` and
|
|
18
|
+
// `redirect_uris`: in the query of a URLSession request, in a form or JSON
|
|
19
|
+
// body (top-level keys; bodies up to 64 KiB), and in the authorize URL a
|
|
20
|
+
// web authentication session opens. The app's registration, the authorize
|
|
21
|
+
// request and the token exchange so all carry the same page URL, and the
|
|
22
|
+
// provider sees one consistent redirect URI. The provider redirects the
|
|
23
|
+
// sign-in window to the callback page, which hands its URL back to the app's
|
|
24
|
+
// page; `appURL(fromCallback:)` rebuilds `icecubesapp://?code=…` for the app
|
|
25
|
+
// (docs/ops-protocol.md, "Signing in").
|
|
26
|
+
|
|
27
|
+
import Foundation
|
|
28
|
+
import SwiftUI
|
|
29
|
+
|
|
30
|
+
public enum _WebAuthRedirects {
|
|
31
|
+
/// The parameters whose values are redirect URIs (OAuth 2's `redirect_uri`,
|
|
32
|
+
/// and `redirect_uris` of dynamic client registration and Mastodon's apps API).
|
|
33
|
+
public static let parameterNames: Set<String> = ["redirect_uri", "redirect_uris"]
|
|
34
|
+
|
|
35
|
+
/// The query items that carry the app's redirect on the callback page.
|
|
36
|
+
static let schemeItem = "sb_scheme"
|
|
37
|
+
static let targetItem = "sb_target"
|
|
38
|
+
|
|
39
|
+
/// Where the page serves its callback page when it does not say
|
|
40
|
+
/// (`SB_WEB_AUTH_CALLBACK` unset: headless runs).
|
|
41
|
+
public static let defaultCallbackPage = "http://localhost/app/oauth-callback.html"
|
|
42
|
+
|
|
43
|
+
/// Bodies larger than this are uploads, not OAuth forms; they are not examined.
|
|
44
|
+
static let bodyLimit = 64 * 1024
|
|
45
|
+
|
|
46
|
+
/// Schemes sessions registered. Only ever added to; one thread on WASI.
|
|
47
|
+
nonisolated(unsafe) static var sessionSchemes: Set<String> = []
|
|
48
|
+
|
|
49
|
+
/// The app's URL schemes, lower-cased: `SB_URL_SCHEMES`, plus those
|
|
50
|
+
/// sessions registered.
|
|
51
|
+
public static var schemes: Set<String> {
|
|
52
|
+
parseSchemes(_environment("SB_URL_SCHEMES") ?? "").union(sessionSchemes)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/// `SB_URL_SCHEMES`: schemes separated by commas or spaces.
|
|
56
|
+
static func parseSchemes(_ text: String) -> Set<String> {
|
|
57
|
+
Set(text.split(whereSeparator: { $0 == "," || $0 == " " || $0 == "\n" }).map { $0.lowercased() })
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/// Adds a scheme for the rest of the run (a session's `callbackURLScheme`).
|
|
61
|
+
public static func register(scheme: String) {
|
|
62
|
+
let scheme = scheme.lowercased()
|
|
63
|
+
guard !scheme.isEmpty, scheme != "http", scheme != "https" else { return }
|
|
64
|
+
sessionSchemes.insert(scheme)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/// The page's callback page (`SB_WEB_AUTH_CALLBACK`, which the page sets).
|
|
68
|
+
public static var callbackPage: String {
|
|
69
|
+
_environment("SB_WEB_AUTH_CALLBACK") ?? defaultCallbackPage
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// MARK: Redirect URIs
|
|
73
|
+
|
|
74
|
+
/// Whether `value` is a URI in one of the app's schemes.
|
|
75
|
+
public static func isAppRedirect(_ value: String) -> Bool {
|
|
76
|
+
guard let colon = value.firstIndex(of: ":") else { return false }
|
|
77
|
+
let scheme = value[..<colon].lowercased()
|
|
78
|
+
guard !scheme.isEmpty, scheme != "http", scheme != "https" else { return false }
|
|
79
|
+
return schemes.contains(scheme)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/// The callback page URL that stands for the app's redirect `value`, or
|
|
83
|
+
/// nil when `value` is not in one of the app's schemes.
|
|
84
|
+
public static func pageURL(forAppRedirect value: String) -> String? {
|
|
85
|
+
guard isAppRedirect(value), let colon = value.firstIndex(of: ":") else { return nil }
|
|
86
|
+
let scheme = String(value[..<colon])
|
|
87
|
+
let target = String(value[value.index(after: colon)...])
|
|
88
|
+
var url = callbackPage
|
|
89
|
+
url += url.contains("?") ? "&" : "?"
|
|
90
|
+
url += "\(schemeItem)=\(encode(scheme))"
|
|
91
|
+
// `myapp://` is the common case; its `//` is implied.
|
|
92
|
+
if target != "//" { url += "&\(targetItem)=\(encode(target))" }
|
|
93
|
+
return url
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/// The app's URL for a URL the callback page was opened at: its scheme and
|
|
97
|
+
/// target back in front, the provider's query items (`code`, `state`, …)
|
|
98
|
+
/// added to the target's query, and the fragment kept. Nil when `url` is
|
|
99
|
+
/// not a callback page URL.
|
|
100
|
+
public static func appURL(fromCallback url: String) -> String? {
|
|
101
|
+
guard let components = URLComponents(string: url),
|
|
102
|
+
let items = components.percentEncodedQueryItems,
|
|
103
|
+
let scheme = items.first(where: { $0.name == schemeItem })?.value?.removingPercentEncoding,
|
|
104
|
+
!scheme.isEmpty
|
|
105
|
+
else { return nil }
|
|
106
|
+
let target = items.first(where: { $0.name == targetItem })?.value?.removingPercentEncoding ?? "//"
|
|
107
|
+
var result = "\(scheme):\(target)"
|
|
108
|
+
let rest = items.filter { $0.name != schemeItem && $0.name != targetItem }
|
|
109
|
+
if !rest.isEmpty {
|
|
110
|
+
result += result.contains("?") ? "&" : "?"
|
|
111
|
+
result += rest.map { item in item.value.map { "\(item.name)=\($0)" } ?? item.name }.joined(separator: "&")
|
|
112
|
+
}
|
|
113
|
+
if let fragment = components.percentEncodedFragment { result += "#\(fragment)" }
|
|
114
|
+
return result
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/// A redirect parameter's value with each of the app's URIs replaced; a
|
|
118
|
+
/// space- or newline-separated list (`redirect_uris`) is rewritten item by item.
|
|
119
|
+
public static func rewriteRedirectValue(_ value: String) -> String {
|
|
120
|
+
let separator: Character = value.contains("\n") ? "\n" : " "
|
|
121
|
+
guard value.contains(separator) else { return pageURL(forAppRedirect: value) ?? value }
|
|
122
|
+
return value.split(separator: separator, omittingEmptySubsequences: false)
|
|
123
|
+
.map { pageURL(forAppRedirect: String($0)) ?? String($0) }
|
|
124
|
+
.joined(separator: String(separator))
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// MARK: Requests
|
|
128
|
+
|
|
129
|
+
/// `url` with the app's URIs in its redirect query items replaced (the
|
|
130
|
+
/// URL itself when there are none).
|
|
131
|
+
public static func rewriting(url: URL) -> URL {
|
|
132
|
+
guard !schemes.isEmpty, let query = url.query, query.contains("redirect_uri"),
|
|
133
|
+
var components = URLComponents(url: url, resolvingAgainstBaseURL: false),
|
|
134
|
+
let items = components.percentEncodedQueryItems
|
|
135
|
+
else { return url }
|
|
136
|
+
var changed = false
|
|
137
|
+
let rewritten = items.map { item -> URLQueryItem in
|
|
138
|
+
guard parameterNames.contains(item.name), let value = item.value?.removingPercentEncoding else { return item }
|
|
139
|
+
let new = rewriteRedirectValue(value)
|
|
140
|
+
guard new != value else { return item }
|
|
141
|
+
changed = true
|
|
142
|
+
return URLQueryItem(name: item.name, value: encode(new))
|
|
143
|
+
}
|
|
144
|
+
guard changed else { return url }
|
|
145
|
+
components.percentEncodedQueryItems = rewritten
|
|
146
|
+
return components.url ?? url
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/// `body` with the app's URIs in its redirect parameters replaced: the
|
|
150
|
+
/// top-level keys of a JSON object, or the fields of a form
|
|
151
|
+
/// (`application/x-www-form-urlencoded`). Other bodies come back as they were.
|
|
152
|
+
public static func rewriting(body: Data?, contentType: String?) -> Data? {
|
|
153
|
+
guard let body, !body.isEmpty, body.count <= bodyLimit, !schemes.isEmpty else { return body }
|
|
154
|
+
let text = String(decoding: body, as: UTF8.self)
|
|
155
|
+
guard text.contains("redirect_uri") else { return body }
|
|
156
|
+
let type = contentType?.lowercased() ?? ""
|
|
157
|
+
if type.contains("json") || (type.isEmpty && text.first(where: { !$0.isWhitespace }) == "{") {
|
|
158
|
+
return rewritingJSON(text).map { Data($0.utf8) } ?? body
|
|
159
|
+
}
|
|
160
|
+
if type.contains("x-www-form-urlencoded") || type.isEmpty {
|
|
161
|
+
return rewritingForm(text).map { Data($0.utf8) } ?? body
|
|
162
|
+
}
|
|
163
|
+
return body
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/// The JSON object with its redirect keys rewritten; nil when nothing changed.
|
|
167
|
+
static func rewritingJSON(_ text: String) -> String? {
|
|
168
|
+
guard case .object(var object)? = try? _JSON.parse(text) else { return nil }
|
|
169
|
+
var changed = false
|
|
170
|
+
for name in parameterNames {
|
|
171
|
+
switch object[name] {
|
|
172
|
+
case .string(let value)?:
|
|
173
|
+
let new = rewriteRedirectValue(value)
|
|
174
|
+
if new != value { object[name] = .string(new); changed = true }
|
|
175
|
+
case .array(let values)?:
|
|
176
|
+
let new = values.map { item -> _JSON in
|
|
177
|
+
if case .string(let value) = item { return .string(rewriteRedirectValue(value)) }
|
|
178
|
+
return item
|
|
179
|
+
}
|
|
180
|
+
if new != values { object[name] = .array(new); changed = true }
|
|
181
|
+
default:
|
|
182
|
+
break
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return changed ? _JSON.object(object).serialized : nil
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/// The form with its redirect fields rewritten; nil when nothing changed.
|
|
189
|
+
static func rewritingForm(_ text: String) -> String? {
|
|
190
|
+
var changed = false
|
|
191
|
+
let fields = text.split(separator: "&", omittingEmptySubsequences: false).map { field -> String in
|
|
192
|
+
guard let equals = field.firstIndex(of: "=") else { return String(field) }
|
|
193
|
+
let name = formDecode(field[..<equals])
|
|
194
|
+
guard parameterNames.contains(name) else { return String(field) }
|
|
195
|
+
let value = formDecode(field[field.index(after: equals)...])
|
|
196
|
+
let new = rewriteRedirectValue(value)
|
|
197
|
+
guard new != value else { return String(field) }
|
|
198
|
+
changed = true
|
|
199
|
+
return "\(field[..<equals])=\(encode(new))"
|
|
200
|
+
}
|
|
201
|
+
return changed ? fields.joined(separator: "&") : nil
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// MARK: Encoding
|
|
205
|
+
|
|
206
|
+
/// RFC 3986's unreserved characters stay; everything else is percent-encoded.
|
|
207
|
+
static func encode(_ text: String) -> String {
|
|
208
|
+
var out = ""
|
|
209
|
+
for byte in text.utf8 {
|
|
210
|
+
switch byte {
|
|
211
|
+
case UInt8(ascii: "A")...UInt8(ascii: "Z"), UInt8(ascii: "a")...UInt8(ascii: "z"),
|
|
212
|
+
UInt8(ascii: "0")...UInt8(ascii: "9"), UInt8(ascii: "-"), UInt8(ascii: "."),
|
|
213
|
+
UInt8(ascii: "_"), UInt8(ascii: "~"):
|
|
214
|
+
out.unicodeScalars.append(UnicodeScalar(byte))
|
|
215
|
+
default:
|
|
216
|
+
let hex = Array("0123456789ABCDEF")
|
|
217
|
+
out += "%\(hex[Int(byte >> 4)])\(hex[Int(byte & 15)])"
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return out
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
static func formDecode(_ text: Substring) -> String {
|
|
224
|
+
let spaced = text.replacingOccurrences(of: "+", with: " ")
|
|
225
|
+
return spaced.removingPercentEncoding ?? spaced
|
|
226
|
+
}
|
|
227
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
// `URLSessionWebSocketTask` over the browser's WebSocket. Each call is a host
|
|
2
|
+
// request of kind `websocket` (docs/ops-protocol.md, "WebSockets"):
|
|
3
|
+
// `open` connects and answers the socket's id once it is open, `send` sends a
|
|
4
|
+
// message, `receive` answers the next message (queued by the page as it
|
|
5
|
+
// arrives), and `close` closes. The pull model matches the task's own: an
|
|
6
|
+
// app calls `receive` again for every message.
|
|
7
|
+
|
|
8
|
+
import Foundation
|
|
9
|
+
import SwiftUI
|
|
10
|
+
|
|
11
|
+
open class URLSessionWebSocketTask: URLSessionTask, @unchecked Sendable {
|
|
12
|
+
public enum Message: Sendable {
|
|
13
|
+
case data(Data)
|
|
14
|
+
case string(String)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public enum CloseCode: Int, Sendable {
|
|
18
|
+
case invalid = 0
|
|
19
|
+
case normalClosure = 1000
|
|
20
|
+
case goingAway = 1001
|
|
21
|
+
case protocolError = 1002
|
|
22
|
+
case unsupportedData = 1003
|
|
23
|
+
case noStatusReceived = 1005
|
|
24
|
+
case abnormalClosure = 1006
|
|
25
|
+
case invalidFramePayloadData = 1007
|
|
26
|
+
case policyViolation = 1008
|
|
27
|
+
case messageTooBig = 1009
|
|
28
|
+
case mandatoryExtensionMissing = 1010
|
|
29
|
+
case internalServerError = 1011
|
|
30
|
+
case tlsHandshakeFailure = 1015
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public var maximumMessageSize = 1_048_576
|
|
34
|
+
public private(set) var closeCode: CloseCode = .invalid
|
|
35
|
+
public private(set) var closeReason: Data?
|
|
36
|
+
|
|
37
|
+
let url: URL
|
|
38
|
+
let protocols: [String]
|
|
39
|
+
/// The connection, opened on `resume()` (or the first send or receive).
|
|
40
|
+
private var connection: Task<Int, any Error>?
|
|
41
|
+
|
|
42
|
+
init(identifier: Int, request: URLRequest, url: URL, protocols: [String]) {
|
|
43
|
+
self.url = url
|
|
44
|
+
self.protocols = protocols
|
|
45
|
+
super.init(identifier: identifier, request: request) { task in
|
|
46
|
+
_ = try? await (task as! URLSessionWebSocketTask).socket()
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/// The page's id for the open socket.
|
|
51
|
+
@MainActor func socket() async throws -> Int {
|
|
52
|
+
if connection == nil {
|
|
53
|
+
let params: _JSON = .object([
|
|
54
|
+
"action": .string("open"),
|
|
55
|
+
"url": .string(url.absoluteString),
|
|
56
|
+
"protocols": .array(protocols.map { .string($0) }),
|
|
57
|
+
])
|
|
58
|
+
let url = self.url
|
|
59
|
+
connection = Task { @MainActor in
|
|
60
|
+
let value = try await Self.host(params, url: url)
|
|
61
|
+
guard case .object(let fields) = value, case .number(let id)? = fields["socket"] else {
|
|
62
|
+
throw URLError(.badServerResponse, userInfo: ["NSErrorFailingURLKey": url])
|
|
63
|
+
}
|
|
64
|
+
return Int(id)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return try await connection!.value
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@MainActor static func host(_ params: _JSON, url: URL) async throws -> _JSON {
|
|
71
|
+
do {
|
|
72
|
+
return try await _Runtime.shared._hostRequest(kind: "websocket", params: params)
|
|
73
|
+
} catch let failure as _HostRequestError {
|
|
74
|
+
throw URLSession.urlError(from: failure.message, url: url)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// MARK: Messages
|
|
79
|
+
|
|
80
|
+
@MainActor public func send(_ message: Message) async throws {
|
|
81
|
+
let id = try await socket()
|
|
82
|
+
var params: [String: _JSON] = ["action": .string("send"), "socket": .number(Double(id))]
|
|
83
|
+
switch message {
|
|
84
|
+
case .string(let text): params["string"] = .string(text)
|
|
85
|
+
case .data(let data): params["data"] = .string(data.base64EncodedString())
|
|
86
|
+
}
|
|
87
|
+
_ = try await Self.host(.object(params), url: url)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
public func send(_ message: Message, completionHandler: @escaping @Sendable ((any Error)?) -> Void) {
|
|
91
|
+
Task { @MainActor in
|
|
92
|
+
do {
|
|
93
|
+
try await self.send(message)
|
|
94
|
+
completionHandler(nil)
|
|
95
|
+
} catch {
|
|
96
|
+
completionHandler(error)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/// The next message; fails once the socket is closed.
|
|
102
|
+
@MainActor public func receive() async throws -> Message {
|
|
103
|
+
let id = try await socket()
|
|
104
|
+
let value = try await Self.host(.object(["action": .string("receive"), "socket": .number(Double(id))]), url: url)
|
|
105
|
+
guard case .object(let fields) = value else { throw URLError(.badServerResponse) }
|
|
106
|
+
if case .string(let text)? = fields["string"] { return .string(text) }
|
|
107
|
+
if case .string(let encoded)? = fields["data"], let data = Data(base64Encoded: encoded) { return .data(data) }
|
|
108
|
+
if case .number(let code)? = fields["closeCode"] {
|
|
109
|
+
closeCode = CloseCode(rawValue: Int(code)) ?? .abnormalClosure
|
|
110
|
+
if case .string(let reason)? = fields["reason"], !reason.isEmpty { closeReason = Data(reason.utf8) }
|
|
111
|
+
finish(response: response, error: URLError(.networkConnectionLost))
|
|
112
|
+
}
|
|
113
|
+
throw URLError(.networkConnectionLost, userInfo: ["NSErrorFailingURLKey": url])
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
public func receive(completionHandler: @escaping @Sendable (Result<Message, any Error>) -> Void) {
|
|
117
|
+
Task { @MainActor in
|
|
118
|
+
do {
|
|
119
|
+
completionHandler(.success(try await self.receive()))
|
|
120
|
+
} catch {
|
|
121
|
+
completionHandler(.failure(error))
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/// Browsers send pings themselves and do not expose them: the handler is called once the socket is open.
|
|
127
|
+
public func sendPing(pongReceiveHandler: @escaping @Sendable ((any Error)?) -> Void) {
|
|
128
|
+
Task { @MainActor in
|
|
129
|
+
do {
|
|
130
|
+
_ = try await self.socket()
|
|
131
|
+
pongReceiveHandler(nil)
|
|
132
|
+
} catch {
|
|
133
|
+
pongReceiveHandler(error)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
public func cancel(with closeCode: CloseCode, reason: Data?) {
|
|
139
|
+
self.closeCode = closeCode
|
|
140
|
+
closeReason = reason
|
|
141
|
+
close(code: closeCode == .invalid ? .normalClosure : closeCode, reason: reason)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
public override func cancel() {
|
|
145
|
+
close(code: .normalClosure, reason: nil)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
private func close(code: CloseCode, reason: Data?) {
|
|
149
|
+
let connection = self.connection
|
|
150
|
+
let url = self.url
|
|
151
|
+
finish(response: response, error: URLError(.cancelled))
|
|
152
|
+
guard let connection else { return }
|
|
153
|
+
Task { @MainActor in
|
|
154
|
+
guard let id = try? await connection.value else { return }
|
|
155
|
+
var params: [String: _JSON] = ["action": .string("close"), "socket": .number(Double(id)), "code": .number(Double(code.rawValue))]
|
|
156
|
+
if let reason { params["reason"] = .string(String(decoding: reason, as: UTF8.self)) }
|
|
157
|
+
_ = try? await Self.host(.object(params), url: url)
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
public protocol URLSessionWebSocketDelegate: URLSessionTaskDelegate {
|
|
163
|
+
func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didOpenWithProtocol protocol: String?)
|
|
164
|
+
func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didCloseWith closeCode: URLSessionWebSocketTask.CloseCode, reason: Data?)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
extension URLSessionWebSocketDelegate {
|
|
168
|
+
public func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didOpenWithProtocol protocol: String?) {}
|
|
169
|
+
public func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didCloseWith closeCode: URLSessionWebSocketTask.CloseCode, reason: Data?) {}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
extension URLSession {
|
|
173
|
+
public func webSocketTask(with url: URL) -> URLSessionWebSocketTask {
|
|
174
|
+
webSocketTask(with: url, protocols: [])
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
public func webSocketTask(with url: URL, protocols: [String]) -> URLSessionWebSocketTask {
|
|
178
|
+
URLSessionWebSocketTask(identifier: takeIdentifier(), request: URLRequest(url: url), url: url, protocols: protocols)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
public func webSocketTask(with request: URLRequest) -> URLSessionWebSocketTask {
|
|
182
|
+
let url = request.url ?? URL(string: "about:blank")!
|
|
183
|
+
let protocols = request.value(forHTTPHeaderField: "Sec-WebSocket-Protocol")?
|
|
184
|
+
.split(separator: ",").map { $0.trimmingCharacters(in: .whitespaces) } ?? []
|
|
185
|
+
return URLSessionWebSocketTask(identifier: takeIdentifier(), request: request, url: url, protocols: protocols)
|
|
186
|
+
}
|
|
187
|
+
}
|