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 🙏

© 2024 – Pkg Stats / Ryan Hefner

varcor

v0.8.0

Published

A tool for streamlined management of environment variables, offering normalization, type enforcement, and generation of configuration skeletons.

Downloads

19

Readme

varcor

varcor is a tool for streamlined management of config values, offering normalization and type enforcement.

Installation

To get started with varcor, install the package via npm:

npm install varcor

Result

The Result type is used to return values or errors throught varcor.

type ResultSuccess<T> = { success: true; value: T; };

type ResultFailure<F> = { success: false; error: F; };

type Result<T, F> = ResultSuccess<T> | ResultFailure<F>;

// Helper methods
Result.success: <T>(value: T) => ResultSuccess<T>;
Result.failure: <F>(error: F) => ResultFailure<F>;

Variables

The Variable class is used to create an instance of a Variable validator/transformer.

Common Functionalities

Parsing Values

The primary method of the class is parse. This will take an optional string and return a Result of T or a list of error strings.

parse(value?: string | undefined): Result<T, string[]>

Variables are Immutable - the follow methods will create new variables.

.optional

Marks a variable as optional. Type of variable becomes T | undefined

.optional(): Variable<T | undefined>

// type becomes string | undefined
const optionalVar = v.string().optional(); 

// Optional Status can be retrieved from the .isOptional property
console.log(optionalVar.isOptional);

.default

Sets a default value for the variable if value is undefined.

.default(value: T | (() => T) ): Variable<T> 

// if value is not supplied, result in default value
const defaultedVar = v.string().default('defaultValue'); 

// Default Value can be retrieved from the .defaultTo property
console.log(defaultedVar.defaultTo);

.from

Sets the name of the variable

.from(name: string): Variable<T>

const namedVar = v.string().from('NAME');

// Variable Name can be retrieved from the .name property
console.log(namedVar.name)

.else<S>

Allows variable type unions

.else<S>(variable: Variable<S>): Variable<T | S>

// type becomes string | number
const stringOrNumber = v.string().else(v.number());

.transform<S>

Applies a custom transformation function to the variable's value. An optional targetType can be provided for documentation purposes.

type Transformer<I, O> = (value: I) => Result<O, string[]>

.transform<S>(transform: Transformer<T, S>, type?: string): Variable<S>

// type number|undefind becomes boolean
const isOdd = v.number().optional().transform(
    value => Result.success((Math.round(value || 0) % 2) === 1)
);

Transformations are particularly powerful, allowing for value conversion, additional validation and type conversion if necessary.


Typed Variables and Helper Functions

varcor provides a series of helper functions designed to define and enforce the types and constraints of your environment variables:

Boolean Variables

Define boolean variables, interpreting various string values ('true', 'false', '1', '0') as booleans.

import { v } from 'varcor';

const DEBUG = v.boolean();

Number Variables

Define numeric environment variables, with support for minimum and maximum constraints.

import { v } from 'varcor';

const PORT = v.number().min(3000).max(9000);

Integer Variables

Similar to number variables, but specifically for integer values.

import { v } from 'varcor';

const RETRY_LIMIT = v.integer().min(1).max(5);

String Variables

Define string variables, with optional validators or regex pattern matching.

import { v } from 'varcor';

const DATABASE_URL = v.string().regex(/mongodb:\/\/.+/, 'mongodb Url');

const MAIN_URL = v.string().url();

const EMAIL = v.string().email();

const UUID = v.string().uuid();

const PASSWORD = v.string().validate(value => 
    value.length > 10 && value.length < 20 
    ? Result.success(value)
    : Result.failure(['must be between 10 and 20 characters.'])
)

Enum Variables

Define enumerated string variables, ensuring the value matches one of the predefined options.

import { v } from 'varcor';

const ENVIRONMENT = v.enum().value('development').value('production').value('test');

You can use a single value enum - the v.literal(value: string) helper function:

const RED = v.literal('RED');

Date and Time Variables

Define variables for date and time, supporting a generic DateTime object, and both JavaScript Date objects and luxon DateTime objects.

Types:

type DateType = 'date' | 'time' | 'datetime' | 'timeTz' | 'datetimeTz';

type DateObject = {
    year: number;
    month: number;
    day: number;
    hour: number;
    minute: number;
    second: number;
    ms: number;
    tz: TimeZone;
};

// `from` defaults to DateTimeTz
v.dateobj:(from?: DateType): Variable<DateObject>

v.jsdate:(from?: DateType): Variable<Date>

v.luxdate:(from?: DateType): Variable<DateTime>

The DateType's each correspond to a ISO subset:

  • date - YYYY-MM-DD
  • time - HHHH:mm:SS[.LLL]
  • datetime - >[(T| )]
  • tz: Z | (+-)HH[:MM]
  • timeTz - []
  • datetimeTz - [timeTz]

Usage:

import { v } from 'varcor';

const OBJ_DATE = v.dateobj('datetimeTz')
const JS_DATE = v.jsdate('date');
const LUX_DATE = v.luxdate('time');

JSON Variables

Parse and validate JSON formatted string variables.

Types:

type JsonValidator<T> = (data: any) => Result<T, string[]>;

v.json<T = any>(validator?: JsonValidator<T>): Variable<T>

Usage:

import { v } from 'varcor';

const CONFIG = v.json();

Zod Schema Variables

Leverage zod schemas for complex JSON object validation.

import { z } from 'zod';
import { v } from 'varcor';

const MY_SCHEMA = z.object({ key: z.string() });
const CONFIG = v.tson(MY_SCHEMA);

DataObject and DataObjectBuilder Usage

DataObject and DataObjectBuilder are utilities for managing the configuration data within your applications. Here's how to use them effectively:

DataObject

DataObject is a simple key-value mapping where values are either strings or undefined.

type DataObject = { [key: string]: string | undefined; };

DataObjectBuilder

The DataObjectBuilder class provides an interface to incrementally build a DataObject. It supports adding data from environment variables, JSON strings, .env files, and more. DataObjectBuilder is immutable, so each method creates a new DataObjectBuilder.

Basic Usage

  1. Creating a New DataObjectBuilder Instance

    To start building a new DataObject, simply instantiate DataObjectBuilder:

    import { DataObjectBuilder } from './DataObjectBuilder';
    
    let builder = new DataObjectBuilder()
  2. Adding DataObjects or DataObjectBuilders

    Incorporate other DataObjects or DataObjectBuilders.

    Signature

    .data(data: DataObject | DataObjectBuilder): 

    Example

    const dataObject: DataObject = {
      PORT: '8080',
      NAME: 'MyApp'
    };
    
    const dataBuilder = v.data.new().addDataObject({
      TYPE: 'Open',
      DATE: '2024-01-01'
    })
    
    builder = builder.data(appConfig)
    builder = builder.data(dataBuilder);
  3. Adding Environment Variables

    Easily include all current environment variables into your data object:

    Signature

    .env();
  4. Adding Data from an Object

    Incorporate configuration data from a regular JavaScript object. Non-string values are automatically converted to JSON strings:

    Signature

    .object(data: Record<string, any>)
    const appConfig = {
      port: 8080,
      name: "MyApp",
      features: { logging: true, debugMode: false },
    };
    
    builder = builder.object(appConfig);
  5. Adding Data from json string

    Signature

    .json(data: string);

    Example

    const jsonString = '{"apiUrl": "https://api.example.com", "timeout": 5000}';
    builder = builder.json(jsonString);
  6. Adding Data from dotenv string

    If you have a dotenv formatted string containing environment variables, you can parse and add those variables:

    Signature

    .dotenv(data: string);

    Example

    const envFormat = `
    # Variables
       
    API_URL=http://api.example.com;
       
    TIMEOUT="5000";
    `
    builder = builder.dotenv(envFormat);
  7. File Helpers

    DataObjectBuilder has two methods that allow importing from json and dotenv files.

    Signatures

    .jsonFile(path: string, options: FileOptions);
    
    .dotenvFile(path: string,  options: FileOptions);

    Each method takes the FileOptions type

    type FileOptions = {      
        // if set, will determine whether to attempt to load file
        when?: boolean;
    
        // if false, will error if file doesn't exist
        optional?: boolean; 
           
        // function to check if file exists, defaults to fs.existsSync
        fileExists?: (path: string) => boolean; 
    
        // function to read file contents, defaults to fs.readFile
        readFile?: (path: string) => string; 
    };

    Example

    //example
    builder = builder.dotenvFile('./production.env', { when: process.env.NODE_ENV === 'production' });
           
    builder = builder.dotenvFile('./development.env', { when: process.env.NODE_ENV === 'development' });
  8. Fluid Pattern

    As DataObjectBuilder methods return a new builder instance, allowing for method chaining:

    const finalDataObject = new DataObjectBuilder()
        .env()
        .object(appConfig)
        .dotenvFile('./.env')
        .data({})
        .toDataObject();
  9. Helper Methods All methods on DataObjectBuilder are available in v.data, to allow easy initializing

    v.data.new();
    v.data.env();
    v.data.data(...);
    v.data.object(...);    
    v.data.json(...);
    v.data.dotenv(...);
    v.data.jsonFile(...);
    v.data.dotenvFile(...);
  10. Finalizing and Retrieving the DataObject

    Once you've added all your data sources, finalize the builder to get your DataObject:

    const finalDataObject: DataObject = builder.toDataObject();
        
    console.log(finalDataObject);

Type Inference and Parsing

  • TODO