editorconfig

This commit is contained in:
2025-01-08 06:55:30 -07:00
parent 481d6fd48d
commit f75988fa3a
28 changed files with 1364 additions and 1339 deletions

View File

@@ -3,5 +3,5 @@ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const randomBool = (chance_of_failure = 0.25) => Math.random() < chance_of_failure;
module.exports = {
sleep, randomBool
};
sleep, randomBool
};

View File

@@ -4,11 +4,11 @@
* @return {string} The generated string
*/
module.exports = (length) => {
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let text = "";
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let text = "";
for (let i = 0; i < length; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
for (let i = 0; i < length; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
};

View File

@@ -4,9 +4,9 @@ const typedefs = require("../typedefs");
/**
* Directed graph, may or may not be connected.
*
*
* NOTE: Assumes that nodes and edges are valid.
*
*
* Example:
* ```javascript
* let nodes = ["a", "b", "c", "d", "e"];
@@ -22,80 +22,80 @@ const typedefs = require("../typedefs");
* ```
*/
class myGraph {
/**
* @param {string[]} nodes Graph nodes IDs
* @param {{ from: string, to: string }[]} edges Graph edges b/w nodes
*/
constructor(nodes, edges) {
this.nodes = [...nodes];
this.edges = structuredClone(edges);
}
/**
* @param {string[]} nodes Graph nodes IDs
* @param {{ from: string, to: string }[]} edges Graph edges b/w nodes
*/
constructor(nodes, edges) {
this.nodes = [...nodes];
this.edges = structuredClone(edges);
}
/**
* @param {string} node
* @returns {string[]}
*/
getDirectHeads(node) {
return this.edges.filter(edge => edge.to == node).map(edge => edge.from);
}
/**
* @param {string} node
* @returns {string[]}
*/
getDirectHeads(node) {
return this.edges.filter(edge => edge.to == node).map(edge => edge.from);
}
/**
* @param {string} node
* @returns {string[]}
*/
getDirectTails(node) {
return this.edges.filter(edge => edge.from == node).map(edge => edge.to);
}
/**
* @param {string} node
* @returns {string[]}
*/
getDirectTails(node) {
return this.edges.filter(edge => edge.from == node).map(edge => edge.to);
}
/**
* Kahn's topological sort
* @returns {string[]}
*/
topoSort() {
let inDegree = {};
let zeroInDegreeQueue = [];
let topologicalOrder = [];
/**
* Kahn's topological sort
* @returns {string[]}
*/
topoSort() {
let inDegree = {};
let zeroInDegreeQueue = [];
let topologicalOrder = [];
// Initialize inDegree of all nodes to 0
for (let node of this.nodes) {
inDegree[node] = 0;
}
// Initialize inDegree of all nodes to 0
for (let node of this.nodes) {
inDegree[node] = 0;
}
// Calculate inDegree of each node
for (let edge of this.edges) {
inDegree[edge.to]++;
}
// Calculate inDegree of each node
for (let edge of this.edges) {
inDegree[edge.to]++;
}
// Collect nodes with 0 inDegree
for (let node of this.nodes) {
if (inDegree[node] === 0) {
zeroInDegreeQueue.push(node);
}
}
// Collect nodes with 0 inDegree
for (let node of this.nodes) {
if (inDegree[node] === 0) {
zeroInDegreeQueue.push(node);
}
}
// process nodes with 0 inDegree
while (zeroInDegreeQueue.length > 0) {
let node = zeroInDegreeQueue.shift();
topologicalOrder.push(node);
// process nodes with 0 inDegree
while (zeroInDegreeQueue.length > 0) {
let node = zeroInDegreeQueue.shift();
topologicalOrder.push(node);
for (let tail of this.getDirectTails(node)) {
inDegree[tail]--;
if (inDegree[tail] === 0) {
zeroInDegreeQueue.push(tail);
}
}
}
return topologicalOrder;
}
for (let tail of this.getDirectTails(node)) {
inDegree[tail]--;
if (inDegree[tail] === 0) {
zeroInDegreeQueue.push(tail);
}
}
}
return topologicalOrder;
}
/**
* Check if the graph contains a cycle
* @returns {boolean}
*/
detectCycle() {
// If topological order includes all nodes, no cycle exists
return this.topoSort().length < this.nodes.length;
}
/**
* Check if the graph contains a cycle
* @returns {boolean}
*/
detectCycle() {
// If topological order includes all nodes, no cycle exists
return this.topoSort().length < this.nodes.length;
}
}
module.exports = myGraph;

View File

@@ -1,23 +1,23 @@
/**
* Stringifies only values of a JSON object, including nested ones
*
*
* @param {any} obj JSON object
* @param {string} delimiter Delimiter of final string
* @returns {string}
*/
const getNestedValuesString = (obj, delimiter = ", ") => {
let values = [];
for (key in obj) {
if (typeof obj[key] !== "object") {
values.push(obj[key]);
} else {
values = values.concat(getNestedValuesString(obj[key]));
}
}
let values = [];
for (key in obj) {
if (typeof obj[key] !== "object") {
values.push(obj[key]);
} else {
values = values.concat(getNestedValuesString(obj[key]));
}
}
return values.join(delimiter);
return values.join(delimiter);
}
module.exports = {
getNestedValuesString
getNestedValuesString
}

View File

@@ -6,31 +6,31 @@ const { combine, label, timestamp, printf, errors } = format;
const typedefs = require("../typedefs");
const getLabel = (callingModule) => {
if (!callingModule.filename) return "repl";
const parts = callingModule.filename?.split(path.sep);
return path.join(parts[parts.length - 2], parts.pop());
if (!callingModule.filename) return "repl";
const parts = callingModule.filename?.split(path.sep);
return path.join(parts[parts.length - 2], parts.pop());
};
const allowedErrorKeys = ["name", "code", "message", "stack"];
const metaFormat = (meta) => {
if (Object.keys(meta).length > 0)
return "\n" + JSON.stringify(meta, null, "\t");
return "";
if (Object.keys(meta).length > 0)
return "\n" + JSON.stringify(meta, null, "\t");
return "";
}
const logFormat = printf(({ level, message, label, timestamp, ...meta }) => {
if (meta.error) { // if the error was passed
for (const key in meta.error) {
if (!allowedErrorKeys.includes(key)) {
delete meta.error[key];
}
}
const { stack, ...rest } = meta.error;
return `${timestamp} [${label}] ${level}: ${message}${metaFormat(rest)}\n` +
`${stack ?? ""}`;
if (meta.error) { // if the error was passed
for (const key in meta.error) {
if (!allowedErrorKeys.includes(key)) {
delete meta.error[key];
}
}
return `${timestamp} [${label}] ${level}: ${message}${metaFormat(meta)}`;
const { stack, ...rest } = meta.error;
return `${timestamp} [${label}] ${level}: ${message}${metaFormat(rest)}\n` +
`${stack ?? ""}`;
}
return `${timestamp} [${label}] ${level}: ${message}${metaFormat(meta)}`;
});
/**
@@ -38,30 +38,30 @@ const logFormat = printf(({ level, message, label, timestamp, ...meta }) => {
* @param {typedefs.Module} callingModule The module from which the logger is called
*/
const curriedLogger = (callingModule) => {
let winstonLogger = createLogger({
levels: config.npm.levels,
format: combine(
errors({ stack: true }),
label({ label: getLabel(callingModule) }),
timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
logFormat,
),
transports: [
new transports.Console({ level: "info" }),
new transports.File({
filename: __dirname + "/../logs/debug.log",
level: "debug",
maxsize: 10485760,
}),
new transports.File({
filename: __dirname + "/../logs/error.log",
level: "error",
maxsize: 1048576,
}),
]
});
winstonLogger.on("error", (error) => winstonLogger.error("Error inside logger", { error }));
return winstonLogger;
let winstonLogger = createLogger({
levels: config.npm.levels,
format: combine(
errors({ stack: true }),
label({ label: getLabel(callingModule) }),
timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
logFormat,
),
transports: [
new transports.Console({ level: "info" }),
new transports.File({
filename: __dirname + "/../logs/debug.log",
level: "debug",
maxsize: 10485760,
}),
new transports.File({
filename: __dirname + "/../logs/error.log",
level: "error",
maxsize: 1048576,
}),
]
});
winstonLogger.on("error", (error) => winstonLogger.error("Error inside logger", { error }));
return winstonLogger;
}
module.exports = curriedLogger;

View File

@@ -11,46 +11,46 @@ const base62Pattern = /^[A-Za-z0-9]+$/;
* @throws {TypeError} If the input is not a valid Spotify URI
*/
const parseSpotifyURI = (uri) => {
const parts = uri.split(":");
const parts = uri.split(":");
if (parts[0] !== "spotify") {
throw new TypeError(`${uri} is not a valid Spotify URI`);
}
if (parts[0] !== "spotify") {
throw new TypeError(`${uri} is not a valid Spotify URI`);
}
let type = parts[1];
let type = parts[1];
if (type === "local") {
// Local file format: spotify:local:<artist>:<album>:<title>:<duration>
let idParts = parts.slice(2);
if (idParts.length < 4) {
throw new TypeError(`${uri} is not a valid local file URI`);
}
if (type === "local") {
// Local file format: spotify:local:<artist>:<album>:<title>:<duration>
let idParts = parts.slice(2);
if (idParts.length < 4) {
throw new TypeError(`${uri} is not a valid local file URI`);
}
// URL decode artist, album, and title
const artist = decodeURIComponent(idParts[0] || "");
const album = decodeURIComponent(idParts[1] || "");
const title = decodeURIComponent(idParts[2]);
const duration = parseInt(idParts[3], 10);
// URL decode artist, album, and title
const artist = decodeURIComponent(idParts[0] || "");
const album = decodeURIComponent(idParts[1] || "");
const title = decodeURIComponent(idParts[2]);
const duration = parseInt(idParts[3], 10);
if (isNaN(duration)) {
throw new TypeError(`${uri} has an invalid duration`);
}
if (isNaN(duration)) {
throw new TypeError(`${uri} has an invalid duration`);
}
return { type: "track", is_local: true, artist, album, title, duration };
} else {
// Not a local file
if (parts.length !== 3) {
throw new TypeError(`${uri} is not a valid Spotify URI`);
}
return { type: "track", is_local: true, artist, album, title, duration };
} else {
// Not a local file
if (parts.length !== 3) {
throw new TypeError(`${uri} is not a valid Spotify URI`);
}
const id = parts[2];
const id = parts[2];
if (!base62Pattern.test(id)) {
throw new TypeError(`${uri} has an invalid ID`);
}
if (!base62Pattern.test(id)) {
throw new TypeError(`${uri} has an invalid ID`);
}
return { type, is_local: false, id };
}
return { type, is_local: false, id };
}
}
/**
@@ -60,45 +60,45 @@ const parseSpotifyURI = (uri) => {
* @throws {TypeError} If the input is not a valid Spotify link
*/
const parseSpotifyLink = (link) => {
const localPattern = /^https:\/\/open\.spotify\.com\/local\/([^\/]*)\/([^\/]*)\/([^\/]+)\/(\d+)$/;
const standardPattern = /^https:\/\/open\.spotify\.com\/([^\/]+)\/([^\/?]+)/;
const localPattern = /^https:\/\/open\.spotify\.com\/local\/([^\/]*)\/([^\/]*)\/([^\/]+)\/(\d+)$/;
const standardPattern = /^https:\/\/open\.spotify\.com\/([^\/]+)\/([^\/?]+)/;
if (localPattern.test(link)) {
// Local file format: https://open.spotify.com/local/artist/album/title/duration
const matches = link.match(localPattern);
if (!matches) {
throw new TypeError(`${link} is not a valid Spotify local file link`);
}
if (localPattern.test(link)) {
// Local file format: https://open.spotify.com/local/artist/album/title/duration
const matches = link.match(localPattern);
if (!matches) {
throw new TypeError(`${link} is not a valid Spotify local file link`);
}
// URL decode artist, album, and title
const artist = decodeURIComponent(matches[1] || "");
const album = decodeURIComponent(matches[2] || "");
const title = decodeURIComponent(matches[3]);
const duration = parseInt(matches[4], 10);
// URL decode artist, album, and title
const artist = decodeURIComponent(matches[1] || "");
const album = decodeURIComponent(matches[2] || "");
const title = decodeURIComponent(matches[3]);
const duration = parseInt(matches[4], 10);
if (isNaN(duration)) {
throw new TypeError(`${link} has an invalid duration`);
}
if (isNaN(duration)) {
throw new TypeError(`${link} has an invalid duration`);
}
return { type: "track", is_local: true, artist, album, title, duration };
} else if (standardPattern.test(link)) {
// Not a local file
const matches = link.match(standardPattern);
if (!matches || matches.length < 3) {
throw new TypeError(`${link} is not a valid Spotify link`);
}
return { type: "track", is_local: true, artist, album, title, duration };
} else if (standardPattern.test(link)) {
// Not a local file
const matches = link.match(standardPattern);
if (!matches || matches.length < 3) {
throw new TypeError(`${link} is not a valid Spotify link`);
}
const type = matches[1];
const id = matches[2];
const type = matches[1];
const id = matches[2];
if (!base62Pattern.test(id)) {
throw new TypeError(`${link} has an invalid ID`);
}
if (!base62Pattern.test(id)) {
throw new TypeError(`${link} has an invalid ID`);
}
return { type, is_local: false, id };
} else {
throw new TypeError(`${link} is not a valid Spotify link`);
}
return { type, is_local: false, id };
} else {
throw new TypeError(`${link} is not a valid Spotify link`);
}
}
/**
@@ -107,14 +107,14 @@ const parseSpotifyLink = (link) => {
* @returns {string}
*/
const buildSpotifyURI = (uriObj) => {
if (uriObj.is_local) {
const artist = encodeURIComponent(uriObj.artist ?? "");
const album = encodeURIComponent(uriObj.album ?? "");
const title = encodeURIComponent(uriObj.title ?? "");
const duration = uriObj.duration ? uriObj.duration.toString() : "";
return `spotify:local:${artist}:${album}:${title}:${duration}`;
}
return `spotify:${uriObj.type}:${uriObj.id}`;
if (uriObj.is_local) {
const artist = encodeURIComponent(uriObj.artist ?? "");
const album = encodeURIComponent(uriObj.album ?? "");
const title = encodeURIComponent(uriObj.title ?? "");
const duration = uriObj.duration ? uriObj.duration.toString() : "";
return `spotify:local:${artist}:${album}:${title}:${duration}`;
}
return `spotify:${uriObj.type}:${uriObj.id}`;
}
/**
@@ -123,19 +123,19 @@ const buildSpotifyURI = (uriObj) => {
* @returns {string}
*/
const buildSpotifyLink = (uriObj) => {
if (uriObj.is_local) {
const artist = encodeURIComponent(uriObj.artist ?? "");
const album = encodeURIComponent(uriObj.album ?? "");
const title = encodeURIComponent(uriObj.title ?? "");
const duration = uriObj.duration ? uriObj.duration.toString() : "";
return `https://open.spotify.com/local/${artist}/${album}/${title}/${duration}`;
}
return `https://open.spotify.com/${uriObj.type}/${uriObj.id}`
if (uriObj.is_local) {
const artist = encodeURIComponent(uriObj.artist ?? "");
const album = encodeURIComponent(uriObj.album ?? "");
const title = encodeURIComponent(uriObj.title ?? "");
const duration = uriObj.duration ? uriObj.duration.toString() : "";
return `https://open.spotify.com/local/${artist}/${album}/${title}/${duration}`;
}
return `https://open.spotify.com/${uriObj.type}/${uriObj.id}`
}
module.exports = {
parseSpotifyURI,
parseSpotifyLink,
buildSpotifyURI,
buildSpotifyLink
parseSpotifyURI,
parseSpotifyLink,
buildSpotifyURI,
buildSpotifyLink
}