Mangrove Reviews JS Library
Read and write Open Reviews from JavaScript. The library covers the reviewer API, signing with a key the app holds, logging people in through the signer so the app holds no keys, binding a key to a reviewer identity, and following a subject or reviewer from the Fediverse.
- Function reference for every call and its arguments.
- Integration guides for the flows: the login, identities and Fediverse.
- The servers behind the calls: reviewer API, signer and Fediverse bridge.
Installation
npm install mangrove-reviews
The library needs fetch, URL and WebCrypto, which browsers and Node 20 and later provide.
A script tag can load dist/index.umd.js instead; it defines mangrove-reviews, with the
signer calls under signer.
Read reviews
import { getReviews, getSubject } from 'mangrove-reviews'
const reviews = await getReviews({ sub: 'https://example.com' })
const aggregate = await getSubject('https://example.com')
Write reviews with a local key
The app generates a keypair and keeps it, for example as JWK in IndexedDB. Reviews of
https://example.com and of geo:0,0?q=<any name>&u=30 are removed from the dataset,
so use those subjects for tests.
import {
generateKeypair,
keypairToJwk,
signAndSubmitReview
} from 'mangrove-reviews'
const keypair = await generateKeypair()
const jwk = await keypairToJwk(keypair) // store this to keep the same reviewer
await signAndSubmitReview(keypair, {
sub: 'https://example.com',
rating: 75,
opinion: 'Great website to be used as an example.',
metadata: { nickname: 'docs reader' }
})
Write reviews through the signer
The signer holds one key per person who logs in with OpenStreetMap, Bluesky, Google, GitHub or a passkey, so the app manages no keys and the person keeps one identity across devices and apps. The app needs a client id; the login guide says how to get one and walks through the callback.
import {
loginUrl,
parseCallback,
signAndSubmitReview
} from 'mangrove-reviews/signer'
window.location.href = loginUrl(clientId, callbackUrl, 'osm', undefined, state)
// On the callback page:
const { sessionToken, did } = parseCallback(window.location.href)
await signAndSubmitReview(sessionToken, {
sub: 'https://example.com',
rating: 80
})
A logged-in person can add a passkey to their account, list their passkeys and remove one:
import {
passkeyAddUrl,
listPasskeys,
removePasskey
} from 'mangrove-reviews/signer'
window.location.href = await passkeyAddUrl(sessionToken, callbackUrl, state)
// The signer's page returns to callbackUrl with #passkey=added&state=... in the fragment.
const passkeys = await listPasskeys(sessionToken)
await removePasskey(sessionToken, passkeys[0].id)
Follow from the Fediverse
import { getFediverseHandle, followUrl } from 'mangrove-reviews'
const { acct, actor } = await getFediverseHandle({ sub: 'https://example.com' })
// Show `acct` with a copy button, or open the follow dialog on the person's server:
window.open(followUrl('mastodon.social', actor))
Errors
A refused request rejects with an ApiError carrying the HTTP status and the server's
reason in body, such as the field of a review that is wrong or 401 for an expired session.
License
Apache-2.0