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

@efstajas/versioned-parser

v0.1.4

Published

A TypeScript-first utility for managing multiple versions of parse functions.

Downloads

156

Readme

🤓 Versioned Parser

A TypeScript-first utility for managing multiple versions of parse functions.

Let's say your app writes some data somewhere in the form of stringified JSON (whether it's localstorage, IPFS or any other storage). Because you're a good citizen, you use a parser like zod to ensure that this data matches your expected schema at runtime whenever you read it, and you even parse any data you write through your parser to ensure you never end up writing anything invalid. You probably have a bunch of functions in your app that accept or return data shaped like your schema.

All is well until you add a new feature, and you now need to add additional keys and values to your schema. Because this external data may have been written by a previous version of your app, you need to be able to read any version of your schema, but you only want to write the latest version. And you want to make sure that all your functions that accept or return either only the latest version or any version are tightly typed accordingly, so that you never end up accidentally writing an outdated version of your schema.

This is where Versioned Parser comes in.

👋 Getting started

Install

npm install @efstajas/versioned-parser

✨ Creating a Versioned Parser

For the examples below, we're going to use zod as our data validation tool, but Versioned Parser will work with any parse function that takes a single input parameter, throws an error if the expected schema is not followed, and returns the parsed data.

Let's create three versions of a dummy Zod schema, where each adds another mandatory key. When you have your schema versions defined, simply call createVersionedParser and pass the versions of your parse function (function that takes a single input parameter, throws an error if the expected schema is not followed, and returns the parsed data) in an array as the first argument, where the latest version must be first (index zero), and older versions follow.

import z from 'zod';
import { createVersionedParser } from '@efstajas/versioned-parser';

// Define example schemas. We're using Zod here, but any parse function will work.

const schemaV1 = z.object({
  foo: z.string(),
  bar: z.number(),
});

const schemaV2 = schemaV1.extend({
  type: z.literal('yes'),
});

const schemaV3 = schemaV2.extend({
  anotherOne: z.literal('no'),
});


/*
  Create the versioned parser. The first argument is an array of parse functions, where index zero is the newest version, 
  and the rest are older versions in descending order.
*/
const parser = createVersionedParser([
  schemaV3.parse,
  schemaV2.parse,
  schemaV1.parse,
]);

🤓 Parsing data

Once you've created your Versioned Parser, you can start parsing data using three functions; parseLatest, parseAny, or parseSpecific. All these functions magically infer the correct type from your array of parse functions:

// [...]

// Type of res1 is { foo: string, bar: number, type: 'yes', anotherOne: 'no' }
const res1 = parser.parseLatest(someInput);

// Type of res2 is a union of all versions of the schema.
const res2 = parser.parseAny(someInput);

// Type of res3 is { foo: string, bar: number, type: 'yes' }
const res3 = parser.parseSpecific(1, someInput);

As you can see, parseLatest always takes the latest version of your parse function, and thus enforces the latest schema. The result of this function is strongly typed according to the return type of the first parse function you passed in your version array.

parseAny on the other hand will attempt parsing the supplied data with all possible versions. It'll first try the latest version, then the second-latest, and so on. If none of the parse functions match, it throws an error.

parseSpecific allows you to apply a specific parse function according to its index in the version array you passed to createVersionedParser.

Type helpers

In addition to actually running your parse functions, Versioned Parser also comes with a number of helpful TypeScript generics that allow you to strongly type functions processing parsed data as accepting or returning only specific versions of your data:

  // [...]

 // LatestVersion generic will be the type of the latest version of the parser's schema.
 type LatestVersionOfMySchema = LatestVersion<typeof parser>;
 
 // AnyVersion generic will be a union of all versions of the parser's schema.
 type AnyVersionOfMySchema = AnyVersion<typeof parser>;
 
 
 // SpecificVersion generic returns the type of a specific version of the parser's schema,
 // where `VI` is the index of the parse function as provided to the parser type `PT`.
 type SpecificVersionOfMySchema = SpecificVersion<typeof parser, 0>;