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

@openref/vue

v0.1.0

Published

Headless Vue layer for theme authors: composables, slots and the frozen props contract

Readme

@openref/vue

The headless layer an OPENREF theme is written against. It carries state, composables, the page model a rendered page travels as, and the registry of positions a theme fills. It carries no markup and no styles at all, which is what makes a theme possible without reworking the renderer: everything a theme needs is reachable through a composable or arrives as a prop, so a theme never reaches into a store.

This is the whole surface a theme author needs. @openref/theme-telltale is written against it and nothing else of this project, which is what the second theme exists to prove.

Install

npm install @openref/vue

Vue 3 arrives with it as a dependency rather than a peer. There is nothing else to install: the IR types the slot props are declared in are re-exported here, so typing the value a component is handed does not cost a second package.

Use

import { defineTheme } from '@openref/vue';
import ProvenanceTag from './components/ProvenanceTag';

export default defineTheme({
  name: 'aurora',
  layout: () => import('./Layout'),
  components: { ProvenanceTag },
  assets: { css: ['@openref/theme-aurora/theme.css'] },
});

A theme is data and runs nothing at import time. Each component is handed the props its position declares, and reads anything else through the composables:

import { h, type VNode } from 'vue';
import type { IRConfidence } from '@openref/vue';

export default function ProvenanceTag(props: {
  readonly confidence: IRConfidence;
  readonly collector: string;
}): VNode {
  const attrs = { class: `oref-prov-${props.confidence}`, title: props.collector };

  return h('abbr', attrs, props.confidence);
}

defineTheme is an identity function with a type on it. ThemeDefinition is five members, one of them required: name, and then layout, components, tokens and assets. layout and components.AppShell are one position under two names, and a theme writing both is refused.

The composables

Every one of them reads the provided document state and throws a ThemeContractError when nothing provided it, which is a wiring mistake rather than a deployment choice.

| Composable | Returns | | ------------------------- | ------------------------------------------------------------------------------------------------------- | | useDocument() | The document, its info, hash, kind, navigation, servers, security, node ids, the active id and select | | useNode(id?) | One node materialized lazily, and whether it exists; no id follows the selection | | useOperation(id?) | Parameters, request body, responses, security and deprecated; undefined for a channel | | useChannel(id?) | Operations and messages; undefined for an HTTP operation | | useRuntime(id?) | Guards, scopes, roles, rate limit, streaming, errors, source and drift, with provenance intact | | useHealth() | The Documentation Health report, its score and mark, suppression, checks and counts | | useSchemaView() | The schema viewer state: the view, the expanded paths and the expand, collapse and toggle actions | | useSearch(limit?) | The query, the hits and available; limit defaults to DEFAULT_HIT_LIMIT, 20 | | useSocket(id?) | The event client: status, log, message, and connect, send, close | | useTheme() | The resolved theme: name, tokens as a map, assets, slot(name) and overridden | | useSlot(name, fallback) | The theme's component for a position, or the fallback; reads the slot registry rather than the state |

import { useHealth, useOperation, useRuntime } from '@openref/vue';

const { operation, parameters } = useOperation();
const { guards, scopes, drift } = useRuntime();
const { score, scoreMark } = useHealth();

useHealth().available distinguishes "nothing was measured" from a score of zero, and useSearch().available is false when no search port was supplied. useTheme().tokens is a map and never a style string, because a nonce cannot authorize an inline style attribute.

How a theme reaches state

There is no store and no module level singleton. Two references mounted on one page, which federation makes ordinary, would share a singleton and would not share a provided value, so it is Vue provide/inject with Symbol keys throughout.

createDocState({ document, theme?, search?, activeNodeId?, view? }) builds the state and provideDocState(state) puts it in scope; that call also provides the slot registry. Three more keys exist for the things a page may or may not have: RUNNER_KEY with provideRunner and useRunnerPort, SOCKET_KEY with provideSocket and useSocketPort, and SLOT_REGISTRY_KEY with provideSlots and useSlotRegistry. Those three answer undefined when nothing provided them, because a missing runner is a deployment choice, while a missing document state is a mistake.

The surface is frozen, and it is written down

PUBLIC-API.md in this package is the export surface rather than a summary of it: test/integration/public-surface.spec.ts reads the built type declarations and fails both ways, when this package exports a name that file does not list and when that file lists a name the package does not export. It checks dist/, not src/, because only the artefact reaches you.

Three contracts are pinned by type level tests that fail to compile rather than at runtime: the slot props, the theme definition, and, over in @openref/nest, the collector interface. Adding a name is a minor version; removing or retyping one is a major version, and widening an exported union counts as retyping it, because a total Record over that union is a sanctioned way to write a theme.

The slot registry

SLOT_NAMES is the registry, 21 positions in this order:

AppShell        NavTree       CommandPalette  DocumentOverview  SchemaPage
OperationHeader RuntimePanel  ProvenanceTag   DriftCard         ParamTable
ResponseList    CodeSample    SchemaTree      ShapeForm         AuthPanel
ServerSelect    SendButton    ResponseView    StreamLog         HealthScore
StateNotice

SlotPropsMap declares what each one is handed, SlotName is its keyof, and SlotProps<'ParamTable'> is one position's props. All three live in src/slots/domain/slot-props.types.ts, and all three are frozen public API.

SERVER_RESOLVED_SLOTS is the subset whose override resolves on the server only: DocumentOverview, OperationHeader, RuntimePanel, ProvenanceTag, DriftCard, ParamTable, ResponseList and HealthScore. The browser adopts that markup without hydrating it, so a handler attached in one of those positions is never attached at all, and a root element other than the one SERVER_RESOLVED_ROOTS names loses its markup on hydration. @openref/theme-kit finds both before a reader does.

Two entry points, split by gesture

| Specifier | What is on it | | --------------------- | ------------------------------------------------------------- | | @openref/vue | Everything a page needs before a reader touches anything | | @openref/vue/runner | The try-it surface, loaded only when a reader opens a console |

The split is a measurement rather than a taxonomy. A barrel that the first paint imports statically carries every module it re-exports, whichever side names the symbol, so useRunner on the main barrel cost every reader of every page close to a kilobyte for a console most never open. A theme that overrides AuthPanel, SendButton, ResponseView, ShapeForm or StreamLog imports from @openref/vue/runner and pays the same way the shipped console does.

useRunner(id?), useRunnerFor(operation) and prettyResponseBody are what is over there. useRunnerFor is the one the renderer itself calls, because a rendered page carries the operation projection rather than the IR. The port stays on the main barrel: RUNNER_KEY, provideRunner and useRunnerPort are how a runner is handed to a page at all, so the first paint reaches for them by construction, and they are 116 bytes.

What a theme still has to style by hand

An L2 theme replaces every position and still receives markup drawn outside all of them, under the reference's own oref- class names. Those names are not frozen and a theme that ignores them ships unstyled regions with nothing going red on its own side. The list is pinned by a test in @openref/theme-telltale, which fails when a name arrives or leaves, and THEME-BOUNDARY.md in that package is the honest account of what the contract does not yet carry. Read it before writing a theme.