Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fleshing out abstracts, rebuilding curation, and trimming types that … #58

Merged
merged 1 commit into from
Apr 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Sources/AutomergeRepo/DocumentId.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Base58Swift
import struct Foundation.Data
import struct Foundation.UUID

/// A type that represents an Automerge-repo compatible document identifier
/// A unique Automerge document identifier
public struct DocumentId: Sendable, Hashable, Comparable, Identifiable {
/// A bs58 encoded string that represents the identifier
public let id: String
Expand Down
46 changes: 36 additions & 10 deletions Sources/AutomergeRepo/Documentation.docc/Documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,48 @@ introduction to automerge repo and what it provides, what problem it solves

### Managing a collection of Automerge documents

- ``AutomergeRepo/DocumentSyncCoordinator``
- ``AutomergeRepo/Repo``
- ``AutomergeRepo/DocHandle``
- ``AutomergeRepo/DocumentId``
- ``AutomergeRepo/EphemeralMessageDelegate``
- ``AutomergeRepo/AutomergeRepo``

### Network Adapters

- ``AutomergeRepo/NetworkSyncProvider``
- ``AutomergeRepo/BonjourSyncConnection``
- ``AutomergeRepo/WebsocketSyncConnection``

- ``AutomergeRepo/NetworkSubsystem``
- ``AutomergeRepo/NetworkProvider``
- ``AutomergeRepo/NetworkEventReceiver``
- ``AutomergeRepo/NetworkAdapterEvents``
- ``AutomergeRepo/SyncUserDefaultsKeys``
- ``AutomergeRepo/SynchronizerDefaultKeys
- ``AutomergeRepo/SyncV1``
- ``AutomergeRepo/AvailablePeer``

### WebSocket Network Adapter

- ``AutomergeRepo/WebSocketProvider``

### Peer to Peer Network Adapter

- ``AutomergeRepo/PeerToPeerProvider``
- ``AutomergeRepo/PeerToPeerProviderConfiguration``
- ``AutomergeRepo/PeerToPeerConnection``
- ``AutomergeRepo/PeerConnectionInfo``
- ``AutomergeRepo/ProtocolState``

### Share Policy

- ``AutomergeRepo/SharePolicy``
- ``AutomergeRepo/ShareAuthorizing``

### Storage Adapters

- ``AutomergeRepo/StorageProvider``
- ``AutomergeRepo/StorageSubsystem``
- ``AutomergeRepo/CHUNK``

### Automerge-Repo Sync Protocol

- ``AutomergeRepo/SyncV1Msg``
- ``PeerMetadata``
- ``AutomergeRepo/CBORCoder``
- ``AutomergeRepo/MSG_DOCUMENT_ID``
- ``AutomergeRepo/PEER_ID``
- ``AutomergeRepo/STORAGE_ID``
- ``AutomergeRepo/SYNC_MESSAGE``

2 changes: 1 addition & 1 deletion Sources/AutomergeRepo/Networking/Backoff.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Foundation

/// A type that provides a computation for a random back-off value based on an integer number of iterations.
public enum Backoff {
enum Backoff {
// fibonacci numbers for 0...15
static let fibonacci = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]

Expand Down
7 changes: 4 additions & 3 deletions Sources/AutomergeRepo/Networking/NetworkAdapterEvents.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/// Network events that an Automerge-Repo network provider sends into the repository to notify
/// the repository and other internal components of new connections, peers, and other Automerge messages
/// that may need to be propogateed.
/// Network events that a network provider sends to its delegate.
///
/// The delegate is typically a ``Repo`` which wants to know of new connections,
/// new or removed peers, and any other ``SyncV1Msg`` messages that may need to be propagated.
public enum NetworkAdapterEvents: Sendable, CustomDebugStringConvertible {
/// A description of the event.
public var debugDescription: String {
Expand Down
12 changes: 8 additions & 4 deletions Sources/AutomergeRepo/Networking/NetworkProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@ import Automerge
/// - After the underlying transport connection is established due to a call to `connect`, the provider emits
/// ``NetworkAdapterEvents/ready(payload:)``, which includes a payload that indicates a
/// reference to the network provider (`any NetworkAdapter`).
/// - After the connection is established, the adapter sends a ``SyncV1/join(_:)`` message to request peering.
/// - When the NetworkAdapter receives a ``SyncV1/peer(_:)`` message, it emits
/// - After the connection is established, the adapter sends a ``SyncV1Msg/join(_:)`` message to request peering.
/// - When the NetworkAdapter receives a ``SyncV1Msg/peer(_:)`` message, it emits
/// ``NetworkAdapterEvents/peerCandidate(payload:)``.
/// - If a message other than `peer` is received, the adapter should terminate the connection and emit
/// ``NetworkAdapterEvents/close``.
/// - All other messages are emitted as ``NetworkAdapterEvents/message(payload:)``.
/// - When a transport connection is closed, the adapter should emit ``NetworkAdapterEvents/peerDisconnect(payload:)``.
/// - When `disconnect` is invoked on a network provider, it should send a ``SyncV1/leave(_:)`` message, terminate the
/// - When `disconnect` is invoked on a network provider, it should send a ``SyncV1Msg/leave(_:)`` message, terminate
/// the
/// connection, and emit ``NetworkAdapterEvents/close``.
///
/// A connecting transport may optionally enable automatic reconnection on connection failure. Any configurable
Expand All @@ -36,7 +37,7 @@ import Automerge
/// - When a connection is established, emit ``NetworkAdapterEvents/ready(payload:)``.
/// - When the transport receives a `join` message, verify that the protocols being requested are compatible. If they
/// are not,
/// return an ``SyncV1/error(_:)`` message, close the connection, and emit ``NetworkAdapterEvents/close``.
/// return an ``SyncV1Msg/error(_:)`` message, close the connection, and emit ``NetworkAdapterEvents/close``.
/// - When any other message is received, it is emitted with ``NetworkAdapterEvents/message(payload:)``.
/// - When the transport receives a `leave` message, close the connection and emit ``NetworkAdapterEvents/close``.
@AutomergeRepo
Expand Down Expand Up @@ -73,6 +74,9 @@ public protocol NetworkProvider: Sendable {
}

/// A type that accepts provides a method for a Network Provider to call with network events.
///
/// Mostly commonly, this is a ``Repo`` instance, and describes the interface that a network provider
/// uses for its delegate callbacks.
@AutomergeRepo
public protocol NetworkEventReceiver: Sendable {
/// Receive and process an event from a Network Provider.
Expand Down
2 changes: 1 addition & 1 deletion Sources/AutomergeRepo/Networking/NetworkSubsystem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import PotentCBOR
/// is
/// the responsibility of the network provider instance.
@AutomergeRepo
public final class NetworkSubsystem {
final class NetworkSubsystem {
// TODO: When swift allows, switch this to a class that's locked to the same local actor
// as Repo

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Foundation
import Network
import OSLog

/// A Peer to Peer Network provider
/// An Automerge-repo network provider that connects to other instances over a peer to peer network.
///
/// Provides a incoming and outgoing connections to peers available over Bonjour.
@AutomergeRepo
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/// A type that provides type-safe strings for TXTRecord publication of peers over Bonjour
public enum TXTRecordKeys: Sendable {
enum TXTRecordKeys: Sendable {
/// The peer identifier.
public static let peer_id = "peer_id"
/// The human-readable name for the peer.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import OSLog

/// An Automerge-repo network provider that connects to other instances using a WebSocket.
@AutomergeRepo
public final class WebSocketProvider: NetworkProvider {
public let name = "WebSockket"
Expand Down
13 changes: 13 additions & 0 deletions Sources/AutomergeRepo/PeerMetadata.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,28 @@ import Foundation
// isEphemeral: bool
// }

/// A type that represents metadata associated with the storage capabilities of a remote peer.
public struct PeerMetadata: Hashable, Sendable, Codable, CustomDebugStringConvertible {
/// The ID of the peer's storage
///
/// Multiple peers can technically share the same persistent storage.
public var storageId: STORAGE_ID?

/// A Boolean value that indicates any data sent to this peer is ephemeral.
///
/// Typically, this means that the peer doesn't have any local persistent storage.
public var isEphemeral: Bool

/// Creates a new instance of peer metadata
/// - Parameters:
/// - storageId: An optional storage ID
/// - isEphemeral: A Boolean value that indicates any data sent to this peer is ephemeral
public init(storageId: STORAGE_ID? = nil, isEphemeral: Bool) {
self.storageId = storageId
self.isEphemeral = isEphemeral
}

/// A description of the metadata
public var debugDescription: String {
"[storageId: \(storageId ?? "nil"), ephemeral: \(isEphemeral)]"
}
Expand Down
24 changes: 24 additions & 0 deletions Sources/AutomergeRepo/Repo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ public protocol EphemeralMessageDelegate: Sendable {
func receiveEphemeralMessage(_ msg: SyncV1Msg.EphemeralMsg) async
}

/// A repository for Automerge documents that coordinates storage and synchronization.
///
/// Initialize a repository with a storage provider to enable automatic loading and saving of Automerge documents to
/// persistent storage.
/// Add one or more network adapters to support synchronization of updates between any connected peers.
/// Documents are shared on request, or not, based on ``SharePolicy`` you provide when creating the repository.
@AutomergeRepo
public final class Repo {
public nonisolated let peerId: PEER_ID
Expand Down Expand Up @@ -92,6 +98,23 @@ public final class Repo {
network = NetworkSubsystem()
}

/// Create a new repository with the custom share policy type you provide
/// - Parameter sharePolicy: A type that conforms to ``ShareAuthorizing`` to use to determine if a repository shares
/// a document.
/// - Parameter saveDebounce: The delay time to accumulate changes to a document before initiating a network sync
/// with available peers. The default is 2 seconds.
public nonisolated init(
sharePolicy: some ShareAuthorizing,
saveDebounce: RunLoop.SchedulerTimeType.Stride = .seconds(2)
) {
peerId = UUID().uuidString
storage = nil
localPeerMetadata = PeerMetadata(storageId: nil, isEphemeral: true)
self.sharePolicy = sharePolicy
self.saveDebounce = saveDebounce
network = NetworkSubsystem()
}

/// Create a new repository with the share policy and storage provider that you provide.
/// - Parameters:
/// - sharePolicy: The policy to use to determine if a repository shares a document.
Expand Down Expand Up @@ -652,6 +675,7 @@ public final class Repo {
// doesn't exist, for example - jump forward to attempting to fetch
// it from a peer.
if let doc = try await loadFromStorage(id: id) {
handle.doc = doc
handle.state = .ready
docHandlePublisher.send(handle.snapshot())
return try await resolveDocHandle(id: id)
Expand Down
8 changes: 8 additions & 0 deletions Sources/AutomergeRepo/ShareAuthorizing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ public protocol ShareAuthorizing: Sendable {
}

/// A type that encapsulates the logic to choose if a repository shares a document.
///
/// The built-in share policies include ``agreeable`` and ``readonly``.
/// Provide your own closure that accepts a ``PEER_ID`` and ``DocumentId`` to return a Boolean value that indicates if
/// the document should be shared on request.
///
/// If you need a type that supports more state and logic to determine authorization to share,
/// initialize a ``Repo`` with your own type that conforms to ``ShareAuthorizing`` with
/// ``Repo/init(sharePolicy:saveDebounce:)-8umfb``.
public struct SharePolicy: ShareAuthorizing, Sendable {
/// Returns a Boolean value that indicates whether a document may be shared.
/// - Parameters:
Expand Down
2 changes: 1 addition & 1 deletion Sources/AutomergeRepo/Storage/DocumentStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import OSLog

/// A type that provides coordinated, concurrency safe access to persist Automerge documents.
@AutomergeRepo
public final class DocumentStorage {
final class DocumentStorage {
let chunkNamespace = "incrChanges"
var compacting: Bool
let _storage: any StorageProvider
Expand Down
4 changes: 2 additions & 2 deletions Sources/AutomergeRepo/Sync/SyncV1Msg+encode+decode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ public extension SyncV1Msg {
/// Attempts to decode the data you provide as a peer message.
///
/// - Parameter data: The data to decode
/// - Returns: The decoded message, or ``SyncV1/unknown(_:)`` if the decoding attempt failed.
/// - Returns: The decoded message, or ``SyncV1Msg/unknown(_:)`` if the decoding attempt failed.
static func decodePeer(_ data: Data) -> SyncV1Msg {
if let peerMsg = attemptPeer(data) {
.peer(peerMsg)
Expand Down Expand Up @@ -35,7 +35,7 @@ public extension SyncV1Msg {
/// - data: The data to decode.
/// - withGossip: A Boolean value that indicates whether to include decoding of handshake messages.
/// - withHandshake: A Boolean value that indicates whether to include decoding of gossip messages.
/// - Returns: The decoded message, or ``SyncV1/unknown(_:)`` if the previous decoding attempts failed.
/// - Returns: The decoded message, or ``SyncV1Msg/unknown(_:)`` if the previous decoding attempts failed.
///
/// The decoding is ordered from the perspective of an initiating client expecting a response to minimize attempts.
/// Enable `withGossip` to attempt to decode head gossip messages, and `withHandshake` to include handshake phase
Expand Down
2 changes: 1 addition & 1 deletion Tests/AutomergeRepoTests/BackoffTests.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import AutomergeRepo
@testable import AutomergeRepo
import XCTest

final class BackoffTests: XCTestCase {
Expand Down
Loading