Skip to content

ArthurYung/keeper

Repository files navigation

English | 简体中文

License Version Downloads

Keeper is a library for securely accessing properties of js objects.

It generates a Keeper instance by receiving a string describing the object type. Through the API provided by this instance, we can access data of the expected safe type, or create a new object that fully complies with the type description for use.

Keeper also has excellent TypeScript support. It can generate corresponding type declaration files based on the received type description string, eliminating the need to manually create these files.

Example:

const userKeeper = createKeeper(`
  name string
  age  int    renamefrom:user_age
`);

// this type is {name: string, age: number}
const data = userKeeper.from({
  name: "bruce",
  user_age: "18.0",
});

console.log(data); // { name: 'bruce', age: 18 }
const age = userKeeper.read({ user_age: "18.2" }, "age"); // 18

📦 Install

npm i keeper-js

🔨 Usage

Type Description

Keeper defines objects by receiving a string text that describes the object, which follows the format below:

<property> <type> <extentions>
  • <property>: Property name, supporting strings or numbers.
  • <type>: Property type, which can be a basic type (such as string, int, float, see details below) or an array type (such as int[]). In addition, it also supports using *<extends> format to implement nested types.
  • <extensions> (optional): Additional descriptions of the current property, currently supporting <copyas>:<alias> (copy the current type as a new property named <alias>) and <renamefrom>:<property> (the current property value returns from the <property> property of the source object).

Each description item is separated by one or more spaces, object descriptions support multiple lines, each line describes a property, and also supports comments with //. Example:

import { createKeeper } from "keeper-js";

const userInfo = createKeeper(`
   name    string
   age     int      renamefrom:user_age
`);

const human = createKeeper(
  `
  id      int
  scores  float[]
  info    *userInfo
`,
  { extends: { userInfo } },
); // Declare the inherited attributes of userInfo.

const data = human.from({
  id: "1",
  scores: ["80.1", "90"],
  info: { name: "bruce", user_age: "18.0" },
});

// data: {
//   id: 1,
//   scores: [80.1, 90], // Transform string into float number.
//   info: {
//     name: 'bruce',
//     age:  18,   // Retrieve the value from 'user_age' and convert the float string into an integer number.
//   }
// }

Object Access

The Keeper instance provides two methods for data retrieval, from(obj) and read(obj, path), which are used to generate a new object based on the type description and source object, and to get the value of the specified path in the source object according to the type description, respectively.

When we need to safely get a value from an object, we can use the read API to operate, for example:

const sourceData = {
  id: "1",
  scores: ["80.1", "90"],
  info: { name: "bruce", user_age: "18.0" },
};
const name = human.read(sourceData, "id"); // 1

This method supports multi-layer nested access, for example:

const userInfo = createKeeper(`
   name    string
   age     int      renamefrom:user_age
`);

const human = createKeeper(
  `
  id       int
  bros     *userInfo[]
  baseInfo *userInfo
`,
  { extends: { userInfo } },
); // Declare the inherited attributes of userInfo.

const sourceData = {
  id: "1",
  bros: [
    { name: "bro1", user_age: "16.0" },
    { name: "bro2", user_age: "17.2" },
  ],
  info: { name: "bruce", user_age: "18.1" },
};
const name = human.read(sourceData, "info.name"); // 'bruce'
const bro1Name = human.read(sourceData, "bros[0].name"); // 'bro1'

When we expect to correct from the source data and get an object that fully conforms to the type declaration definition, we can use the from API to operate, for example:

const sourceData = {
  id: "1",
  bros: [],
  info: { name: "bruce", user_age: "18.1" },
};
human.from(sourceData); // { id: 1, bros: [], { name: 'bruce', age: 18 } }

Note that when the original data is empty and the corresponding declared property is not an empty type (null|undefined), a default value will be given according to the declared type, for example:

const sourceData = {
  id: "1",
  bros: [],
  info: {},
};
human.from(sourceData); // { id: 1, bros: [], { name: '', age: 0 } }
human.read(sourceData, "bros[0].age"); // 0

Typescript Support

Keeper has good ts support. You can get ts types from the defined keeper instance through the exported DefineKeeperInterface type. Monosnap screencast 2024-02-24 01-12-58

In addition, the from() and read() methods also have good ts support: Monosnap screencast 2024-02-24 01-22-08 Monosnap screencast 2024-02-24 01-23-19

Lazy Parsing

The second parameter of createKeeper can be set to lazy as true. In this way, the Keeper will parse the type only at the first access, rather than parsing it at creation.

const userInfo = createKeeper(
  `
  name    string
  age     int      renamefrom:user_age
`,
  { lazy: true },
);

userInfo.properties.has("name"); // false
userInfo.get({ name: "bruce", user_age: "18.0" }, "name");
userInfo.properties.has("name"); // true

The supported types

Data Type JS Type Default Remarks
bool boolean false -
int number 0 Integer type
float number 0 Floating point type
string string '' -
null null null -
undefined undefined undefined -
func Function () => {} -
object Object {} -

Benchmark

Files: benchmark/index.js

result: benchmark.html lazy.api.html

License

MIT

About

A JavaScript library for securely accessing object properties

Resources

License

Stars

Watchers

Forks

Packages

No packages published