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

@bernierllc/template-variable-registry

v1.2.0

Published

Template variable registration, validation, and extraction system for email templates with Handlebars and Jinja2 support

Readme

@bernierllc/template-variable-registry

A comprehensive template variable registration, validation, and extraction system for email templates with support for both Handlebars and Jinja2 template engines.

Installation

npm install @bernierllc/template-variable-registry

Features

  • Variable Registration: Define available template variables with type information and validation rules
  • Context Management: Organize variables into contexts with inheritance support
  • Template Validation: Validate templates against registered variables
  • Variable Extraction: Parse templates to extract all variable references
  • Multi-Engine Support: Works with both Handlebars and Jinja2 templates
  • Autocomplete Support: Generate variable suggestions for UI integration
  • Syntax Validation: Detect template syntax errors
  • Type Safety: Full TypeScript support with strict typing

Usage

Basic Variable Registration

import { VariableRegistry } from '@bernierllc/template-variable-registry';

const registry = new VariableRegistry();

// Register a context with variables
registry.registerContext('user_welcome', {
  name: 'user_welcome',
  description: 'Variables for welcome emails',
  variables: {
    user: {
      type: 'object',
      required: true,
      description: 'User information',
      children: {
        name: {
          type: 'string',
          required: true,
          description: 'User full name'
        },
        email: {
          type: 'string',
          required: true,
          description: 'User email address'
        },
        joinDate: {
          type: 'date',
          required: false,
          description: 'Account creation date'
        }
      }
    },
    company: {
      type: 'object',
      required: true,
      children: {
        name: { type: 'string', required: true },
        logoUrl: { type: 'string', required: false }
      }
    }
  }
});

Template Validation

const template = `
<h1>Welcome {{user.name}}!</h1>
<p>Thanks for joining {{company.name}}.</p>
<p>Your account was created on {{user.joinDate}}.</p>
<p>Visit us at {{company.websiteUrl}}.</p>
`;

const validation = registry.validateTemplate('user_welcome', template);

if (!validation.isValid) {
  console.log('Template errors:');
  validation.errors.forEach(error => {
    console.log(`Line ${error.line}: ${error.message}`);
  });
  // Output: Line 4: Undefined variable 'company.websiteUrl'

  // Get suggestions for undefined variables
  validation.undefinedVariables.forEach(undef => {
    console.log(`Undefined: ${undef.name}`);
    console.log(`Did you mean: ${undef.suggestions?.join(', ')}`);
  });
  // Output: Did you mean: company.logoUrl, company.name
}

// Check for unused required variables
if (validation.unusedVariables.length > 0) {
  console.log('Warning: Required variables not used:');
  validation.unusedVariables.forEach(path => {
    console.log(`  - ${path}`);
  });
}

Variable Extraction

// Extract all variables from a template
const variables = registry.extractVariables(template);

variables.forEach(variable => {
  console.log(`Found: ${variable.path} at line ${variable.line}`);
  console.log(`  Context: ${variable.context}`);
  console.log(`  Is conditional: ${variable.isConditional}`);
  console.log(`  Is loop: ${variable.isLoop}`);
});

Autocomplete Suggestions

// Get all top-level variables
const allSuggestions = registry.getVariableSuggestions('user_welcome');

// Get suggestions with prefix
const userSuggestions = registry.getVariableSuggestions('user_welcome', 'user.');

// Returns:
// [
//   { path: 'user.name', type: 'string', description: 'User full name', required: true },
//   { path: 'user.email', type: 'string', description: 'User email address', required: true },
//   { path: 'user.joinDate', type: 'date', description: 'Account creation date', required: false }
// ]

// Get path completions for partial input
const completions = registry.getPathCompletions('user_welcome', 'user.');

Context Inheritance

// Create base context
registry.registerContext('base_user', {
  name: 'base_user',
  variables: {
    user: {
      type: 'object',
      required: true,
      children: {
        name: { type: 'string', required: true },
        email: { type: 'string', required: true }
      }
    }
  }
});

// Extend base context
registry.registerContext('user_welcome', {
  name: 'user_welcome',
  extends: ['base_user'],
  variables: {
    welcomeMessage: { type: 'string', required: false },
    activationUrl: { type: 'string', required: true }
  }
});

// user_welcome now has: user.name, user.email, welcomeMessage, activationUrl

Validation Rules

import { lengthRule, patternRule, CommonRules } from '@bernierllc/template-variable-registry';

registry.registerContext('signup', {
  name: 'signup',
  variables: {
    email: {
      type: 'string',
      required: true,
      validation: [
        CommonRules.email(),
        lengthRule(5, 255)
      ]
    },
    password: {
      type: 'string',
      required: true,
      validation: [
        lengthRule(8, undefined, 'Password must be at least 8 characters'),
        patternRule(/[A-Z]/, 'Must contain uppercase letter')
      ]
    },
    age: {
      type: 'number',
      required: true,
      validation: [
        CommonRules.positiveNumber()
      ]
    }
  }
});

Jinja2 Template Support

const jinja2Template = `
<h1>Welcome {{ user.name }}!</h1>
{% if user.isPremium %}
  <p>You have premium access!</p>
{% endif %}
<ul>
{% for item in items %}
  <li>{{ item.name }}</li>
{% endfor %}
</ul>
`;

// Works the same way
const validation = registry.validateTemplate('user_welcome', jinja2Template);
const variables = registry.extractVariables(jinja2Template);

API Reference

VariableRegistry

registerContext(contextName: string, context: VariableContext): void

Register a variable context with the registry.

getContext(contextName: string): VariableContext | null

Retrieve a registered context by name.

getAllContexts(): VariableContext[]

Get all registered contexts.

validateTemplate(contextName: string, template: string): ValidationResult

Validate a template against a registered context.

Returns:

  • isValid: boolean - Whether the template is valid
  • errors: ValidationError[] - All validation errors
  • warnings: ValidationWarning[] - Validation warnings
  • unusedVariables: string[] - Required variables not used
  • undefinedVariables: UndefinedVariable[] - Variables not in registry

extractVariables(template: string, engine?: TemplateEngine): ExtractedVariable[]

Extract all variables from a template. Auto-detects engine if not specified.

getVariableSuggestions(contextName: string, prefix?: string): VariableSuggestion[]

Get variable suggestions for autocomplete functionality.

getPathCompletions(contextName: string, partialPath: string): VariableSuggestion[]

Get completions for a partial variable path.

hasContext(contextName: string): boolean

Check if a context exists.

deleteContext(contextName: string): boolean

Remove a context from the registry.

clearAll(): void

Clear all registered contexts.

Type Definitions

VariableDefinition

interface VariableDefinition {
  type: 'string' | 'number' | 'boolean' | 'date' | 'array' | 'object';
  required: boolean;
  description?: string;
  defaultValue?: unknown;
  validation?: ValidationRule[];
  children?: Record<string, VariableDefinition>;  // For objects
}

VariableContext

interface VariableContext {
  name: string;
  description?: string;
  variables: Record<string, VariableDefinition>;
  extends?: string[];  // Parent context names
}

ValidationResult

interface ValidationResult {
  isValid: boolean;
  errors: ValidationError[];
  warnings: ValidationWarning[];
  unusedVariables: string[];
  undefinedVariables: UndefinedVariable[];
}

ExtractedVariable

interface ExtractedVariable {
  name: string;
  path: string;
  line: number;
  column: number;
  context: 'handlebars' | 'jinja2';
  isConditional: boolean;
  isLoop: boolean;
}

Advanced Features

Nested Object Paths

// Supports deep nesting
variables: {
  user: {
    type: 'object',
    required: true,
    children: {
      profile: {
        type: 'object',
        required: false,
        children: {
          address: {
            type: 'object',
            required: false,
            children: {
              street: { type: 'string', required: false },
              city: { type: 'string', required: false }
            }
          }
        }
      }
    }
  }
}

// Use in templates: {{user.profile.address.street}}

Array Access Patterns

variables: {
  items: {
    type: 'array',
    required: true
  }
}

// Handlebars: {{items[0].name}}
// Jinja2: {{ items[0].name }}

Syntax Error Detection

const template = '{{#if condition}}test{{/unless}}';  // Mismatched blocks
const validation = registry.validateTemplate('context', template);

validation.errors.forEach(error => {
  if (error.type === 'syntax_error') {
    console.log(`Syntax error at line ${error.line}: ${error.message}`);
  }
});

Integration Status

Logger Integration

Status: Not applicable - Pure utility package with no runtime logging needs.

Justification: This is a pure utility package that performs template variable registration, validation, and extraction. It has no runtime logging requirements as it's designed to be embedded in other packages that handle logging at their level.

Docs-Suite Integration

Status: Ready - Full TypeDoc documentation exported

Format: TypeDoc with complete API documentation, usage examples, and type definitions. All public APIs are fully documented with JSDoc comments.

NeverHub Integration

Status: Not applicable - Core utility package with no external service dependencies

Justification: This is a core package that provides atomic template variable functionality. It has no need for service discovery, event bus communication, or dynamic configuration. NeverHub integration would be appropriate at the service/suite level that orchestrates this package.

Use Cases

This package is designed for:

  • Email template systems - Validate email templates before sending
  • Template editors - Provide autocomplete and validation in UI
  • Template documentation - Generate documentation of available variables
  • Multi-tenant systems - Different variable contexts per tenant
  • Template testing - Ensure templates use valid variables

Package Type

Core Package - Atomic utility with zero external dependencies, designed for reuse across multiple service and suite packages.

Dependencies

Zero runtime dependencies - pure TypeScript implementation.

Security

Input Validation

All template inputs and variable definitions are thoroughly validated:

  • Template syntax is parsed and validated before processing
  • Variable paths are sanitized to prevent injection attacks
  • Type checking ensures data integrity

Safe Defaults

  • No external dependencies reduces attack surface
  • No network calls or file system access
  • Pure functions with no side effects
  • All data structures are immutable where possible

Dependency Security

  • Zero runtime dependencies means zero transitive dependency vulnerabilities
  • Development dependencies are regularly audited with npm audit
  • All dependencies pinned to specific versions for reproducibility

See Also

License

Copyright (c) 2025 Bernier LLC. All rights reserved.