@haxtheweb/create
Version:
CLI for all things HAX the web
723 lines (694 loc) • 23.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.MicroFrontendRegistryNodeJS = exports.MicroFrontendRegistry = exports.MicroFrontendRegCapabilities = exports.MicroFrontend = void 0;
exports.enableCoreServices = enableCoreServices;
exports.enableExperimentalServices = enableExperimentalServices;
exports.enableHAXcmsServices = enableHAXcmsServices;
exports.enableServices = enableServices;
var _logging = require("./logging.js");
// because node hates mixing modern web at times this is a fork of @haxtheweb/micro-frontend-registry
// and can fall out of date
/**
* Copyright 2022 The Pennsylvania State University
* @license Apache-2.0, see License.md for full text.
*/
// very basic class for micro
const MicroFrontendKeys = ["endpoint", "name", "title", "description", "params", "callback", "method"];
// new micro
class MicroFrontend {
constructor(values = {}) {
// set defaults for each key expected
MicroFrontendKeys.map(key => key === "params" ? this[key] = values[key] || {} : this[key] = values[key] || null);
}
}
exports.MicroFrontend = MicroFrontend;
const MicroFrontendRegCapabilities = function (SuperClass) {
return class extends SuperClass {
constructor() {
super();
this.list = [];
this.MicroFrontend = MicroFrontend;
}
/**
* Adding more or less alias for define
* @param {Object} params
*/
add(params) {
this.define(new MicroFrontend(params));
}
/**
* define a new micro frontend
*
* @param {MicroFrontend} item - instanceof MicroFrontend
* @returns {Boolean} status of definition being accepted
*/
define(item) {
if (!(item instanceof MicroFrontend)) {
(0, _logging.log)("MicroFrontendRegistry: use class MicroFrontend instance but if keys match it will register still.", 'warn');
(0, _logging.log)(item, 'warn');
}
// validate item has all keys we care about
if (Object.keys(item).every(key => MicroFrontendKeys.includes(key))) {
// support for local resolution of vercel vs serve for things that are
// built off of the main registry on localhost
if (item.endpoint.startsWith("/api/")) {
var base = "";
// support base rewrite
if (globalThis.MicroFrontendRegistryConfig.base) {
base = globalThis.MicroFrontendRegistryConfig.base;
}
// keep local based on if we're local, otherwise we need to leverage deployed address
else if ((!globalThis.HAXCMSContext || globalThis.HAXCMSContext !== "nodejs") && (globalThis.location.origin.startsWith("http://127.0.0.1") || globalThis.location.origin.startsWith("http://localhost"))) {
base = globalThis.location.origin.replace(/127.0.0.1:8(.*)/, "localhost:3000").replace(/localhost:8(.*)/, "localhost:3000");
}
// most common case, hit production open api address
else {
base = "https://open-apis.hax.cloud";
}
item.endpoint = `${base}${item.endpoint}`;
}
// check for registry config object
if (globalThis.MicroFrontendRegistryConfig[item.name]) {
Object.keys(globalThis.MicroFrontendRegistryConfig[item.name]).map(key => {
item[key] = globalThis.MicroFrontendRegistryConfig[item.name][key];
});
}
if (!this.has(item.name)) {
this.list.push(item);
return true;
}
} else {
return false;
}
}
/**
* get the definition for a machine named micro
*
* @param {String} name - machine name of the micro record requested
* @returns {MicroFrontend} the micro in question
*/
get(name, testOnly = false) {
if (name && this.list.length > 0) {
const found = this.list.find(item => item.name === name);
if (found) {
return found;
}
}
if (!testOnly) {
(0, _logging.log)(`call for ${name} but not found in micro-frontend-registry`, 'error');
}
return null;
}
/**
* boolean for having the definition for a machine named micro
*
* @param {String} name - machine name of the micro record requested
* @returns {Boolean} if we have this micro
*/
has(name) {
return this.get(name, true) !== null;
}
/**
* set the definition for a machine named micro that was already registered
*
* @param {String} name - machine name of the micro record requested
* @param {MicroFrontend} item - updated micro data
* @returns {MicroFrontend} the micro in question
*/
set(name, item = {}) {
if (name && this.list.length > 0 && this.has(name)) {
const index = this.list.findIndex(item => item.name === name);
this.list[index] = item;
}
return null;
}
/**
* generate the call to the micro based on accepting name and params
*
* @param {String} name - machine name for the micro to call
* @param {Object} params - data to send to endpoint
* @param {Function} callback - Function callback on data return
* @param {Object} caller - reference to DOM node that called this
* @param {String} urlStringAddon - a string to add onto the fetch at the end. edge of edge of edge land here
* @returns {Object} Response object from microservice, otherwise `null`
*/
async call(name, params = {}, callback = null, caller = null, urlStringAddon = "") {
if (this.has(name)) {
const item = this.get(name);
// default post, but this is not cacheable
let method = "POST";
// support definition requiring a certain method
if (item.method) {
method = item.method;
}
// support override when calling
if (params.__method) {
method = params.__method;
delete params.__method;
}
let data = null;
switch (method) {
case "GET":
case "HEAD":
// support for formdata which is already encoded
const searchParams = new URLSearchParams(params).toString();
data = await fetch(searchParams ? `${item.endpoint}?${searchParams}${urlStringAddon}` : item.endpoint + urlStringAddon, {
method: method
}).then(d => {
return d.ok ? d.json() : {
status: d.status,
data: null
};
}).catch((e, d) => {
(0, _logging.log)("Request failed", 'warn');
// this is endpoint completely failed to respond
return {
status: 500,
data: null
};
});
break;
case "POST":
default:
// support for formdata which is already encoded
data = await fetch(item.endpoint + urlStringAddon, {
method: method,
body: params instanceof FormData ? params : JSON.stringify(params)
}).then(d => {
return d.ok ? d.json() : {
status: d.status,
data: null
};
}).catch((e, d) => {
(0, _logging.log)("Request failed", 'warn');
// this is endpoint completely failed to respond
return {
status: 500,
data: null
};
});
break;
}
// endpoints can require a callback be hit every time
if (item.callback) {
await item.callback(data, caller);
}
if (callback) {
await callback(data, caller);
}
return data;
}
return null;
}
/**
* generate the call to the micro as a URL
*
* @param {String} name - machine name for the micro to call
* @param {Object} params - data to send to endpoint
* @returns {String} URL with parameters for a GET
*/
url(name, params = {}) {
if (this.has(name)) {
const item = this.get(name);
// no null submissions
for (var key in params) {
if (params.hasOwnProperty(key)) {
if (params[key] == null) delete params[key];
}
}
return new URL(item.endpoint).toString() + `?${new URLSearchParams(params).toString()}`;
}
return "";
}
};
};
exports.MicroFrontendRegCapabilities = MicroFrontendRegCapabilities;
class MicroFrontendRegistryNodeJS extends MicroFrontendRegCapabilities(Object) {
constructor() {
super();
}
}
// register globally so we can make sure there is only one
exports.MicroFrontendRegistryNodeJS = MicroFrontendRegistryNodeJS;
globalThis.MicroFrontendRegistry = globalThis.MicroFrontendRegistry || {};
globalThis.MicroFrontendRegistryConfig = globalThis.MicroFrontendRegistryConfig || {};
globalThis.MicroFrontendRegistry.requestAvailability = () => {
if (!globalThis.MicroFrontendRegistry.instance) {
// weird but this would imply no DOM and thus node
if (globalThis.document && globalThis.document.body && globalThis.document.body.appendChild) {
globalThis.MicroFrontendRegistry.instance = globalThis.document.createElement(MicroFrontendRegistryEl.tag);
globalThis.document.body.appendChild(globalThis.MicroFrontendRegistry.instance);
} else {
globalThis.MicroFrontendRegistry.instance = new MicroFrontendRegistryNodeJS();
}
}
return globalThis.MicroFrontendRegistry.instance;
};
// most common way to access registry
const MicroFrontendRegistry = exports.MicroFrontendRegistry = globalThis.MicroFrontendRegistry.requestAvailability();
// integrate the core services of our webcomponents API layer
// While not required, this is the home for non-visual aspects of
// our ecosystem that can be leveraged independent of other things
// Examples of a platform specific implementation would be HAXcms
// and it's name spacing
function enableServices(services) {
services.forEach(service => {
switch (service) {
case "core":
enableCoreServices();
break;
case "experimental":
enableExperimentalServices();
break;
case "haxcms":
enableHAXcmsServices();
break;
}
});
}
// map service enable to global
MicroFrontendRegistry.enableServices = enableServices;
// core services
function enableCoreServices() {
// linkValidator
MicroFrontendRegistry.add({
endpoint: "/api/services/website/linkValidator",
name: "@core/linkValidator",
method: "GET",
title: "Validate URLs",
description: "Validates that an array of URLs are valid by returning status codes of a HEAD request",
params: {
links: "link for processing as link otherwise unused"
}
});
// metadata
MicroFrontendRegistry.add({
endpoint: "/api/services/website/metadata",
name: "@core/metadata",
method: "GET",
title: "URL Metadata",
description: "Skims metadata off a link",
params: {
q: "url to process"
},
userContext: [{
action: "paste",
data: "url",
memory: {
isLoggedIn: true
},
result: function (data) {
return {
tag: "a",
content: data.title || data["og:site_name"] || data["og:title"] || data.url,
properties: {
href: data.url,
rel: "nofollow"
}
};
}
}]
});
// mdToHtml
MicroFrontendRegistry.add({
endpoint: "/api/services/media/format/mdToHtml",
name: "@core/mdToHtml",
title: "Markdown to HTML",
description: "Convert Markdown string (or file) to HTML",
params: {
md: "MD or link to be converted",
type: "link for processing as link otherwise unused"
}
});
// htmlToMd
MicroFrontendRegistry.add({
endpoint: "/api/services/media/format/htmlToMd",
name: "@core/htmlToMd",
title: "HTML to MD",
description: "Convert HTML string (or file) to MD",
params: {
html: "HTML or link to be converted",
type: "link for processing as link otherwise unused"
}
});
// htmlToPdf
MicroFrontendRegistry.add({
endpoint: "/api/services/media/format/htmlToPdf",
name: "@core/htmlToPdf",
title: "HTML to PDF",
description: "Convert HTML string (or file) to a PDF",
params: {
html: "HTML or link to be converted",
type: "link for processing as link otherwise unused"
}
});
// prettyHtml
MicroFrontendRegistry.add({
endpoint: "/api/services/media/format/prettyHtml",
name: "@core/prettyHtml",
title: "Pretty HTML",
description: "Format HTML string (or file) to be more human readable",
params: {
html: "HTML or link to be converted",
type: "link for processing as link otherwise unused"
}
});
// crypto
MicroFrontendRegistry.add({
endpoint: "/api/services/security/aes256",
name: "@core/crypto",
title: "Cryptography from string",
description: "Convert a string to or from an aes256 based hash",
params: {
data: "HTML or link to be converted",
op: "decrypt or hash"
}
});
// duckDuckGo
MicroFrontendRegistry.add({
endpoint: "/api/services/website/duckDuckGo",
name: "@core/duckDuckGo",
method: "GET",
title: "Duck Duck Go",
description: "Search results from duck duck go",
params: {
q: "query param to search on"
}
});
// screenshot - kept by itself bc of size of getBrowserInstance
MicroFrontendRegistry.add({
endpoint: "https://screenshoturl.open-apis.hax.cloud/api/screenshotUrl",
name: "@core/screenshotUrl",
method: "GET",
title: "Screenshot page",
description: "Takes screenshot of a URL and returns image",
params: {
urlToCapture: "full url with https",
quality: "Optional image quality parameter"
}
});
// docxToPdf
MicroFrontendRegistry.add({
endpoint: "/api/services/media/format/docxToPdf",
name: "@core/docxToPdf",
title: "Docx to pdf",
description: "Convert .docx file to PDF response (downloaded)",
params: {
body: "FormData class w/ uploaded file encoded into it"
}
});
// docxToHtml
MicroFrontendRegistry.add({
endpoint: "/api/services/media/format/docxToHtml",
name: "@core/docxToHtml",
title: "Docx to HTML",
description: "Convert .docx file to HTML",
params: {
body: "FormData class w/ uploaded file encoded into it"
}
});
// htmlToDocx
MicroFrontendRegistry.add({
endpoint: "/api/services/media/format/htmlToDocx",
name: "@core/htmlToDocx",
title: "HTML to docx",
description: "Convert HTML to .docx file",
params: {
html: "html body to be converted to a docx file download"
}
});
// imgToAscii
MicroFrontendRegistry.add({
endpoint: "/api/services/media/format/imgToAscii",
name: "@core/imgToAscii",
title: "Image to ascii art",
description: "Convert any valid image formatted file to ASCII terminal style art",
params: {
body: "FormData class w/ uploaded file encoded into it"
}
});
// imgManipulation
MicroFrontendRegistry.add({
endpoint: "/api/services/media/image/manipulate",
name: "@core/imgManipulate",
title: "simple image manipulation",
description: "scale, resize, convert and perform operations to manipulate any image",
params: {
src: "image source",
height: "height in numbers",
width: "width in numbers",
quality: "0-100, jpeg quality to reduce image by if jpeg",
fit: "how to crop if height and width are supplied (https://sharp.pixelplumbing.com/api-resize)",
watermark: "SRC for an image to watermark on the output",
wmspot: "nw,ne,se,sw for moving the location of the watermark",
rotate: "https://sharp.pixelplumbing.com/api-operation#rotate",
format: "png, jpg, gif, webp"
}
});
MicroFrontendRegistry.add({
endpoint: "/api/services/text/readability",
name: "@core/readability",
title: "readability score",
description: "Readability metrics from analyzing text",
params: {
body: "Block of text to enhance"
}
});
}
// HAXcms services
function enableHAXcmsServices() {
// docxToSite
MicroFrontendRegistry.add({
endpoint: "/api/apps/haxcms/docxToSite",
name: "@haxcms/docxToSite",
title: "Docx to Site",
description: "Convert .docx file to Site schema",
params: {
body: "FormData class w/ uploaded file encoded into it"
}
});
// htmlToSite
MicroFrontendRegistry.add({
endpoint: "/api/apps/haxcms/convert/htmlToSite",
name: "@haxcms/htmlToSite",
title: "HTML to Site",
description: "Convert HTML file location to Site schema",
params: {
repoUrl: "Location of the repo"
}
});
// evolutionToSite
MicroFrontendRegistry.add({
endpoint: "/system/api/importEvolution",
name: "@haxcms/evolutionToSite",
title: "Evolution to Site",
description: "Convert .zip and schema to valid HAXcms",
params: {
body: "FormData class w/ uploaded file encoded into it"
}
});
// gitbookToSite
MicroFrontendRegistry.add({
endpoint: "/api/apps/haxcms/convert/gitbookToSite",
name: "@haxcms/gitbookToSite",
title: "Gitbook to Site",
description: "Convert Gitbook baseed repo to valid HAXcms",
params: {
md: "Location of the repo"
}
});
// notionToSite
MicroFrontendRegistry.add({
endpoint: "/api/apps/haxcms/convert/notionToSite",
name: "@haxcms/notionToSite",
title: "Notion to Site",
description: "Convert notion baseed repo to valid HAXcms",
params: {
repoUrl: "Location of the repo"
}
});
// haxcmsToSite
MicroFrontendRegistry.add({
endpoint: "/api/apps/haxcms/convert/haxcmsToSite",
name: "@haxcms/haxcmsToSite",
title: "HAXcms to Site",
description: "Use a HAXcms site as the basis for a new one",
params: {
repoUrl: "Location of the site"
}
});
// elmslnToSite
MicroFrontendRegistry.add({
endpoint: "/api/apps/haxcms/convert/elmslnToSite",
name: "@haxcms/elmslnToSite",
title: "ELMS:LN to Site",
description: "Import an ELMS:LN site to HAXcms",
params: {
repoUrl: "Location of the site"
}
});
// pressbooksToSite
MicroFrontendRegistry.add({
endpoint: "/api/apps/haxcms/convert/pressbooksToSite",
name: "@haxcms/pressbooksToSite",
title: "Pressbooks to Site",
description: "Convert pressbooks HTML export to Site schema",
params: {
body: "FormData class w/ uploaded file encoded into it"
}
});
// insights
MicroFrontendRegistry.add({
endpoint: "/api/apps/haxcms/insights",
name: "@haxcms/insights",
title: "Site insights",
description: "States relative to the page, lesson, and site as a whole. Used for content authors.",
params: {
site: "location of the HAXcms site OR site.json data",
type: "site for site.json or link for remote loading",
activeId: "id to query from"
}
});
// contentBrowser
MicroFrontendRegistry.add({
endpoint: "/api/apps/haxcms/contentBrowser",
name: "@haxcms/contentBrowser",
title: "Content browser",
description: "Returns details about content relative to an activeID",
params: {
site: "location of the HAXcms site OR site.json data",
type: "site for site.json or link for remote loading",
activeId: "id to query from"
}
});
// mediaBrowser
MicroFrontendRegistry.add({
endpoint: "/api/apps/haxcms/mediaBrowser",
name: "@haxcms/mediaBrowser",
title: "Media browser",
description: "Returns details about media relative to an activeID",
params: {
site: "location of the HAXcms site OR site.json data",
type: "site for site.json or link for remote loading",
activeId: "id to query from"
}
});
// linkChecker
MicroFrontendRegistry.add({
endpoint: "/api/apps/haxcms/linkChecker",
name: "@haxcms/linkChecker",
title: "Check site links",
description: "Returns details about links relative to an activeID",
params: {
site: "location of the HAXcms site OR site.json data",
type: "site for site.json or link for remote loading",
activeId: "id to query from"
}
});
// courseStats
MicroFrontendRegistry.add({
endpoint: "/api/apps/haxcms/courseStats",
name: "@haxcms/courseStats",
title: "Course stats",
description: "Relevant stats for teaching relative to the ancestor in question",
params: {
site: "location of the HAXcms site OR site.json data",
type: "site for site.json or link for remote loading",
ancestor: "optional: ancestor to print from as opposed to entire site"
}
});
// siteToHtml
MicroFrontendRegistry.add({
endpoint: "/api/apps/haxcms/siteToHtml",
name: "@haxcms/siteToHtml",
title: "HAXcms Full Site HTML",
description: "Load entire HAXcms site via URL as HTML",
params: {
site: "location of the HAXcms site OR site.json data",
type: "site for site.json or link for remote loading",
ancestor: "optional: ancestor to print from as opposed to entire site"
}
});
// pageCache
MicroFrontendRegistry.add({
endpoint: "/api/apps/haxcms/pageCache",
method: "GET",
name: "@haxcms/pageCache",
title: "HAXcms Page cache",
description: "Load a page from a site via uuid",
params: {
site: "location of the HAXcms site OR site.json data",
type: "site for site.json or link for remote loading",
uuid: "page to return content of"
}
});
// siteManifest
MicroFrontendRegistry.add({
endpoint: "/api/apps/haxcms/siteManifest",
name: "@haxcms/siteManifest",
title: "HAXcms manifest",
description: "Load the manifest for a site based on URL",
params: {
site: "location of the HAXcms site OR site.json data"
}
});
// siteGlossary
MicroFrontendRegistry.add({
endpoint: "/api/apps/haxcms/siteGlossary",
name: "@haxcms/siteGlossary",
title: "HAXcms site Glossary",
description: "array of terms found in the glossary slug",
params: {
url: "location of the HAXcms site"
}
});
// views
MicroFrontendRegistry.add({
endpoint: "/api/apps/haxcms/views",
name: "@haxcms/views",
title: "Views, but for HAX",
description: "Views criteria for slicing and remixing HAX site data",
params: {
site: "location of the HAXcms site"
}
});
// termsInPage
MicroFrontendRegistry.add({
endpoint: "/api/apps/haxcms/termsInPage",
name: "@haxcms/termsInPage",
title: "HAXcms Page terms",
description: "array of terms from glossary found in a blob of html",
params: {
body: "HTML blob to process",
site: "location of the HAXcms site OR site.json data",
type: "site for site.json or link for remote loading",
wikipedia: "if wikipedia links should be included in response, if found",
terms: "Optional array of term objects. This is intended for future use / forcibly passing a list from elsewhere"
}
});
// siteToEpub
MicroFrontendRegistry.add({
endpoint: "/api/apps/haxcms/siteToEpub",
name: "@haxcms/siteToEpub",
title: "HAXcms Full Site EPUB",
description: "generate .epub of entire HAXcms site via URL",
params: {
url: "location of the HAXcms site"
}
});
}
// experimental service
function enableExperimentalServices() {
// hydrateSsr
MicroFrontendRegistry.add({
endpoint: "https://webcomponents.hax.cloud/api/hydrateSsr",
name: "@experiments/hydrateSsr",
title: "Hydrate SSR",
description: "Hydrate web components via lit server side",
params: {
html: "blob of HTML or link to html file to load"
}
});
}