From 3d0b4e5ee6694a4bc9e92982ccea80e2d5d8b971 Mon Sep 17 00:00:00 2001 From: callumalpass Date: Sun, 26 Jul 2026 06:16:29 +1000 Subject: [PATCH 1/2] Document bookmarkable collection locations --- src/pages/sdk/api/index.astro | 39 ++++++++++++++++++++++++ src/pages/sdk/authorization/index.astro | 12 ++++---- src/pages/sdk/index.astro | 19 ++++++++---- src/pages/sdk/quickstart/index.astro | 40 +++++++++++++------------ 4 files changed, 80 insertions(+), 30 deletions(-) diff --git a/src/pages/sdk/api/index.astro b/src/pages/sdk/api/index.astro index d0cf7b3..e19039a 100644 --- a/src/pages/sdk/api/index.astro +++ b/src/pages/sdk/api/index.astro @@ -14,6 +14,12 @@ const constructor = `new MdbaseConnect({ navigate?: (url: string) => void | Promise })`; +const browserLocation = `const collectionLocation = + new MdbaseBrowserLocation(manager, { + collectionParameter: "collection", + fallbackPath: "/" + });`; + const error = `try { const result = unwrapOperation(await connection.update(input)); } catch (error) { @@ -32,6 +38,7 @@ const error = `try { headings={[ { href: "#constructor", label: "Constructor" }, { href: "#authorization", label: "Authorization methods" }, + { href: "#browser-location", label: "Browser location" }, { href: "#operations", label: "Operation methods" }, { href: "#notifications", label: "Notifications and timers" }, { href: "#connection", label: "Connection and sync" }, @@ -146,6 +153,38 @@ const error = `try { +

MdbaseBrowserLocation

+ +

+ Keep one location helper beside the application's stable + {" "}MdbaseConnect manager. It treats the explicit URL + identity as authoritative and selects a saved fallback only when exactly + one connection exists. +

+ + + + + + + + + + + + + + +
MethodPurpose
selectedCollectionId()Read the explicit bookmark identity, including one that is not currently authorized
activeConnection()Resolve the bookmarked connection or the sole saved fallback
selectConnection(id, options?)Push or replace a cleaned bookmark URL
authorizationReturnTo()Return the current app-local path after removing temporary OAuth parameters
completeAuthorization(callbackUrl?)Complete PKCE and restore a safe same-origin URL for the approved collection
isAuthorizationCallback(url)Recognize browser and native callback URLs with an authorization result
clearAuthorizationCallback(returnTo?)Remove callback parameters after denial or failure
onChange(listener)Observe saved-connection changes plus browser back and forward navigation
+
+ Collection IDs are bookmark-safe locators. +

+ They may appear in history, logs, and shared URLs. They do not authorize + an operation. Connect still requires the application's stored grant and + enforces it at the collection authority. +

+
+

Collection operations

The methods below belong to MdbaseConnection, not the manager. diff --git a/src/pages/sdk/authorization/index.astro b/src/pages/sdk/authorization/index.astro index f3f7cdf..9c34d4d 100644 --- a/src/pages/sdk/authorization/index.astro +++ b/src/pages/sdk/authorization/index.astro @@ -7,16 +7,17 @@ const capability = connection.authorizationCapabilities([...required]); if (!capability.sufficient) { await connection.requestOperations([...required], { - returnTo: location.pathname + location.search + returnTo: collectionLocation.authorizationReturnTo() }); }`; -const listen = `const stop = connection.onConnectionChange((info) => { - if (!info) { +const listen = `const stop = collectionLocation.onChange(({ connection }) => { + if (!connection) { renderDisconnected(); return; } + const info = connection.info(); renderConnection({ collectionId: info.collectionId, route: info.route, @@ -130,8 +131,9 @@ const recover = `try {

The route is direct, relay, or {" "}hosted. Display it as status information. The SDK selects - the route. A null connection means the SDK has no usable - authorization. + the route. The browser location subscription also reports back and forward + selection changes. A null connection means the bookmarked + collection has no usable authorization.

Use structured recovery actions

diff --git a/src/pages/sdk/index.astro b/src/pages/sdk/index.astro index 9ade993..33c827f 100644 --- a/src/pages/sdk/index.astro +++ b/src/pages/sdk/index.astro @@ -54,6 +54,11 @@ export async function listOpenTasks(client: MdbaseCollectionClient) { One collection's operations, renewal, routing, notifications, and hosted sync Browser APIs or adapters supplied by a native shell + + MdbaseBrowserLocation + Bookmarkable collection selection, authorization return URLs, and browser navigation + A stable MdbaseConnect manager and browser history APIs + MdbaseCollectionClient Domain and feature logic that should be independent of OAuth and hosting @@ -75,12 +80,14 @@ export async function listOpenTasks(client: MdbaseCollectionClient) {

Application structure

Create one MdbaseConnect manager at the application boundary. - Resolve the ID in the URL with manager.connection(id), then - pass that MdbaseConnection into feature code while keeping - OAuth and saved-connection state at the boundary. A bound connection never - changes collections underneath a repository, and feature code written - against MdbaseCollectionClient can also run against the - developer sandbox or another conforming transport. + Pair it with one MdbaseBrowserLocation at the browser + application boundary. Resolve its active connection, then pass that + {" "}MdbaseConnection into feature code. The location helper owns + bookmark selection and OAuth return cleanup; the manager owns saved + authorizations. A bound connection never changes collections underneath a + repository, and feature code written against + {" "}MdbaseCollectionClient can also run against the developer + sandbox or another conforming transport.

diff --git a/src/pages/sdk/quickstart/index.astro b/src/pages/sdk/quickstart/index.astro index 9403c34..0e07aa2 100644 --- a/src/pages/sdk/quickstart/index.astro +++ b/src/pages/sdk/quickstart/index.astro @@ -19,6 +19,7 @@ const install = `pnpm add @mdbase/connect pnpm add -D @mdbase/connect-dev`; const client = `import { + MdbaseBrowserLocation, MdbaseConnect, type JsonObject } from "@mdbase/connect"; @@ -33,34 +34,31 @@ export const mdbase = new MdbaseConnect({ serverUrl: "https://connect.mdbase.dev", manifest: new URL("/.well-known/mdbase-app.json", location.origin).href, redirectUri: new URL("/auth/mdbase/callback", location.origin).href -});`; +}); -const connectButton = `import { mdbase } from "./connect"; +export const collectionLocation = new MdbaseBrowserLocation(mdbase);`; + +const connectButton = `import { collectionLocation, mdbase } from "./connect"; export async function connectCollection() { await mdbase.authorize({ operations: ["describe", "read", "query", "update"], - returnTo: location.pathname + location.search + collectionId: collectionLocation.selectedCollectionId() ?? undefined, + returnTo: collectionLocation.authorizationReturnTo() }); // authorize() navigates to Connect and does not return. }`; -const callback = `import { mdbase } from "./connect"; +const callback = `import { collectionLocation } from "./connect"; export async function completeMdbaseCallback() { - const { connection, returnTo = "/" } = - await mdbase.completeAuthorization(location.href); - const destination = new URL(returnTo, location.origin); - destination.searchParams.set("collection", connection.collectionId); - history.replaceState({}, "", destination); - return connection; // Permanently bound to this collection. + return collectionLocation.completeAuthorization(); }`; -const query = `import { mdbase } from "./connect"; +const query = `import { collectionLocation } from "./connect"; import { unwrapOperation } from "@mdbase/connect"; -const collectionId = new URL(location.href).searchParams.get("collection"); -const collection = collectionId ? mdbase.connection(collectionId) : null; +const collection = collectionLocation.activeConnection(); if (!collection) throw new Error("Choose a collection."); const page = unwrapOperation(await collection.query({ @@ -149,7 +147,9 @@ const validate = `mdbase-connect-dev validate-manifest \\

Call this once on the exact callback route listed in the manifest. PKCE is mandatory. On success, the client stores the grant-bound authorization and - returns the newly approved collection-bound connection. + returns the newly approved collection-bound connection. The location + helper restores the app-local return location, adds its collection ID, and + removes temporary OAuth parameters.

6. Query and update with a revision

@@ -162,11 +162,13 @@ const validate = `mdbase-connect-dev validate-manifest \\
Use a stable collection ID in app URLs.

- A query parameter such as ?collection=<id> works on - static hosts, survives refreshes, and makes a view bookmarkable. Treat an - explicit ID as authoritative: if it is not saved, show the chooser or - reconnect that ID instead of silently opening another collection. Names - are display text and can change. + MdbaseBrowserLocation writes + {" "}?collection=<id>, follows browser back and + forward navigation through onChange(), and auto-selects only + when exactly one connection is saved. An explicit unavailable ID stays + authoritative, so show the chooser or reconnect that ID. Collection IDs + can appear in browser history and logs. They are opaque locators, not + credentials. Names are display text and can change.

From 407d3af3592c1944651822a51017361f01a8d502 Mon Sep 17 00:00:00 2001 From: callumalpass Date: Sun, 26 Jul 2026 08:51:19 +1000 Subject: [PATCH 2/2] Pin documentation to the collection SDK release --- .github/workflows/ci.yml | 2 +- .github/workflows/deploy.yml | 2 +- site-sources.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2645779..cf3df57 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: uses: actions/checkout@v7 with: repository: mdbase-dev/mdbase-connect - ref: fec3e222cebb8f1711d3e6eb111ab8f225680006 + ref: 8e960317f0886f8c504023dad873cf8cec82fd99 path: .sources/mdbase-connect - name: Check out Rust implementation diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 45558b8..e3a9b5c 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -32,7 +32,7 @@ jobs: uses: actions/checkout@v7 with: repository: mdbase-dev/mdbase-connect - ref: fec3e222cebb8f1711d3e6eb111ab8f225680006 + ref: 8e960317f0886f8c504023dad873cf8cec82fd99 path: .sources/mdbase-connect - name: Check out Rust implementation diff --git a/site-sources.json b/site-sources.json index 77134fc..77da7f2 100644 --- a/site-sources.json +++ b/site-sources.json @@ -5,7 +5,7 @@ }, "connect": { "repository": "mdbase-dev/mdbase-connect", - "ref": "fec3e222cebb8f1711d3e6eb111ab8f225680006" + "ref": "8e960317f0886f8c504023dad873cf8cec82fd99" }, "implementations": [ {