0.0.1 • Published 3 years ago

localstorage_demo_wasm v0.0.1

Weekly downloads
-
License
-
Repository
-
Last release
3 years ago

Local Storage Demo WebAssembly Module

Description

This repo is the WebAssembly module as a part of the local storage demo project's frontend.

Project Structure

Overview

Root Folder

This project is compiled and packed using WASM-PACK-PLUGIN for Webpack, so it is a combination of a Rust crate project and an NPM project. This can be seen from the Cargo.toml file and package.json file at the root folder.

src contains the source code written in Rust. The WASM module is developed as a Rust Crate (Library).

demo_webpack_project contains a demo frontend Javascript project with Webpack. This project demonstrates how to use the released WASM module is a JS-based project.

readme_resources folder contains all the resources used in this README.md file.

wasm_npm_pkg contains the final released WASM module as an NPM package.

Source Folder

lib.rs is the rust crate entry point file. It defines the top-level interface between the WASM module and the Javascript project. Exposed functions in this file is used to be called by external Javascript code.

database is the submodule in the rust crate that defines the CRUD operations on the stored data in the WASM module.

security is the other submodule in the rust crate that defines the encrytion/decrytion functions on the data fields. It defines a dataframe that stores the encrypted data together with random numbers used by the encryption algorithm. It uses the Chacha8Poly1035 algorithm as the main symetric encryption algorithm and PBKDF2 algorithm for encrytion key generation.

Demo Webpack Project Folder

webpack.config.dev.js and webpack.config.prod.js are the webpack configuration files for development mode and production mode respectively.

index.js is the entry JS file for the webpack demo project.

public contains the static files used in this webpack demo project, such as index.html.

mock_data contains the mock configuration and survey data used in this webpack demo project.

Usage

This section introduces how to compile the Rust source code into a release-ready NPM package and run the demo webpack project. It not only tests the correctness of the WASM module implementation, but also gives demonstrative instructions on integration of the WASM module with external Webpack projects.

All the following instructions should be run from the Root Folder.

Local Environment Requirement

The project requires node >= 10.0.0 and npm >= 6.0.0. These need to be installed globally. For installation, see install node and npm.

The project also requires Rust >= 1.42.0 with Rustup and Cargo installed. For installation, see install rust.

Install all the required npm packages by running the following command in shell under the root folder:

npm install

The required Rust Crates listed in Cargo.toml is automatically downloaded and compiled by the rust dependency manager Cargo.

Run the Webpack Demo Project

The index.js entry point file in the demo_webpack_project folder tests all the exposed functions by the WASM module. The results are printed out in the console.

To run and see a demo, simply run (under the Root Folder):

npm run start

This automatically opens the default browser at http://localhost:9123. Open up the console to see the outputs. If there is no errors, and you see something similar to the following figure:

then, all the tests on the exposed functions from this WASM module have passed.

Note that by running the above commend, the WASM-PACK Webpack plugin compiles, optimizes and packages the Rust source code for release. This generates a new folder pkg under the demo_webpack_project folder, which is just an NPM package and is ready to be published onto NPM registry.

To generate a production build of the webpack demo project, run:

npm run build:prod

This produces the build/prod folder under the demo_webpack_project folder.

The production build imitates the production mode in a Webpack project, where the JS code file is split into pieces and all the file names are hashed (including the wasm module).

Using the NPM Package in Other Projects

The final NPM package has been released onto the NPM registry which can be simply installed via npm install @dr-travis/localstorage_demo_wasm.

Note that the Typescript declarations are also packaged together so that it can also be integrated into a Typescript-written project.

Key Concepts:

In general, the data organization in this module imitates that of MongoDB, but in a much simpler way.

  • Database: A database contains multiple object stores.

    In this module, there is only one default database. Users can not define their own database. Few operations are provided on the database level.

  • Object Store: An object store stores documents as key-value pairs.

    Specifically, the keys are the document names (JavaScript Strings) and the values are the document objects (JavaScript Objects). The concept of object store is similar to collections in MongoDB or tables in Relational Databases (e.g., MySQL). An object store can store multiple documents.

  • Document: A document is a JavaScript Object.

    The concept of document is similar to documents in MongoDB or rows in Relational Databases.

  • Field: A field in a document is a key-value pair of the corresponding JavaScript object.

    All fields must be JavaScript Strings. This module does not support other formats.

Usage:

  1. Import the module

    The top-level usage is a simple dynamic import as such:

    import("@dr-travis/localstorage_demo_wasm").then((wasm) => {
    	wasm.create_db(); // Create the default database (Must be called as the first thing)
    	wasm.create_store(0); // Create an object store in the default database
    	wasm.some_function(); // Call the exposed functions here
    });
  2. Create the default database

    Before using the module, a default database must be created by calling wasm.create_db();

    There is only ONE database in this module.

  3. Create an object store

    Once the default database is created, one needs to define object stores by calling wasm.create_store(0);. The input parameter should be an integer only ranging from 0 to 255.

    Note that only 256 object stores can be created in this module. Such restriction is due to the following reasons: (i) Passing integers between JavaScript and WASM is more efficient than passing strings (no need to perform character encoding transformation); (ii) Within WASM, an object store is identified by an 8-bit integer. This is normally more than enough. This restriction can be easily changed to meet future requirements.

  4. Data Manipulation

    Once an object store is created, one can manipulate the data on three levels: database level, store level and field level. Limited operations are defined on the database level, because there is only one default database created. Full-fledged CRUD operations are implemented on both store level and field level.

    The encryption and decryption operations are automatically performed on the field level. However, these operations are NOT exposed to the caller function and are automatically performed during field-level CRUD operations.

For detailed usage on each exposed functions from this module, refer to the following function list.

Function List:

Function NameParametersReturn ValuesDescriptionExample
create_db()NoneNoneCreate the default database. Must be called once to use this modulewasm.create_db()
delete_db()NoneNoneDelete the default databasewasm.delete_db()
clear_db()NoneTrue/False (Boolean)Delete all the object stores in the default database. If the operation succeeds, it returns ture. Otherwise, it returns false.wasm.clear_db()
create_store(store_number)Integer within 0 to 255True/False (Boolean)Create an object store in the default database. An integer should be given as the parameter to identify the object store. If the operation succeeds, it returns ture. Otherwise, it returns false.wasm.create_store(128)
delete_store(store_number)Integer within 0 to 255True/False (Boolean)Delete an object store given its store number. If the operation succeeds, it returns ture. Otherwise, it returns false.wasm.delete_store(128)
clear_store(store_number)Integer within 0 to 255True/False (Boolean)Clear all the documents in the given store. The store remains. If the operation succeeds, it returns ture. Otherwise, it returns false.wasm.clear_store(128)
insert_doc(store_number, doc_name, doc)store_number: Integer (0~255) doc_name: String doc: JavaScript ObjectTrue/False (Boolean)Insert a document (JavaScript Object) into the given object store with a document name. The insertion document should NOT exist in the object store.wasm.insert_doc(128,"config",{sample: "Hello World"})
update_doc(store_number, doc_name, doc)store_number: Integer (0~255) doc_name: String doc: JavaScript ObjectTrue/False (Boolean)Update a document (JavaScript Object) in the given object store with a new document name. The document being updated MUST already exist in the object store.wasm.update_doc(128,"config",{sample_new: "New Hello World"})
set_doc(store_number, doc_name, doc)store_number: Integer (0~255) doc_name: String doc: JavaScript ObjectTrue/False (Boolean)Set a document (JavaScript Object) in the given object store with a document name. If the document already exists, it updates the existing document. If not, it inserts a new document.wasm.set_doc(128,"config",{sample_update: "Updated Hello World"})
delete_doc(store_number, doc_name)store_number: Integer (0~255) doc_name: StringTrue/False (Boolean)Delete a document in the given object store with the provided document name. As long as the given store exists, the deletion returns true no matter whether the document exists or not.wasm.delete_doc(128,"config")
get_doc(store_name, doc_name)store_number: Integer (0~255) doc_name: StringJavaScript Object / UndefinedQuery a document in the given object store with the provided document name. If the queried document exists, it returns the document as a JavaScript Object. If the required document does NOT exist, it resturns undefined.wasm.get_doc(store_name, doc_name)
insert_field(store_number, doc_name, key, value, password)store_number: Integer (0~255) doc_name: String key: String value: String password (optional): StringTrue/False (Boolean)Insert a field (as a key-value pair) into a document, given the document name and store number. The document MUST exist in the store, but the field MUST NOT exist in the document. If any requirement is not met, it returns false. Otherwise, it returns true when field insertion succeeds. Password is an optional parameter. When given, this function automatically encrypts the field value. If not given, it does not encrypt the value. Note that the given value MUST be a string. If not, call JSON.stringify() to make it as a string.wasm.insert_field(128, "config", "name", "John Doe", "password")
update_field(store_number, doc_name, key, value, password)store_number: Integer (0~255) doc_name: String key: String value: String password (optional): StringTrue/False (Boolean)Update an existing field (as a key-value pair) into a document, given the document name and store number. The document MUST exist in the store, and the field MUST also exist in the document. If any requirement is not met, it returns false. Otherwise, it returns true when field update succeeds. Password is an optional parameter. When given, this function automatically encrypts the field value. If not given, it does not encrypt the value. Note that the given value MUST be a string. If not, call JSON.stringify() to make it as a string.wasm.update_field(128, "config", "name", "Sammy", "password")
set_field(store_number, doc_name, key, value, password)store_number: Integer (0~255) doc_name: String key: String value: String password (optional): StringTrue/False (Boolean)Set a field (as a key-value pair) into a document, given the document name and store number. The existence of the field does NOT matter. But, the document MUST exist in the store. If the requirement is not met, it returns false. Otherwise, it returns true when field setting succeeds. Password is an optional parameter. When given, this function automatically encrypts the field value. If not given, it does not encrypt the value. Note that the given value MUST be a string. If not, call JSON.stringify() to make it as a string.wasm.set_field(128, "config", "name", "Jack", "password")
delete_field(store_number, doc_name, key)store_number: Integer (0~255) doc_name: String key: StringTrue/False (Boolean)Delete a field in the given document and store. As long as the store and document exist, the deletion always returns true no matter whether the field exists in the document or not.wasm.delete_field(128, "config", "name")
get_field(store_name, doc_name, key, password)store_number: Integer (0~255) doc_name: String key: String password (optional): StringString / UndefinedQuery the value of a field in the given document and store. If the queried field exists, it returns the field value as a JavaScript String. If the required document does NOT exist, it resturns undefined. Password is an optional parameter. When given, this function automatically decrypts the field value. If not given, it does not decrypt the value. Note that the returned value is a string, one has to call JSON.parse() to parse the return value if the original input value is not a stringwasm.get_field(128, "config", "sample", "password")