Options
All
  • Public
  • Public/Protected
  • All
Menu

@xtremax/xbase-js

XBase-JS

XBase-JS is a standardized utility library for more straightforward UI development with minimal configuration.

Installation

npm install @xtremax/xbase-js

or

yarn add @xtremax/xbase-js

Modules

XBase-JS consists of several modules that can be utilized based on the desired need:

  • API

    Create API requests with configurable authorization settings and error handlers based on the returned HTTP response status codes.

    Settings

    • config (Optional): The axios request configuration.
    • errorHandler (Optional): An array of custom error handler functions.

    See the configuration detail here.

    Usage

    You can use API module anywhere in you project by importing it. And since the API module extends axios class, you can also run any operation an axios instance can run such as get, post, patch, etc.

    import { API } from "@xtremax/xbase-js";
    await API({
    method: "get",
    url: "https://jsonplaceholder.typicode.com/posts",
    params: { language: "en" },
    });

    await API.get("https://jsonplaceholder.typicode.com/posts")

    Simply put, API is an axios instance with default interceptors and error handlers. If you want to use your own interceptors, you can eject the default interceptors and replace it with the new one as shown in the code below.

    import { API } from "@xtremax/xbase-js";

    API.interceptors.request.eject(API.defaultRequestInterceptors);
    API.interceptors.request.use(
    function (config) {
    // Do something before request is sent
    return config;
    },
    function (error) {
    // Do something with request error
    return Promise.reject(error);
    }
    );

    API.interceptors.response.eject(API.defaultResponseInterceptors);
    API.interceptors.response.use(
    function (response) {
    // Any status code that lies within the range of 2xx cause this function to trigger
    // Do something with response data
    return response;
    },
    function (error) {
    // Any status codes that falls outside the range of 2xx cause this function to trigger
    // Do something with response error
    return Promise.reject(error);
    }
    );
  • InactiveDetector

    Detect inactive state of the user because there is no interaction in the client side, such as mouse movement, keyboard presses and etc., within the determined period of time.

    Parameters

    1. callback (Optional): Function that will be executed when the waitingTime is reached.

    2. waitingTime (Optional): The time period before a user is considered inactive (in milliseconds). The default value is 20 minutes.

    3. activeState (Optional): Function that will be executed if the user is active and the callback hasn't been called.

    4. otherOptions (Optional): Other object to configure InactiveDetector.

      • beforeCallbackTime: The time period (in milliseconds) before the callback function is executed. The default value is 1 minute.
      • beforeCallback: Function to be executed when the beforeCallbackTime condition is reached.

    Usage

    You can use the InactiveDetector feature independently by importing the module from XBase-JS then use it wherever you want to use it.

    After importing the module, please provide the parameter value as already been mentioned above.

    import { InactiveDetector } from "@xtremax/xbase-js"

    function myCallbackFunction() {
    // ...
    console.log("User is inactive.")
    }
    function myBeforeCallbackFunction() {
    // ...
    console.log("User is 30 seconds left before considered as inactive.")
    }
    const waitingTime = 10 * 60 * 1000; // 10 minutes
    const activeState = () => false;
    const otherOptions = {
    beforeCallbackTime: 30 * 1000 // 30 seconds,
    beforeCallback: myBeforeCallbackFunction,
    }

    // initialize the inactive detector
    const inactiveDetector = InactiveDetector(myCallbackFunction, waitingTime, activeState, otherOptions);

    // start the inactive detector
    inactiveDetector.start();

    // stop the inactive detector
    inactiveDetector.reset();

    In the case of SPA (Single Page Application) or our Microfrontend architecture, the implementation differs slightly. Since inactivity detector is simply function that return two methods (start and reset) so that we can simply reuse the function to start and stop the counter directly on the place that suits the business process of the application.

    import InactiveDetector from @xtremax/xbase-js/inactive-detector;

    // index.js
    // To start the counter of detector can be set in signinRedirectCallback after the login success //
    Auth.signInRedirectCallback(() => {
    InactiveDetector(callback, config.waitingtime).start()
    })

    // To end the counter of detector can be set once the logout process //
    Auth.signOutRedirectCallback() {
    InactiveDetector(callback, config.waitingtime).reset()
    }

    // App.vue (Root Component)
    // This one to handle when the application reload //
    onMounted(() => {
    if(isSignIn) {
    InactiveDetector(callback, config.waitingtime).start()
    } else {
    InactiveDetector(callback, config.waitingtime).reset()
    }
    })
  • Auth

    Authenticate and authorize user with simplified flow on signing in, token renewal, and signing out.

    By default, Auth module is integrated with the InactiveDetector module. You can change the configuration parameter or disable it completely through the settings.

    Settings

    • Important! -> For XBase usage, in the specific case of our IDP service, you must include fetchRequestCredentials='omit' in idpConfigs
    • idpConfigs: All oidc-client-ts' UserManager options except the userStore. Click here for more details. Important : specific case for our IDP service you must include fetchRequestCredentials='omit'
    • inactiveDetectorConfigs (Optional): Since the inactive detector is already integrated by default, you can omit this setting. However, you can set this as false to disable the inactive detector. If you want to change the default behavior, provide the settings in here.
      • inactiveTimeout (Optional): Time period before a user is considered as idle (in milliseconds). Default value is 20 minutes.
      • preInactiveTimeout (Optional): Time period before the inactiveCallback is executed (in milliseconds). Default value is 1 minutes.
      • inactiveCallback (Optional): A callback to be called when the inactiveTimeout is up.
      • preInactiveCallback (Optional): A callback to be called when preInactiveTimeout is up. Return resetTimer callback function to reset the idle time. Default value (using our developed dialog element) :
      (resetTimer) => {
      dialog.open(
      "Your session is about to expire. You will be signed out after you have been inactive for a while. Click Continue to stay signed in.",
      (closeDialog) => {
      closeDialog();
      resetTimer();
      }
      );
      };

    See the configuration detail here.

Usage

  1. Create /oidccallback page in your app (see the sample here), and execute this function:

    import { Auth } from "@xtremax/xbase-js";
    Auth.signInRedirectCallback();
  2. Create /oidccallbacksilent page in your app (see the sample here), and execute this function:

    import { Auth } from "@xtremax/xbase-js";
    Auth.signInSilentCallback();
  3. Register the /oidccallback path as the value of redirect_uri and the /oidccallbacksilent path as the value of silent_redirect_uri in your idpConfigs.

  4. Now you can use the functionality of Auth module anywhere in you project by importing it.

    import { Auth } from "@xtremax/xbase-js";
    Auth.signIn(); // method to sign in, returns Promise
    Auth.signOut(); // method to sign out, returns Promise
    Auth.renewToken(); // method to renew the JWT, returns Promise of the user's object
    Auth. getSignedInUser() // method to get the signed in user's object, returns Promise of the user'sobject
  5. Auth module provides default function for oidc-client-ts' events.

    By default, Auth module has a defined accessTokenExpiring, accessTokenExpired and silentRenewError callbacks.

    • accessTokenExpiring default callback: If the user is still active and the token is expiring, Auth module will call signinSilent. If the signinSilent process failed, it will proceed to call signinRedirect.
    • accessTokenExpired default callback: If the user's token is expired, Auth module will open a dialog requiring the user to click a sign out button, calling signoutRedirect.
    • silentRenewError default callback: If the silent token renewal failed, Auth module will call signoutRedirect.

    However, you can remove and replace them with your own callbacks as shown in the code below.

    import { Auth } from "@xtremax/xbase-js";
    function customAccessTokenExpiringCallback() {
    // your custom access token expiring callback
    // ...
    }
    Auth.events.removeAccessTokenExpiring(Auth.accessTokenExpiringCallback)
    Auth.events.addAccessTokenExpiring(customAccessTokenExpiringCallback)
    function customAccessTokenExpiredCallback() {
    // your custom access token expired callback
    // ...
    }
    Auth.events.removeAccessTokenExpired(Auth.accessTokenExpiredCallback)
    Auth.events.addAccessTokenExpired(customAccessTokenExpiredCallback)
    function customSilentRenewErrorCallback() {
    // your custom silent renew error callback
    // ...
    }
    Auth.events.removeSilentRenewError(Auth.silentRenewError)
    Auth.events.addSilentRenewError(customSilentRenewErrorCallback)
  6. Auth module is integrated with inactive detector by default. You can disable it by setting theinactiveDetectorConfigs as false.

    {
    inactiveDetectorConfigs: false,
    idpConfigs: {
    authority: "https://xticket.dev.xtremax.com/api/idp",
    client_id: "auth-trial",
    redirect_uri: "http://localhost:8080/oidccallback",
    silent_redirect_uri: "http://localhost:8080/oidccallbacksilent",
    post_logout_redirect_uri: "http://localhost:8080/login",
    scope: "openid xbase_api",
    silentRequestTimeoutInSeconds: 300000,
    response_type: "code",
    },
    }
  • Permission

    Provide utility that can be integrated with the application's navigation guard in order to validate the route's accessibility based on the gained user permission.

    Settings

    See the configuration detail here.

    Usage

    You can use Permission module anywhere in your project by importing it. The Permission method receives 1 parameter in the form of string to validate a single permission key,

    import { Permission } from "@xtremax/xbase-js";

    if (await Permission("Xtremax.accessDashboardPage")) {
    // user is allowed to access the /dasboard route
    // if the "Xtremax.accessDashboardPage" value is true
    window.location.redirect("/dashboard")
    } else {
    window.location.redirect("/not-authorized")
    }

    or an array of string to validate multiple permission keys.

    import { Permission } from "@xtremax/xbase-js";

    if (await Permission(["Xtremax.accessDashboardPage", "Xtremax.accessListingPage"])) {
    // user is allowed to access the /dashboard route
    // if the "Xtremax.accessDashboardPage" or "Xtremax.accessListingPage" value is true
    window.location.redirect("/dashboard")
    } else {
    window.location.redirect("/not-authorized")
    }
  • MicroFrontend

    By extending the qiankun library, it supports your need to develop a microfrontend architecture application.

    Settings

    See the configuration detail here.

    Usage

  1. Call load method to load a micro app into the main app.

    import { MicroFrontend } from "@xtremax/xbase-js";

    const microApp = MicroFrontend.load("Service1")
  2. Call unload method to unload a micro app from the main app.

    MicroFrontend.unload(microApp)
    
  3. Call renderAsMicroApp method to render the app as a micro app.

    import XBaseJS, { MicroFrontend } from "@xtremax/xbase-js";
    import XBaseJSConfig from "./xbasejs-config.js";

    function render(props = {}) {
    XBaseJS.configure(XBaseJSConfig)
    const { container } = props;
    instance = app.mount(
    container
    ? container.querySelector(`#${SERVICE_CONTAINER_NAME}`)
    : `#${SERVICE_CONTAINER_NAME}`
    );
    }
    function unmount(){
    instance.$destroy();
    instance = null;
    }

    MicroFrontend.renderAsMicroApp(render, unmount)

    If you use vite and vite-plugin-qiankun, use it this way :

    import XBaseJS, { MicroFrontend } from "@xtremax/xbase-js";
    import XBaseJSConfig from "./xbasejs-config.js";
    import { renderWithQiankun } from "vite-plugin-qiankun/dist/helper";
    function render(props = {}) {
    XBaseJS.configure(XBaseJSConfig)
    const { container } = props;
    instance = app.mount(
    container
    ? container.querySelector(`#${SERVICE_CONTAINER_NAME}`)
    : `#${SERVICE_CONTAINER_NAME}`
    );
    }
    function unmount(){
    instance.$destroy();
    instance = null;
    }
    renderWithQiankun(MicroFrontend.renderAsMicroApp(render, unmount))
  • PubSub

    A JavaScript implementation of a "publish-subscribe" messaging pattern. It is a utility that supports your need to create a communication model between decoupled, independent building blocks of UI application (micro apps).

    Settings

    • validator (Optional): A function to validate message to be published.

    See the configuration detail here.

    Usage

    You can use PubSub module anywhere in your project by importing it. This module provide functionality to publish, subscribe, and unsubscribe to custom events.

  1. Use publish method to publish a custom event.

    import { PubSub } from "@xtremax/xbase-js";
    PubSub.publish("event_name", "Message you want to publish")
  2. Use subscribe to subscribe to a custom event.

    import { PubSub } from "@xtremax/xbase-js";
    const listener = (e)=>{
    // do something with the e.detail
    const message = e.detail;
    }

    PubSub.subscribe("event_name", listener)
  3. Use unsubscribe method to unsubscribe from a custom event.

    import { PubSub } from "@xtremax/xbase-js";
    const listener = (e)=>{
    // do something with the e.detail
    const message = e.detail;
    }

    const token = PubSub.subscribe("event_name", listener)

    // unsubscribe using regular removeEventListener parameter
    PubSub.unsubscribe("event_name", listener)

    // unsubscribe using subscription token
    PubSub.unsubscribe(token)
  4. Cancel all current subscriptions with unsubscribeAll method.

    import { PubSub } from "@xtremax/xbase-js";

    PubSub.unsubscribeAll()
  5. Use subscriptions method to get the subscription details.

    import { PubSub } from "@xtremax/xbase-js";

    const subscriptions = PubSub.subscriptions

Configuration

XBaseJS configuration object consists of 5 optional settings : Auth, API, Permission, PubSub, and MicroFrontend.

Each setting will be used in the respective module. So, you only need to pass settings for any module you plan to use, and omit the others.

Example

Here is a quick example of a complete configuration that uses all of the modules:

import XBaseJS from "@xtremax/xbase-js";

const configs = {
API: {
config: {
baseURL: "https://jsonplaceholder.typicode.com/",
timeout: 1000,
headers: { "X-Custom-Header": "foobar" }
},
errorHandler: [
{
statusCode: 400,
callback: (error) => {
// do custom function
console.log("getting 400 response");
},
},
{
statusCode: [401, 403],
callback: (error) => {
// do custom function
console.log("getting 401 or 403 response");
},
},
],
},
Auth: {
idpConfigs: {
authority: "https://xticket.dev.xtremax.com/api/idp",
client_id: "auth-trial",
redirect_uri: "http://localhost:8080/oidccallback",
silent_redirect_uri: "http://localhost:8080/oidccallbacksilent",
post_logout_redirect_uri: "http://localhost:8080/login",
scope: "openid xbase_api",
silentRequestTimeoutInSeconds: 300000,
response_type: "code",
},
inactiveDetectorConfigs: {
inactiveTimeout: 20 * 60 * 1000,
preInactiveCallback: (resetTimer) => {
const message = "Your session is about to expire. You will be signed out after you have been inactive for a while. Click Continue to stay signed in.";

// create your custom dialog element here

dialog.open(
message,
(closeDialog) => {
closeDialog();
resetTimer();
}
);
},
preInactiveTimeout: 60 * 1000,
},
},
Permission: {
permissionResolver: async () => {
try {
const apiResponse = await new Promise((resolve) =>
setTimeout(
// you can change this mechanism later with fetching a real API endpoint
// to get the permission data
() =>
resolve({
data: {
permission: {
"XBaseUiFramework.Home.View": true,
"XBaseUiFramework.About.View": false,
},
},
}),
3000
)
);
console.log(apiResponse);
return apiResponse.data.permission;
} catch (err) {
console.log(err?.response);
}
},
},
MicroFrontend: {
microApps: [
{
name: "Service1",
entry: "http://localhost:8080",
container: "#micro-service",
props: {
AuthConfigs: AuthConfigs
}
},
{
name: "Service2",
entry: "http://localhost:8081",
container: "#micro-service",
props: {
AuthConfigs: AuthConfigs
}
}
]
},
PubSub: {
validator: (message)=>{
const regex = /^[A-Za-z0-9 ]+$/
return regex.test(message)
}
},
}

XBaseJS.configure(configs)

Generated using TypeDoc