ts-sql-builder
Version:
A straightforward api for SQL query & schema generation
651 lines (633 loc) • 21.4 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// lib/index.ts
var lib_exports = {};
__export(lib_exports, {
$ALL: () => $ALL,
$AND: () => $AND,
$ANY: () => $ANY,
$BETWEEN: () => $BETWEEN,
$IN: () => $IN,
$NOT: () => $NOT,
$OR: () => $OR,
$concat: () => $concat,
$contain: () => $contain,
$isNull: () => $isNull,
$notNull: () => $notNull,
COLUMNS_METADATA_KEY: () => COLUMNS_METADATA_KEY,
Column: () => Column,
FKS_METADATA_KEY: () => FKS_METADATA_KEY,
ForeignKey: () => ForeignKey,
INDEXES_METADATA_KEY: () => INDEXES_METADATA_KEY,
Index: () => Index,
JoinType: () => JoinType,
ORDER: () => ORDER,
PK_METADATA_KEY: () => PK_METADATA_KEY,
PrimaryKey: () => PrimaryKey,
QueryBuilder: () => QueryBuilder,
TABLE_METADATA_KEY: () => TABLE_METADATA_KEY,
Table: () => Table,
buildSchema: () => buildSchema,
createQueryBuilder: () => createQueryBuilder,
getTableMetadata: () => getTableMetadata,
normalizeColumnOptions: () => normalizeColumnOptions,
normalized: () => normalized,
tableSchema: () => tableSchema
});
module.exports = __toCommonJS(lib_exports);
// lib/common/normalize-value.ts
function normalized(value) {
switch (typeof value) {
case "string":
return `'${value}'`;
case "object":
return `'${JSON.stringify(value)}'`;
case "function":
return value();
default:
return JSON.stringify(value);
}
}
// lib/query-builder/query-builder.ts
var import_sql_formatter = require("sql-formatter");
// lib/query-builder/qb.enums.ts
var JoinType = /* @__PURE__ */ ((JoinType2) => {
JoinType2["LEFT"] = "LEFT";
JoinType2["RIGHT"] = "RIGHT";
JoinType2["INNER"] = "INNER";
return JoinType2;
})(JoinType || {});
var ORDER = /* @__PURE__ */ ((ORDER2) => {
ORDER2["DESC"] = "DESC";
ORDER2["ASC"] = "ASC";
return ORDER2;
})(ORDER || {});
// lib/query-builder/query-builder.ts
var QueryBuilder = class _QueryBuilder {
constructor(table, alias) {
this._queryType = "READ";
this._insertColumns = [];
this._values = [];
this._updatedColumns = {};
this._fields = [];
this._table = { name: "" };
this._where = [];
this._groupBy = [];
this._having = [];
this._order = [];
this._offset = -1;
this._limit = -1;
this._joins = [];
this._rawFront = "";
this._rawEnd = "";
this._query = "";
this.addSelect = this.select;
this.andWhere = this.where;
this.andHaving = this.having;
if (table)
this.from(table, alias);
}
insertInto(table, ...columns) {
this._queryType = "CREATE";
this._insertColumns = columns;
return this.from(table);
}
columns(...columns) {
this._insertColumns = columns;
return this;
}
values(...values) {
this._values.push(...values);
return this;
}
update(table, data = {}) {
this._queryType = "UPDATE";
this.from(table);
return this.set(data);
}
set(data, value) {
if (typeof data === "string") {
this._updatedColumns[data] = value;
} else {
this._updatedColumns = __spreadValues(__spreadValues({}, this._updatedColumns), data);
}
return this;
}
delete(table) {
this._queryType = "DELETE";
return this.from(table);
}
from(table, alias) {
this._table = { name: table, alias: alias != null ? alias : table };
return this;
}
select(selection, fromTable) {
if (Array.isArray(selection)) {
selection.forEach((column) => this.select(column, fromTable));
} else if (typeof selection === "string") {
this._fields.push({
name: fromTable ? `${fromTable}.${selection}` : selection
});
} else {
Object.entries(selection).forEach(([name, alias]) => {
this._fields.push({
name: fromTable ? `${fromTable}.${name}` : name,
alias
});
});
}
return this;
}
agg(func, column, alias) {
const name = `${func}(${column})`;
return this.select(alias ? { [name]: alias } : name);
}
count(column = "*", alias) {
return this.agg("COUNT", column, alias);
}
countDistinct(column, alias) {
const name = `COUNT(DISTINCT ${column})`;
return this.select(alias ? { [name]: alias } : name);
}
sum(column, alias) {
return this.agg("SUM", column, alias);
}
avg(column, alias) {
return this.agg("AVG", column, alias);
}
min(column, alias) {
return this.agg("MIN", column, alias);
}
max(column, alias) {
return this.agg("MAX", column, alias);
}
where(...conditions) {
this._where.push(...conditions);
return this;
}
groupBy(...columns) {
this._groupBy.push(...columns);
return this;
}
having(...conditions) {
this._having.push(...conditions);
return this;
}
orderBy(order) {
if (typeof order === "string") {
this._order.push([order, "ASC" /* ASC */]);
} else if (Array.isArray(order)) {
this._order.push(...order.map((o) => [o, "ASC" /* ASC */]));
} else {
this._order.push(...Object.entries(order));
}
return this;
}
offset(n) {
this._offset = n;
return this;
}
limit(n) {
this._limit = n;
return this;
}
join(joinTable) {
var _a, _b;
(_a = joinTable.condition) != null ? _a : joinTable.condition = "TRUE";
(_b = joinTable.alias) != null ? _b : joinTable.alias = joinTable.name;
if (joinTable.select) {
if (joinTable.select === true)
joinTable.select = "*";
this.select(joinTable.select, joinTable.alias);
}
this._joins.push(joinTable);
return this;
}
innerJoin(joinTable) {
return this.join(__spreadProps(__spreadValues({}, joinTable), { type: "INNER" /* INNER */ }));
}
leftJoin(joinTable) {
return this.join(__spreadProps(__spreadValues({}, joinTable), { type: "LEFT" /* LEFT */ }));
}
rightJoin(joinTable) {
return this.join(__spreadProps(__spreadValues({}, joinTable), { type: "RIGHT" /* RIGHT */ }));
}
subQuery(cb) {
return cb ? `(${cb(new _QueryBuilder()).build().getSql()})` : new _QueryBuilder();
}
rawFront(rawSql) {
this._rawFront += rawSql;
return this;
}
rawEnd(rawSql) {
this._rawEnd += rawSql;
return this;
}
handleWhereConditions() {
if (this._where.length) {
this._query += ` WHERE ${this._where.join(" AND ")}`;
}
}
build() {
if (this._rawFront) {
this._query += `${this._rawFront} `;
}
switch (this._queryType) {
case "CREATE": {
this._query += `INSERT INTO ${this._table.name}`;
this._query += ` (${this._insertColumns.map((c) => `"${c}"`).join(", ")})`;
this._query += ` VALUES `;
const listOfValues = this._values.map((values) => {
return `(${values.map((value) => normalized(value)).join(", ")})`;
});
this._query += `${listOfValues.join(", ")}`;
break;
}
case "UPDATE": {
this._query += `UPDATE ${this._table.name} SET `;
const data = Object.entries(this._updatedColumns).map(
([column, value]) => `"${column}" = ${normalized(value)}`
);
this._query += data.join(", ");
this.handleWhereConditions();
break;
}
case "DELETE": {
this._query += `DELETE FROM ${this._table.name}`;
this.handleWhereConditions();
break;
}
default:
this._query += "SELECT ";
const selection = this._fields.map((field) => {
return field.name + (field.alias ? ` AS ${field.alias}` : "");
});
this._query += selection.join(", ");
if (this._table.name) {
this._query += ` FROM "${this._table.name}" ${this._table.alias}`;
}
this._joins.forEach(({ name, alias, type, condition }) => {
alias != null ? alias : alias = name;
condition != null ? condition : condition = "TRUE";
this._query += ` ${type} JOIN "${name}" ${alias} ON (${condition})`;
});
this.handleWhereConditions();
if (this._groupBy.length) {
this._query += ` GROUP BY ${this._groupBy.join(", ")}`;
}
if (this._having.length) {
this._query += ` HAVING ${this._having.join(" AND ")}`;
}
if (this._order.length) {
this._query += ` ORDER BY `;
this._query += this._order.map(([col, order]) => `${col} ${order}`).join(", ");
}
if (this._limit !== -1) {
this._query += ` LIMIT ${this._limit}`;
}
if (this._offset !== -1) {
this._query += ` OFFSET ${this._offset}`;
}
}
if (this._rawEnd) {
this._query += ` ${this._rawEnd}`;
}
return this;
}
format(formatOptions) {
if (!(formatOptions == null ? void 0 : formatOptions.language)) {
formatOptions = __spreadProps(__spreadValues({}, formatOptions), { language: "postgresql" });
}
this._query = (0, import_sql_formatter.format)(this._query, formatOptions);
return this;
}
getSql() {
return this._query;
}
clear() {
this._queryType = "READ";
this._insertColumns = [];
this._values = [];
this._updatedColumns = {};
this._fields = [];
this._table = { name: "" };
this._where = [];
this._groupBy = [];
this._having = [];
this._order = [];
this._offset = -1;
this._limit = -1;
this._joins = [];
this._rawFront = "";
this._rawEnd = "";
this._query = "";
return this;
}
};
// lib/query-builder/qb.functions.ts
function $contain(column, sub) {
return `${column} LIKE '%${sub}%'`;
}
function $concat(...strings) {
return `CONCAT(${strings.join(", ")})`;
}
function createQueryBuilder(table, alias) {
return new QueryBuilder(table, alias);
}
// lib/query-builder/qb.operators.ts
function $AND(...conditions) {
return `(${conditions.join(" AND ")})`;
}
function $OR(...conditions) {
return `(${conditions.join(" OR ")})`;
}
function $NOT(expr) {
return `NOT ${expr}`;
}
function $IN(elem, list) {
const _list = typeof list === "string" ? list : Array.isArray(list) ? `(${list.join(", ")})` : list instanceof QueryBuilder ? `(${list.build().getSql()})` : `(${list(new QueryBuilder()).build().getSql()})`;
return `${elem} IN ${list}`;
}
function $BETWEEN(value, l, r) {
return `BETWEEN ${l} AND ${r}`;
}
function $ALL(value, operator, subQuery) {
const sub = typeof subQuery === "string" ? subQuery : subQuery instanceof QueryBuilder ? subQuery.build().getSql() : subQuery(new QueryBuilder()).build().getSql();
return `${value} ${operator} ALL (${sub})`;
}
function $ANY(value, operator, subQuery) {
const sub = typeof subQuery === "string" ? subQuery : subQuery instanceof QueryBuilder ? subQuery.getSql() : subQuery(new QueryBuilder()).build().getSql();
return `${value} ${operator} ANY (${sub})`;
}
function $isNull(value) {
return `(${value} IS NULL)`;
}
function $notNull(value) {
return `(${value} IS NOT NULL)`;
}
// lib/schema-builder/decorators/table.decorator.ts
var import_reflect_metadata = require("reflect-metadata");
// lib/schema-builder/sb.constants.ts
var TABLE_METADATA_KEY = "schema_builder:table_metadata";
var COLUMNS_METADATA_KEY = "schema_builder:table_columns";
var PK_METADATA_KEY = "schema_builder:table_primary_key";
var FKS_METADATA_KEY = "schema_builder:table_foreign_keys";
var INDEXES_METADATA_KEY = "schema_builder:table_indexes";
var TABLE_REGISTRY = [];
// lib/schema-builder/decorators/table.decorator.ts
function Table(name) {
return (target) => {
const table = name != null ? name : target.name.toLowerCase();
TABLE_REGISTRY.push(target);
Reflect.defineMetadata(TABLE_METADATA_KEY, table, target);
};
}
// lib/schema-builder/decorators/column.decorator.ts
var import_reflect_metadata2 = require("reflect-metadata");
function Column(columnOptions) {
columnOptions = normalizeColumnOptions(columnOptions);
return (target, propertyKey) => {
var _a, _b, _c, _d;
(_a = columnOptions.name) != null ? _a : columnOptions.name = propertyKey.toString();
const columns = (_b = Reflect.getMetadata(COLUMNS_METADATA_KEY, target.constructor)) != null ? _b : [];
columns.push(columnOptions);
Reflect.defineMetadata(COLUMNS_METADATA_KEY, columns, target.constructor);
if (columnOptions.primary) {
const primaryKey = (_c = Reflect.getMetadata(PK_METADATA_KEY, target.constructor)) != null ? _c : [];
primaryKey.push(columnOptions.name);
Reflect.defineMetadata(PK_METADATA_KEY, primaryKey, target.constructor);
}
if (columnOptions.foreignKey) {
const fkOptions = __spreadValues({
column: columnOptions.name
}, columnOptions.foreignKey);
const foreignKeys = (_d = Reflect.getMetadata(FKS_METADATA_KEY, target.constructor)) != null ? _d : [];
foreignKeys.push(fkOptions);
Reflect.defineMetadata(FKS_METADATA_KEY, foreignKeys, target.constructor);
}
};
}
function normalizeColumnOptions(columnOptions) {
return __spreadValues({
nullable: true,
unique: false,
primary: false
}, columnOptions);
}
// lib/schema-builder/decorators/index.decorator.ts
var import_reflect_metadata3 = require("reflect-metadata");
function Index(indexOptions) {
return function(target) {
var _a;
const indexes = (_a = Reflect.getMetadata(INDEXES_METADATA_KEY, target)) != null ? _a : [];
indexes.push(indexOptions);
Reflect.defineMetadata(INDEXES_METADATA_KEY, indexes, target);
};
}
// lib/schema-builder/decorators/foreign-key.decorator.ts
var import_reflect_metadata4 = require("reflect-metadata");
function ForeignKey(fkOptions) {
return function(classOrObj, propertyKey) {
var _a, _b;
const target = typeof classOrObj === "object" ? classOrObj.constructor : classOrObj;
if (typeof classOrObj === "function" && !fkOptions.column) {
throw new Error(
`schema-builder: Property 'column' is required when @ForeignKey is used as a class decorator.`
);
}
(_a = fkOptions.column) != null ? _a : fkOptions.column = propertyKey == null ? void 0 : propertyKey.toString();
if (!fkOptions.column) {
throw new Error(`schema-builder: Ambiguous column for ForeignKey.'`);
}
const foreignKeys = (_b = Reflect.getMetadata(FKS_METADATA_KEY, target)) != null ? _b : [];
foreignKeys.push(fkOptions);
Reflect.defineMetadata(FKS_METADATA_KEY, foreignKeys, target);
};
}
// lib/schema-builder/decorators/primary-key.decorator.ts
var import_reflect_metadata5 = require("reflect-metadata");
function PrimaryKey(columnOrColumns) {
if (!columnOrColumns || typeof columnOrColumns === "string") {
return (target, propertyKey) => {
var _a;
columnOrColumns != null ? columnOrColumns : columnOrColumns = propertyKey.toString();
const primaryKey = (_a = Reflect.getMetadata(PK_METADATA_KEY, target.constructor)) != null ? _a : [];
primaryKey.push(columnOrColumns);
Reflect.defineMetadata(PK_METADATA_KEY, primaryKey, target.constructor);
};
} else {
return (target) => {
var _a;
const primaryKey = (_a = Reflect.getMetadata(PK_METADATA_KEY, target)) != null ? _a : [];
primaryKey.push(...columnOrColumns);
Reflect.defineMetadata(PK_METADATA_KEY, primaryKey, target);
};
}
}
// lib/schema-builder/functions/db-schema.ts
var import_fs = __toESM(require("fs"));
var import_path = __toESM(require("path"));
// lib/schema-builder/functions/table-schema.ts
var import_reflect_metadata6 = require("reflect-metadata");
var import_sql_formatter2 = require("sql-formatter");
function tableSchema(table, formatOptions) {
const { name, indexes, primaryKey, foreignKeys, columns } = getTableMetadata(table);
let sql = `CREATE TABLE ${name} (
`;
columns.forEach((column, index) => {
const { name: name2, type, nullable, unique, default: _default, check } = column;
sql += `${name2} ${type}`;
sql += `${nullable ? "" : "NOT NULL"} ${unique ? "UNIQUE" : ""}`;
if (_default !== void 0)
sql += ` DEFAULT ${normalized(_default)}`;
sql += ` ${check ? `CHECK (${check})` : ""}`;
if (index !== columns.length - 1)
sql += ", ";
});
if (primaryKey == null ? void 0 : primaryKey.length) {
sql += `,
PRIMARY KEY (${primaryKey.join(", ")})`;
}
foreignKeys.forEach(({ column, reference, onDelete, onUpdate }) => {
sql += `,
FOREIGN KEY (${column}) REFERENCES ${reference}`;
if (onDelete)
sql += ` ON DELETE ${onDelete}`;
if (onUpdate)
sql += ` ON UPDATE ${onUpdate}`;
});
sql += "\n);\n";
indexes.forEach(({ name: indexName, columns: columns2, unique }) => {
sql += `CREATE ${unique ? " UNIQUE" : ""} INDEX ${indexName} ON ${name} `;
sql += `(${columns2.join(", ")});
`;
});
if (!(formatOptions == null ? void 0 : formatOptions.language)) {
formatOptions = __spreadProps(__spreadValues({}, formatOptions), { language: "postgresql" });
}
return (0, import_sql_formatter2.format)(sql, formatOptions);
}
function getTableMetadata(table) {
var _a, _b, _c, _d;
const name = Reflect.getMetadata(
TABLE_METADATA_KEY,
table
);
if (!name) {
throw new Error(
`${table.name} is either not a class or not decorated with @Table`
);
}
const indexes = (_a = Reflect.getMetadata(INDEXES_METADATA_KEY, table)) != null ? _a : [];
const primaryKey = (_b = Reflect.getMetadata(PK_METADATA_KEY, table)) != null ? _b : [];
const foreignKeys = (_c = Reflect.getMetadata(FKS_METADATA_KEY, table)) != null ? _c : [];
const columns = (_d = Reflect.getMetadata(COLUMNS_METADATA_KEY, table)) != null ? _d : [];
return { name, indexes, primaryKey, foreignKeys, columns };
}
// lib/schema-builder/functions/db-schema.ts
function buildSchema(options, formatOptions) {
if ("path" in options) {
const { path } = options;
const dirname = import_path.default.dirname(path);
if (!import_fs.default.existsSync(dirname))
import_fs.default.mkdirSync(dirname, { recursive: true });
let schema = "";
TABLE_REGISTRY.forEach((table, index) => {
const tableName = Reflect.getMetadata(TABLE_METADATA_KEY, table);
if (formatOptions == null ? void 0 : formatOptions.comments)
schema += `-- ${tableName}
`;
schema += tableSchema(table, formatOptions);
if (index !== TABLE_REGISTRY.length - 1)
schema += "\n\n\n";
});
import_fs.default.writeFileSync(path, schema);
} else {
const { dirname } = options;
if (!import_fs.default.existsSync(dirname))
import_fs.default.mkdirSync(dirname, { recursive: true });
TABLE_REGISTRY.forEach((table) => {
const tableName = Reflect.getMetadata(TABLE_METADATA_KEY, table);
import_fs.default.writeFileSync(
`${dirname}/${tableName}.schema.sql`,
tableSchema(table, formatOptions)
);
});
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
$ALL,
$AND,
$ANY,
$BETWEEN,
$IN,
$NOT,
$OR,
$concat,
$contain,
$isNull,
$notNull,
COLUMNS_METADATA_KEY,
Column,
FKS_METADATA_KEY,
ForeignKey,
INDEXES_METADATA_KEY,
Index,
JoinType,
ORDER,
PK_METADATA_KEY,
PrimaryKey,
QueryBuilder,
TABLE_METADATA_KEY,
Table,
buildSchema,
createQueryBuilder,
getTableMetadata,
normalizeColumnOptions,
normalized,
tableSchema
});