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

@peripheries/renderer

v0.25.0

Published

The one renderer for pages of federated component bundles: a framework-free core, a React renderer, the runtime a page boots with, and golden pages for testing.

Readme

@peripheries/renderer

The one renderer for pages built from federated component bundles. Hosts, component repos' harnesses and previews all render through it, so a page looks the same in each.

| Entry | Holds | |---|---| | @peripheries/renderer/core | Framework-free: cells and thin-ref hydration, references and conditions, the data context, walks and state names (layerName, the layer catalog LAYER_ROLES), cell props, layout, document structure, bundle refs and resolution, the shared store, loadPageData(page, source), validation, DIAGNOSTICS and PAGE_RULES | | @peripheries/renderer/catalog | checkCatalog: what a component repo runs on the knowledge it emits. The only entry with a JSON Schema validator (ajv) | | @peripheries/renderer/react | PageRenderer, CellView, StructureView, the cell states, useContainerSize, useFormsRuntime and its FormSlotProps and FormAnswer | | @peripheries/renderer/runtime | What a page boots with: the import map and boot script, the shims, installRuntime, the store bundles import | | @peripheries/renderer/theme | The org theme pipeline: parseTheme (version 1 or 2 in, version 2 out, with diagnostics), themeTokens (semantic overrides derived in OKLCH), themeCss (the theme layer), checkTheme (WCAG contrast) | | @peripheries/renderer/print | The print pipeline, framework-free: collectBlocks (the page map), paginate, buildPrintLayout (the packer's decision as CSS), buildPageStylesheet (@page geometry and margin boxes), running headers, @printPolicy parsing, print lint, resolveDocumentProfile, region bindings, map staleness, and the layout.structure[].print writer | | @peripheries/renderer/forms | The forms runtime, framework-free: the steps overlay and its model, conditions, modes, navigation, progress, deep links, and the persistence port (FormsPort), and the form-section contract's type (FormSection, FormContentBlock); the React hook is useFormsRuntime in /react | | @peripheries/renderer/testing | The golden pages, synthetic fixtures, and sources that serve them | | @peripheries/renderer/styles.css | Page and cell containers, the responsive buckets, placeholders and notices, in the renderer layer |

A consumer supplies:

  • a GraphSource for the page's reads (the host's runs GraphQL under the viewer's JWT; fixtures in /testing);
  • its bundles, as PageBundles (pageBundles(scope, rows, loaded, orgId));
  • a RendererEnvironment: core components, navigate, the store, and where diagnostics go.
const runtime = await loadPageData(page, source, { params, baseProps, viewer, context })
<PageRenderer runtime={runtime} bundles={bundles} params={params} baseProps={baseProps} context={context} viewer={viewer} environment={environment} />

What a consumer can hook in

RendererEnvironment takes, besides core, navigate and store:

  • actionsFor(cell): the actions a cell gets; projectCellActions(cell.actions, pageActions) applies the lookup rule (binding key, then registry key, then the cell's own name).
  • runAction: runs a cell's declared action (the host's write path, or a harness's mock); without it a declared action reports FAILED.
  • loadPage: loads a paged label's next page.
  • onPropSources(cell, sources): each rendered cell's props, leaf by leaf, with where each value came from (for an edit layer, with editTarget).
  • retry(): reads the page again, for a failed cell's "Try again".
  • cellProps(cell): props from the page's runtime, merged last; on a form page, useFormsRuntime's cellProps gives each cell form, answers and answer.
  • onCellError(cellId, error): a component threw. The cell shows an error notice either way.
  • onUnresolvedReference(cellId, ref): a prop's @ref resolved to nothing (not reported while its data is loading).
  • frameProps(cell, contract): extra attributes and style on a cell's frame, for what the host doesn't apply yet (a print policy, say).
  • onDiagnostic(d): a page configuration problem.
  • showHiddenCells: where a page is being built or reviewed (an editor, a harness, a preview), a cell its hide_when_empty leaves out shows as a small "hidden (empty)" placeholder instead of nothing.
  • showProblemCodes: a cell's problem notice adds its diagnostic code, for staff and editors; without it a viewer reads only the plain wording.

A consumer with resolved values instead of a graph (the component repos' harness) builds its runtime with pageRuntimeFromValues(page, { values, traversal }), and wraps the page in whatever its head would give it (the org's theme is CSS, not a provider). A declared store default seeds its channel, so every cell sharing the channel reads it.

The names a bundle may rely on

What the renderer writes, for a bundle's CSS and checks. These are the only names: 0.8.0 dropped the old client-prefixed twins and container names.

| Where | Name | |---|---| | The page container | data-page-container, data-page-grid, data-page-id, data-profile; container page | | Each cell | .cell (container cell), data-page-cell, data-cell-id, data-component | | A cell's buckets | data-hide-when, data-show-when, data-stack-when, data-truncate-when | | A structure section | data-section, data-section-level, data-section-kind, data-derived-ordinal | | A missing component's notice | data-missing-component | | A cell hide_when_empty left out, shown by showHiddenCells | .cell-hidden, data-cell-state="hidden-empty" |

The store specifier and runtime global are in @peripheries/runtime's README. The theme reaches a page as CSS, in the theme layer; the renderer ignores a bundle's ThemeProvider export.

Versions

  • 0.25.0 (2026-10-24): hide_when_empty, a cell-level list of prop names.

    • A cell with hide_when_empty: ["title", "items"] renders nothing, and leaves no gap, when every prop it names is empty once bound: null, undefined, text that is blank once trimmed, [] or {}. 0 and false are content. A slot or children filled by child cells is content. Print follows the screen. The check runs before the bundles load, so a server render and the browser agree.
    • A refused, failed or unresolved read is not empty. A named prop that reads a label whose read was refused or failed keeps the cell's error notice. One whose query never ran because its own reference resolved to nothing (QUERY_UNRESOLVED) shows the same notice instead of hiding (hideWhenEmptyProblem). A label still loading isn't decided yet (the cell renders).
    • RendererEnvironment.showProblemCodes: a problem notice adds its diagnostic code (data-cell-code, .cell-notice__code) for staff and editors; a client reads only "This part of the page couldn't load." The host sets it in the platform environment and in a proposal preview.
    • RendererEnvironment.showHiddenCells: an editor, a harness or a preview shows a hidden cell as a small "hidden (empty)" placeholder (CellHidden, .cell-hidden).
    • Validation: validatePage reports PROP_UNKNOWN (warning) at content.cells[i].hide_when_empty[j] for a name the component's props_schema doesn't declare, and HIDE_WHEN_EMPTY_INVALID (error) for anything but a non-empty list of names.
    • A ref cell's node owns hide_when_empty (CONTENT_FIELDS, via RENDER_FIELDS). /core exports hiddenWhenEmpty, hideWhenEmptyProblem and isEmptyPropValue. PAGE_RULES names the key.
    • The renderer's PageCell type adds hide_when_empty?: string[]; @peripheries/runtime's PageCell gains it at its next release.
  • 0.24.0 (2026-10-20): one renderer batch (CX).

    • layerName(system) and LAYER_ROLES in /core: a system's state name (its layer's role, else a registered layer's own type, question, answer, …) from the layer catalog, which is generated from public.system_layer (npm run gen:layers; CI checks it is current). A host and BEA's preview resolve a page's state names alike. An unregistered type has no name.
    • FormSection and FormContentBlock in /forms: a form section cell's props.section, contract v1 (contracts/form-section.schema.json): the section node's id, its authored sectionId, title, description?, sectionType?, its questions ({ key, label, required, definition }, the definition the form-definition contract's question or null) and its content blocks (the form-definition contract's content block). Static: visibility, answers and totals stay runtime (forms.*, FormAnswer, unchanged). v1 is the target shape: the component engine doesn't emit props.section yet; the cutover is in the stack's contracts/README.md.
    • FormQuestionDefinition's options, validation, description and helpText may be null, as the form-definition contract's question has them.
  • 0.23.1 (2026-10-15): the published files carry no comments and no source maps (declarations included); this README is the documentation. No API change.

  • 0.23.0 (2026-10-15): the versions and forms release, in one minor.

    • Main only: prepareDocument reads main systems and placements only, adding version: { is: NULL } to systemCollection, graph_nodeCollection and graph_edgeCollection at any depth. New diagnostics: SYSTEM_NOT_MAIN (a warning: a page names a system that isn't main, read as absent; GraphSource.mainSystems says which are), DESCRIPTOR_VERSION_FILTER (a document filters on version itself) and DESCRIPTOR_STATE_UNSCOPED (a document reads states by node or unfiltered, so a branch's would be read too); both are refused before a document is sent.
    • The version context: PageScope.version (VersionContext { versionId, versionKind }, from @peripheries/runtime) reaches every GraphSource read as an optional trailing ReadContext, and @context.version / @context.versionKind name it to a query. At main the reads are 0.22's. The runtime freezes the read-side types (ResolvedVersionContext, StateAsOf, NodeStateView, Via, ReleaseProblem, RecordedEmbed).
    • Edits at a version: a release or a channel refuses an edit before it is sent (VERSION_IS_RELEASE); a branch's edit carries p_version_id.
    • Embeds and print at a version: PageScope.recordedEmbeds (a release's metadata.embeds) makes an embed render the release its host recorded, else the viewer's context; PageRuntime.embedVersion says which (recorded | context), and PageRuntime.readVersionIds the versions the states were read from, for print's and an embed's "Release N". A state row may carry versionId, unversioned and outOfScope.
    • Render-time functions: RENDER_FUNCTIONS adds the version reads (node_states_as_of, node_state_view, version_context_for, version_scope_members, versions_for, version_diff, auto_release_status), each with the tables it reads for invalidation.
    • Declared ahead: form_answers(p_structure_nodes: [UUID!]!) is a render function now, listed in RENDER_FUNCTIONS_AHEAD until the database has it, so descriptors can read a workbook's answers without a renderer release. Its JSON is a leaf: result.path reads into it and nothing in it is flattened. An alias on a render function's JSON root is refused (DESCRIPTOR_ALIAS_JSON_ROOT), since its JSON is decoded by field name.
    • Systems from the graph: GraphSource.graphSystems(processId) (the main, keyless systems on the process's graphs, with each graph's type and layer's role) is how resolveSystems finds a page's systems; processSystems is optional and read only without it, and metadata.pageProcess is no longer read. A state name is <tier>_<role> for any registered layer's role (eng_answer), not only the four; whether the page's process has it is STATE_UNKNOWN_SYSTEM at load.
    • Releases: validateManifest(pages) returns ReleaseProblem[], advisory before a release; manifestPageFrom(graphNodeId, data) reads a page artifact's state data as a manifest page. The runtime adds PageView and SubmissionData.
    • The forms runtime (@peripheries/renderer/forms): the steps overlay, modes, navigation, saving, progress and deep links behind FormsPort { save, submit, visit, reopen } (and, optional, upload and openFile for file answers), with useFormsRuntime and FormSlotProps in /react. Refusals are codes, never throws (FormsRefusal: CONFLICT, ANSWER_*, FORM_INCOMPLETE, FORM_NOT_OPEN, FORM_COMPLETED, FORM_STEP_UNKNOWN, NO_CAPABILITY, NETWORK). reopen is the "Edit answers" slot on a submitted form. A phone opens in question mode; the slot props carry the question shown (question, its place and the next), goToQuestion, progress.total beside the required count, a step's authored label, and stay one object while what they show is unchanged. A step with only optional questions is complete once seen. The slot props name every chapter, the submission's status, readOnly, and the newest staff review (its note, status, time and writer). The runtime adds FileAnswer and AnswerValue; SubmissionData carries reopens and each note's status.
    • Form cells: on a form page each cell gets, through RendererEnvironment.cellProps (useFormsRuntime's cellProps), form (the slot props), answers (its questions the conditions show, by key) and, with one question, answer: a FormAnswer { key, label, question, value, change, readOnly, required, missing, refusal, inputId, upload?, openFile? }, in the slot props' words. question is the question's definition (FormQuestion.definition); inputId is where goToQuestion moves focus. In question mode FormSlotProps.question carries the same answer.
    • For consumers (nothing is required to keep 0.22's behaviour):
      • a GraphSource adds graphSystems(processId) to find systems by graph (else processSystems is read), mainSystems(ids) to read main only (else every system is read), and takes the trailing ReadContext to read at a version (else it reads main);
      • a host passes PageScope.version (and recordedEmbeds at a release) to read at a version, and refuses nothing else itself;
      • a form host implements FormsPort (upload and openFile only if it takes files) and passes cellProps from useFormsRuntime as RendererEnvironment.cellProps; review.by is a display name or null, never an email or a user id;
      • a component reads form, answers and answer from its props on a form page, and puts answer.inputId on its control.
      • page ids (an embed's page_id, PageScope's page) become anchor ids; the renderer treats them as opaque, so nothing changes.
  • 0.10.0: actions run, documents edit, pages embed.

    • Declared actions run through the environment's runAction; the runtime's ACTION_FIELDS takes subjects and audience on create, edit and append, and create needs edge_type only under a parent.
    • Cells get renderComponent(key, props): another component by registry key, resolved in the cell's scope; an unknown key reports COMPONENT_UNKNOWN (through onDiagnostic, now passed to cells) and returns null.
    • Embed cells: PageCell.embed ({ page_id } or { page_type, project_id?, process_id? }) renders another page, loaded by loadPageData through the optional GraphSource.page(selector), one level deep (PageRuntime.embeds; EMBED_INVALID, EMBED_NOT_FOUND, EMBED_NESTED). embed is in HYDRATED_CELL_FIELDS.
    • A nullable query variable whose whole reference reads @params, @context or @viewer and resolves to nothing is sent as null.
    • PAGE_RULES is shorter (3780 characters) and describes embed cells.
  • 0.9.0: queries, realtime's inputs, the host contract retired, and the renderer half of actions.

    • A label can be { descriptor: QueryDescriptor }, or a node binding to a live query_data node, whose descriptor runs instead of returning its state. GraphSource gains query(descriptor, variables), which returns the shaped result; the golden source answers from queries, by operation name.
    • /core exports validateDescriptor, prepareDocument (the render-time rule, with RENDER_FUNCTIONS), resolveDescriptorVariables (the props resolver, for variables), selectionShape and selectionIssues.
    • loadPageData(page, source, { params, baseProps, viewer, context }) runs queries in rounds: those whose variables read no label with the states and walks, then those that read a label, and live nodes' queries. A failed or unresolved query gives its own label an error (QUERY_FAILED, QUERY_UNRESOLVED); the rest of the page renders.
    • @viewer.* and @context.* resolve in props (PageRenderer takes viewer). viewer, context, input and scope are reserved labels (LABEL_RESERVED).
    • New diagnostics: DESCRIPTOR_*, REF_NOT_SELECTED, QUERY_READS_QUERY, QUERY_UNRESOLVED, QUERY_FAILED, LABEL_RESERVED.
    • graphql is a dependency.
    • PageRuntime.graphNodeIds: every graph node a page reads, whose graphs are its Realtime topics.
    • { registry: "x.y" } query bindings are retired: an error (UNSUPPORTED_REGISTRY_BINDING) that aliases nothing.
    • Actions: a cell declares { action, … } with @input for what the component passes; RendererEnvironment.runAction runs it, a call never throws, and WriteResult.code says why one failed (ACTION_INVALID; the registry form is ACTION_REGISTRY, legacy).
  • 0.8.1: BaseProps carries the org's locale and currency (from its org template's metadata), which @stack/charts formats with.

  • 0.8.0: the old names go. The renderer writes only the attributes and container names above, the runtime installs only __STACK_RUNTIME__ and maps only @stack/store, and the store has no second hook name. A bundle's ThemeProvider is no longer lifted or wrapped around the page (LoadedBundle.ThemeProvider and nearestThemeProvider are gone). @peripheries/runtime 0.8.0 drops LEGACY_RUNTIME_GLOBAL and SPECIFIER_ALIASES.

  • 0.7.3: the page container also carries data-page-grid, the canonical name of the old page-grid attribute, so every attribute the renderer writes has its canonical name (above) before the old ones go.

  • 0.7.2: the package ships its LICENSE: proprietary to Peripheries Studio Inc., all rights reserved.

  • 0.7.1: bundleScope puts the advisor org's main after the shared lineages, so an advisor org's older copy of a core component never shadows core on a client org's page.

  • 0.7.0: shared lineages and the theme. bundleScope appends the platform's shared lineages, and the loader imports them by specifier; @core/ resolves from stack/core (host: is retired); importMapFor and importMapJson put shared lineages in the import map. The new /theme entry derives an org's theme into semantic tokens and checks its contrast. styles.css is in the renderer layer and reads the theme's body font and ink. A document page's container carries data-profile from layout.profile. @peripheries/runtime adds SEMANTIC_TOKENS, LAYER_ORDER, the version 2 theme type, and css on a shared import.

  • 0.6.2: a malformed projection is reported once. validatePageProps judges only a well-formed projection's map; before, a malformed one's map was judged too, so one mistake gave EACH_INVALID and two or three map findings. eachFormProblems (from /core) is the shared judgement.

  • 0.6.1: validatePageProps judges an @each projection by its map: its keys against the prop's items schema (a required key missing is PROP_INVALID, an undeclared one PROP_UNKNOWN), and its literal values against theirs, nested projections too. A malformed projection is no longer also reported as PROP_INVALID; validatePage's EACH_INVALID says it once.

  • 0.6.0: @each projections. A prop value { "@each": "@label.path", "as": "item", "map": … } resolves to the list with map resolved once per element (@item.… reads it), so a list in its own shape fills a prop that wants another without the data or the component changing. EACH_INVALID checks the form; PAGE_RULES teaches it; referencedLabels doesn't count the element label as the page's.

  • 0.5.0: one judgement of a page at patch and publish. CELL_UNKNOWN_COMPONENT carries suggestions, the nearest names in scope (nearestNames, also exported). PROP_UNKNOWN warns of a prop the schema doesn't declare. /catalog adds validatePageProps, which reports PROP_INVALID for a literal value that fails its prop's schema; run it beside validatePage. checkCatalog's pack estimate counts only the cards of linked components, as knowledge_pack now returns them.

  • 0.4.1: a card no longer carries a snippet; a component's first snippet is its example (budget 1,000), shown beside the card in a pattern's pack. The card budget drops to 400.

  • 0.4.0: DIAGNOSTICS and PAGE_RULES; /catalog with checkCatalog; the knowledge types in @peripheries/runtime.

  • 0.3.0: @peripheries/runtime/rank, sibling order on graph_node.rank (compareRank, sortByRank, rankBetween and the rest), so no one vendors it; the WALK_NODE_TYPES_EMPTY warning, because nodeTypes: [] filters nothing (it doesn't mean "enter nothing").

  • 0.2.0: the hooks above, pageRuntimeFromValues, action projection, data-missing-component on a missing component's notice, the theme passed to ThemeProvider, store defaults seeded.

  • 0.1.0: the first release.

Validation

validatePage(page, catalog?) and validateCell(cell, catalog?) return diagnostics, { severity, code, message, path?, cellId?, label? }, with severity one of error, warning or legacy (a form still read, on its way out). loadPageData runs validatePage and adds what loading finds. Given a catalog ({ components: { [name]: { props_schema } } }) it also checks that components exist and required props are given (schema.required only). For a node-managed cell those are warnings, since the page's copy is only a fallback.

Every code is in DIAGNOSTICS (from /core), with what it means and how to fix it; Diagnostic.code is typed from that table. Tools show the fix with the diagnostic, so a writer learns a rule when they break it.

PAGE_RULES (from /core) is the host's rules for composing a page, the short text a writer reads first. It is built from the constants the renderer and validator use, so it changes when they do.

core imports no React at runtime and never reaches the network, and the package never imports the host (npm run lint:deps). Tests sit next to the code; the golden pages run over fixture sources in react/golden-pages.test.tsx.

Publishing: npm run publish:packages (and --dry-run) builds and publishes every package whose version isn't on npm yet, built output only; .github/workflows/publish-packages.yml stages each new version from develop, and a maintainer approves it on npmjs.com. Bump the version here to release.