ryuu.js
Version:
Ryuu JavaScript Utility Library
190 lines (175 loc) • 6.85 kB
text/typescript
import { handleNode } from "./utils/domoutils";
import { handleDataUpdated, onDataUpdated } from "./models/services/dataset";
import { handleFiltersUpdated, onFiltersUpdated, requestFiltersUpdate } from "./models/services/filters";
import { handleVariablesUpdated, onVariablesUpdated, requestVariablesUpdate } from "./models/services/variables";
import { handleAppData, onAppDataUpdated, requestAppDataUpdate } from "./models/services/appdata";
import { navigate } from "./models/services/navigation";
import {
get,
getAll,
post,
put,
delete as del,
domoHttp,
} from "./models/services/http";
import {
isSuccess,
isVerifiedOrigin,
getQueryParams,
setFormatHeaders,
generateUniqueId,
} from "./utils/general";
import { DomoEvent, getToken } from "./models/constants/general";
import { AskReplyMap } from "./models/interfaces/ask-reply";
import { handleAck, handleReply } from "./utils/ask-reply";
/**
* The Domo class provides a unified API for interacting with Domo platform features in client applications.
*
* It exposes HTTP methods, event listeners, emitters, and utility functions for working with datasets, filters, variables, app data, and navigation.
*
* Key features:
* - HTTP request methods (get, post, put, delete, domoHttp)
* - Batch request support via getAll
* - Event listeners for data, filters, variables, and app data updates
* - Emitters for sending variables, app data, and navigation events
* - Utility functions for environment, origin verification, and query parsing
* - Handles cross-frame communication and DOM mutation observation for token injection
*/
class Domo {
private static requests: AskReplyMap = {};
public static channel?: MessageChannel;
public static connected = false;
public static listeners: { [index: string]: Function[] } = {
onDataUpdated: [],
onFiltersUpdated: [],
onAppDataUpdated: [],
onVariablesUpdated: [],
};
////////////////////////////////////
// DOMO API
//////////////////////////////////
static get: typeof get = get;
static getAll: typeof getAll = getAll;
static post: typeof post = post;
static put: typeof put = put;
static delete: typeof del = del;
static domoHttp: typeof domoHttp = domoHttp;
////////////////////////////////////////////
// Event Listeners
//
// These receive messages from the parent window via port1 of the MessageChannel
//////////////////////////////////////////
static onDataUpdated = onDataUpdated;
static onFiltersUpdated = onFiltersUpdated;
static onAppDataUpdated = onAppDataUpdated;
static onVariablesUpdated = onVariablesUpdated;
/* @deprecated */
static readonly onFiltersUpdate = this.onFiltersUpdated;
/* @deprecated */
static readonly onDataUpdate = this.onDataUpdated;
/* @deprecated */
static readonly onAppData = this.onAppDataUpdated;
/////////////////////////////////////////////
// Emitters
//
// These send messages to the parent window via port2 of the MessageChannel
///////////////////////////////////////////
static requestFiltersUpdate = requestFiltersUpdate;
static requestVariablesUpdate = requestVariablesUpdate;
static requestAppDataUpdate = requestAppDataUpdate;
static navigate = navigate;
/* @deprecated */
static readonly filterContainer = this.requestFiltersUpdate;
/* @deprecated */
static readonly sendVariables = this.requestVariablesUpdate;
/* @deprecated */
static readonly sendAppData = this.requestAppDataUpdate;
///////////////////////////////////////////
// General
/////////////////////////////////////////
static handleAck = handleAck;
static handleReply = handleReply;
static getRequests = () => this.requests;
static getRequest = (requestId: string) => this.requests[requestId];
static readonly env = getQueryParams();
static readonly __util = {
isVerifiedOrigin,
getQueryParams,
setFormatHeaders,
isSuccess,
};
/**
* Connects to the parent window's Domo instance using a MessageChannel.
* This method sets up message handlers for various events like filtersUpdated, appData, and variablesUpdated.
* It also sends a subscription message to the parent window.
*
* @param skipFilters - If true, skips the initial filter updates.
*/
private static connect = (skipFilters = false) => {
if (this.connected) return;
this.connected = true;
this.channel = new MessageChannel();
window.parent.postMessage(
JSON.stringify({ requestId: generateUniqueId(), event: "subscribe", skipFilters }),
"*",
[this.channel.port2]
);
const eventHandlers: {
[event in keyof typeof DomoEvent]: (data: any, responsePort: MessagePort) => void;
} = {
[DomoEvent.dataUpdated]: handleDataUpdated.bind(this),
[DomoEvent.filtersUpdated]: handleFiltersUpdated.bind(this),
[DomoEvent.appData]: handleAppData.bind(this),
[DomoEvent.variablesUpdated]: handleVariablesUpdated.bind(this),
[DomoEvent.ack]: handleAck.bind(this),
};
this.channel.port1.onmessage = (e: MessageEvent) => {
const [responsePort] = e.ports;
const handler = eventHandlers[e.data.event as keyof typeof DomoEvent];
handler?.(e.data, responsePort);
};
};
/**
* Allows consumers to override or extend static methods/properties of the Domo class.
*
* Example Usage:
* import Domo, { get as originalGet } from 'domo.js';
*
* Domo.extend({
* get: (url, options) => {
* // custom logic
* return originalGet(url, options);
* }
* });
*
* @param overrides An object whose keys are static method/property names and values are the new implementations.
*/
static extend(overrides: Partial<Record<keyof typeof Domo, any>>) {
for (const key in overrides) {
if (Object.prototype.hasOwnProperty.call(Domo, key))
(Domo as any)[key as keyof typeof Domo] =
overrides[key as keyof typeof Domo];
}
}
}
/**
* MutationObserver callback that injects the authentication token into any newly added HTML elements.
*
* This function is triggered whenever nodes are added to the DOM (either in the document or head).
* It retrieves the current token and applies it to any new HTMLElement using the handleNode utility.
*
* @param mutations - An array of MutationRecord objects representing the changes to the DOM.
*/
const __mutationObserverCallback = (mutations: any) => {
const token = getToken();
for (const record of mutations) {
record.addedNodes.forEach((node: any) => {
if (node instanceof HTMLElement) handleNode(node, token);
});
}
};
const ob = new MutationObserver(__mutationObserverCallback);
ob.observe(document.documentElement, { childList: true });
ob.observe(document.head, { childList: true });
export default Domo;
export { Domo, __mutationObserverCallback };