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

@learncard/render-method-plugin

v3.0.6

Published

LearnCard plugin for attaching W3C renderMethod to Verifiable Credentials

Downloads

1,069

Readme

@learncard/render-method-plugin

Attach W3C renderMethod entries to Verifiable Credentials using SVG Mustache templates.

The plugin is stateless and has no required dependent methods or control planes.

Install

This plugin is part of the LearnCard monorepo and is built with the workspace. It is registered automatically as part of the default LearnCard wallet stack (see packages/learn-card-base/src/helpers/walletHelpers.ts).

To add it manually to a custom LearnCard instance:

import { getRenderMethodPlugin } from '@learncard/render-method-plugin';

const lc = await baseLc.addPlugin(getRenderMethodPlugin(baseLc));

API

Write side

| Method | Signature | Description | |---|---|---| | attachRenderMethod | (vc, config?) => UnsignedVC | Attaches a TemplateRenderMethod to the VC and injects the JSON-LD context. Opt-in: returns the VC unchanged when config is omitted. Merges with existing renderMethod entries. | | buildTemplateRenderMethod | (config) => TemplateRenderMethod | Builds a TemplateRenderMethod descriptor without mutating a VC. |

Read side

Layered API — pick the level that fits your need:

| Method | Signature | When to use | |---|---|---| | findTemplateRenderMethod | (vc, suite \| suites[]) => TemplateRenderMethod \| null | Most common. Filter by renderSuite string. Pass an array for capability negotiation. | | findTemplateRenderMethods | (vc, suite \| suites[]) => TemplateRenderMethod[] | All matches of a suite (or any of several). | | getSvgMustacheRenderMethod | (vc) => TemplateRenderMethod \| null | Backward-compatible alias for findTemplateRenderMethod(vc, 'svg-mustache'). | | findRenderMethod | (vc, predicate) => T \| null | Escape hatch: arbitrary predicate for non-template render methods. | | findRenderMethods | (vc, predicate) => T[] | Same, all matches. | | getRenderMethods | (vc) => RenderMethod[] | Raw access. Unwraps CertifiedBoostCredential, normalizes object↔array. No validation. | | buildRenderData | (vc, renderProperty?) => Record<string, unknown> | Portable Mustache context (adds vc / credential / credentialSubjects aliases). Optional RFC 6901 overlay. |

Type guards (composable selection)

| Guard | Narrows to | |---|---| | isTemplateRenderMethod(rm) | TemplateRenderMethod (Zod-validated shape) | | isSvgMustacheRenderMethod(rm) | TemplateRenderMethod with renderSuite === 'svg-mustache' |

Constants

  • DEFAULT_TEMPLATE_ID — URL of the default hosted LearnCard template. Pass as templateId to opt in.
  • RENDER_METHOD_CONTEXT — JSON-LD context URL injected by attachRenderMethod. See the draft context warning.

Throws

  • attachRenderMethod / buildTemplateRenderMethod throw if:
    • templateId is provided but is not an http:// or https:// URL.
    • templateValue is provided but is empty or whitespace-only.
    • buildTemplateRenderMethod is called without either field set.

AttachRenderMethodConfig

templateId and templateValue are mutually exclusive — provide one or the other. The discriminated union enforces this at compile time.

| Field | Type | Description | |-------|------|-------------| | templateId | string | HTTPS URL of a hosted SVG Mustache template. Only http:// and https:// schemes are accepted. | | templateValue | string | Inline SVG Mustache content (embedded as a URL-encoded data:image/svg+xml, URI). Must be non-empty. | | renderProperty | string[]? | JSON Pointer paths (RFC 6901) scoping which VC fields are exposed to the template. |

Opt-in semantics

attachRenderMethod is opt-in. Calling it without a config returns the VC unchanged, which means callers must explicitly choose to attach a render method:

// No-op — returns vc unchanged
lc.invoke.attachRenderMethod(vc);

// Attaches the default template
lc.invoke.attachRenderMethod(vc, { templateId: DEFAULT_TEMPLATE_ID });

// Attaches a custom hosted template
lc.invoke.attachRenderMethod(vc, { templateId: 'https://example.com/badge.svg' });

// Attaches an inline template (URL-encoded into a data: URI)
lc.invoke.attachRenderMethod(vc, { templateValue: '<svg>...</svg>' });

This contract intentionally avoids polluting every issued credential's @context with the draft render-method context URL. See the draft context warning.

Quick start

Use the default LearnCard template

import { DEFAULT_TEMPLATE_ID } from '@learncard/render-method-plugin';

const vcWithRender = lc.invoke.attachRenderMethod(unsignedVc, {
    templateId: DEFAULT_TEMPLATE_ID,
});
const signedVc = await lc.invoke.issueCredential(vcWithRender);

Use a custom hosted template

const vcWithRender = lc.invoke.attachRenderMethod(unsignedVc, {
    templateId: 'https://templates.example.com/badge.svg',
    renderProperty: ['/credentialSubject/name', '/credentialSubject/description'],
});
const signedVc = await lc.invoke.issueCredential(vcWithRender);

Use an inline template

const svgTemplate =
    '<svg xmlns="http://www.w3.org/2000/svg"><text>{{credentialSubject.name}}</text></svg>';

const vcWithRender = lc.invoke.attachRenderMethod(unsignedVc, {
    templateValue: svgTemplate,
});
const signedVc = await lc.invoke.issueCredential(vcWithRender);

Build a render method descriptor independently

const renderMethod = lc.invoke.buildTemplateRenderMethod({
    templateId: 'https://templates.example.com/badge.svg',
});
// renderMethod.type === 'TemplateRenderMethod'
// renderMethod.renderSuite === 'svg-mustache'

Feature-flag interaction

In apps/learn-card-app, the useRenderMethodEnabled hook gates the display path (the RenderMethodDisplay component, the BoostDisplayStyleSelector, the getSvgMustacheRenderMethod lookup). It does not gate the write path — whether to attach a render method to a credential is a per-callsite decision and follows the opt-in semantics above.

In other words: enabling the LaunchDarkly flag turns on rendering for credentials that already have a renderMethod, but it does not retroactively change which credentials carry one.

Draft context warning

RENDER_METHOD_CONTEXT currently points to a community-group draft hosted on Digital Bazaar's GitHub Pages:

https://digitalbazaar.github.io/vc-render-method-context/contexts/v2rc2.jsonld

The v2rc2 suffix means "v2, release candidate 2". This is not a finalized W3C TR. Risks of issuing VCs that reference this URL in their @context:

  • The URL could move, be renamed, or 404 — JSON-LD context resolution would fail at verification time.
  • Term definitions could change between rc2 and a final spec, breaking interpretation.
  • The context is not statically cached by DidKit — verification incurs a network fetch.

Mitigation plan (not yet implemented):

  • Bundle the context locally, mirroring the pattern in packages/learn-card-contexts/.
  • Migrate to the stable W3C TR context URL when the spec finalizes.

Tracking: https://www.w3.org/TR/vc-render-method/

The opt-in semantics above are the primary mitigation for now — credentials that don't need a render method don't get this context URL.

Extending: supporting a new render suite

The read-side API is intentionally generic so new render suites can be added by callers without a plugin release. For TemplateRenderMethod-shaped suites, you don't write any predicate code:

// Filter by a single suite
const found = lc.invoke.findTemplateRenderMethod(vc, 'html-mustache');

// Capability negotiation — first match across the suites your renderer supports
const renderable = lc.invoke.findTemplateRenderMethod(vc, [
    'svg-mustache',
    'html-mustache',
]);

// All matches (e.g., when offering the user a choice)
const allHtml = lc.invoke.findTemplateRenderMethods(vc, 'html-mustache');

For non-template render methods (e.g., a future WebRenderingTemplate2022 with a different shape), use the predicate-based findRenderMethod escape hatch with your own type guard.

The plugin handles selection and data shaping uniformly across suites. The actual rendering (Mustache hydration + sanitization) stays in the app/UI layer because sanitization policy (DOMPurify for SVG, different rules for HTML, etc.) is renderer-specific.

Architectural boundary

  • In the plugin (this package): data domain — attach renderMethod to VCs, find renderMethod entries, shape Mustache contexts. Pure, isomorphic, CLI-safe.
  • In the app: render domain — Mustache hydration into a target format, DOMPurify sanitization, DOM insertion. Browser-specific.

The seam is "data that describes how to render" (plugin) vs. "execute the render and put pixels on screen" (app).

Notes

  • Only svg-mustache is supported as a render suite at this time. The renderer in apps/learn-card-app/src/helpers/renderMethod.helpers.ts performs DOMPurify sanitization on the hydrated SVG before insertion.
  • See src/types.ts for types, src/plugin.ts for the write side, src/read.ts for the read side.
  • See src/test/plugin.test.ts for the full behavior contract.