mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Node: Migrate to ES Modules
This commit is contained in:
118
src/util.js
118
src/util.js
@ -1,13 +1,16 @@
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const commandExistsSync = require('command-exists').sync;
|
||||
const writeFileAtomicSync = require('write-file-atomic').sync;
|
||||
const _ = require('lodash');
|
||||
const yauzl = require('yauzl');
|
||||
const mime = require('mime-types');
|
||||
const yaml = require('yaml');
|
||||
const { default: simpleGit } = require('simple-git');
|
||||
const { Readable } = require('stream');
|
||||
import * as path from 'node:path';
|
||||
import * as fs from 'node:fs';
|
||||
import * as http2 from 'node:http2';
|
||||
import { Readable } from 'node:stream';
|
||||
import { createRequire } from 'node:module';
|
||||
|
||||
import yaml from 'yaml';
|
||||
import { sync as commandExistsSync } from 'command-exists';
|
||||
import { sync as writeFileAtomicSync } from 'write-file-atomic';
|
||||
import _ from 'lodash';
|
||||
import yauzl from 'yauzl';
|
||||
import mime from 'mime-types';
|
||||
import { default as simpleGit } from 'simple-git';
|
||||
|
||||
/**
|
||||
* Parsed config object.
|
||||
@ -18,7 +21,7 @@ let CACHED_CONFIG = null;
|
||||
* Returns the config object from the config.yaml file.
|
||||
* @returns {object} Config object
|
||||
*/
|
||||
function getConfig() {
|
||||
export function getConfig() {
|
||||
if (CACHED_CONFIG) {
|
||||
return CACHED_CONFIG;
|
||||
}
|
||||
@ -46,7 +49,7 @@ function getConfig() {
|
||||
* @param {any} defaultValue - Default value to return if the key is not found
|
||||
* @returns {any} Value for the given key
|
||||
*/
|
||||
function getConfigValue(key, defaultValue = null) {
|
||||
export function getConfigValue(key, defaultValue = null) {
|
||||
const config = getConfig();
|
||||
return _.get(config, key, defaultValue);
|
||||
}
|
||||
@ -56,7 +59,7 @@ function getConfigValue(key, defaultValue = null) {
|
||||
* @param {string} key Key to set
|
||||
* @param {any} value Value to set
|
||||
*/
|
||||
function setConfigValue(key, value) {
|
||||
export function setConfigValue(key, value) {
|
||||
// Reset cache so that the next getConfig call will read the updated config file
|
||||
CACHED_CONFIG = null;
|
||||
const config = getConfig();
|
||||
@ -69,7 +72,7 @@ function setConfigValue(key, value) {
|
||||
* @param {string} auth username:password
|
||||
* @returns {string} Basic Auth header value
|
||||
*/
|
||||
function getBasicAuthHeader(auth) {
|
||||
export function getBasicAuthHeader(auth) {
|
||||
const encoded = Buffer.from(`${auth}`).toString('base64');
|
||||
return `Basic ${encoded}`;
|
||||
}
|
||||
@ -79,7 +82,7 @@ function getBasicAuthHeader(auth) {
|
||||
* Also returns the agent string for the Horde API.
|
||||
* @returns {Promise<{agent: string, pkgVersion: string, gitRevision: string | null, gitBranch: string | null, commitDate: string | null, isLatest: boolean}>} Version info object
|
||||
*/
|
||||
async function getVersion() {
|
||||
export async function getVersion() {
|
||||
let pkgVersion = 'UNKNOWN';
|
||||
let gitRevision = null;
|
||||
let gitBranch = null;
|
||||
@ -87,9 +90,10 @@ async function getVersion() {
|
||||
let isLatest = true;
|
||||
|
||||
try {
|
||||
const require = createRequire(import.meta.url);
|
||||
const pkgJson = require(path.join(process.cwd(), './package.json'));
|
||||
pkgVersion = pkgJson.version;
|
||||
if (!process['pkg'] && commandExistsSync('git')) {
|
||||
if (commandExistsSync('git')) {
|
||||
const git = simpleGit();
|
||||
const cwd = process.cwd();
|
||||
gitRevision = await git.cwd(cwd).revparse(['--short', 'HEAD']);
|
||||
@ -117,7 +121,7 @@ async function getVersion() {
|
||||
* @param {number} ms Milliseconds to wait
|
||||
* @returns {Promise<void>} Promise that resolves after the given amount of milliseconds
|
||||
*/
|
||||
function delay(ms) {
|
||||
export function delay(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
@ -127,7 +131,7 @@ function delay(ms) {
|
||||
* @returns {string} Random hex string
|
||||
* @example getHexString(8) // 'a1b2c3d4'
|
||||
*/
|
||||
function getHexString(length) {
|
||||
export function getHexString(length) {
|
||||
const chars = '0123456789abcdef';
|
||||
let result = '';
|
||||
for (let i = 0; i < length; i++) {
|
||||
@ -142,7 +146,7 @@ function getHexString(length) {
|
||||
* @param {string} fileExtension File extension to look for
|
||||
* @returns {Promise<Buffer|null>} Buffer containing the extracted file. Null if the file was not found.
|
||||
*/
|
||||
async function extractFileFromZipBuffer(archiveBuffer, fileExtension) {
|
||||
export async function extractFileFromZipBuffer(archiveBuffer, fileExtension) {
|
||||
return await new Promise((resolve, reject) => yauzl.fromBuffer(Buffer.from(archiveBuffer), { lazyEntries: true }, (err, zipfile) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
@ -181,7 +185,7 @@ async function extractFileFromZipBuffer(archiveBuffer, fileExtension) {
|
||||
* @param {string} zipFilePath Path to the ZIP archive
|
||||
* @returns {Promise<[string, Buffer][]>} Array of image buffers
|
||||
*/
|
||||
async function getImageBuffers(zipFilePath) {
|
||||
export async function getImageBuffers(zipFilePath) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Check if the zip file exists
|
||||
if (!fs.existsSync(zipFilePath)) {
|
||||
@ -237,7 +241,7 @@ async function getImageBuffers(zipFilePath) {
|
||||
* @param {any} readableStream Readable stream to read from
|
||||
* @returns {Promise<Buffer[]>} Array of chunks
|
||||
*/
|
||||
async function readAllChunks(readableStream) {
|
||||
export async function readAllChunks(readableStream) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Consume the readable stream
|
||||
const chunks = [];
|
||||
@ -261,7 +265,7 @@ function isObject(item) {
|
||||
return (item && typeof item === 'object' && !Array.isArray(item));
|
||||
}
|
||||
|
||||
function deepMerge(target, source) {
|
||||
export function deepMerge(target, source) {
|
||||
let output = Object.assign({}, target);
|
||||
if (isObject(target) && isObject(source)) {
|
||||
Object.keys(source).forEach(key => {
|
||||
@ -278,7 +282,7 @@ function deepMerge(target, source) {
|
||||
return output;
|
||||
}
|
||||
|
||||
const color = {
|
||||
export const color = {
|
||||
byNum: (mess, fgNum) => {
|
||||
mess = mess || '';
|
||||
fgNum = fgNum === undefined ? 31 : fgNum;
|
||||
@ -298,7 +302,7 @@ const color = {
|
||||
* Gets a random UUIDv4 string.
|
||||
* @returns {string} A UUIDv4 string
|
||||
*/
|
||||
function uuidv4() {
|
||||
export function uuidv4() {
|
||||
if ('crypto' in global && 'randomUUID' in global.crypto) {
|
||||
return global.crypto.randomUUID();
|
||||
}
|
||||
@ -309,7 +313,7 @@ function uuidv4() {
|
||||
});
|
||||
}
|
||||
|
||||
function humanizedISO8601DateTime(date) {
|
||||
export function humanizedISO8601DateTime(date) {
|
||||
let baseDate = typeof date === 'number' ? new Date(date) : new Date();
|
||||
let humanYear = baseDate.getFullYear();
|
||||
let humanMonth = (baseDate.getMonth() + 1);
|
||||
@ -322,7 +326,7 @@ function humanizedISO8601DateTime(date) {
|
||||
return HumanizedDateTime;
|
||||
}
|
||||
|
||||
function tryParse(str) {
|
||||
export function tryParse(str) {
|
||||
try {
|
||||
return JSON.parse(str);
|
||||
} catch {
|
||||
@ -337,7 +341,7 @@ function tryParse(str) {
|
||||
* @param {string} inputPath The path to be converted.
|
||||
* @returns The relative URL path from which the client can access the file.
|
||||
*/
|
||||
function clientRelativePath(root, inputPath) {
|
||||
export function clientRelativePath(root, inputPath) {
|
||||
if (!inputPath.startsWith(root)) {
|
||||
throw new Error('Input path does not start with the root directory');
|
||||
}
|
||||
@ -350,11 +354,11 @@ function clientRelativePath(root, inputPath) {
|
||||
* @param {string} filename The file name to remove the extension from.
|
||||
* @returns The file name, sans extension
|
||||
*/
|
||||
function removeFileExtension(filename) {
|
||||
export function removeFileExtension(filename) {
|
||||
return filename.replace(/\.[^.]+$/, '');
|
||||
}
|
||||
|
||||
function generateTimestamp() {
|
||||
export function generateTimestamp() {
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = String(now.getMonth() + 1).padStart(2, '0');
|
||||
@ -371,7 +375,7 @@ function generateTimestamp() {
|
||||
* @param {string} directory The root directory to remove backups from.
|
||||
* @param {string} prefix File prefix to filter backups by.
|
||||
*/
|
||||
function removeOldBackups(directory, prefix) {
|
||||
export function removeOldBackups(directory, prefix) {
|
||||
const MAX_BACKUPS = Number(getConfigValue('numberOfBackups', 50));
|
||||
|
||||
let files = fs.readdirSync(directory).filter(f => f.startsWith(prefix));
|
||||
@ -389,7 +393,7 @@ function removeOldBackups(directory, prefix) {
|
||||
* @param {'name' | 'date'} sortBy Sort images by name or date
|
||||
* @returns {string[]} List of image file names
|
||||
*/
|
||||
function getImages(directoryPath, sortBy = 'name') {
|
||||
export function getImages(directoryPath, sortBy = 'name') {
|
||||
function getSortFunction() {
|
||||
switch (sortBy) {
|
||||
case 'name':
|
||||
@ -415,7 +419,7 @@ function getImages(directoryPath, sortBy = 'name') {
|
||||
* @param {import('node-fetch').Response} from The Fetch API response to pipe from.
|
||||
* @param {import('express').Response} to The Express response to pipe to.
|
||||
*/
|
||||
function forwardFetchResponse(from, to) {
|
||||
export function forwardFetchResponse(from, to) {
|
||||
let statusCode = from.status;
|
||||
let statusText = from.statusText;
|
||||
|
||||
@ -434,6 +438,7 @@ function forwardFetchResponse(from, to) {
|
||||
|
||||
to.statusCode = statusCode;
|
||||
to.statusMessage = statusText;
|
||||
|
||||
from.body.pipe(to);
|
||||
|
||||
to.socket.on('close', function () {
|
||||
@ -457,10 +462,9 @@ function forwardFetchResponse(from, to) {
|
||||
* @param {object} headers Request headers
|
||||
* @returns {Promise<string>} Response body
|
||||
*/
|
||||
function makeHttp2Request(endpoint, method, body, headers) {
|
||||
export function makeHttp2Request(endpoint, method, body, headers) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
const http2 = require('http2');
|
||||
const url = new URL(endpoint);
|
||||
const client = http2.connect(url.origin);
|
||||
|
||||
@ -511,7 +515,7 @@ function makeHttp2Request(endpoint, method, body, headers) {
|
||||
* @param {string} yamlString YAML-serialized object
|
||||
* @returns
|
||||
*/
|
||||
function mergeObjectWithYaml(obj, yamlString) {
|
||||
export function mergeObjectWithYaml(obj, yamlString) {
|
||||
if (!yamlString) {
|
||||
return;
|
||||
}
|
||||
@ -540,7 +544,7 @@ function mergeObjectWithYaml(obj, yamlString) {
|
||||
* @param {string} yamlString YAML-serialized array
|
||||
* @returns {void} Nothing
|
||||
*/
|
||||
function excludeKeysByYaml(obj, yamlString) {
|
||||
export function excludeKeysByYaml(obj, yamlString) {
|
||||
if (!yamlString) {
|
||||
return;
|
||||
}
|
||||
@ -569,14 +573,14 @@ function excludeKeysByYaml(obj, yamlString) {
|
||||
* @param {string} str Input string
|
||||
* @returns {string} Trimmed string
|
||||
*/
|
||||
function trimV1(str) {
|
||||
export function trimV1(str) {
|
||||
return String(str ?? '').replace(/\/$/, '').replace(/\/v1$/, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple TTL memory cache.
|
||||
*/
|
||||
class Cache {
|
||||
export class Cache {
|
||||
/**
|
||||
* @param {number} ttl Time to live in milliseconds
|
||||
*/
|
||||
@ -633,7 +637,7 @@ class Cache {
|
||||
* @param {string} text Text with color formatting
|
||||
* @returns {string} Text without color formatting
|
||||
*/
|
||||
function removeColorFormatting(text) {
|
||||
export function removeColorFormatting(text) {
|
||||
// ANSI escape codes for colors are usually in the format \x1b[<codes>m
|
||||
return text.replace(/\x1b\[\d{1,2}(;\d{1,2})*m/g, '');
|
||||
}
|
||||
@ -643,7 +647,7 @@ function removeColorFormatting(text) {
|
||||
* @param {number} n Number of times to repeat the separator
|
||||
* @returns {string} Separator string
|
||||
*/
|
||||
function getSeparator(n) {
|
||||
export function getSeparator(n) {
|
||||
return '='.repeat(n);
|
||||
}
|
||||
|
||||
@ -652,7 +656,7 @@ function getSeparator(n) {
|
||||
* @param {string} url String to check
|
||||
* @returns {boolean} If the URL is valid
|
||||
*/
|
||||
function isValidUrl(url) {
|
||||
export function isValidUrl(url) {
|
||||
try {
|
||||
new URL(url);
|
||||
return true;
|
||||
@ -660,35 +664,3 @@ function isValidUrl(url) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getConfig,
|
||||
getConfigValue,
|
||||
setConfigValue,
|
||||
getVersion,
|
||||
getBasicAuthHeader,
|
||||
extractFileFromZipBuffer,
|
||||
getImageBuffers,
|
||||
readAllChunks,
|
||||
delay,
|
||||
deepMerge,
|
||||
color,
|
||||
uuidv4,
|
||||
humanizedISO8601DateTime,
|
||||
tryParse,
|
||||
clientRelativePath,
|
||||
removeFileExtension,
|
||||
generateTimestamp,
|
||||
removeOldBackups,
|
||||
getImages,
|
||||
forwardFetchResponse,
|
||||
getHexString,
|
||||
mergeObjectWithYaml,
|
||||
excludeKeysByYaml,
|
||||
trimV1,
|
||||
Cache,
|
||||
makeHttp2Request,
|
||||
removeColorFormatting,
|
||||
getSeparator,
|
||||
isValidUrl,
|
||||
};
|
||||
|
Reference in New Issue
Block a user