Version 6.16.0

All pages

The solid-memo-client

@solid-memo/solid-memo-client is a second implementation of the Solid client modules the app reads and writes pods and signs in through, built on no Inrupt library. Inrupt's clients stay the default, and a build or a test run takes this one with SOLID_CLIENT=solid-memo-client. This page says what it replaces, what it's built on, how it keeps a session and grants access, where it differs from Inrupt's, and how it's tested.

What it replaces

The app reaches the Solid client libraries only through modules of its own, whose types are in client/interface.ts and name no library (boundaries.md, vendor-code.md). The package, packages/solid-memo-client/, has a module of the same name for each, with the same exports and types:

ModuleReplacesBuilt on
rdf.ts, with dataset.tspackages/solid/src/client/rdf.ts: datasets and their subjectsn3's RDF/JS terms. Datasets and subjects are the package's own frozen objects; a dataset's edits are a chain of steps that changesOf replays.
resources.tspackages/solid/src/client/resources.ts: documents, containers and files, the profile lookup, and the Turtle and JSON-LD parsersfetch; n3 to parse and write Turtle; jsonld-streaming-parser to parse JSON-LD
session.ts, with oidcLogin.ts, oidcSession.ts, oidcStore.ts and oidcBrowser.tspackages/solid/src/client/session.ts: the sign-in clientoauth4webapi for Solid-OIDC, WebCrypto for the DPoP keys, IndexedDB for the session, Web Locks between tabs
access.tspackages/solid/src/client/access.ts: the public-read grantfetch and n3
turtleDocument.tspackages/shacl/src/turtleDocument.ts: a shape's Turtle, readfetch and n3

The bodies of its PATCH requests come from solid's client/sparqlUpdate.ts, as Inrupt's client's do, and a write refuses the same terms (writableTerms.ts). The package imports solid for these, for the interface and, in its tests, for the contract suites. It imports no @inrupt/ package, its tests included, and no package imports it, as npm run check:boundaries checks.

Switching clients

SOLID_CLIENT picks the client when a Vite or Vitest config loads:

ValueClient
unset, empty or inruptInrupt's, in packages/solid/src/client/: the default
solid-memo-clientthis package's
anything elsenone: the config fails to load, and its error names the values above

The switch is a Vite plugin, solidClientPlugin in scripts/solidClient.ts. Every package's Vitest config has it, through vitest.shared.ts, and so do apps/web's Vite config and e2e/pod's Vitest config. With solid-memo-client, it resolves every import of a module its REPLACEMENTS list names to the package's module of the same name, the package's own imports aside. No caller changes. With Inrupt's client, the plugin has no hooks, so a test's vi.mock of a client module works as it does without the plugin.

SOLID_CLIENT=solid-memo-client npm run build      # the site, the Studio included, on this client
SOLID_CLIENT=solid-memo-client npm run dev        # the site from source, on this client
SOLID_CLIENT=solid-memo-client npm run test:pod   # the end-to-end tests against real servers
SOLID_CLIENT=solid-memo-client npm run test:unit -w @solid-memo/shacl   # one package's tests

A package's tests run with the switch on without coverage, through test:unit. With coverage, its test task fails: the modules the switch replaces are never loaded, so its coverage falls below 100%.

The user journeys test the build in apps/web/dist/: a build with the switch on, then npm run journeys. Run locally, the journeys reuse any server already on port 4173, which may serve another build (testing.md).

Under the switch, the tests of the four Inrupt modules this client replaces (packages/solid/src/client/{access,rdf,resources,session}.test.ts) test what is the library's alone, and some fail. The rest of solid runs without them, as CI runs it:

SOLID_CLIENT=solid-memo-client npm run test:unit -w @solid-memo/solid -- --exclude 'src/client/{access,rdf,resources,session}.test.ts'

Turbo hashes SOLID_CLIENT (globalEnv) and scripts/solidClient.ts (globalDependencies) into every task, so a result cached on one client never stands for the other. The tasks that resolve through the plugin have the package's src/ and package.json among their inputs: solid's test and test:roundtrip, shacl's and composition's test, and web's test and build. They run again when either changes, on either client, but not for a new release of one of its dependencies that only package-lock.json names.

npm run check:exports counts an export of this package as used where the module it replaces uses the same name, and npm run check walks its run-time dependencies for releases before 1.0.0, as it walks the app's (vendor-code.md).

Sign-in

The sign-in client (authentication.md) runs Solid-OIDC's authorization-code flow on oauth4webapi (oidcLogin.ts):

What a session keeps

Each login makes a new ES256 key pair with WebCrypto, its private key not extractable, and binds the tokens to it. The browser keeps (oidcStore.ts):

WhereWhat
IndexedDB, database solid-memo-oidc, store sessionthe session a reload restores: the issuer, the client, the redirect URL, the DPoP key pair as CryptoKey objects, the WebID, and the refresh token when the provider gave one
IndexedDB, database solid-memo-oidc, store registrationsthe clients providers registered the app as, by issuer and redirect URL; logout keeps them
sessionStorage, solid-memo:oidc-loginduring a login, the request under way: the issuer, the client, the redirect URL, the PKCE code verifier, the state and the nonce, and for a silent restore the page it left; removed when the provider sends the learner back
memorythe access token, and when it expires

The access token is never stored. IndexedDB keeps the key pair as CryptoKey objects, which localStorage, holding only text, can't.

Restore, refresh and logout

The session's requests are in oidcSession.ts:

The public-read grant

grantPublicRead (access.ts) makes a published release readable by everyone, on Web Access Control (WAC) or Access Control Policies (ACP), as Inrupt's universal access does (boundaries.md):

Every request goes by URL, so the publisher's fence sees where each goes.

Differences from Inrupt's client

Both pass the same contract. Beyond it, this client differs:

Tests