Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

tiny-decoders

Package Overview
Dependencies
Maintainers
1
Versions
30
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

tiny-decoders - npm Package Versions

13

15.1.0

Diff

Changelog

Source

Version 15.1.0 (2023-10-23)

This release adds the Infer utility type. It’s currently basically just an alias to the TypeScript built-in ReturnType utility type, but in a future version of tiny-decoders it’ll need to do a little bit more than just ReturnType. If you’d like to reduce the amount of migration work when upgrading to that future version, change all your ReturnType<typeof myDecoder> to Infer<typeof myDecoder> now!

lydell
published 15.0.0 •

Changelog

Source

Version 15.0.0 (2023-10-23)

This release changes the options parameter of fieldsAuto and fieldsUnion from:

{ exact = "allow extra" }: { exact?: "allow extra" | "throw" } = {}

To:

{ allowExtraFields = true }: { allowExtraFields?: boolean } = {}

This is because:

  • A future tiny-decoders version will be return value based instead of throwing errors, so "throw" will not make sense anymore.
  • tiny-decoders used to have a third alternative for that option – that’s why it’s currently a string union rather than a boolean. While at it, we could just as well simplify into a boolean.
lydell
published 14.0.0 •

Changelog

Source

Version 14.0.0 (2023-10-22)

This release removes the fields function, which was deprecated in version 11.0.0. See the release notes for version 11.0.0 for how to replace fields with fieldsAuto, chain and custom decoders.

lydell
published 13.0.0 •

Changelog

Source

Version 13.0.0 (2023-10-22)

[!WARNING]
This release contains a breaking change, but no TypeScript errors! Be careful!

Version 11.0.0 made changes to fieldsAuto, but had a temporary behavior for backwards compatibility, awaiting the changes to fieldsUnion in version 12.0.0. This release (13.0.0) removes that temporary behavior.

You need to be on the lookout for these two patterns:

fieldsAuto({
  field1: undefinedOr(someDecoder),
  field2: () => someValue,
});

Previously, the above decoder would succeed even if field1 or field2 were missing.

  • If field1 was missing, the temporary behavior in fieldsAuto would call the decoder at field1 with undefined, which would succeed due to undefinedOr. If you did the version 11.0.0 migration perfectly, this shouldn’t matter. But upgrading to 13.0.0 might uncover some places where you use undefinedOr(someDecoder) but meant to use field(someDecoder, { optional(true) }) or field(undefinedOr(someDecoder), { optional(true) }) (the latter is the “safest” approach in that it is the most permissive).
  • If field2 was missing, the temporary behavior in fieldsAuto would call the decoder at field2 with undefined, which would succeed due to that decoder ignoring its input and always succeeding with the same value.

Here’s an example of how to upgrade the “always succeed” pattern:

const productDecoder: Decoder<Product> = fieldsAuto({
  name: string,
  price: number,
  version: () => 1,
});

Use chain instead:

const productDecoder: Decoder<Product> = chain(
  fieldsAuto({
    name: string,
    price: number,
  }),
  (props) => ({ ...props, version: 1 }),
);

It’s a little bit more verbose, but unlocks further changes that will come in future releases.

lydell
published 12.0.0 •

Changelog

Source

Version 12.0.0 (2023-10-22)

This release changes how fieldsUnion works. The new way should be easier to use, and it looks more similar to the type definition of a tagged union.

  • Changed: The first argument to fieldsUnion is no longer the common field name used in the JSON, but the common field name used in TypeScript. This doesn’t matter if you use the same common field name in both JSON and TypeScript. But if you did use different names – don’t worry, you’ll get TypeScript errors so you won’t forget to update something.

  • Changed: The second argument to fieldsUnion is now an array of objects, instead of an object with decoders. The objects in the array are “fieldsAuto objects” – they fit when passed to fieldsAuto as well. All of those objects must have the first argument to fieldsUnion as a key, and use the new tag function on that key.

  • Added: The tag function. Used with fieldsUnion, once for each variant of the union. tag("MyTag") returns a Field with a decoder that requires the input "MyTag" and returns "MyTag". The metadata of the Field also advertises that the tag value is "MyTag", which fieldsUnion uses to know what to do. The tag function also lets you use a different common field in JSON than in TypeScript (similar to the field function for other fields).

Here’s an example of how to upgrade:

fieldsUnion("tag", {
  Circle: fieldsAuto({
    tag: () => "Circle" as const,
    radius: number,
  }),
  Rectangle: fields((field) => ({
    tag: "Rectangle" as const,
    width: field("width_px", number),
    height: field("height_px", number),
  })),
});

After:

fieldsUnion("tag", [
  {
    tag: tag("Circle"),
    radius: number,
  },
  {
    tag: tag("Rectangle"),
    width: field(number, { renameFrom: "width_px" }),
    height: field(number, { renameFrom: "height_px" }),
  },
]);

And here’s an example of how to upgrade a case where the JSON and TypeScript names are different:

fieldsUnion("type", {
  circle: fieldsAuto({
    tag: () => "Circle" as const,
    radius: number,
  }),
  square: fieldsAuto({
    tag: () => "Square" as const,
    size: number,
  }),
});

After:

fieldsUnion("tag", [
  {
    tag: tag("Circle", { renameTagFrom: "circle", renameFieldFrom: "type" }),
    radius: number,
  },
  {
    tag: tag("Square", { renameTagFrom: "square", renameFieldFrom: "type" }),
    size: number,
  },
]);
lydell
published 11.0.0 •

Changelog

Source

Version 11.0.0 (2023-10-21)

This release deprecates fields, and makes fieldsAuto more powerful so that it can do most of what only fields could before. Removing fields unlocks further changes that will come in future releases. It’s also nice to have just one way of decoding objects (fieldsAuto), instead of having two. Finally, the changes to fieldsAuto gets rid of a flawed design choice which solves several reported bugs: #22 and #24.

  • Changed: optional has been removed and replaced by undefinedOr and a new function called field. The optional function did two things: It made a decoder also accept undefined, and marked fields as optional. Now there’s one function for each use case.

  • Added: The new field function returns a Field type, which is a decoder with some metadata. The metadata tells whether the field is optional, and whether the field has a different name in the JSON object.

  • Changed: fieldsAuto takes an object like before, where the values are Decoders like before, but now the values can be Fields as well (returned from the field function). Passing a plain Decoder instead of a Field is just a convenience shortcut for passing a Field with the default metadata (the field is required, and has the same name both in TypeScript and in JSON).

  • Changed: fieldsAuto no longer computes which fields are optional by checking if the type of the field includes | undefined. Instead, it’s based purely on the Field metadata.

  • Changed: const myDecoder = fieldsAuto<MyType>({ /* ... */ }) now needs to be written as const myDecoder: Decoder<MyType> = fieldsAuto({ /* ... */ }). It is no longer recommended to specify the generic of fieldsAuto, and doing so does not mean the same thing anymore. Either annotate the decoder as any other, or don’t and infer the type.

  • Added: recursive. It’s needed when making a decoder for a recursive data structure using fieldsAuto. (Previously, the recommendation was to use fields for recursive objects.)

  • Changed: TypeScript 5+ is now required, because the above uses const type parameters) (added in 5.0), and leads to the exactOptionalPropertyTypes (added in 4.4) option in tsconfig.json being recommended (see the documentation for the field function for why).

The motivation for the changes are:

  • Supporting TypeScript’s exactOptionalPropertyTypes option. That option decouples optional fields (field?:) and union with undefined (| undefined). Now tiny-decoders has done that too.

  • Supporting generic decoders. Marking the fields as optional was previously done by looking for fields with | undefined in their type. However, if the type of a field is generic, TypeScript can’t know if the type is going to have | undefined until the generic type is instantiated with a concrete type. As such it couldn’t know if the field should be optional or not yet either. This resulted in it being very difficult and ugly trying to write a type annotation for a generic function returning a decoder – in practice it was unusable without forcing TypeScript to the wanted type annotation. #24

  • Stop setting all optional fields to undefined when they are missing (rather than leaving them out). #22

  • Better error messages for missing required fields.

    Before:

    At root["firstName"]:
    Expected a string
    
    Got: undefined
    

    After:

    At root:
    Expected an object with a field called: "firstName"
    Got: {
      "id": 1,
      "first_name": "John"
    }
    

    In other words, fieldsAuto now checks if fields exist, rather than trying to access them regardless. Previously, fieldsAuto ran decoderAtKey(object[key]) even when key did not exist in object, which is equivalent to decoderAtKey(undefined). Whether or not that succeeded was up to if decoderAtKey was using optional or not. This resulted in the worse (but technically correct) error message. The new version of fieldsAuto knows if the field is supposed to be optional or not thanks to the Field type and the field function mentioned above.

    [!WARNING]
    Temporary behavior: If a field is missing and not marked as optional, fieldsAuto still tries the decoder at the field (passing undefined to it). If the decoder succeeds (because it allows undefined or succeeds for any input), that value is used. If it fails, the regular “missing field” error is thrown. This means that fieldsAuto({ name: undefinedOr(string) }) successfully produces { name: undefined } if given {} as input. It is supposed to fail in that case (because a required field is missing), but temporarily it does not fail. This is to support how fieldsUnion is used currently. When fieldsUnion is updated to a new API in an upcoming version of tiny-decoders, this temporary behavior in fieldsAuto will be removed.

  • Being able to rename fields with fieldsAuto. Now you don’t need to refactor from fieldsAuto to fields anymore if you need to rename a field. This is done by using the field function.

  • Getting rid of fields unlocks further changes that will come in future releases. (Note: fields is only deprecated in this release, not removed.)

Here’s an example illustrating the difference between optional fields and accepting undefined:

fieldsAuto({
  // Required field.
  a: string,

  // Optional field.
  b: field(string, { optional: true }),

  // Required field that can be set to `undefined`:
  c: undefinedOr(string),

  // Optional field that can be set to `undefined`:
  d: field(undefinedOr(string), { optional: true }),
});

The inferred type of the above is:

type Inferred = {
  a: string;
  b?: string;
  c: string | undefined;
  d?: string | undefined;
};

In all places where you use optional(x) currently, you need to figure out if you should use undefinedOr(x) or field(x, { optional: true }) or field(undefinedOr(x), { optional: true }).

The field function also lets you rename fields. This means that you can refactor:

fields((field) => ({
  firstName: field("first_name", string),
}));

Into:

fieldsAuto({
  firstName: field(string, { renameFrom: "first_name" }),
});

If you used fields for other reasons, you can refactor them away by using recursive, chain and writing custom decoders.

Read the documentation for fieldsAuto and field to learn more about how they work.

lydell
published 10.0.0 •

Changelog

Source

Version 10.0.0 (2023-10-15)

Changed: multi has a new API.

Before:

type Id = { tag: "Id"; id: string } | { tag: "LegacyId"; id: number };

const idDecoder: Decoder<Id> = multi({
  string: (id) => ({ tag: "Id" as const, id }),
  number: (id) => ({ tag: "LegacyId" as const, id }),
});

After:

type Id = { tag: "Id"; id: string } | { tag: "LegacyId"; id: number };

const idDecoder: Decoder<Id> = chain(multi(["string", "number"]), (value) => {
  switch (value.type) {
    case "string":
      return { tag: "Id" as const, id: value.value };
    case "number":
      return { tag: "LegacyId" as const, id: value.value };
  }
});

Like before, you specify the types you want (string and number above), but now you get a tagged union back ({ type: "string", value: string } | { type: "number", value: number }) instead of supplying functions to call for each type. You typically want to pair this with chain, switching on the different variants of the tagged union.

This change unlocks further changes that will come in future releases.

lydell
published 9.0.0 •

Changelog

Source

Version 9.0.0 (2023-10-15)

Changed: repr now prints objects and arrays slightly differently, and some options have changed.

tiny-decoders has always printed representations of values on a single line. This stems back to when tiny-decoders used to print a “stack trace” (showing you a little of each parent object and array) – then it was useful to have a very short, one-line representation. Since that’s not a thing anymore, it’s more helpful to print objects and arrays multi-line: One array item or object key–value per line.

Here’s how the options have changed:

  • recurse: boolean: Replaced by depth: number. Defaults to 0 (which prints the current object or array, but does not recurse).
  • recurseMaxLength: Removed. maxLength is now used always. This is because values are printed multi-line; apart for the indentation there’s the same amount of space available regardless of how deeply nested a value is.
  • maxObjectChildren: The default has changed from 3 to 5, which is the same as for maxArrayChildren.
  • Finally, the new indent: string option is the indent used when recursing. It defaults to " " (two spaces).

Before:

At root["user"]:
Expected a string
Got: {"firstName": "John", "lastName": "Doe", "dateOfBirth": Date, (4 more)}

After:

At root["user"]:
Expected a string
Got: {
  "firstName": "John",
  "lastName": "Doe",
  "dateOfBirth": Date,
  "tags": Array(2),
  "likes": 42,
  (2 more)
}
lydell
published 8.0.0 •

Changelog

Source

Version 8.0.0 (2023-10-14)

Changed: stringUnion now takes an array instead of an object.

Before:

stringUnion({ green: null, red: null });

After:

stringUnion(["green", "red"]);

This is clearer, and made the implementation of stringUnion simpler.

If you have an object and want to use its keys for a string union there’s an example of that in the type inference file.

lydell
published 7.0.1 •

Changelog

Source

Version 7.0.1 (2022-08-07)

  • Fixed: The TypeScript definitions can now be found if you use "type": "module" in your package.json and "module": "Node16" or "module": "NodeNext" in your tsconfig.json.
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc