Repository navigation
refactor(solid): put the Solid client libraries behind the app's own modules - #324
Merged
Merged
Conversation
The package's files named @inrupt/solid-client's data model everywhere: its dataset and subject types and its readers and builders, in every mapper, in records.ts, ownership.ts and datasets.ts. Swapping or only trying out another library would have meant touching all of them. client/rdf.ts now gives the data model names of the app's own (Dataset, Subject, NamedSubject, SubjectBuilder, and datasets that know their source) and passes the operations through under the names the code already calls, so the callers change only their imports and type names. It is a thin layer over the same library, so nothing behaves or writes differently. Reading and writing pods, sign-in and access control still call the library and get their own modules next. Refs: #323
…odule The change log, the resource info, a subject's predicates and a dataset's graphs were read and built in several files across the package, so a switch of libraries would have meant changes well beyond the RDF module. They now sit behind functions client/rdf.ts exports (changesOf, sourceOf, datasetFromQuads, withQuads, isEmptyDataset, propertiesOf, localNameOf, parseQuads), and the one-pass dataset builder moves into client/ beside it, since it rebuilds the library's structure. The module's doc comment and its tests now also state what the app relies on of the library beyond the names of its calls: blank-node subjects left out of getThingAll, subjects made by name resolved against the document read, how dates, language tags and decimals are read. A second implementation has to match each of these. Refs: #323
Every read and write of a pod's resources now goes through client/resources.ts, so that the rest of the package names no Solid client library and a later switch of libraries is new code behind the same calls. The module keeps the library's names and requests (headers, URLs and the PATCH a saved edit sends, with datasets.ts's own body and the library's where it writes none), and the Turtle and JSON-LD parsers move there from the RDF module. A failure the server answers with is now an HttpError of the app's own, with the library's message, status and response, so the checks of statusCode and response.status in datasets.ts, containers.ts, the copier, the release drafts and the UI's retryable keep working, as does the message the type index shows. The SHACL package, which may not import the solid package, reads Turtle through one module of its own, turtleDocument.ts, the only place it names the library. Refs: #323
The session gateway and authFetch called the authentication library directly, so a switch to another Solid-OIDC client would have meant rewriting the gateway's own logic with it. The library now sits behind a small sign-in client in client/session.ts: restore, login, logout, and a fetch and a WebID that are always the current session's. The gateway keeps what depends on no library, the route a login returns to, issuer discovery and the client ID choice, and takes the client it is given. The composition gets the gateway, the authenticated fetch and the logged-in WebID from one factory, createSolidSession, so all three act on the same client. The gateway's tests now run on a fake client, and the client's own tests cover the library calls. Refs: #323
The release publisher and the storage gateway called the Solid client library themselves, so a switch of library would have meant rewriting both and their tests. The public-read grant now lives in client/access.ts, with the request fixes it needs for node-solid-server and the Community Solid Server, since those work around how this library writes access control. The fence that keeps the grant to the release's own ACL or ACR stays in the publisher, around whatever grant it is given, because that is the app's rule and not the library's. The profile lookup becomes getProfileStorages in client/resources.ts, and the gateway takes it as an option. Composition wires both, the publisher's and gateway's tests run on fakes of the injected functions, and the boundary rule for access control now points at client/access.ts. Refs: #323
The tests still built their datasets with the library's mock helpers and mocked the library's modules, so a switch of libraries would have meant rewriting most of the package's tests as well as client/. They now build datasets with testing/datasets.ts, which uses the RDF module's new datasetAt, and they mock client/resources and client/rdf instead of the library. The resources tests now mock getSolidDatasetLinear, because that is what the code calls. Only the tests of client/ still import the library. The patch tests now make their changes with edits through the RDF module instead of writing the library's change log by hand. A change with a blank node, which those edits never record, comes from a mocked changesOf. The review state tests edit subjects with buildThing instead of rewriting their predicates. With no test mocking the library's getSolidDataset any more, the check in linearDataset.ts for a document the library parsed without our parser could never be true: the library is given only our parser, and it throws on any other type. The check is removed. Refs: #323
The earlier steps moved every use of @inrupt/solid-client and @inrupt/solid-client-authn-browser behind modules of the app's own in packages/solid/src/client/, with one Turtle reader in shacl. Nothing kept it that way: a new file could import the libraries again and quietly widen what a later switch would have to touch. The boundary check now fails on any import of either library, of any form, outside those files, tests aside, and still keeps the access control to the public-read grant. docs/boundaries.md names the client modules and the rule. Refs: #323
The client libraries now sit behind modules of the app's own in packages/solid/src/client/ and shacl's turtleDocument.ts, but the docs still said the libraries were used throughout the package, that the mappers translate Inrupt's types and that tests mock @inrupt modules. The cost of a swap is now known: a second implementation of those modules with no caller changes. The vendor page names that cost and the behavior a replacement has to match, and the architecture, testing, authentication, privacy, standards and data model pages, the package's README and its description follow. Closes: #323
ixuz
force-pushed
the
solid-client-seams
branch
from
October 26, 2026 20:36
648195e to
861b329
Compare
ixuz
enabled auto-merge
October 26, 2026 20:40
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What and why
Replacing the Solid client libraries used to mean touching most of
packages/solidand its tests at once. The libraries were imported in more than 40 files, internals were read in several places, and tests mocked the libraries directly. This pull request moves every use of them behind modules of the app's own. A later switch is then new code behind fixed interfaces, with no changes in the callers. Nothing changes for users, and nothing changes in what the app writes to a pod.packages/solid/src/client/imports them:rdf.ts(the data model),resources.ts(reading and writing pods, with an ownHttpError),session.ts(the sign-in client),access.ts(the public-read grant) andlinearDataset.ts.packages/shacl/src/turtleDocument.tsis shacl's only importer. The types get the app's own names (Dataset,Subject,NamedSubject,SubjectBuilder). The functions keep the names the code already called, so callers changed their imports and type names only.client/. The change log, the resource info the ETag map is keyed by, a subject's predicates and a dataset's graphs are reached through functionsrdf.tsexports (changesOf,sourceOf,propertiesOf,datasetFromQuads,withQuads,isEmptyDataset,parseQuads).client/rdf.tsdocuments it andclient/rdf.test.tspins it against the current library:HttpErrorkeeps the library'sstatusCode,response.statusand message, whichretryable.tsandtypeIndex.tsrely on, and carries the library's error ascause. Its name isHttpError, so the crash screen's copyable details now start withHttpError:instead ofError:.solidSessionGateway.tskeeps the logic that depends on no library: the login hash, issuer discovery, and 401 as expiry. It takes the client fromclient/session.ts, which provides restore, login, logout, and a fetch and a WebID that are always the current session's.compositiongets one factory,createSolidSession, that returns{ sessionGateway, fetch, webId }. The own-pod release reads from fix(composition): read a release outside the user's own pod as no one #325 ask that same client who is logged in.createSolidReleasePublishertakesgrantPublicRead, and the storage gateway takes its profile lookup (getProfileStorages). The publisher's fence and its server workarounds stay around the grant. The publisher's tests also run the real grant through the fence, for WAC and ACP.src/testing/, and tests mock the app's modules, not the libraries'.npm run check:boundarieslets onlyclient/and shacl's module import an@inrupt/package outside tests. Onlyclient/access.tsmay import the access-control functions.vendor-code.mdnow states what a swap really costs. A replacement provides theclient/modules with the current library's type shapes, because callers readsubject.urland use the builder chain, and it matches the behavior their tests pin.boundaries.md,architecture.md,testing.md,authentication.md, the package's README and a few links follow.Out of scope, as the issue says: a second implementation, a build-time switch and choosing a replacement library.
Closes: #323
Checklist
Refs:, notCloses:. Leave this unchecked otherwise.npm run checkpasses (lint, Markdown and spelling included),npm run lint:reusetoo if a file was added, andnpm run crosschecktoo ifns/ordecks/changed. (crosscheckn/a:ns/anddecks/are unchanged)docs/is listed in docs/docs.ttl. (no new page)packages/ui/src/i18n/). (n/a: no message changed)ns/was agreed in an issue first; no publishedns/shapes/*/vN.ttlordecks/*/vN.ttlwas edited; generated files were regenerated, not edited. (n/a:ns/anddecks/are unchanged)!and aBREAKING CHANGE:footer).npm run check:breakingsees a line ofapi/that goes, but not a feature removed, nor a reader that reads less than before.npm run lint:reuse), or a new deck its line inREUSE.toml; third-party content is credited.npm run dev) and tried the change, and considered the user journeys.type(scope): subject,!and aBREAKING CHANGE:footer if it breaks something), with an imperative subject and a body that explains why (CONTRIBUTING.md).