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

@fourlights/mapper

v1.4.0

Published

A simple data mapper written in typescript

Downloads

113

Readme

@fourlights/mapper

This package makes it easy to map from one data-type to another. It provides a simple API for structuring your mapping needs and is extendable using plugins.

It's useful for:

  • ingesting external APIs and transforming the data to your internal structure
  • exposing a sparse DTO on public APIs
  • anonymizing pii/sensitive data for RBAC or testing (through mapper-plugin-anonymize)

Usage examples

Mapping to a sparse DTO and computing composite properties.

import { map, pick } from '@fourlights/mapper'

// Some object returned from an API
const customer = {
	id: '123567',
	firstName: 'Jane',
	lastName: 'Doe',
	creditCard: {
		number: '6271701225979642',
		issuer: 'Cabal',
		expiryDate: '03/2026',
		countryCode: 'AR',
	},
	email: '[email protected]',
}

// The sparse DTO
const customerDTO = map<typeof customer>(customer, {
	id: (d) => d.id,
	fullName: (d) => `${d.firstName} ${d.lastName}`,
	email: (d) => d.email,
	creditCard: (d) => pick(d, ['issuer', 'countryCode']), // or omit(d, ['number', 'expiryDate'])
})
// customerDTO
{
	id: '1234567',
	fullName: 'Jane Doe',
	email: '[email protected]',
	creditCard: {
		issuer: 'Cabal',
		countryCode: 'AR',
	},
}

Mutating nested objects or arrays during mapping

import { map, Flatten, type MapperConfig } from '@fourlights/mapper'

// input
const page = { status: { private: true, archived: true }, tags: ['cool', 'example'] }

// config
const config: MapperConfig<typeof page> = {
	tags: { value: (data) => data.tags, apply: (r) => r.toUpperCase() },
	is: { value: (data) => data.status, options: { structure: Flatten } },
	is_not: {
		value: (data) => data.status,
		apply: (r) => !r,
		options: { structure: Flatten },
	},
}

// result
const result = map(page, config)
// result
{
	tags: ['COOL', 'EXAMPLE'],
	is_private: true,
	is_archived: true,
	is_not_private: false,
	is_not_archived: false,
}

Features

  • Map data using a configuration object
  • Automatically map nested objects and arrays
  • Use plugins to extend functionality
  • Supports TypeScript, ESM and CJS

Table of contents

Installation

You can install this package using npm:

npm install @fourlights/mapper

Usage

Import the package in your TypeScript or JavaScript file:

import mapper from '@fourlights/mapper'

And then define the mapper configuration for your data:

import { differenceInYears } from 'date-fns' // just as an example

const user = { firstName: 'John', lastName: 'Doe', birthdate: new Date(1990, 1, 1) }

const config: MapperConfig<typeof user> = {
	name: (data) => `${data.firstName} ${data.lastName}`,
	birthdate: (data) => data.birthdate,
	age: (data) => differenceInYears(new Date(), data.birthdate),
}

console.log(mapper.map(user, config))

Which will output (depending on the current date):

{
	"name": "John Doe",
	"birthdate": "1990-01-01T00:00:00.000Z",
	"age": 33
}

The above example uses a shorthand syntax for mapping properties. The shorthand key: (data) => data.key is equivalent to key: { value: (data) => data.key }. The long-form is required when you want to use options or when you want to map nested objects or arrays.

Advanced usage

More advanced configurations allow for manipulating nested arrays and objects:

const page = { status: { private: true, archived: true }, tags: ['cool', 'example'] }
const config: MapperConfig<typeof page> = {
	tags: { value: (data) => data.tags, apply: (r) => r.toUpperCase() },
	is: { value: (data) => data.status, options: { structure: mapper.Flatten } },
	is_not: {
		value: (data) => data.status,
		apply: (r) => !r,
		options: { structure: mapper.Flatten },
	},
}

console.log(mapper.map(page, config))

Which will output:

{
	tags: ['COOL', 'EXAMPLE'],
	is_private: true,
	is_archived: true,
	is_not_private: false,
	is_not_archived: false,
}

Both the apply and options.structure functions receive the following arguments on each iteration:

  • rowValue: the value of the current row (e.g. cool)
  • outerKey: the key of the parent holding the iterable value (e.g. tags)
  • innerKey: the key of the current row (normally the array-index or object property name, e.g. 0 or private, but if you supply a structure function it will be the result of that function)

Options

The options object can contain the following properties:

  • structure: an optional function that will be called for each key in the object or array. The function should return the key that will be used in the output object. If this function is not provided, the original key will be used.
  • init: an optional function that sets an explicit initial value for the property.

Structure functions

Two functions for manipulating structure are provided out of the box:

  • mapper.Flatten: flattens the keys of the object or array. This is useful when you want to flatten nested objects or arrays. Uses an underscore as a separator.
  • mapper.Keep: keeps the current structure intact (default)

Plugins

The package also provides a plugin system that allows you to re-use mapper configuration logic. You can create your own plugins by implementing the MapperPlugin interface.

Example usage:

import { map, type MapperConfig } from '@fourlights/mapper'
import {
	ChangeCasingPlugin,
	type ChangeCasingPluginPropertyOptions,
} from '@fourlights/mapper/plugins/changeCasing'

const user = { firstName: 'John', lastName: 'Doe', birthdate: new Date(1990, 1, 1) }
const config: MapperConfig<typeof user> = {
	name: (data) => `${data.firstName} ${data.lastName}`,
	birthdate: (data) => data.birthdate,
	age: (data) => differenceInYears(new Date(), data.birthdate),
}

console.log(map(user, config, { plugins: [new ChangeCasingPlugin({ casing: 'upper' })] }))

// or alternatively you can set the plugin options per property
const alternativeconfig: MapperConfig<typeof user, ChangeCasingPluginPropertyOptions> = {
	name: {
		value: (data) => `${data.firstName} ${data.lastName}`,
		options: { casing: 'upper' },
	},
	birthdate: (data) => data.birthdate,
	age: (data) => differenceInYears(new Date(), data.birthdate),
}

console.log(map(user, alternativeconfig, { plugins: [new ChangeCasingPlugin()] }))

Which will output:

{
	name: 'JOHN DOE',
	birthdate: '1990-01-01T00:00:00.000Z',
	age: 33,
}

Refer to the changeCasing plugin for an example on how to implement a plugin.

While the above is trivial, plugins can be used to implement more complex logic, such as the automatic anonymization of PII data.

List of available plugins:

Other examples

For more examples, please refer to the tests and the playground.

Contributing

Contributions are welcome. Please open an issue or submit a pull request on GitHub.

License

This package is licensed under the MIT license.