Skip to content
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
1 change: 1 addition & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@
^Meta$
^\.automerge$
^codecov\.yml$
^cran-comments\.md$
8 changes: 3 additions & 5 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ Authors@R: c(
comment = c(ROR = "03wc8by49"))
)
Description: A WebSocket-based implementation of the 'automerge-repo'
synchronization protocol used by 'sync.automerge.org'. Acts as a sync
server, enabling R to serve as a synchronization hub for 'Automerge'
synchronization protocol used by 'sync.automerge.org'. Acts as a sync
server, enabling 'R' to serve as a synchronization hub for 'Automerge'
clients in 'JavaScript', 'Rust', and other languages, and as a client
for fetching, editing, and synchronizing documents hosted on remote
servers.
Expand All @@ -27,10 +27,8 @@ Imports:
later,
nanonext (>= 1.8.1),
promises,
secretbase (>= 1.2.0),
utils
secretbase (>= 1.3.0)
Suggests:
httpuv,
openssl,
testthat (>= 3.0.0)
Config/Needs/website: tidyverse/tidytemplate
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
YEAR: 2025
YEAR: 2026
COPYRIGHT HOLDER: autosync authors
12 changes: 9 additions & 3 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Generated by roxygen2: do not edit by hand

S3method(print,sync_client)
S3method(print,sync_doc)
S3method(print,sync_server)
S3method(print,autosync_client)
S3method(print,autosync_doc)
S3method(print,autosync_server)
export(auth_config)
export(create_document)
export(generate_document_id)
Expand All @@ -28,9 +28,14 @@ importFrom(httr2,oauth_client)
importFrom(httr2,oauth_flow_auth_code)
importFrom(httr2,oauth_redirect_uri)
importFrom(httr2,oauth_server_metadata)
importFrom(jose,jwt_decode_sig)
importFrom(jose,read_jwk)
importFrom(later,later)
importFrom(later,run_now)
importFrom(nanonext,handler_ws)
importFrom(nanonext,http_server)
importFrom(nanonext,is_error_value)
importFrom(nanonext,ncurl)
importFrom(nanonext,random)
importFrom(nanonext,recv)
importFrom(nanonext,recv_aio)
Expand All @@ -40,6 +45,7 @@ importFrom(nanonext,stream)
importFrom(nanonext,tls_config)
importFrom(nanonext,unresolved)
importFrom(nanonext,write_cert)
importFrom(promises,then)
importFrom(secretbase,base58dec)
importFrom(secretbase,base58enc)
importFrom(secretbase,base64dec)
Expand Down
12 changes: 1 addition & 11 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,6 @@
# autosync (development version)

* The `amsync_*` connector functions were renamed `sync_*`: `sync_server()`,
`sync_client()`, `sync_fetch()` and `sync_token()` (with S3 classes
`sync_server`, `sync_client` and `sync_doc`).
* `sync_client()` now opens a connection only and no longer takes a `doc_id`.
Open one or more live documents over the connection with the new
`$open_doc(doc_id)` method, which returns a `sync_doc` handle exposing
`$doc`, `$push()`, `$active` and `$close()`. A single connection can sync
several documents, and `$close()` tears them all down.
* Project browsing and live editing moved to the `shinysync` package
(`project_open()`, `project_app()` and `project_edit()`); autosync no longer
depends on `shiny` or `bslib`.
* Initial CRAN release.

# autosync 0.0.1

Expand Down
20 changes: 10 additions & 10 deletions R/auth.R
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ oidc_issuer <- function() {
#' @keywords internal
discover_jwks_uri <- function(issuer) {
config_url <- paste0(issuer, "/.well-known/openid-configuration")
resp <- nanonext::ncurl(config_url, timeout = 5000L)
resp <- ncurl(config_url, timeout = 5000L)

if (nanonext::is_error_value(resp$data) || resp$status != 200L) {
if (is_error_value(resp$data) || resp$status != 200L) {
stop("Failed to fetch OIDC configuration from: ", config_url)
}

Expand All @@ -53,13 +53,13 @@ discover_jwks_uri <- function(issuer) {
#'
#' @keywords internal
fetch_jwks <- function(jwks_uri) {
resp <- nanonext::ncurl(
resp <- ncurl(
jwks_uri,
timeout = 5000L,
response = "Cache-Control"
)

if (nanonext::is_error_value(resp$data) || resp$status != 200L) {
if (is_error_value(resp$data) || resp$status != 200L) {
stop("Failed to fetch JWKS from: ", jwks_uri)
}

Expand All @@ -76,7 +76,7 @@ fetch_jwks <- function(jwks_uri) {
next
}
key <- tryCatch(
jose::read_jwk(jsonenc(jwk)),
read_jwk(jsonenc(jwk)),
error = function(e) NULL
)
if (!is.null(key)) {
Expand Down Expand Up @@ -167,7 +167,7 @@ validate_token <- function(
}

header <- tryCatch(
jsondec(jose::base64url_decode(parts[1L])),
jsondec(base64dec(parts[1L], url = TRUE)),
error = function(e) NULL
)
if (is.null(header) || is.null(header$kid)) {
Expand All @@ -189,7 +189,7 @@ validate_token <- function(

# Verify signature and decode claims
claims <- tryCatch(
jose::jwt_decode_sig(token, key),
jwt_decode_sig(token, key),
error = function(e) {
msg <- conditionMessage(e)
if (grepl("expired", msg, ignore.case = TRUE)) {
Expand Down Expand Up @@ -329,7 +329,7 @@ validate_token <- function(
#' @param custom_validator Function(claims) returning TRUE/FALSE for
#' custom validation logic. Receives the decoded JWT claims as a list.
#'
#' @return An amsync_auth_config object.
#' @return An object of class `"autosync_auth_config"`.
#'
#' @examples
#' # Google (default issuer)
Expand Down Expand Up @@ -380,7 +380,7 @@ auth_config <- function(
allowed_domains = allowed_domains,
custom_validator = custom_validator
),
class = "amsync_auth_config"
class = "autosync_auth_config"
)
}

Expand Down Expand Up @@ -496,7 +496,7 @@ sync_token <- function(
#' Extracts and validates a Bearer token (JWT) from the Authorization header
#' of the WebSocket upgrade request.
#'
#' @param auth_config An amsync_auth_config object.
#' @param auth_config A `autosync_auth_config` object.
#' @param headers Named list of HTTP request headers.
#'
#' @return List with `valid` (logical), `email` (character or NULL),
Expand Down
8 changes: 5 additions & 3 deletions R/autosync-package.R
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#' autosync: 'Automerge' Sync Server and Client for R
#' autosync: 'Automerge' Sync Server and Client
#'
#' A WebSocket-based implementation of the 'automerge-repo' synchronization
#' protocol used by 'sync.automerge.org'. Acts as a sync server, enabling R to
#' protocol used by 'sync.automerge.org'. Acts as a sync server, enabling 'R' to
#' serve as a synchronization hub for 'Automerge' clients in 'JavaScript',
#' 'Rust', and other languages, and as a client for fetching, editing, and
#' synchronizing documents hosted on remote servers.
Expand Down Expand Up @@ -47,8 +47,10 @@
#'
#' @importFrom automerge am_create am_get am_keys am_length am_load am_save am_sync_decode am_sync_encode am_sync_state am_sync_state_decode am_sync_state_encode AM_ROOT
#' @importFrom httr2 oauth_client oauth_flow_auth_code oauth_redirect_uri oauth_server_metadata
#' @importFrom jose jwt_decode_sig read_jwk
#' @importFrom later later run_now
#' @importFrom nanonext http_server random recv recv_aio send stop_aio stream tls_config unresolved write_cert
#' @importFrom nanonext handler_ws http_server is_error_value ncurl random recv recv_aio send stop_aio stream tls_config unresolved write_cert
#' @importFrom promises then
#' @importFrom secretbase base64enc base64dec base58enc base58dec cborenc cbordec jsondec jsonenc
#' @importFrom utils str
"_PACKAGE"
16 changes: 8 additions & 8 deletions R/client.R
Original file line number Diff line number Diff line change
Expand Up @@ -84,18 +84,18 @@ join_msg <- function(peer_id) {
#' for and send local changes for every open document. This is a cheap no-op
#' when there are no changes.
#'
#' @return An environment of class `"sync_client"` with reference semantics,
#' @return An environment of class `"autosync_client"` with reference semantics,
#' representing the connection:
#' \describe{
#' \item{`open_doc(doc_id, timeout)`}{Open a live document over this
#' connection and return a `sync_doc` handle for it (see below).
#' connection and return a `autosync_doc` handle for it (see below).
#' Repeated calls for the same `doc_id` reuse the document already open
#' on the connection rather than requesting it again.}
#' \item{`close()`}{Disconnect and stop syncing all open documents.}
#' \item{`active`}{Logical, whether the connection is active.}
#' }
#'
#' A `sync_doc` handle returned by `$open_doc()` is itself an environment
#' A `autosync_doc` handle returned by `$open_doc()` is itself an environment
#' with:
#' \describe{
#' \item{`doc`}{The live automerge document, kept in sync with the server.}
Expand Down Expand Up @@ -269,7 +269,7 @@ sync_client <- function(
# Track the pending aio so close() can settle it before tearing down the
# stream, leaving no stale promise continuation for a later run_now to hit.
client$recv_aio <- aio
promises::then(
then(
aio,
onFulfilled = function(value) {
tryCatch(
Expand Down Expand Up @@ -337,7 +337,7 @@ sync_client <- function(
},
handle
)
class(handle) <- "sync_doc"
class(handle) <- "autosync_doc"
handle
}

Expand Down Expand Up @@ -430,7 +430,7 @@ sync_client <- function(
invisible()
}

class(client) <- "sync_client"
class(client) <- "autosync_client"
on.exit() # stream now owned by client$close

# Start the async loops
Expand All @@ -441,7 +441,7 @@ sync_client <- function(
}

#' @export
print.sync_client <- function(x, ...) {
print.autosync_client <- function(x, ...) {
cat("Automerge Sync Connection\n")
cat(" Server:", x$url, "\n")
cat(" Documents:", length(x$documents), "\n")
Expand All @@ -450,7 +450,7 @@ print.sync_client <- function(x, ...) {
}

#' @export
print.sync_doc <- function(x, ...) {
print.autosync_doc <- function(x, ...) {
cat("Automerge Document\n")
cat(" Document:", x$doc_id, "\n")
cat(" Active:", x$active, "\n")
Expand Down
Loading
Loading