From 5b2f32b3daa0d4ddd9367e423140b06dfee0fc90 Mon Sep 17 00:00:00 2001 From: Lincoln Quirk Date: Wed, 16 Oct 2024 12:05:30 -0400 Subject: [PATCH] make ApolloStore an open class, and its dependencies are public --- apollo-ios/Sources/Apollo/ApolloStore.swift | 43 ++++++------ apollo-ios/Sources/Apollo/DataLoader.swift | 3 +- .../CacheDataExecutionSource.swift | 17 ++--- .../Apollo/GraphQLDependencyTracker.swift | 25 ++++--- .../Apollo/GraphQLResultAccumulator.swift | 68 ++++++++++--------- .../Apollo/GraphQLResultNormalizer.swift | 38 ++++++----- 6 files changed, 105 insertions(+), 89 deletions(-) diff --git a/apollo-ios/Sources/Apollo/ApolloStore.swift b/apollo-ios/Sources/Apollo/ApolloStore.swift index 5d7d7a3c6..3b2ee5a01 100644 --- a/apollo-ios/Sources/Apollo/ApolloStore.swift +++ b/apollo-ios/Sources/Apollo/ApolloStore.swift @@ -21,11 +21,11 @@ public protocol ApolloStoreSubscriber: AnyObject { } /// The `ApolloStore` class acts as a local cache for normalized GraphQL results. -public class ApolloStore { - private let cache: any NormalizedCache - private let queue: DispatchQueue +open class ApolloStore { + public let cache: any NormalizedCache + public let queue: DispatchQueue - internal var subscribers: [any ApolloStoreSubscriber] = [] + open var subscribers: [any ApolloStoreSubscriber] = [] /// Designated initializer /// - Parameters: @@ -36,7 +36,7 @@ public class ApolloStore { self.queue = DispatchQueue(label: "com.apollographql.ApolloStore", attributes: .concurrent) } - fileprivate func didChangeKeys(_ changedKeys: Set, identifier: UUID?) { + open func didChangeKeys(_ changedKeys: Set, identifier: UUID?) { for subscriber in self.subscribers { subscriber.store(self, didChangeKeys: changedKeys, contextIdentifier: identifier) } @@ -47,7 +47,7 @@ public class ApolloStore { /// - Parameters: /// - callbackQueue: The queue to call the completion block on. Defaults to `DispatchQueue.main`. /// - completion: [optional] A completion block to be called after records are merged into the cache. - public func clearCache(callbackQueue: DispatchQueue = .main, completion: ((Result) -> Void)? = nil) { + open func clearCache(callbackQueue: DispatchQueue = .main, completion: ((Result) -> Void)? = nil) { queue.async(flags: .barrier) { let result = Result { try self.cache.clear() } DispatchQueue.returnResultAsyncIfNeeded( @@ -65,7 +65,7 @@ public class ApolloStore { /// to assist in de-duping cache hits for watchers. /// - callbackQueue: The queue to call the completion block on. Defaults to `DispatchQueue.main`. /// - completion: [optional] A completion block to be called after records are merged into the cache. - public func publish(records: RecordSet, identifier: UUID? = nil, callbackQueue: DispatchQueue = .main, completion: ((Result) -> Void)? = nil) { + open func publish(records: RecordSet, identifier: UUID? = nil, callbackQueue: DispatchQueue = .main, completion: ((Result) -> Void)? = nil) { queue.async(flags: .barrier) { do { let changedKeys = try self.cache.merge(records: records) @@ -90,7 +90,7 @@ public class ApolloStore { /// - Parameters: /// - subscriber: A subscriber to receive content change notificatons. To avoid a retain cycle, /// ensure you call `unsubscribe` on this subscriber before it goes out of scope. - public func subscribe(_ subscriber: any ApolloStoreSubscriber) { + open func subscribe(_ subscriber: any ApolloStoreSubscriber) { queue.async(flags: .barrier) { self.subscribers.append(subscriber) } @@ -101,7 +101,7 @@ public class ApolloStore { /// - Parameters: /// - subscriber: A subscribe that has previously been added via `subscribe`. To avoid retain cycles, /// call `unsubscribe` on all active subscribers before they go out of scope. - public func unsubscribe(_ subscriber: any ApolloStoreSubscriber) { + open func unsubscribe(_ subscriber: any ApolloStoreSubscriber) { queue.async(flags: .barrier) { self.subscribers = self.subscribers.filter({ $0 !== subscriber }) } @@ -113,7 +113,7 @@ public class ApolloStore { /// - body: The body of the operation to perform. /// - callbackQueue: [optional] The callback queue to use to perform the completion block on. Will perform on the current queue if not provided. Defaults to nil. /// - completion: [optional] The completion block to perform when the read transaction completes. Defaults to nil. - public func withinReadTransaction( + open func withinReadTransaction( _ body: @escaping (ReadTransaction) throws -> T, callbackQueue: DispatchQueue? = nil, completion: ((Result) -> Void)? = nil @@ -143,7 +143,7 @@ public class ApolloStore { /// - body: The body of the operation to perform /// - callbackQueue: [optional] a callback queue to perform the action on. Will perform on the current queue if not provided. Defaults to nil. /// - completion: [optional] a completion block to fire when the read-write transaction completes. Defaults to nil. - public func withinReadWriteTransaction( + open func withinReadWriteTransaction( _ body: @escaping (ReadWriteTransaction) throws -> T, callbackQueue: DispatchQueue? = nil, completion: ((Result) -> Void)? = nil @@ -172,7 +172,7 @@ public class ApolloStore { /// - Parameters: /// - query: The query to load results for /// - resultHandler: The completion handler to execute on success or error - public func load( + open func load( _ operation: Operation, callbackQueue: DispatchQueue? = nil, resultHandler: @escaping GraphQLResultHandler @@ -201,18 +201,20 @@ public class ApolloStore { } public class ReadTransaction { - fileprivate let cache: any NormalizedCache - - fileprivate lazy var loader: DataLoader = DataLoader { [weak self] batchLoad in + public let cache: any NormalizedCache + + @_spi(Execution) + public lazy var loader: DataLoader = DataLoader { [weak self] batchLoad in guard let self else { return [:] } return try cache.loadRecords(forKeys: batchLoad) } - fileprivate lazy var executor = GraphQLExecutor( + @_spi(Execution) + public lazy var executor = GraphQLExecutor( executionSource: CacheDataExecutionSource(transaction: self) ) - fileprivate init(store: ApolloStore) { + public init(store: ApolloStore) { self.cache = store.cache } @@ -237,7 +239,8 @@ public class ApolloStore { ) } - func readObject( + @_spi(Execution) + public func readObject( ofType type: SelectionSet.Type, withKey key: CacheKey, variables: GraphQLOperation.Variables? = nil, @@ -264,9 +267,9 @@ public class ApolloStore { public final class ReadWriteTransaction: ReadTransaction { - fileprivate var updateChangedKeysFunc: DidChangeKeysFunc? + public let updateChangedKeysFunc: DidChangeKeysFunc? - override init(store: ApolloStore) { + public override init(store: ApolloStore) { self.updateChangedKeysFunc = store.didChangeKeys super.init(store: store) } diff --git a/apollo-ios/Sources/Apollo/DataLoader.swift b/apollo-ios/Sources/Apollo/DataLoader.swift index 2f6afd72a..f8725e584 100644 --- a/apollo-ios/Sources/Apollo/DataLoader.swift +++ b/apollo-ios/Sources/Apollo/DataLoader.swift @@ -1,4 +1,5 @@ -final class DataLoader { +@_spi(Execution) +public final class DataLoader { public typealias BatchLoad = (Set) throws -> [Key: Value] private var batchLoad: BatchLoad diff --git a/apollo-ios/Sources/Apollo/ExecutionSources/CacheDataExecutionSource.swift b/apollo-ios/Sources/Apollo/ExecutionSources/CacheDataExecutionSource.swift index 183d4d5f4..00ea7766b 100644 --- a/apollo-ios/Sources/Apollo/ExecutionSources/CacheDataExecutionSource.swift +++ b/apollo-ios/Sources/Apollo/ExecutionSources/CacheDataExecutionSource.swift @@ -5,9 +5,10 @@ import ApolloAPI /// A `GraphQLExecutionSource` configured to execute upon the data stored in a ``NormalizedCache``. /// /// Each object exposed by the cache is represented as a `Record`. -struct CacheDataExecutionSource: GraphQLExecutionSource { - typealias RawObjectData = Record - typealias FieldCollector = CacheDataFieldSelectionCollector +@_spi(Execution) +public struct CacheDataExecutionSource: GraphQLExecutionSource { + public typealias RawObjectData = Record + public typealias FieldCollector = CacheDataFieldSelectionCollector /// A `weak` reference to the transaction the cache data is being read from during execution. /// This transaction is used to resolve references to other objects in the cache during field @@ -24,13 +25,13 @@ struct CacheDataExecutionSource: GraphQLExecutionSource { /// When executing on cache data all selections, including deferred, must be executed together because /// there is only a single response from the cache data. Any deferred selection that was cached will /// be returned in the response. - var shouldAttemptDeferredFragmentExecution: Bool { true } + public var shouldAttemptDeferredFragmentExecution: Bool { true } init(transaction: ApolloStore.ReadTransaction) { self.transaction = transaction } - func resolveField( + public func resolveField( with info: FieldExecutionInfo, on object: Record ) -> PossiblyDeferred { @@ -72,14 +73,14 @@ struct CacheDataExecutionSource: GraphQLExecutionSource { return transaction.loadObject(forKey: reference.key) } - func computeCacheKey(for object: Record, in schema: any SchemaMetadata.Type) -> CacheKey? { + public func computeCacheKey(for object: Record, in schema: any SchemaMetadata.Type) -> CacheKey? { return object.key } /// A wrapper around the `DefaultFieldSelectionCollector` that maps the `Record` object to it's /// `fields` representing the object's data. - struct CacheDataFieldSelectionCollector: FieldSelectionCollector { - static func collectFields( + public struct CacheDataFieldSelectionCollector: FieldSelectionCollector { + public static func collectFields( from selections: [Selection], into groupedFields: inout FieldSelectionGrouping, for object: Record, diff --git a/apollo-ios/Sources/Apollo/GraphQLDependencyTracker.swift b/apollo-ios/Sources/Apollo/GraphQLDependencyTracker.swift index 814cac186..e362cc2f5 100644 --- a/apollo-ios/Sources/Apollo/GraphQLDependencyTracker.swift +++ b/apollo-ios/Sources/Apollo/GraphQLDependencyTracker.swift @@ -2,44 +2,47 @@ import ApolloAPI #endif -final class GraphQLDependencyTracker: GraphQLResultAccumulator { +@_spi(Execution) +public final class GraphQLDependencyTracker: GraphQLResultAccumulator { - let requiresCacheKeyComputation: Bool = true + public let requiresCacheKeyComputation: Bool = true private var dependentKeys: Set = Set() + + public init() {} - func accept(scalar: JSONValue, info: FieldExecutionInfo) { + public func accept(scalar: JSONValue, info: FieldExecutionInfo) { dependentKeys.insert(info.cachePath.joined) } - func accept(customScalar: JSONValue, info: FieldExecutionInfo) { + public func accept(customScalar: JSONValue, info: FieldExecutionInfo) { dependentKeys.insert(info.cachePath.joined) } - func acceptNullValue(info: FieldExecutionInfo) { + public func acceptNullValue(info: FieldExecutionInfo) { dependentKeys.insert(info.cachePath.joined) } - func acceptMissingValue(info: FieldExecutionInfo) throws -> () { + public func acceptMissingValue(info: FieldExecutionInfo) throws -> () { dependentKeys.insert(info.cachePath.joined) } - func accept(list: [Void], info: FieldExecutionInfo) { + public func accept(list: [Void], info: FieldExecutionInfo) { dependentKeys.insert(info.cachePath.joined) } - func accept(childObject: Void, info: FieldExecutionInfo) { + public func accept(childObject: Void, info: FieldExecutionInfo) { } - func accept(fieldEntry: Void, info: FieldExecutionInfo) -> Void? { + public func accept(fieldEntry: Void, info: FieldExecutionInfo) -> Void? { dependentKeys.insert(info.cachePath.joined) return () } - func accept(fieldEntries: [Void], info: ObjectExecutionInfo) { + public func accept(fieldEntries: [Void], info: ObjectExecutionInfo) { } - func finish(rootValue: Void, info: ObjectExecutionInfo) -> Set { + public func finish(rootValue: Void, info: ObjectExecutionInfo) -> Set { return dependentKeys } } diff --git a/apollo-ios/Sources/Apollo/GraphQLResultAccumulator.swift b/apollo-ios/Sources/Apollo/GraphQLResultAccumulator.swift index 2e5c9d00c..8046ddedf 100644 --- a/apollo-ios/Sources/Apollo/GraphQLResultAccumulator.swift +++ b/apollo-ios/Sources/Apollo/GraphQLResultAccumulator.swift @@ -24,23 +24,26 @@ public protocol GraphQLResultAccumulator: AnyObject { func finish(rootValue: ObjectResult, info: ObjectExecutionInfo) throws -> FinalResult } -func zip(_ accumulator1: Accumulator1, _ accumulator2: Accumulator2) -> Zip2Accumulator { +@_spi(Execution) +public func zip(_ accumulator1: Accumulator1, _ accumulator2: Accumulator2) -> Zip2Accumulator { return Zip2Accumulator(accumulator1, accumulator2) } -func zip(_ accumulator1: Accumulator1, _ accumulator2: Accumulator2, _ accumulator3: Accumulator3) -> Zip3Accumulator { +@_spi(Execution) +public func zip(_ accumulator1: Accumulator1, _ accumulator2: Accumulator2, _ accumulator3: Accumulator3) -> Zip3Accumulator { return Zip3Accumulator(accumulator1, accumulator2, accumulator3) } -final class Zip2Accumulator: GraphQLResultAccumulator { - typealias PartialResult = (Accumulator1.PartialResult, Accumulator2.PartialResult) - typealias FieldEntry = (Accumulator1.FieldEntry?, Accumulator2.FieldEntry?) - typealias ObjectResult = (Accumulator1.ObjectResult, Accumulator2.ObjectResult) - typealias FinalResult = (Accumulator1.FinalResult, Accumulator2.FinalResult) +@_spi(Execution) +public final class Zip2Accumulator: GraphQLResultAccumulator { + public typealias PartialResult = (Accumulator1.PartialResult, Accumulator2.PartialResult) + public typealias FieldEntry = (Accumulator1.FieldEntry?, Accumulator2.FieldEntry?) + public typealias ObjectResult = (Accumulator1.ObjectResult, Accumulator2.ObjectResult) + public typealias FinalResult = (Accumulator1.FinalResult, Accumulator2.FinalResult) private let accumulator1: Accumulator1 private let accumulator2: Accumulator2 - let requiresCacheKeyComputation: Bool + public let requiresCacheKeyComputation: Bool fileprivate init(_ accumulator1: Accumulator1, _ accumulator2: Accumulator2) { self.accumulator1 = accumulator1 @@ -51,64 +54,65 @@ final class Zip2Accumulator PartialResult { + public func accept(scalar: JSONValue, info: FieldExecutionInfo) throws -> PartialResult { return (try accumulator1.accept(scalar: scalar, info: info), try accumulator2.accept(scalar: scalar, info: info)) } - func accept(customScalar: JSONValue, info: FieldExecutionInfo) throws -> PartialResult { + public func accept(customScalar: JSONValue, info: FieldExecutionInfo) throws -> PartialResult { return (try accumulator1.accept(customScalar: customScalar, info: info), try accumulator2.accept(customScalar: customScalar, info: info)) } - func acceptNullValue(info: FieldExecutionInfo) throws -> PartialResult { + public func acceptNullValue(info: FieldExecutionInfo) throws -> PartialResult { return (try accumulator1.acceptNullValue(info: info), try accumulator2.acceptNullValue(info: info)) } - func acceptMissingValue(info: FieldExecutionInfo) throws -> PartialResult { + public func acceptMissingValue(info: FieldExecutionInfo) throws -> PartialResult { return (try accumulator1.acceptMissingValue(info: info), try accumulator2.acceptMissingValue(info: info)) } - func accept(list: [PartialResult], info: FieldExecutionInfo) throws -> PartialResult { + public func accept(list: [PartialResult], info: FieldExecutionInfo) throws -> PartialResult { let (list1, list2) = unzip(list) return (try accumulator1.accept(list: list1, info: info), try accumulator2.accept(list: list2, info: info)) } - func accept(childObject: ObjectResult, info: FieldExecutionInfo) throws -> PartialResult { + public func accept(childObject: ObjectResult, info: FieldExecutionInfo) throws -> PartialResult { return (try accumulator1.accept(childObject: childObject.0, info: info), try accumulator2.accept(childObject: childObject.1, info: info)) } - func accept(fieldEntry: PartialResult, info: FieldExecutionInfo) throws -> FieldEntry? { + public func accept(fieldEntry: PartialResult, info: FieldExecutionInfo) throws -> FieldEntry? { return (try accumulator1.accept(fieldEntry: fieldEntry.0, info: info), try accumulator2.accept(fieldEntry: fieldEntry.1, info: info)) } - func accept(fieldEntries: [FieldEntry], info: ObjectExecutionInfo) throws -> ObjectResult { + public func accept(fieldEntries: [FieldEntry], info: ObjectExecutionInfo) throws -> ObjectResult { let (fieldEntries1, fieldEntries2) = unzip(fieldEntries) return (try accumulator1.accept(fieldEntries: fieldEntries1, info: info), try accumulator2.accept(fieldEntries: fieldEntries2, info: info)) } - func finish(rootValue: ObjectResult, info: ObjectExecutionInfo) throws -> FinalResult { + public func finish(rootValue: ObjectResult, info: ObjectExecutionInfo) throws -> FinalResult { return (try accumulator1.finish(rootValue: rootValue.0, info: info), try accumulator2.finish(rootValue: rootValue.1, info: info)) } } -final class Zip3Accumulator: GraphQLResultAccumulator { - typealias PartialResult = (Accumulator1.PartialResult, Accumulator2.PartialResult, Accumulator3.PartialResult) - typealias FieldEntry = (Accumulator1.FieldEntry?, Accumulator2.FieldEntry?, Accumulator3.FieldEntry?) - typealias ObjectResult = (Accumulator1.ObjectResult, Accumulator2.ObjectResult, Accumulator3.ObjectResult) - typealias FinalResult = (Accumulator1.FinalResult, Accumulator2.FinalResult, Accumulator3.FinalResult) +@_spi(Execution) +public final class Zip3Accumulator: GraphQLResultAccumulator { + public typealias PartialResult = (Accumulator1.PartialResult, Accumulator2.PartialResult, Accumulator3.PartialResult) + public typealias FieldEntry = (Accumulator1.FieldEntry?, Accumulator2.FieldEntry?, Accumulator3.FieldEntry?) + public typealias ObjectResult = (Accumulator1.ObjectResult, Accumulator2.ObjectResult, Accumulator3.ObjectResult) + public typealias FinalResult = (Accumulator1.FinalResult, Accumulator2.FinalResult, Accumulator3.FinalResult) private let accumulator1: Accumulator1 private let accumulator2: Accumulator2 private let accumulator3: Accumulator3 - let requiresCacheKeyComputation: Bool + public let requiresCacheKeyComputation: Bool fileprivate init(_ accumulator1: Accumulator1, _ accumulator2: Accumulator2, @@ -122,57 +126,57 @@ final class Zip3Accumulator PartialResult { + public func accept(scalar: JSONValue, info: FieldExecutionInfo) throws -> PartialResult { return (try accumulator1.accept(scalar: scalar, info: info), try accumulator2.accept(scalar: scalar, info: info), try accumulator3.accept(scalar: scalar, info: info)) } - func accept(customScalar: JSONValue, info: FieldExecutionInfo) throws -> PartialResult { + public func accept(customScalar: JSONValue, info: FieldExecutionInfo) throws -> PartialResult { return (try accumulator1.accept(customScalar: customScalar, info: info), try accumulator2.accept(customScalar: customScalar, info: info), try accumulator3.accept(customScalar: customScalar, info: info)) } - func acceptNullValue(info: FieldExecutionInfo) throws -> PartialResult { + public func acceptNullValue(info: FieldExecutionInfo) throws -> PartialResult { return (try accumulator1.acceptNullValue(info: info), try accumulator2.acceptNullValue(info: info), try accumulator3.acceptNullValue(info: info)) } - func acceptMissingValue(info: FieldExecutionInfo) throws -> PartialResult { + public func acceptMissingValue(info: FieldExecutionInfo) throws -> PartialResult { return (try accumulator1.acceptMissingValue(info: info), try accumulator2.acceptMissingValue(info: info), try accumulator3.acceptMissingValue(info: info)) } - func accept(list: [PartialResult], info: FieldExecutionInfo) throws -> PartialResult { + public func accept(list: [PartialResult], info: FieldExecutionInfo) throws -> PartialResult { let (list1, list2, list3) = unzip(list) return (try accumulator1.accept(list: list1, info: info), try accumulator2.accept(list: list2, info: info), try accumulator3.accept(list: list3, info: info)) } - func accept(childObject: ObjectResult, info: FieldExecutionInfo) throws -> PartialResult { + public func accept(childObject: ObjectResult, info: FieldExecutionInfo) throws -> PartialResult { return (try accumulator1.accept(childObject: childObject.0, info: info), try accumulator2.accept(childObject: childObject.1, info: info), try accumulator3.accept(childObject: childObject.2, info: info)) } - func accept(fieldEntry: PartialResult, info: FieldExecutionInfo) throws -> FieldEntry? { + public func accept(fieldEntry: PartialResult, info: FieldExecutionInfo) throws -> FieldEntry? { return (try accumulator1.accept(fieldEntry: fieldEntry.0, info: info), try accumulator2.accept(fieldEntry: fieldEntry.1, info: info), try accumulator3.accept(fieldEntry: fieldEntry.2, info: info)) } - func accept(fieldEntries: [FieldEntry], info: ObjectExecutionInfo) throws -> ObjectResult { + public func accept(fieldEntries: [FieldEntry], info: ObjectExecutionInfo) throws -> ObjectResult { let (fieldEntries1, fieldEntries2, fieldEntries3) = unzip(fieldEntries) return (try accumulator1.accept(fieldEntries: fieldEntries1, info: info), try accumulator2.accept(fieldEntries: fieldEntries2, info: info), try accumulator3.accept(fieldEntries: fieldEntries3, info: info)) } - func finish(rootValue: ObjectResult, info: ObjectExecutionInfo) throws -> FinalResult { + public func finish(rootValue: ObjectResult, info: ObjectExecutionInfo) throws -> FinalResult { return (try accumulator1.finish(rootValue: rootValue.0, info: info), try accumulator2.finish(rootValue: rootValue.1, info: info), try accumulator3.finish(rootValue: rootValue.2, info: info)) diff --git a/apollo-ios/Sources/Apollo/GraphQLResultNormalizer.swift b/apollo-ios/Sources/Apollo/GraphQLResultNormalizer.swift index 3b49a7b03..0f2037393 100644 --- a/apollo-ios/Sources/Apollo/GraphQLResultNormalizer.swift +++ b/apollo-ios/Sources/Apollo/GraphQLResultNormalizer.swift @@ -3,55 +3,57 @@ import Foundation import ApolloAPI #endif -enum ResultNormalizerFactory { +@_spi(Execution) +public enum ResultNormalizerFactory { - static func selectionSetDataNormalizer() -> SelectionSetDataResultNormalizer { + public static func selectionSetDataNormalizer() -> SelectionSetDataResultNormalizer { SelectionSetDataResultNormalizer() } - static func networkResponseDataNormalizer() -> RawJSONResultNormalizer { + public static func networkResponseDataNormalizer() -> RawJSONResultNormalizer { RawJSONResultNormalizer() } } -class BaseGraphQLResultNormalizer: GraphQLResultAccumulator { +@_spi(Execution) +public class BaseGraphQLResultNormalizer: GraphQLResultAccumulator { - let requiresCacheKeyComputation: Bool = true + public let requiresCacheKeyComputation: Bool = true private var records: RecordSet = [:] fileprivate init() {} - final func accept(scalar: JSONValue, info: FieldExecutionInfo) -> JSONValue? { + public final func accept(scalar: JSONValue, info: FieldExecutionInfo) -> JSONValue? { return scalar } - func accept(customScalar: JSONValue, info: FieldExecutionInfo) -> JSONValue? { + public func accept(customScalar: JSONValue, info: FieldExecutionInfo) -> JSONValue? { return customScalar } - final func acceptNullValue(info: FieldExecutionInfo) -> JSONValue? { + public final func acceptNullValue(info: FieldExecutionInfo) -> JSONValue? { return NSNull() } - final func acceptMissingValue(info: FieldExecutionInfo) -> JSONValue? { + public final func acceptMissingValue(info: FieldExecutionInfo) -> JSONValue? { return nil } - final func accept(list: [JSONValue?], info: FieldExecutionInfo) -> JSONValue? { + public final func accept(list: [JSONValue?], info: FieldExecutionInfo) -> JSONValue? { return list } - final func accept(childObject: CacheReference, info: FieldExecutionInfo) -> JSONValue? { + public final func accept(childObject: CacheReference, info: FieldExecutionInfo) -> JSONValue? { return childObject } - final func accept(fieldEntry: JSONValue?, info: FieldExecutionInfo) throws -> (key: String, value: JSONValue)? { + public final func accept(fieldEntry: JSONValue?, info: FieldExecutionInfo) throws -> (key: String, value: JSONValue)? { guard let fieldEntry else { return nil } return (try info.cacheKeyForField(), fieldEntry) } - final func accept( + public final func accept( fieldEntries: [(key: String, value: JSONValue)], info: ObjectExecutionInfo ) throws -> CacheReference { @@ -63,15 +65,17 @@ class BaseGraphQLResultNormalizer: GraphQLResultAccumulator { return CacheReference(cachePath) } - final func finish(rootValue: CacheReference, info: ObjectExecutionInfo) throws -> RecordSet { + public final func finish(rootValue: CacheReference, info: ObjectExecutionInfo) throws -> RecordSet { return records } } -final class RawJSONResultNormalizer: BaseGraphQLResultNormalizer {} +@_spi(Execution) +public final class RawJSONResultNormalizer: BaseGraphQLResultNormalizer {} -final class SelectionSetDataResultNormalizer: BaseGraphQLResultNormalizer { - override final func accept(customScalar: JSONValue, info: FieldExecutionInfo) -> JSONValue? { +@_spi(Execution) +public final class SelectionSetDataResultNormalizer: BaseGraphQLResultNormalizer { + override public final func accept(customScalar: JSONValue, info: FieldExecutionInfo) -> JSONValue? { if let customScalar = customScalar as? (any JSONEncodable) { return customScalar._jsonValue }