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

error-c

v2.1.0

Published

error-c is constant based error define micro framework, you can define error with message(dynamic) more staticaly and safely on typescript

Downloads

6

Readme

error-c

What is this package do?

error-c is constant based error define micro framework, you can define error with message(dynamic) more staticaly and safely on typescript.

The goal of packages is similar to that of typeScript.

When your using typescript, perhaps you want to generate SAFER code through static type analysis.

However, think about this

const DEFINE_ERRORS = {
  E0001: "This is error!",
  E0002: "I'm error too!",
  E0003: "I'm ${whoareyou}!",
  E0004: "I'm not ${notyou}, i'm ${whoareyou}!",
};

The goal of the definition of this code is clear.

You want to use error with dynamicaly generated message

However, there is no way to know what value is needed for the message of that particular error code.

Until now, not now.

Thanks for typescript infer, now we can guess what values are needed for errors

If you're not using typescript, you don't need to use this package at all.

Look at this, it's also in the examples folder.

import errorc from "error-c";

const DEFINE_ERRORS = {
    E0001: "This is error!",
    E0002: "I'm error too!",
    E0003: "I'm ${whoareyou}!",
    E0004: "I'm not ${notyou}, i'm ${whoareyou}!",
} as const;

const fail = errorc(
    "namespace",
    DEFINE_ERRORS,
    {
        defaultMode: "release",
    }
);

console.log(fail.E0004({ notyou: "pizza", whoareyou: "potato" }));

That code is simply a generate error message function that create by automated generator.

It's ok, but the generator is very powerful than your thought

Imagine that you accidentally wrote a value into the "noryou" field instead of the "notyou" field.

This is a common mistake in practice.

the compiler tells you that there is an error in the noryou part

This code throws a compile error.

Or, you don't know which fields are in a particular error code.

Now you can, see below

autocomplete tells me that struct has "notyou", "whoareyou" fields

This magic make by typescript feature, no additional plugins, no automated build scripts.

All you need to do now is modify DEFINE_ERRORS. Typescript takes care of the rest.

How to use?


Define

First of all, to use this library, you need to define it.

You can define it like the code below.

import { Define } from "error-c";

const DEFINE_ERRORS = {
  SIMPLE0: "This is error!",
  SIMPLE1: "I'm not ${notyou}, i'm ${whoareyou}!",
  // You can define nested object
  INNER : {
    HELLO : "Inner hello?",
    RENNI :{
      HELLO : "Inner.Inner hello?",
    }
  }
} as const;

WARNING

as const is VERY IMPORTANT never take it off

Importantly, DO NOT explicitly define types.


Generate

After writing the definition, the next thing to do is create an error generating function.

You can do this simply as below.

import errorc, {FunctionGenerator, NamespaceGenerate} from "error-c";

const DEFINE_ERRORS = {
    E0001: "Hello, world!",
    E0002: "I'm ${whoareyou}!",
    INNER: {
        INNER_ERROR: "inner error"
    },
} as const;

const fnStyle = errorc("function", DEFINE_ERRORS);
// or
// const fnStyle = FunctionGenerator(DEFINE_ERRORS);

const nsStyle = errorc("namespace", DEFINE_ERRORS);
// or
// const nsStyle = errorc(DEFINE_ERRORS);

// or
// const nsStyle = NamespaceGenerate(DEFINE_ERRORS);

// > Hello, world!
console.log(fnStyle("E0001", {}))
console.log(nsStyle.E0001())

// > I'm potato!
console.log(fnStyle("E0002", {whoareyou : "potato"}))
console.log(nsStyle.E0002({whoareyou : "potato"}))

// > inner error
console.log(fnStyle("INNER.INNER_ERROR", {}))
console.log(nsStyle.INNER.INNER_ERROR())

As you can see, there is two generator, function and namespace

Two are similar

But the namespace has the advantage to omitting it if there is no parameters

Also, IDEs such as vscode support shortcuts to declarations, namespace is recommended to use the namespace method whenever possible.


Conditional message

import errorc from "error-c"

const DEFINE_ERRORS = {
    CONDMSG: { release: "hello, release", debug: "hello, debug" }
} as const;

const noDefault = errorc("namespace", DEFINE_ERRORS);
const defaultIsDebug = errorc("namespace", DEFINE_ERRORS, { defaultMode: "debug", });
const defaultIsRelease = errorc("namespace", DEFINE_ERRORS, { defaultMode: "release", });

console.log(noDefault.CONDMSG());                   // expected : 'hello, release'
console.log(noDefault.CONDMSG("debug"));            // expected : 'hello, debug'
console.log(noDefault.CONDMSG("release"));          // expected : 'hello, release'
console.log(defaultIsDebug.CONDMSG());              // expected : 'hello, debug'
console.log(defaultIsDebug.CONDMSG("debug"));       // expected : 'hello, debug'
console.log(defaultIsDebug.CONDMSG("release"));     // expected : 'hello, release'
console.log(defaultIsRelease.CONDMSG());            // expected : 'hello, release'
console.log(defaultIsRelease.CONDMSG("debug"));     // expected : 'hello, debug'
console.log(defaultIsRelease.CONDMSG("release"));   // expected : 'hello, release'

You can create conditional messages. Currently, only debug and release are available. If there is no defaultMode, it is release .


types

This is useful if you want to allow only certain types of messages.

import errorc from "error-c"

export type DefaultType =
    | bigint
    | number
    | string
    | object
    | boolean
    | ((context: Record<string, any>) => string);

const DEFINE_ERRORS = {
    NUMBER: "${value:number}",
    STRING: "${value:string}",
    OBJECT: "${value:object}",
    BOOLEAN: "${value:boolean}",    // boolean
    FUNCTION: "${value:function}",  // (context : Record<string, any>) => string
    DEFAULT: "${value}",            // infer as `DefaultType`
} as const;

const ns = errorc("namespace", DEFINE_ERRORS);

console.log(ns.NUMBER({ value: 1 }));
console.log(ns.STRING({ value: "" }));
console.log(ns.OBJECT({ value: { foo: { bar: "foo-bar" } } }));
console.log(ns.BOOLEAN({ value: true }));
console.log(ns.FUNCTION({ value: (context) => "function" }));

console.log(ns.DEFAULT({ value: 1 }));
console.log(ns.DEFAULT({ value: {} }));
console.log(ns.DEFAULT({ value: { foo: { bar: "foo-bar" } } }));
console.log(ns.DEFAULT({ value: true }));
console.log(ns.DEFAULT({ value: (context) => "function" }));

Type constaints

WARNING

The type provided by errorc is not a typescript type,

But a type defined separately by errorc.

Don't confuse this.


Redefinition output type

If you want to make the output type into class or object not string, you can use the like this.

import errorc from "error-c";

const DEFINE_ERRORS = {
    EOF: "End Of File",
    UnexpectedError: {
        debug: "unexpected error cause '${cause}'",
        release: "unexpected error",
    },
} as const;

class CustomError extends Error {
    readonly code: string
    readonly context: Record<string, any>
    constructor(message: string, code: string, context: Record<string, any>) {
        super(message)
        this.code = code
        this.context = context
    }
}

const ns = errorc(
    DEFINE_ERRORS,
    {
        handler: (message, code, context) => new CustomError(message, code, context)
    }
);

// CustomError(
//     "unexpected error cause '${secret data for debug}'",
//     "UnexpectedError",
//     {
//         cause : "secret data for debug"
//     }
// )
console.log(ns.UnexpectedError({ cause: "secret data for debug" }, "debug"))


// CustomError(
//     "unexpected error",
//     "UnexpectedError",
//     {}
// )
console.log(ns.UnexpectedError({ cause: "secret data for debug" }, "release"))

handler automatically determine whether it's debug or release, provide the appropriate context

You can use context, code, and messages to emit other type instead of string.

More examples

Many example can be found here

How does it work?

I stumbled across some interesting code here

type ExtractSemver<SemverString extends string> =
  SemverString extends `${infer Major}.${infer Minor}.${infer Patch}`
    ? { major: Major; minor: Minor; patch: Patch }
    : { error: "Cannot parse semver string" };

Surprisingly, typescript able to cut some of the parts from literals.

Using this, I cut and pasted literal, and created the package by using generics as a kind of function.

I also think this method will be useful for text template libraries like internationalization(i18n).