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

@jsonlang/core

v0.0.14

Published

Json Programming Language Core

Downloads

80,588

Readme

👋 JsonLang

It is a Typescript package that provides a simple JSON Programming Language, allowing you to execute a safe logic in Frontend or Backend (NodeJS). Furthermore, it can be stored in the database and rendered to the Frontend-Side to execute/run some business logic.

JsonLang is designed to be extendable. You can define new rules with sync/async handlers.

npm version install size npm downloads Vulnerabilities Maintenance Build Programming Language License FOSSA Status Github Sponsor

Installation

npm install @jsonlang/core

🎉 Features

  1. Typescript. It's a strongly typed npm package
  2. the JsonLang structure is Simple and Optimized. Its structure and rules have a shortcut to make your JSON in a small size.
  3. Its structure is always Consistent. i.e. {"$R": "R1", "$I": ["value1", "value2", {"$R": "R2", "$I": [...] }, ...] }.
  4. Safe & Secure. Each Rule has a secure handler.
  5. Extendable. Easy to add new rules.
  6. Sync/Async. All rules in JsonLang are sync rules, but you can extend it and add async rules.
  7. DRY. You can pass any rule result in a variable to be used in another rule which makes JsonLang JSON more optimized

🏗️ Structure

JsonLang have three main parameters:

  1. $R: (String) is the rule name itself. i.e. and, or, ==, >.
  2. $I: (any[]) is an array of inputs which will be passed to the Rule handler/function, their type depends on the Rule handler, or it can be a nested rule
  3. $O?: (Symbol [Optional]), is an optional field, it accept a name of variable which used to save the Rule result in a variable and can be called in any other rule by { "$R": "Var": "$I": ["variableX"] }. The output value should be unique. If you define the same value more than once, the last one will override the value of the previous one.

🛠️ Methods

Execute

  async execute(jsonLang: IJsonLangParams, data?: {}, options?: { sync: false }): Promise<RuleResult>

Execute is used to run the JsonLang and takes two parameters.

  1. JsonLang: check the Structure
  2. Data: schemaless data object to read/write to it. To get data use the Rule Data
  3. options: execution options. it has one property called sync if it false the execute will be async for async rules, else it will be sync

Execute is the Async version of JsonLang, use it to run all builtin rules and any extended Sync or Async Rules

  execute(jsonLang: IJsonLangParams, data: {} | undefined, options: { sync: true }): RuleResult

Execute is the Sync version of JsonLang, use it to run all builtin rules and any extended Sync Rules

Extend

The First Way:

  registerOne(ruleDefinition: RuleDefinition, ruleHandler: RuleHandler): void

Extend JsonLang by adding 2 params

  • ruleDefinition: Object of
    • identifier: { name: string, shortcut?: string, group?: string }, name(required) is the Rule name, shortcut(optional) is the shortcut. i.e Sum is the name, and + is the shortcut, and group a name of group to categorize/group set of rule under group.
    • inputs: JsonSchema to define the rule inputs
    • output: JsonSchema to define the rule output
  • ruleHandler: Object of two props sync & async, you need to pass the implementation Function (...inputs: RuleInput[]) => RuleResult), inputs(required) is array of all inputs needs for the handler check Input in Structure, and data is the schemaless data check Data in the Execute Section
  registerMany(rules: Rules): void

registerMany allows registering a Map() of rules. The Map key is RuleDefinition, and the Map value is the RuleHandler

The Second Way:

This way is the best practice way to extend JSONLang using decorators and import

import { JsonLang } from '@jsonlang/core';
import { MathRules } from '@jsonlang/math';
import { LogicRules } from '@jsonlang/logic';
import { ArrayRules } from '@jsonlang/array';
import { ObjectRules } from '@jsonlang/object';

const jsonLang = new JsonLang();

jsonLang.import(MathRules, LogicRules, ArrayRules, ObjectRules);

Example

import { JsonLang } from '@jsonlang/core';

const jsonLang = new JsonLang();

@JsonLangExtension('Test') // here pass the groupName
export class TestRules {

  @RuleExtension({
    /* rule name and shortcut */
    identifier: { name: 'RuleOne', shortcut: 'R' },

    /* key-value pair <variableName, jsonschema> for rule's inputs */
    inputs: { inputOne: {type: 'string', enum: ['TP', 'RF']}, inputTwo: {type: 'number'} },

    /* jsonschema object for rule's output */
    output: { type: 'boolean', default: false } 
  })
  RuleOne(
    inputOne: string,
    inputTwo: number
  ): boolean {
    // do the implementation here
  }

  @RuleExtension({
    identifier: { name: 'RuleTwo' },
    inputs: { type: 'array', items: { type: 'string' } },
    output: { type: 'number' } 
  })
  RuleTwo(...inputs: string[]): number {
    // do the implementation here
  }
}

jsonLang.import(TestRules);

⚒️ Builtin Rules

Core

For more Info and examples

import { JsonLang } from '@jsonlang/core';
  • Data

    • Input[]: Array (Size: 2) var name you need to access, Enum of "External" or "Internal"
    • Output: any.
    • Description: if the Input is ["External"] it will return the schemaless data object which you pass it to the execute method, else if the input is ["Internal"], it will return the value of any Output from any rules, Check the Output part or the value passed from the parent rule like filter in array rules.
  • More...

Logical

For more Info and examples

import { JsonLang } from '@jsonlang/core';
import { LogicRules } from '@jsonlang/logic';

const jsonLang = new JsonLang();

jsonLang.import(LogicRules);
  • And or &&

    • Input[]: Array (Size: Unlimited).
    • Output: Boolean (true or false).
    • Description: Do the Anding operation, if any value in Input[] has a value of (null, 0, false), it will return false, else it will return true.
  • Or or ||

    • Input[]: Array (Size: Unlimited).
    • Output: Boolean (true or false).
    • Description: Do the Oring operation, if all values in Input[] has a value of (null, 0, false), it will return false, else it will return true .
  • Equal or ==

    • Input[]: Array (Size: 2).
    • Output: Boolean (true or false).
    • Description: It takes an array of 2 inputs to compare if element one Equal element two or not.
  • NotEqual or =

    • Input[]: Array (Size: 2).
    • Output: Boolean (true or false).
    • Description: It takes an array of 2 inputs to compare if element one Not Equal to element two or not.
  • Not or !

    • Input[]: Array (Size: 1).
    • Output: Boolean (true or false).
    • Description: It takes an array of 1 input inverts its value. If it true it will return false and vice versa.
  • GreaterThan or >

    • Input[]: Array (Size: 2).
    • Output: Boolean (true or false).
    • Description: It takes an array of 2 inputs to compare if element one Greater Than element two or not.
  • LessThan or <

    • Input[]: Array (Size: 2).
    • Output: Boolean (true or false).
    • Description: It takes an array of 2 inputs to compare if element one Less Than element two or not.
  • GreaterThanOrEqual or >=

    • Input[]: Array (Size: 2).
    • Output: Boolean (true or false).
    • Description: It takes an array of 2 inputs to compare if element one Greater Than or Equal element two or not.
  • LessThanOrEqual or <=

    • Input[]: Array (Size: 2).
    • Output: Boolean (true or false).
    • Description: It takes an array of 2 inputs to compare if element one Less Than or Equal element two or not.
  • More...

Math

For more Info and examples

import { JsonLang } from '@jsonlang/core';
import { MathRules } from '@jsonlang/math';

const jsonLang = new JsonLang();

jsonLang.import(MathRules);
  • IsNumber

    • Input[]: Array (Size: 1).
    • Output: Boolean (true or false).
    • Description: Check if the value dataType is a number or not.
  • Sum or +

    • Input[]: Array (Size: unlimited).
    • Output: number.
    • Description: Used to Sum all values. i.e. Input1 + Input2 + .... + InputN.
  • Subtract or -

    • Input[]: Array (Size: unlimited).
    • Output: number.
    • Description: Used to Subtract all values. i.e. Input1 - Input2 - .... - InputN.
  • Multiply or *

    • Input[]: Array (Size: unlimited).
    • Output: number.
    • Description: Used to Multiply all values. i.e. Input1 * Input2 * .... * InputN.
  • Divide or /

    • Input[]: Array (Size: unlimited).
    • Output: number.
    • Description: Used to Divide all values. i.e. Input1 / Input2 / .... / InputN.
  • More...

Array

For more Info and examples

import { JsonLang } from '@jsonlang/core';
import { ArrayRules } from '@jsonlang/array';

const jsonLang = new JsonLang();

jsonLang.import(ArrayRules);
  • All

    • Input[]: Array (Size: Unlimited).
    • Output: Array (Size: Unlimited).
    • Description: It takes an array of inputs and returns them again. It is used to run a list of nested Rules.
  • Filter

    • Input[]: Array (Size: 2) {elements: any[], rule: IJsonLangParams}.
    • Output: Any[].
    • Description: It accepts array of elements with any type to filter them using nested/inner rules, the filter rule will pass each elements as a Data with scope Internal, to access it by the inner rules, you will need to use Data Rule with scope local, check this example.
  • Map

    • Input[]: Array (Size: 2) {elements: any[], rule: IJsonLangParams}.
    • Output: Any[].
    • Description: It accepts array of elements with any type to map them using nested/inner rules, the filter rule will pass each elements as a Data with scope Internal, to access it by the inner rules, you will need to use Data Rule with scope local.
  • Foreach

    • Input[]: Array (Size: 2) {elements: any[], rule: IJsonLangParams}.
    • Output: true.
    • Description: It accepts array of elements with any type to iterate over them using nested/inner rules, the filter rule will pass each elements as a Data with scope Internal, to access it by the inner rules, you will need to use Data Rule with scope local.
  • Flatten

    • Input[]: Array (Size: 2) {elements: any[], level?: number}.
    • Output: true.
    • Description: It accepts array of elements with any type to flatten this array with any level.
  • More...

Object

For more Info and examples

import { JsonLang } from '@jsonlang/core';
import { ObjectRules } from '@jsonlang/object';

const jsonLang = new JsonLang();

jsonLang.import(ObjectRules);
  • Get [In Progress]

    • Input[]: Array (Size: 3) {path: string, defaultValue?: any, data:{}}.
    • Output: Any.
    • Description: It accepts two inputs, the 1st one (required) is a path to get the Data, and the 2nd one (optional) is a default value of the path is not found. the path must follow the dotted style var1.var2 for nested fields and brackets with number for arrays var1.var2[3].var3
  • Set [In Progress]

    • Input[]: Array (Size: 3) {path: string, value: any, data:{}}.
    • Output: Any.
    • Description: It accepts two inputs. The 1st one (required) is a path to update/mutate the Data, and the 2nd one is the value to set. the path must follow the dotted style var1.var2 for nested fields and brackets with number for arrays var1.var2[3].var3. If the path does not exist, the Set Rule will create it.
  • Update [In Progress]

    • Input[]: Array (Size: 3) {path: string, value: any, data:{}}.
    • Output: Any.
    • Description: It accepts two inputs. The 1st one (required) is a path to update/mutate the Data, and the 2nd one is the value to update. the path must follow the dotted style var1.var2 for nested fields and brackets with number for arrays var1.var2[3].var3. If the path does not exist, the Update rule won't do anything.
  • Delete [In Progress]

    • Input[]: Array (Size: 2) {path: string, data:{}}.
    • Output: Any.
    • Description: It accepts two inputs, a path to mutate the Data by deleting a field in the request path. the path must follow the dotted style var1.var2 for nested fields and brackets with number for arrays var1.var2[3].var3. If the path does not exist, the Delete rule won't do anything.
  • More...

💻Examples

For more examples

One Level Example

import { JsonLang } from '@jsonlang/core';
import { LogicRules } from '@jsonlang/logic';

const jsonLang = new JsonLang();

jsonLang.import(LogicRules);

jsonLang.execute( { "$R": "LessThan" , "$I": [10, 20] }, undefined, { sync: true }  ); // true

// or for short
jsonLang.execute( { "$R": "<" , "$I": [10, 20] }, undefined, { sync: true } ); // true

// or use the async function
jsonLang.execute( { "$R": "<" , "$I": [10, 20] } )
  .then(result => {
    console.log(result); // true
  }); 

Nested Levels Example

import { JsonLang } from '@jsonlang/core';
import { MathRules } from '@jsonlang/math';
import { ObjectRules } from '@jsonlang/object';

const jsonLang = new JsonLang();

jsonLang.import(MathRules, ObjectRules);

const result = jsonLang.execute({ 
  $R: '+',
  $I: [
    {
      $R: '+',
      $I: [
        1,
        { $R: '*', $I: [2, 3] },
        5
      ]
    },
    {
      $R: '+',
      $I: [
        1,
        { $R: '*', $I: [3, 3], $O: 'x' },
        5
      ]
    },
    { $R: 'Var', $I: ['x'] },
    { $R: 'Get', $I: ['user.age', null, { $R: 'Data', $I: ['External'] }] }
  ]
}, { user: { name: 'test', age: 100 } }, { sync: true });

console.log(result);
// 136

Access Inner Data

import { JsonLang } from '@jsonlang/core';
import { LogicRules } from '@jsonlang/logic';
import { ArrayRules } from '@jsonlang/array';
import { ObjectRules } from '@jsonlang/object';

const jsonLang = new JsonLang();

jsonLang.import(LogicRules, ArrayRules, ObjectRules);

const result = jsonLang.execute({ $R: 'All', $I: [
  { 
    $R: 'Filter',
    $I: [[1, 3, 5], 'i', { $R: '>', $I: [{ $R: 'Data', $I: ['Internal'] }, 2] }],
  },
  { 
    $R: 'Filter',
    $I: [
      { $R: 'Get', $I: ['data.test', null, { $R: 'Data', $I: ['External'] }] }, 
      'j',
      { $R: '<', $I: [{ $R: 'Data', $I: ['Internal'] }, 500] }
    ]
  }
] }, { data: { id: 'test', test: [100, 300, 700] } }, { sync: true });

console.log(result);

// [ [ 3, 5 ], [ 100, 300 ] ]

Extend Rules Example

import { JsonLang } from '@jsonlang/core';
import { ObjectRules } from '@jsonlang/object';

const jsonLang = new JsonLang();

jsonLang.import(ObjectRules);

jsonLang.registerOne({ name: 'Test', shortcut: 't' }, { 
  sync: (input: any) => { return `${input} Test` },
  async: async (input: any) => { return `${input} Test` } 
});

const result = await jsonLang.execute({ 
  $R: 'Test',
  $I: [
    { $R: 'Get', $I: ['user.age', null, { $R: 'Data' }] }
  ]
}, { user: { name: 'test', age: 100 } }, { sync: true });

console.log(result);
// 100 Test

🧱 Customization

You can extend JsonLang and add any logic you want from well-known sync/async packages like lodash, moment, ajv, axios, mysql, mongoose, ...etc.

Just use the register functions and follow its structure to add whatever you want.

⚠️ Warnings

JsonLang can be extended with any function, and you can override the existing rules, but make sure that any method you will add won't:

  1. Have any security issue
  2. Async method without timeout or with unhandled errors
  3. Block the event loop in backend nodejs https://nodejs.org/en/docs/guides/dont-block-the-event-loop/
  4. Abuse the CPU or the memory

🔌 Compatibility

This library uses Array.map and Array.reduce, so it's not exactly Internet Explorer 8 friendly.

📗 What's Next?

  • Adding more math, logic, object, array, date, and casting methods.
  • Provide plugins to wrap well-known packages like MathJs, Jsonata, Axios, Lodash, MomentJs, ...etc.
  • Adding Jsonschema validation for rules inputs
  • Allow saving, loading, calling other block of rules.
  • Allow logging with different level for rules while execution.
  • Make a UI diagram react npm package to generate, show, build, run and show instances logs.
  • Add more use-cases for many ideas for using JsonLang.

📜 License

JsonLang is MIT licensed