Code Monkey home page Code Monkey logo

x-value's Introduction

NPM version Repository package.json version Coveralls MIT license

X-Value

X-Value (X stands for "cross") is a medium-somewhat-neutral runtime type validation library.

Comparing to alternatives like io-ts and Zod, X-Value uses medium/value concept and allows values to be decoded from and encoded to different mediums.

Installation

yarn add x-value
# or
npm install x-value

Usages

Defining types with X-Value is similar to io-ts/Zod.

import * as x from 'x-value';

const Oops = x.object({
  foo: x.string,
  bar: x.number.optional(),
});

const Rock = x.record(x.string, x.number);

const Aha = x.array(Oops);

const Tick = x.tuple(x.string, x.number);

const Um = x.union(Oops, x.boolean);

const I = x.intersection(
  Oops,
  x.object({
    yoha: x.boolean,
  }),
);

interface R {
  type: 'recursive';
  child: R;
}

const R = x.recursive<R>(R =>
  x.object({
    type: x.literal('recursive'),
    child: R,
  }),
);

Get static type of type object:

type Oops = x.TypeOf<typeof Oops>;
type JSONOops = x.MediumTypeOf<typeof Oops, x.JSONTypes>;

Refine type:

const Email = x.string.refine(value => value.includes('@'));

// Or with refined or nominal type:
const Email = x.string.refine<`${string}@${string}`>(value =>
  value.includes('@'),
);
const Email = x.string.refine<Nominal<'email'>>(value => value.includes('@'));

// Or just nominal type without extra constraints:
const Email = x.string.nominal<'email'>();

Decode from medium:

let value = Oops.decode(x.json, '{"foo":"abc","bar":123}');

Encode to medium:

let json = Oops.encode(x.json, {foo: 'abc', bar: 123});

Transform from medium to medium:

let json = Oops.transform(x.queryString, x.json, 'foo=abc&bar=123');

Type is guard:

if (Oops.is(value)) {
  // ...
}

Type satisfies assertion (will throw if does not satisfy):

let oops = Oops.satisfies(value);

Diagnose for type issues:

let issues = Oops.diagnose(value);

Mediums and Values

Mediums are what's used to store values: JSON strings, query strings, buffers etc.

For example, a string "2022-03-31T16:00:00.000Z" in JSON medium with type Date represents value new Date('2022-03-31T16:00:00.000Z').

Assuming we have 3 mediums: browser, server, rpc; and 2 types: ObjectId, Date. Their types in mediums and value are listed below.

Type\Medium Browser RPC Server Value
ObjectId string packed as string ObjectId string
Date Date packed as string Date Date

We can encode values to mediums:

let id = '6246056b1be8cbf6ca18401f';

ObjectId.encode(browser, id); // string '6246056b1be8cbf6ca18401f'
ObjectId.encode(rpc, id);     // packed string '"6246056b1be8cbf6ca18401f"'
ObjectId.encode(server, id);  // new ObjectId('6246056b1be8cbf6ca18401f')

let date = new Date('2022-03-31T16:00:00.000Z');

Date.encode(browser, date); // new Date('2022-03-31T16:00:00.000Z')
Date.encode(rpc, date);     // packed string '"2022-03-31T16:00:00.000Z"'
Date.encode(server, date);  // new Date('2022-03-31T16:00:00.000Z')

Or decode packed data of mediums to values:

// All result in '6246056b1be8cbf6ca18401f'
ObjectId.decode(browser, '6246056b1be8cbf6ca18401f');
ObjectId.decode(rpc, '"6246056b1be8cbf6ca18401f"');
ObjectId.decode(server, new ObjectId('6246056b1be8cbf6ca18401f'));

// All result in new Date('2022-03-31T16:00:00.000Z')
Date.decode(browser, new Date('2022-03-31T16:00:00.000Z'));
Date.decode(rpc, '"2022-03-31T16:00:00.000Z"');
Date.decode(server, new Date('2022-03-31T16:00:00.000Z'));

Ideally there's no need to have "value" as a separate concept because it's essentially "ECMAScript runtime medium". But to make decode/encode easier among different mediums, "value" is promoted as an interchangeable medium.

New Atomic Type

Before we can add medium support for a new type of atomic value, we need to add new atomic value. It is quite easy to do so:

import * as x from 'x-value';

// 1. Create a symbol for the new atomic type.
const newAtomicTypeSymbol = Symbol();

// 3. Create the new atomic type with constraint.
const NewAtomic = x.atomic(newAtomicTypeSymbol, value =>
  Buffer.isBuffer(value),
);

declare global {
  namespace XValue {
    interface Types {
      // 2. Define the symbol to value type mapping.
      [newAtomicTypeSymbol]: Buffer;
    }
  }
}

New Medium

After creating the new atomic type, we need to create/extend a new medium that supports this type:

interface SuperJSONTypes extends x.JSONTypes {
  // 1. Define the unpacked type for decode/encode operation.
  [newAtomicTypeSymbol]: string;
}

const superJSON = x.json.extend<SuperJSONTypes>('Super JSON', {
  codecs: {
    // 2. Define the codec.
    [newAtomicTypeSymbol]: {
      decode(value) {
        if (typeof value !== 'string') {
          throw new TypeError(
            `Expected hex string, getting ${Object.prototype.toString.call(
              value,
            )}`,
          );
        }

        return Buffer.from(value, 'hex');
      },
      encode(value) {
        return value.toString('hex');
      },
    },
  },
});

Medium Packing

When decode() from a medium, X-Value unpacks data for a structured input (e.g., JSON.parse()). It packs the data again on encode() (e.g., JSON.stringify()).

For medium that requires packing:

interface PackedTypes {
  // 1. Define the packed type.
  packed: string;
}

const packed = x.medium<PackedTypes>('Packed ', {
  // 2. Define packing methods.
  packing: {
    pack(data) {
      return JSON.stringify(data);
    },
    unpack(json) {
      return JSON.parse(json);
    },
  },
});

The superJSON medium is actually a packed medium. However, the related definitions are inherited from x.JSONTypes.

License

MIT License.

x-value's People

Contributors

vilicvane avatar boenfu avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.