1.0.13 • Published 3 years ago

xrtlibrary-traverse v1.0.13

Weekly downloads
2
License
BSD-3-Clause
Repository
-
Last release
3 years ago

XRTLibrary-Traverse

Introduction

In practice, we got trouble in reading configurations from JSON object securely and also in less lines of code.

For example, we have a socket configuration like:

let config = {
    "address": "127.0.0.1",
    "port": 443
};

In real word, you shouldn't read the configuration like this:

let address = config["address"];
let port = config["port"];

Because it's insecure, the "address" and "port" must exist and the "port" must be an integer larger than 0. So if you want to write secure codes, you have to write a lot of codes.

So we wrapped these codes into this module, now you can use following code to read it securely in only several lines of code. Like:

const XRTLibTraverse = require("xrtlibrary-traverse");
try {
    //  Wrap the configuration.
    let info = XRTLibTraverse.WrapObject(config, false);

    //  Read address and port.
    let address = info.sub("address").notNull().typeOf(String).unwrap();
    let port = info.sub("port").notNull().integer().minExclusive(0).unwrap();
} catch(error) {
    console.log("Invalid configuration.");
    //  Exit here.
}

Installation

To install this package, you can use NPM by typing following command:

npm install xrtlibrary-traverse --save

Then you can import this library in your JavaScript code:

const XRTLibTraverse = require("xrtlibrary-traverse");

API

(Constant) Traverse.DEFAULT_COMPARATOR

The default value comparator used by traverse module (which uses JavaScript's "==", "<=", "<", ">=", ">" operators to compare values).

(Class) Traverse

Traverse helper.

traverse.typeOf(constructor)

Check the type of inner object.

Exception(s):

  • Traverse.ParameterError: Raised if the constructor is not valid.
  • Traverse.TypeError: Raised if the inner object is not constructed by the constructor.

Parameter(s):

  • constructor ({new(...args: any[]): object}): The constructor of the type.

Return value:

  • (Traverse) Self reference.

traverse.numeric()

Assume that the inner object is numeric.

Exception(s):

  • Traverse.TypeError: Raised if the inner object is not numeric.

Return value:

  • (Traverse) Self reference.

traverse.integer()

Assume that the inner object is an integer.

Exception(s):

  • Traverse.TypeError: Raised if the inner object is not an integer.

Return value:

  • (Traverse) Self reference.

traverse.boolean()

Assume that the inner object is a boolean.

Exception(s):

  • Traverse.TypeError: Raised if the inner object is not boolean.

Return value:

  • (Traverse) Self reference.

traverse.string()

Assume that the inner object is a string.

Exception(s):

  • Traverse.TypeError: Raised if the inner object is not string.

Return value:

  • (Traverse) Self reference.

traverse.stringValidate(charTable)

Validate the string by given character table.

Exception(s):

  • Traverse.ParameterError: Raised if the character table is not string.
  • Traverse.FormatError: Raised when the inner string mismatched with the character table.
  • Traverse.TypeError: Raised when the inner object is not string.

Parameter(s):

  • charTable (String): The character table.

Return value:

  • (Traverse) Self reference.

traverse.stringValidateByRegExp(re)

Validate the string by given regular expression.

Exception(s):

  • Traverse.ParameterError: Raised if the "re" parameter is not a RegExp object.
  • Traverse.FormatError: Raised when the inner string mismatched with the regular expression.
  • Traverse.TypeError: Raised if the inner object is not string.

Parameter(s):

  • re (RegExp): The regular expression.

Return value:

  • (Traverse) Self reference.

traverse.jsonLoad()

Load JSON object from current inner object (a string).

Exception(s):

  • Traverse.ParseError: Raised when the failed to parse the JSON object.
  • Traverse.TypeError: Raised if the inner object is not string.

Return value:

  • (Traverse) The parsed JSON object wrapped with Traverse.

Example:

let input = "{\"key\": \"value\"}";
let info = XRTLibTraverse.WrapObject(input, false);
console.log(info.notNull().string().jsonLoad().sub("key").unwrap());  //  Output: "value".

traverse.jsonSave()

Save JSON object to a new Traverse object.

Exception(s):

  • Traverse.TypeError: Raised when cyclic object value was found.
  • Traverse.Error: Raised when other JSON serialization error occurred.

Return value:

  • (Traverse) The serialized JSON string wrapped with Traverse.

Example:

let input = {"key": "value"};
let info = XRTLibTraverse.WrapObject(input, false);
console.log(info.jsonSave().unwrap());     //  Output: "{\"key\": \"value\"}".

traverse.sub(name)

Go to sub directory.

Exception(s):

  • Traverse.TypeError: Raised in one of following situations:
    • The inner object is NULL.
    • The inner object is not an Object or a Map object.
  • Traverse.ParameterError: Raised if the "name" parameter is not a string and the inner object is a Object.
  • Traverse.KeyNotFoundError: Raised if the sub path can't be found.

Parameter(s):

  • name (*): The name(key) of sub directory.

Return value:

  • (Traverse) Traverse object of sub directory.

Example:

let info = XRTLibTraverse.WrapObject({"a": {"b": "c"}}, false);
console.log(info.sub("a").sub("b").unwrap());  //  Output: "c".
let mapping = new Map();
mapping.set("a", {"b": "c"});
let info = XRTLibTraverse.WrapObject(mapping, false);
console.log(info.sub("a").sub("b").unwrap());  //  Output: "c".

traverse.optionalSub(name, defaultValue)

Go to sub directory which can be non-existed.

Exception(s):

  • Traverse.TypeError: Raised in one of following situations:
    • The inner object is NULL.
    • The inner object is not an Object or a Map object.
  • Traverse.ParameterError: Raised if the "name" parameter is not a string and the inner object is a Object.

Parameter(s):

  • name (*): The name(key) of sub directory.
  • defaultValue (*): The default value if the directory doesn't exist.

Return value:

  • (Traverse) Traverse object of sub directory.

Example:

let info = XRTLibTraverse.WrapObject({"a": "b"}, false);
console.log(info.optionalSub("a", "non-exist").unwrap());  //  Output: "b".
console.log(info.optionalSub("b", "non-exist").unwrap());  //  Output: "non-exist".

traverse.notNull()

Assume that the inner object is not NULL.

Exception(s):

  • Traverse.TypeError: Raised when the inner object is NULL.

Return value:

  • (Traverse) Self reference.

traverse.min(threshold, comparator = Traverse.DEFAULT_COMPARATOR)

Give minimum value threshold to the inner object.

Expected:

  • inner >= threshold

Exception(s):

  • Traverse.ParameterError: Raised if the type of the inner object is different to the threshold object.
  • Traverse.ValueOutOfRangeError: Raised if the value is not within the threshold.

Parameter(s):

  • threshold (*): The threshold.
  • comparator (Traverse.Comparator): (Optional) The comparator.

Return value:

  • (Traverse) Self reference.

Example:

let info = XRTLibTraverse.WrapObject(100, false);
info.min(50);  //  Nothing happened.
info.min(150); //  An error will be raised.

traverse.minExclusive(threshold, comparator = Traverse.DEFAULT_COMPARATOR)

Give exclusive minimum value threshold to the inner object.

Expected:

  • inner > threshold

Exception(s):

  • Traverse.ParameterError: Raised if the type of the inner object is different to the threshold object.
  • Traverse.ValueOutOfRangeError: Raised if the value is not within the threshold.

Parameter(s):

  • threshold (*): The threshold.
  • comparator (Traverse.Comparator): (Optional) The comparator.

Return value:

  • (Traverse) Self reference.

traverse.max(threshold, comparator = Traverse.DEFAULT_COMPARATOR)

Give maximum value threshold to the inner object.

Expected:

  • inner <= threshold

Exception(s):

  • Traverse.ParameterError: Raised if the type of the inner object is different to the threshold object.
  • Traverse.ValueOutOfRangeError: Raised if the value is not within the threshold.

Parameter(s):

  • threshold (*): The threshold.
  • comparator (Traverse.Comparator): (Optional) The comparator.

Return value:

  • (Traverse) Self reference.

Example:

let info = XRTLibTraverse.WrapObject(100, false);
info.max(150);  //  Nothing happened.
info.max(50); //  An error will be raised.

traverse.maxExclusive(threshold, comparator = Traverse.DEFAULT_COMPARATOR)

Give exclusive maximum value threshold to the inner object.

Expected:

  • inner < threshold

Exception(s):

  • Traverse.ParameterError: Raised if the type of the inner object is different to the threshold object.
  • Traverse.ValueOutOfRangeError: Raised if the value is not within the threshold.

Parameter(s):

  • threshold (*): The threshold.
  • comparator (Traverse.Comparator): (Optional) The comparator.

Return value:

  • (Traverse) Self reference.

traverse.range(minValue, maxValue, comparator = Traverse.DEFAULT_COMPARATOR)

Give value threshold to the inner object.

Expected:

  • min <= inner <= max

Exception(s):

  • Traverse.ParameterError: Raised if the type of the inner object is different to the threshold objects.
  • Traverse.ValueOutOfRangeError: Raised if the value is not within the thresholds.

Parameter(s):

  • minValue (*): The minimum threshold.
  • maxValue (*): The maximum threshold.
  • comparator (Traverse.Comparator): (Optional) The comparator.

Return value:

  • (Traverse) Self reference.

traverse.selectFromArray(from)

Select an item from specific array (inner object as the index).

Exception(s):

  • Traverse.TypeError: Raised in following situations:
    • The inner object is NULL.
    • The inner object is not an integer.
  • Traverse.IndexOutOfRangeError: Raised if the index out of range.
  • Traverse.ParameterError: Raised if the "from" parameter is not valid (not an array).

Parameter(s):

  • from (Array): The array.

Return value:

  • (Traverse) Traverse object of selected item.

Example:

let info = XRTLibTraverse.WrapObject({
    "array": ["a", "b", "c", "d"],
    "index1": 0,
    "index2": 3
}, false);
console.log(info.sub("index1").selectFromArray(info.sub("array").unwrap()).unwrap());   //  Output: "a".
console.log(info.sub("index2").selectFromArray(info.sub("array").unwrap()).unwrap());   //  Output: "d".

traverse.selectFromObject(from)

Select an item from specific object (inner object as the key).

Exception(s):

  • Traverse.TypeError: Raised in following situations:
    • The inner object is NULL.
    • The inner object is not an integer.
  • Traverse.KeyNotFoundError: Raised if the key doesn't exist.

Parameter(s):

  • from (Object): The object.

Return value:

  • (Traverse) Traverse object of selected item.

Example:

let info = XRTLibTraverse.WrapObject({
    "protocol": "SOCKS5"
});
console.log(info.sub("protocol").selectFromObject({
    "SOCKS5": 5,
    "SOCKSv4a": 4
}).unwrap());   //  Output: 5.

traverse.selectFromObjectOptional(from, defaultValue)

Select an optional item from specific object (inner object as the key).

Exception(s):

  • Traverse.TypeError: Raised in following situations:
    • The inner object is NULL.
    • The inner object is not an integer.

Parameter(s):

  • from (Object): The object.
  • defaultValue (*): The default value if the key doesn't exist.

Return value:

  • (Traverse) Traverse object of selected item.

traverse.selectFromMap(from)

Select an item from specific map (inner object as the key).

Exception(s):

  • Traverse.TypeError: Raised if the inner object is NULL.
  • Traverse.KeyNotFoundError: Raised if the key doesn't exist.
  • Traverse.ParameterError: Raised if the "from" parameter is not valid (not a Map object).

Parameter(s):

  • from (Map): The map.

Return value:

  • (Traverse) Traverse object of selected item.

traverse.selectFromMapOptional(from, defaultValue)

Select an optional item from specific map (inner object as the key).

Exception(s):

  • Traverse.TypeError: Raised if the inner object is NULL.
  • Traverse.ParameterError: Raised if the "from" parameter is not valid (not a Map object).

Parameter(s):

  • from (Map): The map.
  • defaultValue (*): The default value when the key doesn't exist.

Return value:

  • (Traverse) Traverse object of selected item.

traverse.objectForEach(callback)

Iterate an object.

Exception(s):

  • Traverse.TypeError: Raised in following situations:
    • The inner object is NULL.
    • The inner object is not an Object.

Parameter(s):

  • callback ((value: Traverse) => void): The callback.

Return value:

  • (Traverse) Self reference.

Example:

let info = XRTLibTraverse.WrapObject({
    "a": 1,
    "b": 2,
    "c": 3
}, false);
info.objectForEach(function(value) {
    console.log(value.unwrap());
});
//  Output:
//    1
//    2
//    3

traverse.objectForEachEx(callback)

Iterate an object (will callback with key parameter).

Exception(s):

  • Traverse.TypeError: Raised in following situations:
    • The inner object is NULL.
    • The inner object is not an Object.

Parameter(s):

  • callback ((value: Traverse, key: string) => void): The callback.

Return value:

  • (Traverse) Self reference.

Example:

let info = XRTLibTraverse.WrapObject({
    "a": 1,
    "b": 2,
    "c": 3
}, false);
info.objectForEachEx(function(value, key) {
    console.log(key + " => " + value.unwrap());
});
//  Output:
//    a => 1
//    b => 2
//    c => 3

traverse.objectSet(key, value)

Set a key-value pair within an object.

Exception(s):

  • Traverse.TypeError: Raised in following situations:
    • The inner object is NULL.
    • The inner object is not an Object.

Parameter(s):

  • key (String): The key.
  • value (*): The value.

Return value:

  • (Traverse) Self reference.

Example:

let data = {};
let info = XRTLibTraverse.WrapObject(data, false);
info.objectSet("key", "value");
console.log(data);   //  Output: {"key": "value"}

traverse.objectHas(key)

Get whether an object has specified key.

Exception(s):

  • Traverse.TypeError: Raised in following situations:
    • The inner object is NULL.
    • The inner object is not an Object.

Parameter(s):

  • key (String): The key.

Return value:

  • (Boolean) True if so.

Example:

let data = {"a": 1234, "c": 5678};
let info = XRTLibTraverse.WrapObject(data, false);
console.log(info.objectHas("a"));   //  Output: true
console.log(info.objectHas("b"));   //  Output: false
console.log(info.objectHas("c"));   //  Output: true

traverse.arrayLength()

Get the length of an array.

Exception(s):

  • Traverse.TypeError: Raised in following situations:
    • The inner object is NULL.
    • The inner object is not an array.

Return value:

  • (Number) The length.

traverse.arrayGetItem(offset)

Get an item from an array.

Exception(s):

  • Traverse.TypeError: Raised in following situations:
    • The inner object is NULL.
    • The inner object is not an array.
  • Traverse.ParameterError: Raised if offset is not an integer.
  • Traverse.IndexOutOfRangeError: Raised if offset is out of range.

Parameter(s):

  • offset (Number): The offset of the item within the array.

Return value:

  • (Traverse) Traverse object of the item.

traverse.arraySetItem(offset, value)

Set an item from an array.

Exception(s):

  • Traverse.TypeError: Raised in following situations:
    • The inner object is NULL.
    • The inner object is not an array.
  • Traverse.ParameterError: Raised if offset is not an integer.
  • Traverse.IndexOutOfRangeError: Raised if offset is out of range.

Parameter(s):

  • offset (Number): The offset of the item within the array.
  • value (***): The item value.

Return value:

  • (Traverse) Self reference.

traverse.arrayPushItem(value)

Push an item to an array.

Exception(s):

  • Traverse.TypeError: Raised in following situations:
    • The inner object is NULL.
    • The inner object is not an array.

Parameter(s):

  • value (***): The item value.

Return value:

  • (Traverse) Self reference.

Example:

let nums = XRTLibTraverse.WrapObject([], false);
for (let i = 1; i <= 3; ++i) {
    nums.arrayPushItem(i);
}
console.log(nums.unwrap());
//  Output: [1, 2, 3]

traverse.arrayPopItem()

Pop an item from an array.

Exception(s):

  • Traverse.TypeError: Raised in following situations:
    • The inner object is NULL.
    • The inner object is not an array.
  • Traverse.IndexOutOfRangeError: Raised if the array is already empty.

Return value:

  • (Traverse) Traverse object of the popped item.
let nums = XRTLibTraverse.WrapObject([1, 2, 3], false);
for (let i = 1; i <= 3; ++i) {
    console.log(nums.arrayPopItem().unwrap());
}
//  Output: 3, 2, 1

traverse.arrayShiftItem()

Shift an item from an array.

Exception(s):

  • Traverse.TypeError: Raised in following situations:
    • The inner object is NULL.
    • The inner object is not an array.
  • Traverse.IndexOutOfRangeError: Raised if the array is already empty.

Return value:

  • (Traverse) Traverse object of the shifted item.
let nums = XRTLibTraverse.WrapObject([1, 2, 3], false);
for (let i = 1; i <= 3; ++i) {
    console.log(nums.arrayShiftItem().unwrap());
}
//  Output: 1, 2, 3

traverse.arrayUnshiftItem(value)

Unshift an item to an array.

Exception(s):

  • Traverse.TypeError: Raised in following situations:
    • The inner object is NULL.
    • The inner object is not an array.

Parameter(s):

  • value (***): The item value.

Return value:

  • (Traverse) Self reference.

Example:

let nums = XRTLibTraverse.WrapObject([], false);
for (let i = 1; i <= 3; ++i) {
    nums.arrayUnshiftItem(i);
}
console.log(nums.unwrap());
//  Output: [3, 2, 1]

traverse.arrayForEach(callback)

Iterate an array.

Exception(s):

  • Traverse.TypeError: Raised in following situations:
    • The inner object is NULL.
    • The inner object is not an array.

Parameter(s):

  • callback ((item: Traverse) => void): The callback.

Return value:

  • (Traverse) Self reference.

Example:

let info = XRTLibTraverse.WrapObject(["I", "love", "you"], false);
info.arrayForEach(function(item) {
    console.log(item.unwrap());
});
//  Output: "I", "love", "you".

traverse.arrayForEachWithDeletion(callback)

Iterate an array with deletion.

Exception(s):

  • Traverse.TypeError: Raised in following situations:
    • The inner object is NULL.
    • The inner object is not an array.

Parameter(s):

  • callback ((item: Traverse) => Boolean): The callback.

Return value:

  • (Traverse) Self reference.

Note(s):

  • If the callback returns true, the item would be deleted.

Example:

let info = XRTLibTraverse.WrapObject(["I", "love", "you"], false);
info.arrayForEachWithDeletion(function(item) {
    return !(item.unwrap() == "love");
});
console.log(info.unwrap());  //  Output: ["love"]

traverse.arrayMinLength(minLength)

Assume the array has a minimum length.

Exception(s):

  • Traverse.TypeError: Raised in following situations:
    • The inner object is NULL.
    • The inner object is not an array.
  • Traverse.SizeError: Raised if the array size exceeds.

Parameter(s):

  • minLength (Number): The minimum length.

Return value:

  • (Traverse) Self reference.

traverse.arrayMaxLength(maxLength)

Assume the array has a maximum length.

Exception(s):

  • Traverse.TypeError: Raised in following situations:
    • The inner object is NULL.
    • The inner object is not an array.
  • Traverse.SizeError: Raised if the array size exceeds.

Parameter(s):

  • maxLength (Number): The maximum length.

Return value:

  • (Traverse) Self reference.

traverse.isNull()

Get whether the inner object is NULL.

Return value:

  • (Boolean) True if so.

traverse.oneOf(selections)

Assume that the inner object is in specific selections.

Exception(s):

  • Traverse.ParameterError: Raised if the type of "selections" parameter is not supported.
  • Traverse.KeyNotFoundError: Raised if the item doesn't exist in the "selections".

Parameter(s):

  • selections (Set|Map|Array|Object): The selections.

Return value:

  • (Traverse) Self reference.

Example:

let info = XRTLibTraverse.WrapObject("test", false);

//  Array.
info.oneOf(["ubuntu", "test"]);   //  Nothing happened.

//  Set.
let testSet = new Set();
testSet.add("test");
info.oneOf(testSet);              //  Nothing happened.

//  Map.
let testMap = new Map();
testMap.set("test", "value");
info.oneOf(testMap);              //  Nothing happened.

//  Object.
info.oneOf({
    "test": "value"
});                               //  Nothing happened.
info.oneOf({});                   //  Error will occurred.

traverse.customRule(callback)

Assume that the inner conforms to custom rule.

Note(s):

  • The callback should return true if the inner conforms the custom rule.

Exception(s):

  • Traverse.Parameter: Raised in the following situations:
    • The callback is not a Function.
    • The callback doesn't return a Boolean.
  • Traverse.Error: Raised when the callback return false.

Parameter(s):

  • callback ((item: *) => Boolean))

Return value:

  • (Traverse) Self reference.

Example:

let info = XRTLibTraverse.WrapObject("info", false);

info.customRule(function(inner) {
    if (inner == "info") {
        return true;
    } else {
        return false;
    }
});

traverse.inner()

(Compatible, use unwrap() in new application) Get the inner object.

Return:

  • (*) The inner object.

traverse.unwrap()

Unwrap the traverse object.

Return:

  • (*) The inner object.

(Class) Traverse.Comparator<T>

Value comparator for traverse module.

new Traverse.Comparator()

Constructs a new object.

comparator.eq(a, b)

Get whether two values ("a" and "b") are equal.

Parameter(s):

  • a (T): The value "a".
  • b (T): The value "b".

Return value:

  • (Boolean) True if so.

comparator.le(a, b)

Get whether value "a" is less than or equal to value "b".

Parameter(s):

  • a (T): The value "a".
  • b (T): The value "b".

Return value:

  • (Boolean) True if so.

comparator.lt(a, b)

Get whether value "a" is less than value "b".

Parameter(s):

  • a (T): The value "a".
  • b (T): The value "b".

Return value:

  • (Boolean) True if so.

comparator.ge(a, b)

Get whether value "a" is greater than or equal to value "b".

Parameter(s):

  • a (T): The value "a".
  • b (T): The value "b".

Return value:

  • (Boolean) True if so.

comparator.gt(a, b)

Get whether value "a" is greater than value "b".

Parameter(s):

  • a (T): The value "a".
  • b (T): The value "b".

Return value:

  • (Boolean) True if so.

Example (use customized comparator)

First, create a class that inherits from this class, implements all 5 methods with your own comparation algorithm, like following:

const Util = require("util");

/*
 *  My custom comparator.
 *
 *  @constructor
 *  @extends {Traverse.Comparator}
 */
function CustomComparator() {
    //  Let parent class initialize.
    Traverse.Comparator.call(this);

    //
    //  Public methods.
    //
    this.eq = function(a, b) {
        return a.key == b.key;
    };
    this.le = function(a, b) {
        return a.key <= b.key;
    };
    this.lt = function(a, b) {
        return a.key < b.key;
    };
    this.ge = function(a, b) {
        return a.key >= b.key;
    };
    this.gt = function(a, b) {
        return a.key > b.key;
    };
}

//  Set inheritance.
Util.inherits(CustomComparator, Traverse.Comparator);

Then you have to create an instance of your custom comparator, like following:

let comparator = new CustomComparator();

Now you can use it with Traverse:

let root = WrapObject({
    "key": 100
}, false);
root.range({"key": 10}, {"key": 1000}, comparator);

(Class) Traverse.Error

Traverse error.

Extend(s):

  • Error

(Class) Traverse.ParameterError

Traverse parameter error.

Extend(s):

  • Traverse.Error

(Class) Traverse.TypeError

Traverse type error.

Extend(s):

  • Traverse.Error

(Class) Traverse.FormatError

Traverse format error.

Extend(s):

  • Traverse.Error

(Class) Traverse.ParseError

Traverse parse error.

Extend(s):

  • Traverse.Error

(Class) Traverse.SizeError

Traverse size error.

Extend(s):

  • Traverse.Error

(Class) Traverse.KeyNotFoundError

Traverse key not found error.

Extend(s):

  • Traverse.Error

(Class) Traverse.IndexOutOfRangeError

Traverse index out of range error.

Extend(s):

  • Traverse.Error

(Class) Traverse.ValueOutOfRangeError

Traverse value out of range error.

Extend(s):

  • Traverse.Error

WrapObject(inner, force)

Wrap an object with Traverse.

Parameter(s):

  • inner (*): The inner object.
  • force (Boolean): Still wrap the object when the inner object is a Traverse.

Return value:

  • The traverse object.

Example:

let wrap1 = XRTLibTraverse.WrapObject({"key": "value"}, false);
let wrap2 = XRTLibTraverse.WrapObject(wrap1, false);
let wrap3 = XRTLibTraverse.WrapObject(wrap1, true);

//  Next three statements have the same output (output: "value").
console.log(wrap1.sub("key").inner());
console.log(wrap2.sub("key").inner());
console.log(wrap3.unwrap().sub("key").inner());
1.0.13

3 years ago

1.0.11

5 years ago

1.0.10

5 years ago

1.0.9

5 years ago

1.0.8

5 years ago

1.0.7

5 years ago

1.0.6

5 years ago

1.0.5

5 years ago

1.0.4

5 years ago

1.0.3

5 years ago

1.0.2

5 years ago

1.0.1

6 years ago