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

pojo-builder

v1.0.3

Published

A simple POJO builder for TypeScript

Downloads

15

Readme

pojo-builder

npm License Build Downloads

pojo-builder is a lightweight and intuitive JavaScript/TypeScript library that facilitates the dynamic construction of Plain Old JavaScript Objects (POJOs) using Proxies. It intelligently infers and assigns types (object or array) based on how nested properties are utilized, allowing for flexible and deferred assignments.

Table of Contents

Features

  • Deferred Initialization: Properties are only initialized as objects or arrays when their usage clarifies the intended type.
  • Dynamic Type Inference: Automatically determines whether a property should be an object or an array based on access patterns.
  • Nested Structures: Supports multiple levels of nesting with seamless chaining.
  • Function Handling: Detects and appropriately handles array methods and object methods.
  • Lightweight: Minimal overhead with no external dependencies.
  • TypeScript Support: Provides type definitions for a better developer experience.

Installation

Install pojo-builder via npm:

npm install pojo-builder

Or using yarn:

yarn add pojo-builder

Usage

Basic Example

Create and build a simple POJO with direct property assignments.

import build, { unwrap, isBuilder } from 'pojo-builder';

const builder = build({});

// Assigning a direct field
builder.some_field = 'hello world';

// Retrieving the built object
const result = unwrap(builder);

console.log(result);
/*
Output:
{
  some_field: 'hello world'
}
*/

Nested Objects

Dynamically create nested objects without prior initialization.

import build, { unwrap, isBuilder } from 'pojo-builder';

const builder = build({});

// Assigning a nested object field
builder.some_object.some_field = 'hello world';

// Retrieving the built object
const result = unwrap(builder);

console.log(result);
/*
Output:
{
  some_object: {
    some_field: 'hello world'
  }
}
*/

Arrays

Utilize array methods and index assignments to build arrays dynamically.

import build, { unwrap, isBuilder } from 'pojo-builder';

const builder = build({});

// Using an array method, which initializes 'some_array' as an array
builder.some_array.push('some_value');

// Assigning to an array index, which initializes 'some_array' as an array
builder.some_array[0] = 'another_value';

// Retrieving the built object
const result = unwrap(builder);

console.log(result);
/*
Output:
{
  some_array: ['another_value']
}
*/

Building Elasticsearch Queries

pojo-builder simplifies the construction of complex Elasticsearch queries by allowing direct assignment deep within the object structure without manual initialization.

import build, { unwrap } from 'pojo-builder';

const builder = build({});

// Building a complex Elasticsearch query
builder.query.bool.must.push({
  match: {
    title: 'Elasticsearch'
  }
});
builder.query.bool.filter.term.status = 'published';
builder.sort[0].date = { order: 'desc' };

// Retrieving the built Elasticsearch query
const esQuery = unwrap(builder);

console.log(JSON.stringify(esQuery, null, 2));
/*
Output:
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "title": "Elasticsearch"
          }
        }
      ],
      "filter": {
        "term": {
          "status": "published"
        }
      }
    }
  },
  "sort": [
    {
      "date": {
        "order": "desc"
      }
    }
  ]
}
*/

Building MongoDB Queries

Similarly, pojo-builder can be used to construct MongoDB queries with conditional and deeply nested fields effortlessly.

import build, { unwrap } from 'pojo-builder';

const builder = build({});

// Building a complex MongoDB query
builder.find.name = 'John Doe';
builder.find.age = { $gte: 30 };
builder.find.address.city = 'New York';
builder.update.$set.email = '[email protected]';
builder.options.sort.age = -1;
builder.options.limit = 10;

// Retrieving the built MongoDB query
const mongoQuery = unwrap(builder);

console.log(JSON.stringify(mongoQuery, null, 2));
/*
Output:
{
  "find": {
    "name": "John Doe",
    "age": {
      "$gte": 30
    },
    "address": {
      "city": "New York"
    }
  },
  "update": {
    "$set": {
      "email": "[email protected]"
    }
  },
  "options": {
    "sort": {
      "age": -1
    },
    "limit": 10
  }
}
*/

Conditional Assignments

When building query objects, you might need to conditionally add fields based on certain conditions. pojo-builder allows you to add properties conditionally without worrying about the initialization of intermediate objects and arrays.

Example with Elasticsearch

import build, { unwrap } from 'pojo-builder';

const builder = build({});

const includeDescription = true;
const includeTags = false;

// Building a conditional Elasticsearch query
builder.query.bool.must.push({
  match: {
    title: 'Advanced Elasticsearch'
  }
});

if (includeDescription) {
  builder.query.bool.must.push({
    match: {
      description: 'Deep dive into Elasticsearch'
    }
  });
}

if (includeTags) {
  builder.query.bool.filter.terms.tags = ['search', 'database'];
}

// Retrieving the built Elasticsearch query
const esQuery = unwrap(builder);

console.log(JSON.stringify(esQuery, null, 2));
/*
Output (includeDescription = true, includeTags = false):
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "title": "Advanced Elasticsearch"
          }
        },
        {
          "match": {
            "description": "Deep dive into Elasticsearch"
          }
        }
      ]
    }
  }
}
*/

Example with MongoDB

import build, { unwrap } from 'pojo-builder';

const builder = build({});

const includeEmail = true;
const includePhone = false;

// Building a conditional MongoDB query
builder.find.username = 'jane_doe';
builder.find.age = { $gte: 25 };

if (includeEmail) {
  builder.find.email = '[email protected]';
}

if (includePhone) {
  builder.find.phone = '123-456-7890';
}

builder.update.$set.status = 'active';

const mongoQuery = unwrap(builder);

console.log(JSON.stringify(mongoQuery, null, 2));
/*
Output (includeEmail = true, includePhone = false):
{
  "find": {
    "username": "jane_doe",
    "age": {
      "$gte": 25
    },
    "email": "[email protected]"
  },
  "update": {
    "$set": {
      "status": "active"
    }
  }
}
*/

In both examples, pojo-builder automatically initializes the necessary intermediate objects and arrays based on how properties are accessed and assigned, eliminating the need for manual initialization and making the code cleaner and more maintainable.

API Reference

build(obj)

Creates a builder proxy for the given object. The builder allows for dynamic and deferred property assignments, intelligently inferring whether properties should be objects or arrays based on usage.

Parameters

  • obj (T): The initial object to wrap with the builder.

Returns

  • T: A proxy object that facilitates deferred and intelligent property assignments.

Example

import build, { unwrap } from 'pojo-builder';

const builder = build({});

builder.name = 'John Doe';
builder.address.city = 'New York';

const result = unwrap(builder);
console.log(result);
/*
Output:
{
  name: 'John Doe',
  address: {
    city: 'New York'
  }
}
*/

unwrap(obj)

Retrieves the original object from the builder proxy.

Parameters

  • obj (T): The builder proxy to unwrap.

Returns

  • T: The original constructed object.

Example

import { unwrap } from 'pojo-builder';

const builder = build({});
builder.age = 30;

const result = unwrap(builder);
console.log(result);
/*
Output:
{
  age: 30
}
*/

isBuilder(obj)

Checks whether a given object is a builder proxy.

Parameters

  • obj (any): The object to check.

Returns

  • boolean: true if the object is a builder proxy, otherwise false.

Example

import { isBuilder } from 'pojo-builder';

const builder = build({});
console.log(isBuilder(builder)); // true

const plainObject = { name: 'Alice' };
console.log(isBuilder(plainObject)); // false

Advanced Usage

Handling Functions

pojo-builder intelligently handles function calls, especially array methods. When an array method like push is invoked, the builder initializes the corresponding property as an array.

import build, { unwrap } from 'pojo-builder';

const builder = build({});

// Using array methods
builder.items.push('item1');
builder.items.push('item2');

// Retrieving the built object
const result = unwrap(builder);
console.log(result);
/*
Output:
{
  items: ['item1', 'item2']
}
*/

Conditional Assignments

When building query objects, you might need to conditionally add fields based on certain conditions. pojo-builder allows you to add properties conditionally without worrying about the initialization of intermediate objects and arrays.

Example with Elasticsearch

import build, { unwrap } from 'pojo-builder';

const builder = build({});

const includeDescription = true;
const includeTags = false;

// Building a conditional Elasticsearch query
builder.query.bool.must.push({
  match: {
    title: 'Advanced Elasticsearch'
  }
});

if (includeDescription) {
  builder.query.bool.must.push({
    match: {
      description: 'Deep dive into Elasticsearch'
    }
  });
}

if (includeTags) {
  builder.query.bool.filter.terms.tags = ['search', 'database'];
}

// Retrieving the built Elasticsearch query
const esQuery = unwrap(builder);

console.log(JSON.stringify(esQuery, null, 2));
/*
Output (includeDescription = true, includeTags = false):
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "title": "Advanced Elasticsearch"
          }
        },
        {
          "match": {
            "description": "Deep dive into Elasticsearch"
          }
        }
      ]
    }
  }
}
*/

Example with MongoDB

import build, { unwrap } from 'pojo-builder';

const builder = build({});

const includeEmail = true;
const includePhone = false;

// Building a conditional MongoDB query
builder.find.username = 'jane_doe';
builder.find.age = { $gte: 25 };

if (includeEmail) {
  builder.find.email = '[email protected]';
}

if (includePhone) {
  builder.find.phone = '123-456-7890';
}

builder.update.$set.status = 'active';

const mongoQuery = unwrap(builder);

console.log(JSON.stringify(mongoQuery, null, 2));
/*
Output (includeEmail = true, includePhone = false):
{
  "find": {
    "username": "jane_doe",
    "age": {
      "$gte": 25
    },
    "email": "[email protected]"
  },
  "update": {
    "$set": {
      "status": "active"
    }
  }
}
*/

In both examples, pojo-builder automatically initializes the necessary intermediate objects and arrays based on how properties are accessed and assigned, eliminating the need for manual initialization and making the code cleaner and more maintainable.

Avoiding Over-Inference

pojo-builder infers the type (object or array) based on the first usage of a property. Subsequent conflicting usages will not change the initialized type. It's essential to ensure consistent usage to avoid unexpected behaviors.

import build, { unwrap } from 'pojo-builder';

const builder = build({});

// First usage as an object
builder.config.settings.theme = 'dark';

// Attempting to use as an array later
builder.config.settings.push('extra_setting'); // This will throw an error

const result = unwrap(builder);
console.log(result);

Note: Always decide the intended type (object or array) before performing deep assignments to maintain consistency.

License

This project is licensed under the MIT License.


Happy Building!