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

@sstim/core

v0.2.0

Published

Validate and write sensory-stimulation descriptions against a published SSTIM profile

Readme

@sstim/core

Validate sensory-stimulation descriptions against a published SSTIM profile.

SSTIM is an open standard for describing what a stimulus actually is: signals, channels, renderings, techniques, protocols, sessions, exposure and evidence. This package is a client for it. The ontology, its SHACL shapes and its profiles are published independently, and this code only reads them.

npm install @sstim/core
npx sstim validate my-stimulus.ttl --profile core
my-stimulus.ttl
  profile core at https://w3id.org/sstim/0.18.0
  ok     SHACL conformance
  ok     every SSTIM term is defined in the core closure
  ok     nothing minted under https://w3id.org/sstim

Three checks, not one

SHACL conformance on its own does not tell you your file is right, for a reason that catches everyone once: SHACL is silent about a term it has never heard of. Misspell a property, or reach for one from a module your profile does not contain, and the shapes have nothing to say. Your file validates. A consumer loading that profile then meets a predicate it cannot interpret, and your mistake surfaces in their pipeline instead of yours.

  1. Conformance against the shapes of the profile you named, not the largest one available.
  2. Containment: every SSTIM term you used is defined inside that profile's closure.
  3. Namespace discipline: you have not minted anything under https://w3id.org/sstim. Reuse their terms; mint your records in your own namespace.

One limitation, reported rather than hidden

rdf-validate-shacl has no SPARQL constraint component, so sh:sparql constraints cannot be evaluated in JavaScript.

  • Kernel, Core and Core Plus shape graphs contain none. Results are exactly what pySHACL would produce.
  • Full contains many. This client removes them, counts them, and marks the result PARTIAL with the number, so a Full result is never presented as equivalent:
  PARTIAL  69 sh:sparql constraint(s) were not evaluated: this runtime has no
           SPARQL constraint component.
           For a complete Full-profile check use the Python client
           (pip install sstim) or pySHACL.

report.partial and report.unevaluated expose the same fact programmatically.

Profiles

Take the smallest one that carries what you actually assert. Moving up later is additive, so starting small costs nothing.

| Profile | What it adds | |---|---| | kernel | Two process anchors. A discovery entry point, with no shapes | | core | Engine-independent stimulus description: signals, channels, renderings | | core-plus | Reusable descriptors and calibrated quantities, including frequency extents | | full | Techniques, protocols, configurations, sessions, evidence, exposure, vocabulary |

npx sstim profiles                   # what a release offers
npx sstim modules --profile full     # what that profile pulls in, and from where

Versions are resolved, not guessed

With no --version, the newest frozen release is resolved by reading owl:versionIRI from the stable IRI. This matters more than it looks: the manifest served at https://w3id.org/sstim/manifest is the development line, so code that fetches it and believes it pinned something has pinned nothing.

npx sstim validate my.ttl --profile core --version 0.17.0

Every module a manifest lists carries a sha256, and the served bytes are checked against it before anything is parsed. A truncated download or a substituted file stops the run rather than quietly validating your data against a graph that is not SSTIM. In Node, verified modules are cached by checksum, and a frozen release's manifest by version, since neither can change. So a run pinned to a version works with no network once it has run online, and a cache entry can never be stale. An unpinned run still asks the network which release is newest.

As a library

import { validate, resolveProfile } from '@sstim/core'

const report = await validate('my-stimulus.ttl', { profile: 'core' })
if (!report.ok) console.log(String(report))

// Resolve once, validate many
const closure = await resolveProfile('full', { version: '0.17.0' })
closure.versionIri                       // 'https://w3id.org/sstim/0.17.0'
closure.semanticModules.map(m => m.id)
const reports = await Promise.all(files.map(f => validate(f, { closure })))

It runs in the browser too: pass Turtle text rather than a path, and caching is skipped where there is no filesystem. manifest resolves from a local checkout or a frozen release directory instead, which needs no network at all.

Writing a session

Session records one stimulation block from any tool that has a clock. It computes offsets from your clock's readings, sums delivered time from the playback events, and refuses at the call anything the Full profile would reject, with the reason. That matters more here than in Python: this runtime cannot evaluate SHACL-SPARQL, and every such constraint that could apply to a session record is enforced where the value is written.

import { Session } from '@sstim/core/session'   // browser-safe, depends only on n3

const session = new Session('https://example.org/lab/run-001/', {
  label: '40 Hz auditory steady-state block', duration: 60,
  masterVolume: 0.5, timing: 'audio-hardware', clock: ctx.currentTime
})
const tone = session.signal({ hz: 40, shape: 'sine' })
session.channel('Tone, headphones', {
  modality: 'auditory', medium: 'air-conducted-sound', placement: 'ears',
  signal: tone, parameter: 'amplitude', mechanism: 'amplitude-modulation', carrierHz: 1000
})
session.event('playback-start', { at: ctx.currentTime })
session.close({ at: ctx.currentTime, completed: true })
const turtle = session.toTurtle()

In Node, await session.write('run-001.ttl') validates first and writes nothing if it fails. The Python client has the same builder and emits the same triples, which a shared golden file in the test suites proves. Worked examples for PsychoPy, jsPsych and Lab Streaming Layer are in examples/tools.

What this does not do

It does not tell you a stimulation is safe, effective, or ethically approved. Conformance means a file is well formed against the model. Those are different reviews, and SSTIM does not perform them.

More

Apache-2.0. SSTIM itself is CC BY 4.0.