tspace-mysql
Version:
Tspace MySQL is a promise-based ORM for Node.js, designed with modern TypeScript and providing type safety for schema databases.
434 lines (424 loc) • 13.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.utils = void 0;
const __1 = require("..");
const constants_1 = require("../constants");
const typeOf = (data) => Object.prototype.toString.apply(data).slice(8, -1).toLocaleLowerCase();
const isDate = (data) => {
if (typeOf(data) === 'date')
return true;
return false;
};
const timestamp = (dateString) => {
const d = dateString == null ? new Date() : new Date(dateString);
const year = d.getFullYear();
const month = `0${(d.getMonth() + 1)}`.slice(-2);
const date = `0${(d.getDate())}`.slice(-2);
const hours = `0${(d.getHours())}`.slice(-2);
const minutes = `0${(d.getMinutes())}`.slice(-2);
const seconds = `0${(d.getSeconds())}`.slice(-2);
const ymd = `${[
year,
month,
date
].join('-')}`;
const his = `${[
hours,
minutes,
seconds
].join(':')}`;
return `${ymd} ${his}`;
};
const date = (value) => {
const d = value == null ? new Date() : new Date(value);
const year = d.getFullYear();
const month = `0${(d.getMonth() + 1)}`.slice(-2);
const date = `0${(d.getDate())}`.slice(-2);
const now = `${year}-${month}-${date}`;
return now;
};
const escape = (v, hard = false) => {
if (typeof v !== 'string') {
if (Number.isNaN(v))
return 'NaN';
if (v === Infinity)
return 'Infinity';
if (v === -Infinity)
return '-Infinity';
return v;
}
if (checkValueHasRaw(v) && !hard)
return v;
return v.replace(/[\0\b\t\n\r\x1a\'\\]/g, "\\'");
};
const escapeActions = (v) => {
if (typeof v !== 'string') {
if (Number.isNaN(v))
return 'NaN';
if (v === Infinity)
return 'Infinity';
if (v === -Infinity)
return '-Infinity';
return v;
}
if (checkValueHasRaw(v))
return v;
return v.replace(/[\0\b\r\x1a\'\\]/g, "''");
};
const escapeXSS = (str) => {
if (typeof str !== 'string')
return str;
return str
.replace(/[;\\]/gi, '')
.replace(/on\w+="[^"]+"/gi, '')
.replace(/\s+(onerror|onload)\s*=/gi, '')
.replace(/\s+alert*/gi, '')
.replace(/\([^)]*\) *=>/g, '')
.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
};
const columnRelation = (name) => {
const matches = name?.match(/[A-Z]/g) ?? [];
if (matches.length < 1)
return `${name.toLocaleLowerCase()}`;
return name.replace(matches[0], `_${matches[0].toUpperCase()}`);
};
const generateUUID = () => {
const date = +new Date();
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
let r = Math.random() * 16;
r = (date + r) % 16 | 0;
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
});
};
const transfromBooleanToNumber = (data) => {
if (typeOf(data) === 'boolean')
return Number(data);
return data;
};
const transfromDateToDateString = (data) => {
const isDate = (d) => {
return typeOf(d) === 'date';
};
const isISODateString = (s) => {
if (typeof s !== 'string')
return false;
const isoRegex = /^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?$/;
return isoRegex.test(s) && !isNaN(Date.parse(s.replace(' ', 'T')));
};
if (isDate(data)) {
return timestamp(data);
}
if (isISODateString(data)) {
return timestamp(new Date(data));
}
if (typeOf(data) === 'object' && data !== null) {
for (const key in data) {
const d = data[key];
if (isDate(d)) {
data[key] = timestamp(d);
continue;
}
if (isISODateString(d)) {
data[key] = timestamp(new Date(d));
}
}
}
return data;
};
const snakeCase = (data) => {
try {
if (typeof (data) !== "object")
return data;
if (typeof data === 'string') {
return String(data).replace(/([A-Z])/g, (str) => `_${str.toLocaleLowerCase()}`);
}
Object.entries(data).forEach(([oldName, _]) => {
const newName = oldName.replace(/([A-Z])/g, (str) => `_${str.toLocaleLowerCase()}`);
if (newName !== oldName) {
if (data.hasOwnProperty(oldName)) {
data = { ...data,
[newName]: data[oldName]
};
delete data[oldName];
}
}
if (typeof (data[newName]) === "object" && !(data[newName] instanceof __1.Blueprint)) {
data[newName] = snakeCase(data[newName]);
}
});
return data;
}
catch (e) {
return data;
}
};
const camelCase = (data) => {
try {
if (typeof (data) !== "object") {
return data;
}
if (typeof data === 'string') {
return String(data).replace(/(.(_|-|\s)+.)/g, (str) => str[0] + (str[str.length - 1].toUpperCase()));
}
Object.entries(data).forEach(([oldName]) => {
const newName = oldName.replace(/(.(_|-|\s)+.)/g, (str) => str[0] + (str[str.length - 1].toUpperCase()));
if (newName !== oldName) {
if (data.hasOwnProperty(oldName)) {
data = { ...data,
[newName]: data[oldName]
};
delete data[oldName];
}
}
if (typeof (data[newName]) === "object" && !(data[newName] instanceof __1.Blueprint)) {
data[newName] = camelCase(data[newName]);
}
});
return data;
}
catch (e) {
return data;
}
};
const consoleDebug = (sql, retry = false) => {
if (typeof sql !== "string" || sql == null)
return;
const colors = {
keyword: '\x1b[35m',
string: '\x1b[32m',
special: '\x1b[38;2;77;215;240m',
operator: '\x1b[31m',
reset: '\x1b[0m'
};
const extractKeywords = (obj) => {
return Object.values(obj)
.flatMap(v => typeof v === 'object' ? [] : v)
.filter(v => typeof v === 'string')
.filter(v => !v.startsWith('$') &&
!/[a-z][A-Z]/.test(v))
.map(v => v.toUpperCase());
};
const sqlKeywords = extractKeywords(constants_1.CONSTANTS);
const colorSQL = (query) => {
return query
.replace(/'[^']*'/g, m => `${colors.string}${m}${colors.reset}`)
.replace(/([`"])(?:\\.|(?!\1).)*\1/g, m => `${colors.special}${m}${colors.reset}`)
.replace(new RegExp(`\\b(${sqlKeywords.join('|').replace(/ /g, '\\s+')})\\b`, 'g'), (m) => `${colors.keyword}${m}${colors.reset}`);
};
if (retry) {
console.log(`\n\x1b[31mRETRY QUERY:\x1b[0m ${colorSQL(sql.trim())};`);
return;
}
console.log(`\n\x1b[34mQUERY:\x1b[0m ${colorSQL(sql.trim())};`);
};
const consoleExec = (startTime, endTime) => {
const diffInMilliseconds = endTime - startTime;
const diffInSeconds = diffInMilliseconds / 1000;
console.log(`\x1b[34mDURATION:\x1b[0m \x1b[32m${diffInSeconds} sec\x1b[0m`);
};
const consoleCache = (provider) => {
console.log(`\n\x1b[34mCACHE:\x1b[0m \x1b[33m${provider}\x1b[0m`);
};
const randomString = (length = 100) => {
let str = '';
const salt = 3;
for (let i = 0; i < length / salt; i++) {
str += Math.random().toString(36).substring(salt);
}
return str.toLocaleLowerCase().replace(/'/g, '').slice(-length);
};
const faker = (value) => {
value = value.toLocaleLowerCase();
if (!value.search('uuid'))
return generateUUID();
if (!value.search('timestamp'))
return timestamp();
if (!value.search('datetime'))
return timestamp();
if (!value.search('date'))
return date();
if (!value.search('tinyint'))
return [true, false][Math.round(Math.random())];
if (!value.search('boolean'))
return [true, false][Math.round(Math.random())];
if (!value.search('longtext'))
return randomString(500);
if (!value.search('text'))
return randomString(500);
if (!value.search('int'))
return Number(Math.floor((Math.random() * 999) + 1));
if (!value.search('float'))
return Number((Math.random() * 100).toFixed(2));
if (!value.search('double'))
return Number((Math.random() * 100).toFixed(2));
if (!value.search('json')) {
return JSON.stringify({
id: Number(Math.floor(Math.random() * 1000)),
name: randomString(50)
});
}
if (!value.search('varchar')) {
const regex = /\d+/g;
const limit = Number(value?.match(regex)?.pop() ?? 255);
return randomString(limit);
}
return 'fake data';
};
const hookHandle = async (hooks, result) => {
for (const hook of hooks)
await hook(result);
return;
};
const chunkArray = (array, length) => {
const chunks = [];
for (let i = 0; i < array.length; i += length) {
chunks.push(array.slice(i, i + length));
}
return chunks;
};
const wait = (ms) => {
if (ms === 0)
return;
return new Promise(ok => setTimeout(ok, Number.isNaN(ms) ? 0 : ms));
};
const softNumber = (n) => {
const number = Number(n);
if (number === -1) {
return 2 ** 31 - 1; // int 32 bit
}
if (Number.isNaN(number)) {
return -1;
}
return Number.parseInt(`${number}`);
};
const checkValueHasRaw = (value) => {
return typeof value === 'string' && value.includes(constants_1.CONSTANTS.RAW);
};
const transfromValueHasRaw = (value) => {
if (checkValueHasRaw(value)) {
return `${transfromBooleanToNumber(value)}`.replace(constants_1.CONSTANTS.RAW, "");
}
return typeof value === 'number' ? value : `'${transfromBooleanToNumber(value)}'`;
};
const transfromValueHasOp = (str) => {
if (typeof str !== "string")
str = String(str);
if (!str.includes(constants_1.CONSTANTS.OP) || !str.includes(constants_1.CONSTANTS.VALUE)) {
return null;
}
const opRegex = new RegExp(`\\${constants_1.CONSTANTS.OP}\\(([^)]+)\\)`);
const valueRegex = new RegExp(`\\${constants_1.CONSTANTS.VALUE}\\((.*)\\)$`);
const opMatch = str.match(opRegex);
const valueMatch = str.match(valueRegex);
const op = opMatch ? opMatch[1] : "";
const value = valueMatch ? valueMatch[1] : "";
return {
op: op,
value: value,
};
};
const valueAndOperator = (value, operator, useDefault = false) => {
if (useDefault)
return [operator, "="];
if (operator == null) {
return [[], "="];
}
if (operator.toUpperCase() === constants_1.CONSTANTS.LIKE) {
operator = operator.toUpperCase();
}
return [value, operator];
};
const baseModelTemplate = ({ model, schema, imports, relation }) => {
return `import {
type T,
Model,
Blueprint
} from 'tspace-mysql';
${imports}
const schema = {
${schema}
}
type TS = T.Schema<typeof schema>
type TR = T.Relation<{
${relation.types}
}>
class ${model} extends Model<TS,TR> {
protected boot () : void {
this.useSchema(schema);
// -------------------------------------------------
${relation.useds}
}
}
export { ${model} }
export default ${model}`;
};
const decoratorModelTemplate = ({ model, schema, imports }) => {
return `import {
Model,
Blueprint,
Column,
HasMany,
BelongsTo
} from 'tspace-mysql';
${imports}
class ${model} extends Model {
${schema}
}
export { ${model} }
export default ${model}`;
};
const applyTransforms = async ({ result, transforms, action }) => {
if (transforms == null)
return;
if (Array.isArray(result)) {
const promises = result.map(item => applyTransforms({ result: item, transforms, action }));
await Promise.all(promises);
return;
}
if (result === null || typeof result !== 'object') {
return;
}
for (const key in transforms) {
if (key in result) {
const fn = transforms[key][action];
if (fn) {
result[key] = await fn(result[key]);
}
}
}
return;
};
const utils = {
typeOf,
isDate,
consoleDebug,
consoleExec,
consoleCache,
faker,
columnRelation,
timestamp,
date,
escape,
escapeActions,
escapeXSS,
generateUUID,
transfromBooleanToNumber,
transfromDateToDateString,
snakeCase,
camelCase,
randomString,
hookHandle,
chunkArray,
wait,
softNumber,
transfromValueHasRaw,
transfromValueHasOp,
checkValueHasRaw,
valueAndOperator,
baseModelTemplate,
decoratorModelTemplate,
applyTransforms
};
exports.utils = utils;
exports.default = utils;
//# sourceMappingURL=index.js.map