npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@muze-nl/jsfs-solid

v0.4.4

Published

A filesystem adapter for JSFS that can read/write Solid Storage PODs

Readme

GitHub License GitHub package.json version NPM Version npm bundle size Project stage: Experimental

JSFS-Solid

JSFS-Solid is a small Solid client built around a filesystem-shaped API. It is meant for web applications that want to use a Solid Pod as user-owned storage without forcing the whole application to be written as an RDF database client.

It combines:

  • JSFS for a small filesystem abstraction.
  • Metro for HTTP requests and middleware.
  • Metro-OIDC for Solid/OIDC authorization.
  • Metro-OLDM and OLDM for reading and writing Turtle resources as JavaScript objects.
  • JAQT as the recommended JavaScript-native query layer for OLDM data.

This package is experimental. The overall direction is stable, but the public API still needs tests, clearer package exports, and more Solid interoperability work before it should be treated as production-ready.

Why this exists

Solid gives applications a way to store data in user-controlled Pods. A lot of application data is naturally file-like: notes, contacts, settings, bookmarks, documents, media, lists, and small Turtle resources.

JSFS-Solid starts from that use case:

const client = await solidClient('https://example.pod/profile/card#me', {
  client_info: {
    client_name: 'My App'
  }
})

const storage = client.storage[0]
const entries = await storage.list()
const file = await storage.read('contacts.ttl')

The goal is not to hide Solid or RDF completely. The goal is to make the common path approachable while still leaving the underlying pieces visible and replaceable.

Main design choices

1. Pod storage is presented as a filesystem

Each storage root from the WebID profile becomes a JSFS filesystem. You can list, read, write, check existence, create directories, and remove resources through a small API.

This is a good fit for applications that know which resources they use. It is less suitable for applications that need global RDF graph queries across unknown documents. For those, a SPARQL/RDF query engine such as Comunica may be a better fit.

2. Authentication is public-first

JSFS-Solid does not force a login at startup.

It first tries to read resources normally. The OIDC authorization flow is only used when a protected resource requires it. This is deliberate: Solid data can be public, private, or mixed, and applications should not ask users to log in before they actually need access to protected data.

One consequence is that WebID discovery must work without authentication, or the application must provide enough information explicitly. In the current implementation, the WebID profile is read first and is expected to expose a Solid OIDC issuer.

3. Turtle resources become OLDM object data

Turtle resources are parsed with OLDM. A read result can contain both the original contents and parsed object data:

const file = await storage.read('profile/card')

file.contents // original text, for text resources
file.data     // parsed OLDM graph, when the response is linked data

OLDM intentionally gives RDF data an object-shaped JavaScript interface. That makes small Solid applications easier to write, but it also means developers should understand OLDM's mapping rules for repeated predicates, language tags, datatypes, blank nodes, collections, and source graphs.

4. Querying should stay in JavaScript when possible

JAQT is the preferred query layer for OLDM data. It keeps querying close to normal JavaScript objects instead of requiring SPARQL for simple application-level filtering and projection.

Example:

import { from } from '@muze-nl/jaqt'

const contactsFile = await storage.read('contacts.ttl')

const contacts = from(contactsFile.data.data)
  .where({ a: 'vcard$Individual' })
  .select({
    id: contact => contact.id,
    name: contact => String(contact.vcard$fn ?? '')
  })

JAQT is currently a dependency of this package, but the query story should become more explicit in the API and documentation.

5. The stack is made of replaceable parts

JSFS-Solid is intentionally not one large Solid framework. It composes smaller libraries:

  • Replace JSFS if you do not want a filesystem API.
  • Replace Metro middleware if you need a different HTTP/auth flow.
  • Replace OLDM if you want to work directly with RDF/JS datasets.
  • Use JAQT only where object-level querying helps.

This keeps the architecture inspectable and adaptable, which is important for Muze projects.

Installation

npm install @muze-nl/jsfs-solid

Browser usage

Using the browser bundle from a CDN:

<script src="https://cdn.jsdelivr.net/npm/@muze-nl/jsfs-solid/dist/browser.min.js"></script>
<script>
  async function main() {
    const client = await solidClient('https://example.pod/profile/card#me', {
      client_info: {
        client_name: 'My App'
      }
    })

    const entries = await client.storage[0].list()
    console.log(entries)
  }

  main()
</script>

The browser bundle currently exposes:

solidClient
SolidAdapter

on globalThis.

ESM usage

Using ES modules:

import solidClient from '@muze-nl/jsfs-solid'

const client = await solidClient('https://example.pod/profile/card#me', {
  client_info: {
    client_name: 'My App'
  }
})

Creating a client

const client = await solidClient(webid, options)

The webid argument is the user's Solid WebID.

The options object is passed through to the underlying Metro/OIDC/OLDM stack. At minimum, browser applications should provide client information:

const client = await solidClient('https://example.pod/profile/card#me', {
  client_info: {
    client_name: 'My App'
  }
})

The returned client contains:

  • profile: the WebID profile parsed with OLDM.
  • storage: an array of JSFS filesystems, one for each storage root found in the profile.
  • issuer: the first Solid OIDC issuer found in the profile, or null.
  • inbox: the first inbox found in the profile, or null.
  • id(): returns the ID token, but only after the client has authenticated.
  • HTTP methods from Metro: get, post, put, delete, and patch.

The HTTP methods use the same public-first authorization behavior: they can trigger Solid/OIDC authorization when a protected resource requires it.

Working with storage

The storage entries are JSFS filesystems. The core methods are:

storage.cd(path)
storage.read(path)
storage.write(path, contents, metadata)
storage.exists(path)
storage.remove(path)
storage.list(path)
storage.mkdir(path)
storage.rmdir(path)

Listing a container:

const entries = await storage.list('/')

for (const entry of entries) {
  console.log(entry.path, entry.type)
}

A list entry contains at least:

{
  filename,
  path,
  type // 'file' or 'folder'
}

Reading a resource:

const file = await storage.read('contacts.ttl')

A read result contains:

{
  type,
  name,
  contents,
  data,
  http: {
    headers,
    status,
    url
  }
}

contents contains the raw body for text and JSON resources. data is present when the linked-data middleware could parse the response into OLDM data.

Writing a resource:

await storage.write('notes/hello.txt', 'Hello world', {
  type: 'text/plain'
})

For Turtle resources, write serialized Turtle text or use OLDM's writer first:

const turtle = await file.data.write()
await storage.write('contacts.ttl', turtle, {
  type: 'text/turtle'
})

Tradeoffs

JSFS-Solid is intentionally small and application-oriented. That gives it a clear shape, but it also means it does not try to cover every Solid or RDF use case.

Use JSFS-Solid when:

  • Your application treats a Pod mostly as user-owned storage.
  • You want public resources to work without an upfront login.
  • You want a small filesystem API for containers and resources.
  • You want Turtle data as JavaScript objects through OLDM.
  • You prefer JavaScript-native querying through JAQT for app-level data.

Consider another Solid/RDF stack when:

  • You need mature access-control management such as WAC, ACP, or access grants.
  • You need live notifications or subscriptions.
  • You need SPARQL or federated queries over many documents.
  • You need schema-driven TypeScript types and validation.
  • You need a production-ready SDK with broader Solid coverage today.

Current limitations

  • The package is experimental and does not yet have a real automated test suite.
  • Some internal dependencies are imported through deep package paths; these should become stable package exports.
  • WebID profile discovery currently assumes the profile can be read before authentication, unless enough information is supplied through options.
  • Writes are currently resource-level writes. ETag/conditional write support should be added before relying on this in multi-device or collaborative applications.
  • Access-control management, notifications, type registry support, and richer Solid discovery are not first-class yet.
  • JAQT is present but not yet documented as a first-class query API.
  • OLDM simplifies RDF into an object model; applications that need full RDF semantics should use an RDF-native library directly.

Dependencies

Roadmap

See ROADMAP.md.

Contributing

See CONTRIBUTING.md.

License

This software is licensed under the MIT open source license. See LICENSE.