1.0.0-alpha.10-PRE3 • Published 10 months ago

moonlifedb v1.0.0-alpha.10-PRE3

Weekly downloads
-
License
ISC
Repository
-
Last release
10 months ago

MoonlifeDB: A simple JSON database

Version: 1.0.0 ALPHA 10-PRE3

If you need help or you want to help with development, you can contact the main developer: uwu.fox on Discord or squlissa on Twitter

npm i moonlifedb@latest

Source code: MoonlifeDB on GitHub (You can also report bugs here!)

  • TypeScript: We use 5.1.3, but you can use older versions (tested with 4.7+)
  • lodash: We use 4.17.21 (not tested with older or newer versions)

How to use the database?

WARNING: The database is currently in alpha state. It means there are still a lot of functions missing. When we release it in beta, it will be mostly safe to use. WARNING 2: Main Database class is mostly done, but in Alpha 9 we redone it completely and we are still testing it, so use at your own risk!

How to start working with Database?

Installing and importing

First of all you need to install it. You can run this command:

npm i moonlifedb@latest

After it got installed you need to import it. You can do it like that:

import { Database, LocalStorage } from 'moonlifedb';

Or like that:

const { Database, LocalStorage } = require('moonlifedb');

You just imported the class and now we have to call the construction. You can do it like that:

const adapter = new LocalStorage({ path: 'path' }) // Note #1
const db = new Database(adapter)

*Note #1: path is an exact path to the folder, where you will be storing json files. For example, if you have a folder in the same head folder as project, use "./database" (you can use any name OS allows!)*

Done! Now you can use all functions of the database! But you also have to create a table. How to do this?

Create your first table!

There are many ways. The first one is very simple: do it manually. You can just create a json file with any name in the folder you specified in the constructor. the second one is more complicated but allows to do way more things. You need to import the TableCreator class.

WARNING! THIS CLASS IS NOT STATED IN FULL DOCUMENTATION!

import { TableCreator, LocalStorage } from 'moonlifedb';
const adapter = new LocalStorage({ path: './database' }); // this is an example!
const creator = new TableCreator(adapter);

creator.create("anyNameHere"); // Note #2, #3

***Note #2**: you can use any name **file system allows**. That means, for example, you can't use dots, start file names with numbers, etc.*<br />
***Note #3**: In current version it's **not supported**, but it also has second argument to create structure file for this table. It means you can strictly specify what types of values needed for this table.*<br /><br />

## How to work with Database?
The Database has some main methods to work with JSON files.<br />
For example we need to store some info about Bob in "accounts" table:
```json
{
    "Bob": {
        "age": 28,
        "loves": "coding"
    }
}

Write

To write it, use Database#create() method:

db.create('accounts', { 
    key: 'Bob', 
    value: { 
        "age": 28, 
        "loves": "coding" 
    }
});

Read

To access it, use Database#read() method:

const result = db.read('accounts', { key: 'Bob' });
// and it will return value we specified when created this line.

Edit

If you want to edit some info about Bob, use Database#edit() method:

db.edit('accounts', { key: 'Bob.loves', value: "music" }); // Note #4

*Note #4: What you've seen is called pointers. If you want to know more about these, read the article "What are pointers?"*

Check

If you need to check if this key exists, use Database#check() method:

db.check('accounts', { key: 'Bob' });
// Should return "true" (boolean) if this key actually exists.
// Returns "false" (boolean) if value of this key is undefined (does not exist in the database)

How about asynchronous?

Yes, async functions are cool! You should definetly use them! Database#create() and Database#edit() are already async functions and they return a promise. That means if you do this:

// this only works in async function's body
const result = await db.create('accounts', { key: 'Jack', value: { "age": 17, "loves": "animals" } })
// the "retult" variable will be the same, as value and you can work with it later.

With Database#edit() is a little different:

const result = await db.edit('accounts', { key: 'Jack.age', value: 18 } })
// as you can see, we used pointers here.
// the "result" variable will not be the same as value. Instead, it returns whole value in database after it got edited. So it means it will be: { age: 18, loves: "animals" }

Database#check() is not async because it returns boolean. But we have a bit different method: Database#checkres()! It is asynchronous and it returns a promise. They also have the same signature and event recognizes both as "check"! If this key exists it returns it's value. If not it returns undefined:

const result = await db.checkres('accounts', { key: 'Jack' });
// and it should return this: { age: 18, loves: "animals" }

What are pointers?

Small but very important topic. What are pointers? They can specify what exactly you need to match. For example, if you have a big object in value, isn't painful to always edit whole object when you need to do a change in only 1 subkey? That's exactly what pointers fix! There are 2 types of them: dot (.) and tilde (~).

Dot pointer

Dot is allowed in any method (except Database#create()). It means precise path to this subkey. For example we have an object like this:

"object": {
    "stats": {
        "hp": 10,
        "mana": 100
    },
    "heldItem": {
        "name": "Sword",
        "id": "1029812",
        "uidd": "6c813-141d4-63e5cb2-001",
        "abilities": {
            "first": {
                "mana": 20
            }
        }
    }
}

And for example we need to get uidd of held item. To do this we can just access it with:

const result = db.read('items', { key: 'object' });
const uidd = result.heldItem.uidd

but it's too slow and tiring. Instead we can do:

const result = db.read('items', { key: 'object.heldItem.uidd' });

It does absolutely the same, but way faster! What about other methods? Well, they work with the same principle. You can edit a single subkey, or you can check if subkey exists. Do what you want!

Tilde pointer

Tilde is only allowed it Database#read() method and has 2 sides: mainKey~subKey. mainKey is the key, that you are trying to access, and subKey is a key of an object inside value of this mainKey. When doing a request with this pointer it returns an object with every occurence, that ends with subKey. For example, we have an object like this:

"object": {
    "stats": {
        "hp": 10,
        "mana": 100
    },
    "heldItem": {
        "name": "Sword",
        "id": "1029812",
        "uidd": "6c813-141d4-63e5cb2-001",
        "abilities": {
            "first": {
                "mana": 20
            }
        }
    }
}

And if we do this request:

const result = db.read('items', { key: 'object~mana' })

it returns an object like this:

{
  "object.stats.mana": 100,
                ^^^^       
  "object.heldItem.abilities.first.mana": 20
                                   ^^^^ // Note #5
}

As you can see, every occurence ends with our subKey. *Note #5: You can't use comments in JSON files! It only for demonstration!*

Can I listen to these events?

Database EventEmitter

Yes! Of course you can! You can use Database class to listen for access events.

const adapter = new LocalStorage({ path: 'path' })
const db = new Database(adapter)

db.on('access',
    async (event) => {
        console.log(event);
    }
)

Event constructor

argument event returns Event class, which looks like this:

Event {
  method:    string, // Note #6
  type:     'get'|'put',
  body: {
    table:   string,
    key:     string,
    value:   any, // Note #7
    resolve: boolean,
    newline: boolean
  }:         object
}

*Note #6: Database#check() and Database#checkres() have the same method name: check! The only difference is Database#checkres()has Event.body.resolve set to true!* *Note #7: Event.body.value is mostly used to return value of this key, but in some cases like Database#check() it returns boolean value because it method checks if value for this key exists and also because it's not async!*

Documentation

WARNING! If you can't find a class, that database has but not stated in documentation, it means it's still in development and not supported!

WARNING! This documentation is still incomplete. Will be released fully in future updates!

Database

Main database class to work with json files.

Database Constructor

PARAMETERTYPEDESCRIPTION
adapterLocalStorage | ExternalConnectionAdapter to search for the database folder.
settingsObjectSettings to control how database works.
PARAMETERTYPEDESCRIPTION
alertsboolean | undefined(optional) Should alerts be enabled?
ignoreDeprecationsboolean | undefined(optional) Should deprecation force alerts be ignorred?
useTabulationJSONFormatter | undefined(optional) A constructor how database should be formatted (adds spacing for json objects)
typeShardCollection | 'SingleFile' | undefined,(optional, not supported)
adapter: LocalStorage | ExternalConnection,
settings: {
    alerts: boolean | undefined,
    ignoreDeprecations: boolean | undefined
    overwrite: boolean | undefined,
    useTabulation: JSONFormatter | undefined
    type: ShardCollection | 'SingleFile' | undefined,
} | undefined
Database {
    adapter: LocalStorage | ExternalConnection;
    tablePath: string;
    ip: string | undefined;
    port: string | undefined;
    alerts: boolean;
    ignore: boolean;
    useTabulation: JSONFormatter | undefined;
    type: ShardCollection | 'SingleFile'
}

Database#create()

Create a new line in database.

PARAMETERTYPEDESCRIPTION
tablestringTable name: JSON file name
actionObject:key: stringvalue: anyresolve?: booleankey - identifier in table.value - Value to be referred to this key.(optional) resolve - resolve value of this object as promise.

Returns Promise<any|void> and has put type.

table: string,
action: {
    key: string,
    value: any,
    resolve: boolean | undefined
}

Database#edit()

Edit an existing line in database or create new one.

PARAMETERTYPEDESCRIPTION
tablestringTable name: JSON file name
actionObject:key: stringvalue: anyresolve?: booleannewline?: booleankey - identifier in table.value - Value to be referred to this key.(optional) resolve - resolve value of this object as promise.(optional) newline - if this line does not exist, create it instead.

Returns Promise<any|void> and has put type.

table: string,
action: {
    key: string,
    value: any,
    resolve: boolean | undefined
    newline: boolean | undefined
}

Database#remove()

Remove a line in database.

PARAMETERTYPEDESCRIPTION
tablestringTable name: JSON file name
actionObject:key: stringkey - identifier in table to delete

Returns Promise<void> and has put type.

table: string,
action: {
    key: string
}

Database#read()

Read and return a line from database.

PARAMETERTYPEDESCRIPTION
tablestringTable name: JSON file name
actionObject:key: stringkey - identifier in table to search for

Returns any and has get type.

table: string,
action: {
    key: string,
}

Database#check()

Check if this line in database exists.

PARAMETERTYPEDESCRIPTION
tablestringTable name: JSON file name
actionObject:key: stringkey - identifier in table to search for

Returns boolean and has get type.

table: string,
action: {
    key: string,
}

Database#checkres()

Check if this line in database exists.

PARAMETERTYPEDESCRIPTION
tablestringTable name: JSON file name
actionObject:key: stringkey - identifier in table to search for

HAS THE SAME SIGNATURE "check" IN EVENT MANAGER!

Returns Promise<any|undefined> and has get type.

table: string,
action: {
    key: string,
}

LocalStorage

Database adapter to search for folder on your local device.

LocalStorage Constructor

PARAMETERTYPEDESCRIPTION
optionsObject:path: stringA path to the database local folder.
options: {
    path: string
}
LocalStorage {
    tablePath: string
}

JSONFormatter

A formatter of JSON file: add tabulation.

JSONFormatter Constructor

PARAMETERTYPEDESCRIPTION
optionsObject:whitespace: number | 'tab' | '\t' | undefinedA If whitespace undefined or 'tab' or '\t', it returns '\t'. If number, it returns that many spaces.
options: {
    whitespace: number | 'tab' | '\t' | undefined,
} | undefined
JSONFormatter {
    whitespace: number | '\t'
}

Event

Event constructor to contain information about single database access.

Event Constructor

PARAMETERTYPEDESCRIPTION
methodstringMethod name.
typestring: 'get' | 'put'Type of database access.
bodyobjectAny object returned.
Event {
    method: string,
    type: 'get'|'put',
    body: {
        table: string,
        key: string,
        value: any,
        resolve: boolean,
        newline: boolean
    }: object
}

Snowflake

Snowflake Constructor

PARAMETERTYPEDESCRIPTION
settingsobjectRead table below
PARAMETERTYPEDESCRIPTION
workernumber | bigintZero-based worker ID in number or bigint. 0 by default and can't be negative.
epochnumber | bigintEpoch ofset in milliseconds where to start generating.
Snowflake {
    worker: number | bigint;
    epoch: number | bigint;
    seq: number | bigint;
	lastTime: number | bigint;
}

Snowflake#generate()

Generates unique SnowflakeID based on epoch, worker and sequence.

Returns string, no constructor required.


Snowflake#generateRaw()

Generates unique SnowflakeID based on epoch, worker and sequence. Also returns raw data: binary result, epoch (binary and decimal), worker (binary and decimal) and sequence (binary and decimal).

Returns object, no constructor required.

result: string,
raw: {
    result: string,
    epoch: number | bigint,
    epochBinary: string,
    worker: number | bigint,
    workerBinary: string,
    sequence: number | bigint,
    sequenceBinary: string
}

Snowflake#decode()

Decodes snowflake and returns parts of it.

PARAMETERTYPEDESCRIPTION
snowflakestringSnowflakeID

Returns object.

{
    epoch:    number | bigint,
    worker:   number | bigint, 
    sequence: number | bigint 
}

Team Moonlife