Conextra Framework

Documentation for Conextra framework modules

List of modules

This is a list of modules that are part of the Conextra framework.
Conextra is a framework for building web applications using Node.js and WebSocket.

Conextra Power for web development:

- module 'config-sets' configure and change app settings in real-time
- module 'data-context' monitoring data changes 
- module 'data-context-binding' bind data to DOM elements
- module 'tiny-https-server' SPA and PWA services
- module 'url-fragment-extender' manage URL fragments
- module 'ws13' create WebSocket connection
- module 'ws-user' manage users over WebSocket

It helps you create SPWA (Single-page Progressive Web Application) and efficiently perform real-time updates over WebSocket.
The modules are designed to work together to provide a complete solution for building SPAs and PWAs.

'config-sets' module

NPM License NPM Version NPM Last Update NPM Total Downloads

This manual is also available in HTML5.

This Node.js module manages configuration settings by reading from and writing to a config-sets.json file.
It handles command-line arguments and watches for changes to the configuration file.
It allows you to create applications that can be configured in real time.

Install of 'config-sets' module

npm install config-sets

Basic Usage of 'config-sets' module

Application settings are stored in a JSON file named config-sets.json.
The file is located in the same directory as the application.
The JSON file contains the following properties:

  • isProduction: true/false
  • production:
  • development:
'use strict';
//Import the required modules.
const configSets = require('config-sets');

// Setup the configuration. 
//Automatically read the config-sets.json file. 
//Metadata is optional. If you would like to add a comment to the key, use the '-metadata-key' format.
const config = configSets({ 
    '-metadata-key1': ' key1 comment ',
    key1: 'value1', 
    '-metadata-key2': ' key2 comment ',
    key2: 'value2' 
});

//Example of using in code. 
if (config.key1 === 'value1') { ... }

//Example of using the module configuration. 
//This allows you to get the module settings from the config-sets.json file.
const moduleConfig = configSets('moduleName', { key1: 'value1', key2: 'value2' });

console.log(moduleConfig);
//Watch 'key1' property for changes. 
//Return true to continue watching, false to stop watching.
config.on('key1', function (ev) { console.log(ev); return true; });

file: config-sets.json

{
  "isProduction": true,
  "production": {
    /* key1 comment */
    "key1": "value1",
    /* key2 comment */
    "key2": "value2",
    "moduleName": {
        "key1": "value1",
        "key2": "value2"
    }
  },
  "development": {}
}

'data-context' module

NPM License NPM Version NPM Last Update NPM Total Downloads

This manual is also available in HTML5.

This Node.js module monitors data changes in a data context and notifies listeners when changes occur.

Install of 'data-context' module

npm install config-sets

Basic Usage of 'data-context' module

node.js example:

'use strict';

//Import the required modules.
const { createDataContext, parse } = require('data-context');
//import { createDataContext, parse } from "data-context";

//Create a JSON string.
var strJSON = `{
    "count": 0
}`;

//Interval id.
var intervalId = null;

//Create data context.
const context = parse(
    //Parse the JSON string.
    strJSON,
    //Reviver function. Create data context.
    createDataContext
);

//Listen to the count property.
context.on('count', (event) => {

    console.log('event:', event);

    if (event.newValue > 10) {

        console.log('I am dead.');
        clearInterval(intervalId);

        //I am dead. Remove listener.
        return false;
    }

    //I am live. Continue listening.
    return true;
});

context.on('-change', (event) => {

    //Stringify the changes.
    var str = context.stringifyChanges(
        //Reviver function. Default is null.
        null,
        //Indentation. Default is 0.
        4,
        //Include only modified data. Default is true.
        true,
        //Set data to unmodified after stringification. Default is true.
        true
    );
    console.log('changes:', str);

    //I am live. Continue listening.
    return true;
});

//Start the interval.
intervalId = setInterval(() => {

    //Increment the count property.
    context.count++;
}, 1000);

browser example:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>data-context</title>
    <!-- STEP 1. Import the module. Import for an HTML page hosted on the server. -->
    <script type="text/javascript" src="./index.js"></script>
    <!-- STEP 1. Import the module. Import for a standalone HTML page. -->
    <!--<script src="https://cdn.jsdelivr.net/npm/data-context"></script>-->
    <script>

        'use strict';

        // STEP 3. Import the module.
        importModules(['data-context'], function (DC) {

            var { createDataContext, parse } = DC;

            //Create a JSON string.
            var strJSON = `{
                "count": 0
            }`;

            //Interval id.
            var intervalId = null;

            //Create data context.
            const context = parse(
                //Parse the JSON string.
                strJSON,
                //Reviver function. Create data context.
                createDataContext
            );

            //Listen to the count property.
            context.on('count', (event) => {

                console.log('event:', event);

                if (event.newValue > 10) {

                    console.log('I am dead.');
                    clearInterval(intervalId);

                    //I am dead. Remove listener.
                    return false;
                }

                //I am live. Continue listening.
                return true;
            });

            context.on('-change', (event) => {

                //Stringify the changes.
                var str = context.stringifyChanges(
                    //Reviver function. Default is null.
                    null,
                    //Indentation. Default is 0.
                    4,
                    //Include only modified data. Default is true.
                    true,
                    //Set data to unmodified after stringification. Default is true.
                    true
                );
                console.log('changes:', str);

                //I am live. Continue listening.
                return true;
            });

            //Start the interval.
            intervalId = setInterval(() => {

                //Increment the count property.
                context.count++;
            }, 1000);
        });

        // STEP 2. Add module import function.
        /**
         * Module import function.
         * @param {string[]} importIdentifierArray Modules to import.
         * @param {(...importModules:any[]) => void} callback Callback function.
         */
        function importModules(importIdentifierArray, callback) {

            var thisScope = "undefined" != typeof globalThis
                ? globalThis
                : "undefined" != typeof window
                    ? window
                    : "undefined" != typeof global
                        ? global : "undefined" != typeof self
                            ? self
                            : {};

            if (!thisScope.modules) { thisScope.modules = {}; }

            waitModules();


            function waitModules() {

                if (importIdentifierArray.length) {

                    for (let i = 0; i < importIdentifierArray.length; i++) {

                        if (!thisScope.modules[importIdentifierArray[i]]) { return setTimeout(waitModules, 10); }
                    }
                }

                callback.call(thisScope, ...importIdentifierArray.map(function (id) { return thisScope.modules[id]; }));
            }
        }
    </script>
</head>
<body>
    <h3>Example 'data-context'</h3>
    <p>Press F12. Console results.</p>
</body>
</html>

'data-context-binding' module

NPM License NPM Version NPM Last Update NPM Total Downloads

This manual is also available in HTML5.

Data context binding library for browser.
This library is used to bind data to DOM elements and update the DOM when the data changes.

Basic Usage of 'data-context-binding' module

hello-world.html:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>data-context-binding</title>
    <!-- STEP 1. Import the modules 'data-context' and 'data-context-binding'.
        Import for an HTML page hosted on the server. -->
    <!--<script src="./datacontext.js"></script>-->
    <!--<script src="./browser.js"></script>-->
    <!-- STEP 1. Import the modules 'data-context' and 'data-context-binding'.
        Import for a standalone HTML page. -->
    <script async src="https://cdn.jsdelivr.net/npm/data-context"></script>
    <script async src="https://cdn.jsdelivr.net/npm/data-context-binding"></script>
    <!-- STEP 2. Add the data to a container with the ID 'data'. Its contents will be read automatically. -->
    <script id="data" type="application/json">
        {
            "doc": {
                /* 1: The metadata-title context object */
                /* 2: The metadata-title context object */
                "title": "Hello World", // The title comment
                /* The metadata-description context object */
                "description": "This is a simple example of data context binding.", // The description comment
                /* { "hidden": "This is a must be hidden value." } */
                "debug": false
            }
        }
    </script>
</head>
<body>
    <!-- STEP 3. Bind data to the DOM elements.
        In this container we use the 'doc' object.
        The 'path' attribute is part of property path. -->
    <div path="doc">
        <div>
            <!-- STEP 3. Bind data to the DOM elements.
            The `path` attribute  is property path in the data object.
            The `bind` attribute is performed data binding. -->
            <h1 path="title" bind>Title</h1>
            <!-- STEP 3. Bind data to the DOM elements.
            The `path` attribute  is property path in the data object.
            The `bind` attribute is performed data binding. -->
            <p path="description" bind>Description</p>
        </div>
    </div>
    <hr />
    <h3>Edit the values:</h3>
    <label for="title">Title:</label>
    <br />
    <!-- STEP 3. Bind data to the DOM elements.
    The `path` attribute  is property path in the data object.
    The `bind` attribute is performed data binding. -->
    <input type="text" path="doc.title" bind style="width:380px" />
    <br />
    <br />
    <label>Description:</label>
    <br />
    <!-- STEP 3. Bind data to the DOM elements.
    The `path` attribute  is property path in the data object.
    The `bind` attribute is performed data binding. -->
    <input type="text" path="doc.description" bind style="width:380px" />
    <hr />
    <label>Live changes:</label>
    <br />
    <!-- STEP 3. Bind data to the DOM elements.
    The `bind` attribute is performed data binding. 
    Listens for data context changes -->
    <textarea cols="50" rows="10" bind="change"></textarea>
</body>
</html>

'tiny-https-server' module

NPM License NPM Version NPM Last Update NPM Total Downloads

This manual is also available in HTML5.

Tiny web server is disain for SPA (Single Page Application).
A tiny web server serves static files, supports HTTPS, subdomains, middleware, and service workers.

Install of 'tiny-https-server' module

npm install tiny-https-server

Basic Usage of 'tiny-https-server' module

You can use tiny-https-server in your project like this:

const WebCluster = require('tiny-https-server');

consr cluster = WebCluster({
    isDebug: true, // Enable debug mode to see logs in the console
    parallelism: 'auto 2' // Start 2 workers and scale up and down automatically
}, function _initServer(server) {
    // Add a request handler for the /hello path
    server.on('request', (req, res, next) => {
    
        if (req.url === '/hello') {

            res.writeHead(200, { 'Content-Type': 'text/plain' });
            res.end('Hello World');
            return;
        }
    
        next();
    });
});

'url-fragment-extender' module

NPM License NPM Version NPM Last Update NPM Total Downloads

This manual is also available in HTML5.

This allows for easy manipulation and extension of URL fragments, as well as handling custom events based on the URL hash.

Install of 'url-fragment-extender' module

You can install 'url-fragment-extender' using this command:

npm install url-fragment-extender

and use in browser 'tiny-https-server' router:

<script async src="node_modules/url-fragment-extender@2"></script>

or:

<script src="https://cdn.jsdelivr.net/npm/url-fragment-extender" ></script>

Basic Usage of 'tiny-https-server' module

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>url-fragment-extender</title>
    <!-- STEP 1. Import the modules 'url-fragment-extender'.
        Import for an HTML page hosted on the server. -->
    <script async type="text/javascript" src="./browser.js"></script>
    <!-- STEP 1. Import the modules 'url-fragment-extender'.
        Import for a standalone HTML page. -->
    <!--<script async src="https://cdn.jsdelivr.net/npm/url-fragment-extender"></script>-->
    <script>

        // STEP 3. Import the module.
        importModules(['url-fragment-extender'], function (UFE) {

            UFE.on('', function (event) {
                $('#content').innerHTML = '';
                //I am live. Continue listening.
                return true;
            });

            UFE.on('temp_1', function (event) {
                $('#content').innerHTML = $('#temp_1').innerHTML;
                //I am live. Continue listening.
                return true;
            });

            UFE.on('temp_2', function (event) {
                $('#content').innerHTML = $('#temp_2').innerHTML;
                //I am live. Continue listening.
                return true;
            });

            UFE.on('temp_3', function (event) {
                $('#content').innerHTML = $('#temp_3').innerHTML;
                //I am live. Continue listening.
                return true;
            });
        });


        // STEP 2. Add module import function.
        /**
         * Module import function - step 2.
         * @param {string[]} importIdentifierArray Modules to import.
         * @param {(...importModules:any[]) => void} callback Callback function.
         */
        function importModules(importIdentifierArray, cb) {

            var thisScope = "undefined" != typeof globalThis
                ? globalThis
                : "undefined" != typeof window
                    ? window
                    : "undefined" != typeof global
                        ? global : "undefined" != typeof self
                            ? self
                            : {};

            if (!thisScope.modules) { thisScope.modules = {}; }

            waitModules();


            function waitModules() {

                if (importIdentifierArray.length) {

                    for (let i = 0; i < importIdentifierArray.length; i++) {

                        if (!thisScope.modules[importIdentifierArray[i]]) { return setTimeout(waitModules, 10); }
                    }
                }

                cb.call(thisScope, ...importIdentifierArray.map(function (id) { return thisScope.modules[id]; }));
            }
        }
        function $(selector, element) { return (element || document).querySelector(selector); }
    </script>
    <style>
        .button {
            display: inline-block;
            padding: 10px 20px;
            font-size: 16px;
            color: #fff;
            background-color: #007bff;
            text-align: center;
            text-decoration: none;
            border-radius: 5px;
            transition: background-color 0.3s ease;
            margin: 5px;
        }

            .button:hover {
                background-color: #0056b3;
            }
    </style>
</head>
<body>

    <div>
        <a class="button" href="#temp_1">template 1</a>
        <a class="button" href="#temp_2">template 2</a>
        <a class="button" href="#temp_3">template 3</a>
    </div>
    <br />
    <div id="content"></div>

    <template id="temp_1">
        <div style="background-color:darkgreen;color:whitesmoke"><h1>template 1</h1></div>
    </template>
    <template id="temp_2">
        <div style="background-color:burlywood;color:darkslateblue"><h2>template 2</h2></div>
    </template>
    <template id="temp_3">
        <div><h3>template 3</h3></div>
    </template>

</body>
</html>

'ws13' module

NPM License NPM Version NPM Last Update NPM Total Downloads

This manual is also available in HTML5.

The WebSocket API is an advanced technology that makes it possible to open a two-way interactive communication session between the user's browser and a server. With this API, you can send messages to a server and receive event-driven responses without having to poll the server for a reply.

Install of 'ws13' module

npm install ws13

Basic Usage of 'ws13' module

Example of use on a server:

const { createServer } = require('node:http');
const createWebSocket = require('ws13');

const server = createServer();
let wsList = [];

server.on('upgrade', function (request) {

    // upgrade WebSocket
    const websocket = createWebSocket({ request });

    // has WebSocket, the handshake is done
    if (websocket) {

        // inserts a WebSocket from the list
        wsList.push(websocket);

        // add listeners
        websocket
            .on('error', console.error)
            .on('open', function () {
                /* now you can send and receive messages */
            })
            .on('message', function (event) {
                // send to everyone
                wsList.forEach((ws) => { ws.send(event.data); });
            })
            .on('close', function () {
                // removing a WebSocket from the list
                wsList = wsList.filter(ws => ws !== websocket);
            });
    } else {
        // handshake not accepted
        request.destroy();
    }
});

'ws-user' module

NPM License NPM Version NPM Last Update NPM Total Downloads

This manual is also available in HTML5.

This module is used to manage users over WebSocket.
It allows you to create a user manager that can handle user authentication and authorization.

Install of 'ws-user' module

npm install ws-user

Basic Usage of 'ws-user' module

Use the 'ws-user' module analogous to the 'ws13' module.
Example of use on a server:

const { createServer } = require('node:http');
const { createUserManager } = require('ws-user');

const server = createServer();

server.on('upgrade', function (request) {

    const wsUser = CreateWsUser(request);

    if (wsUser) {

        wsUser.onerror = (error) => {
            console.error(error);
        };
        wsUser.onopen = () => {
            console.log('Server started');
        };
        wsUser.onclose = () => {
            console.log('Server closed');
        };
        wsUser.onmessage = (event) => {
            // Received message from client and websocket is paused, disable to receive messages
            console.log(event.data);
            wsUser.messageHandled(); // continue, websocket is open, enable to receive messages
        };
        wsUser.extensionCommands = {
            'myCommand': (event) => {
                console.log(event);
                const myData = event.message; // Received `myData` from client
                // Do something
                // Send response to client
                wsUser.send(`$myCommand:${myData}`);
                event.done(); //  or wsUser.messageHandled(); `myCommand` is handled > continue, websocket is open
            }
        };
    }
});