From 0c94fbdd818a2131b4cf9b12598a44328ad2816a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chriss=20Mej=C3=ADa?= Date: Mon, 17 Sep 2018 18:38:10 -0600 Subject: [PATCH] alpha 0.0.6 --- connection.ts | 86 +++++++ datatypes.ts | 276 ++++++++++++++++++++ decorators.ts | 123 +++++++++ index.ts | 25 +- interfaces/config.ts | 45 ++++ interfaces/db/defaults.ts | 28 ++ interfaces/db/fields.ts | 91 +++++++ interfaces/db/models.ts | 107 ++++++++ interfaces/db/types.ts | 42 +++ model.ts | 525 ++++++++++++++++++++++++++++++++++++++ package-lock.json | 218 +++++++++++++++- package.json | 11 +- tsconfig.json | 4 +- 13 files changed, 1562 insertions(+), 19 deletions(-) create mode 100644 connection.ts create mode 100644 datatypes.ts create mode 100644 decorators.ts create mode 100644 interfaces/config.ts create mode 100644 interfaces/db/defaults.ts create mode 100644 interfaces/db/fields.ts create mode 100644 interfaces/db/models.ts create mode 100644 interfaces/db/types.ts create mode 100644 model.ts diff --git a/connection.ts b/connection.ts new file mode 100644 index 0000000..9f443a0 --- /dev/null +++ b/connection.ts @@ -0,0 +1,86 @@ +//////////////////////////////////////////////////////////////////////////////////////////// +// The MIT License (MIT) // +// // +// Copyright (C) 2016 Chriss Mejía - me@chrissmejia.com - chrissmejia.com // +// // +// Permission is hereby granted, free of charge, to any person obtaining a copy // +// of this software and associated documentation files (the "Software"), to deal // +// in the Software without restriction, including without limitation the rights // +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // +// copies of the Software, and to permit persons to whom the Software is // +// furnished to do so, subject to the following conditions: // +// // +// The above copyright notice and this permission notice shall be included in all // +// copies or substantial portions of the Software. // +// // +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // +// SOFTWARE. // +//////////////////////////////////////////////////////////////////////////////////////////// + +import { Config } from "./interfaces/config"; +import { Promise } from "es6-promise"; + +import { Models } from "./interfaces/db/models"; +import * as mysql from "mysql"; + +/** +* Unicoderns DB Connection +*/ +export class DB { + protected connections: mysql.Pool; + public config: Config; + + /** + * Configuration methods + * + * Create a connection pool + * + * @var config system configuration file + */ + constructor(config: Config) { + this.config = config; + this.connections = mysql.createPool(config.connection); + } + + /** + * Plain query + * + * @var sql MySQL query + * @var params Object (key/value) with parameters to replace in the query + * @return Promise with query result + */ + public query(query: Models.Query): Promise { + // Create promise + const p: Promise = new Promise( + (resolve: (data: any) => void, reject: (err: mysql.MysqlError) => void) => { + // Get connection + this.connections.getConnection((err: mysql.MysqlError, connection) => { + if (err) { // Improve error log + reject(err); + throw err; + } + // Query Mysql + let mysqlQuery = connection.query(query.sql, query.values, (err: mysql.MysqlError | null, rows: any) => { + connection.release(); + if (this.config.dev) { + console.log("SQL Query: " + mysqlQuery.sql); + } + + if (err) { // Improve error log + reject(err); + throw err; + } + // Resolve promise + resolve(rows); + }); + }); + } + ); + return p; + } +} diff --git a/datatypes.ts b/datatypes.ts new file mode 100644 index 0000000..aea8e43 --- /dev/null +++ b/datatypes.ts @@ -0,0 +1,276 @@ +//////////////////////////////////////////////////////////////////////////////////////////// +// The MIT License (MIT) // +// // +// Copyright (C) 2016 Chriss Mejía - me@chrissmejia.com - chrissmejia.com // +// // +// Permission is hereby granted, free of charge, to any person obtaining a copy // +// of this software and associated documentation files (the "Software"), to deal // +// in the Software without restriction, including without limitation the rights // +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // +// copies of the Software, and to permit persons to whom the Software is // +// furnished to do so, subject to the following conditions: // +// // +// The above copyright notice and this permission notice shall be included in all // +// copies or substantial portions of the Software. // +// // +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // +// SOFTWARE. // +//////////////////////////////////////////////////////////////////////////////////////////// + +import { Fields } from "./interfaces/db/fields"; +import { Types } from "./interfaces/db/types"; +import { Defaults } from "./interfaces/db/defaults"; + +import { Model } from "./model" + +/** + * JSloth DB Datatypes + */ +export class Datatypes { + + /** + * Merge 2 objects + * + * @var commonType Object 1 + * @var customType Object 2 (will overwrite Object 1 keys) + * @return Merged object + */ + private mergeTypes(commonType: any, customType: any) { + let type = { ...commonType, ...customType }; + return type; + } + + /** + * Fill SQl defaults for fields + * + * @var settings Object with custom settings + * @return Object with defaults + */ + private fillDefault(settings: Types.General = {}): Types.General { + let type: Types.General = { + primaryKey: settings.primaryKey || false, + notNull: settings.notNull || false, + unique: settings.unique || false, + // binary: settings.binary || false, + unsigned: settings.unsigned || false, + zeroFill: settings.zeroFill || false, + autoincrement: settings.autoincrement || false, + generated: settings.generated || false, + protected: settings.protected || false, + private: settings.private || false + }; + return type; + } + + ///////////////////////////////////////////////////////////////////// + // Numbers + ///////////////////////////////////////////////////////////////////// + + public TINYINT(settings: Types.General = {}): Fields.DataType { + let commonType = this.fillDefault(settings); + let customType: Fields.DataType = { + type: "TINYINT" + }; + return this.mergeTypes(commonType, customType); + } + + public SMALLINT(settings: Types.General = {}): Fields.DataType { + let commonType = this.fillDefault(settings); + let customType: Fields.DataType = { + type: "SMALLINT" + }; + return this.mergeTypes(commonType, customType); + } + + public INT(settings: Types.General = {}): Fields.DataType { + let commonType = this.fillDefault(settings); + let customType: Fields.DataType = { + type: "INT" + }; + return this.mergeTypes(commonType, customType); + } + + // ------------------------------------------------------------------ + // Special Numbers + // ------------------------------------------------------------------ + public ID(settings: Types.General = {}): Fields.DataType { + let commonType = this.fillDefault(settings); + let customType: Fields.DataType = { + type: "INT", + size: settings.size || 0, + primaryKey: true, + notNull: true, + unique: true, + unsigned: true, + autoincrement: true + }; + return this.mergeTypes(commonType, customType); + } + + /** + * Define a foreign key + * + * @param name Name of the db field. + * @param model Db Model to link. + * @param settings Field settings. + */ + public FOREIGNKEY(localField: string, linkedField: string, model: Model, settings: Types.General = {}): Fields.ForeignKey { + let commonType = this.fillDefault(settings); + let customType: Fields.ForeignKey = { + type: "INT", + size: settings.size || 0, + model: model, + localField: localField, + linkedField: linkedField + }; + return this.mergeTypes(commonType, customType); + } + + public STATICKEY(keys: any, settings: Types.General = {}): Fields.StaticKey { + let commonType = this.fillDefault(settings); + let customType: Fields.StaticKey = { + type: "INT", + size: settings.size || 0, + keys: keys + }; + return this.mergeTypes(commonType, customType); + } + + // ------------------------------------------------------------------ + // Float Numbers + // ------------------------------------------------------------------ + public FLOAT(settings: Types.General = {}): Fields.FloatType { + let commonType = this.fillDefault(settings); + let customType: Fields.DataType = { + type: "FLOAT" + }; + return this.mergeTypes(commonType, customType); + } + + public DOUBLE(settings: Types.General = {}): Fields.FloatType { + let commonType = this.fillDefault(settings); + let customType: Fields.DataType = { + type: "DOUBLE" + }; + return this.mergeTypes(commonType, customType); + } + + public DECIMAL(settings: Types.General = {}): Fields.FloatType { + let commonType = this.fillDefault(settings); + let customType: Fields.DataType = { + type: "DECIMAL" + }; + return this.mergeTypes(commonType, customType); + } + + ///////////////////////////////////////////////////////////////////// + // Strings + ///////////////////////////////////////////////////////////////////// + + public CHAR(settings: Types.General = {}): Fields.DataType { + let commonType = this.fillDefault(settings); + let customType: Fields.DataType = { + type: "CHAR", + size: settings.size + }; + return this.mergeTypes(commonType, customType); + } + + public VARCHAR(settings: Types.General = {}): Fields.DataType { + let commonType = this.fillDefault(settings); + let customType: Fields.VarCharType = { + type: "VARCHAR", + size: settings.size || 0 + }; + return this.mergeTypes(commonType, customType); + } + + public TINYTEXT(settings: Types.General = {}): Fields.DataType { + let commonType = this.fillDefault(settings); + let customType: Fields.DataType = { + type: "TINYTEXT", + size: settings.size + }; + return this.mergeTypes(commonType, customType); + } + + public TEXT(settings: Types.General = {}): Fields.DataType { + let commonType = this.fillDefault(settings); + let customType: Fields.DataType = { + type: "TEXT" + }; + return this.mergeTypes(commonType, customType); + } + + public LONGTEXT(settings: Types.General = {}): Fields.DataType { + let commonType = this.fillDefault(settings); + let customType: Fields.DataType = { + type: "LONGTEXT", + }; + return this.mergeTypes(commonType, customType); + } + + ///////////////////////////////////////////////////////////////////// + // Binary + ///////////////////////////////////////////////////////////////////// + + public BOOL(settings: Types.Bool = {}): Fields.BoolType { + let commonType = this.fillDefault(settings); + let customType: Fields.BoolType = { + type: "BOOL", + default: settings.default || 0 + }; + return this.mergeTypes(commonType, customType); + } + + ///////////////////////////////////////////////////////////////////// + // Date/Time + ///////////////////////////////////////////////////////////////////// + + public YEAR(settings: Types.Timestamp = {}): Fields.DataTimestampType { + let commonType = this.fillDefault(settings); + let customType: Fields.DataType = { + type: "YEAR" + }; + return this.mergeTypes(commonType, customType); + } + + public DATE(settings: Types.Timestamp = {}): Fields.DataTimestampType { + let commonType = this.fillDefault(settings); + let customType: Fields.DataType = { + type: "DATE" + }; + return this.mergeTypes(commonType, customType); + } + + public TIME(settings: Types.Timestamp = {}): Fields.DataTimestampType { + let commonType = this.fillDefault(settings); + let customType: Fields.DataType = { + type: "TIME" + }; + return this.mergeTypes(commonType, customType); + } + + public DATETIME(settings: Types.Timestamp = {}): Fields.DataTimestampType { + let commonType = this.fillDefault(settings); + let customType: Fields.DataType = { + type: "DATETIME" + }; + return this.mergeTypes(commonType, customType); + } + + public TIMESTAMP(settings: Types.Timestamp = {}): Fields.DataTimestampType { + let commonType = this.fillDefault(settings); + let customType: Fields.DataTimestampType = { + type: "TIMESTAMP", + default: settings.default || Defaults.Timestamp["CURRENT_TIMESTAMP"] + }; + return this.mergeTypes(commonType, customType); + } + +} \ No newline at end of file diff --git a/decorators.ts b/decorators.ts new file mode 100644 index 0000000..5ba569b --- /dev/null +++ b/decorators.ts @@ -0,0 +1,123 @@ +//////////////////////////////////////////////////////////////////////////////////////////// +// The MIT License (MIT) // +// // +// Copyright (C) 2016 Chriss Mejía - me@chrissmejia.com - chrissmejia.com // +// // +// Permission is hereby granted, free of charge, to any person obtaining a copy // +// of this software and associated documentation files (the "Software"), to deal // +// in the Software without restriction, including without limitation the rights // +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // +// copies of the Software, and to permit persons to whom the Software is // +// furnished to do so, subject to the following conditions: // +// // +// The above copyright notice and this permission notice shall be included in all // +// copies or substantial portions of the Software. // +// // +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // +// SOFTWARE. // +//////////////////////////////////////////////////////////////////////////////////////////// + +/** + * Centralized db field registry + */ +const REGISTRY = new Map>>(); + +/** + * Register field + * + * @param target Db table name. + * @param privacy Type of field (public/secret). + * @param key Field name. + * @param alias Optional alias for the field. + */ +function register(target: string, privacy: string, key: string, alias?: string) { + let map: Map> = REGISTRY.get(target) || new Map>(); + REGISTRY.set(target, map); + + let list: Map | undefined; + if (map.has(privacy)) { + list = map.get(privacy); + } else { + list = new Map(); + map.set(privacy, list); + } + + if ((typeof list !== "undefined") && (!list.has(key))) { + list.set(key, alias || ""); + } +} + +/** + * Get field map for a table + * + * @param target Db table name. + * @return Field map. + */ +export function getList(target: string): Map> { + // Clone structure and return to prevent any changes in the original one. + let clone: Map> = new Map>(); + let original = REGISTRY.get(target) || new Map>(); + let publicFields = original.get("public"); + let secretFields = original.get("secret"); + + // Force string clone + let strCopy = (text: string): string => { + return (' ' + text).slice(1); + } + + if ((publicFields) && (publicFields.size)) { + let clonePublicfields: Map = new Map(); + publicFields.forEach((value, key, map) => { + if (typeof value !== "undefined") { + clonePublicfields.set(strCopy(key), strCopy(value)); + } else { + clonePublicfields.set(strCopy(key), ""); + } + }); + clone.set("public", clonePublicfields); + } + + if ((secretFields) && (secretFields.size)) { + let cloneSecretfields: Map = new Map(); + secretFields.forEach((value, key, map) => { + if (typeof value !== "undefined") { + cloneSecretfields.set(strCopy(key), strCopy(value)); + } else { + cloneSecretfields.set(strCopy(key), ""); + } + }); + clone.set("secret", cloneSecretfields); + } + return clone; +} + +/** + * Public field decorator + * + * @param alias Optional alias for the field. + * @param target Db table name. + * @param key Field name. + */ +export function field(alias?: string) { + return function (target: any, key: string) { + register((target.constructor.name).charAt(0).toLowerCase() + (target.constructor.name).slice(1), "public", key, alias || key); + } +} + +/** + * Secret field decorator + * + * @param alias Optional alias for the field. + * @param target Db table name. + * @param key Field name. + */ +export function secret(alias?: string) { + return function (target: any, key: string) { + register((target.constructor.name).charAt(0).toLowerCase() + (target.constructor.name).slice(1), "secret", key, alias || key); + } +} \ No newline at end of file diff --git a/index.ts b/index.ts index 6cc6685..2254f96 100644 --- a/index.ts +++ b/index.ts @@ -23,17 +23,14 @@ //////////////////////////////////////////////////////////////////////////////////////////// /** - * Unicoderns ORM Loader - */ -export default class ORM { - - /** - * Inicial test. - * - * @return void - */ - public test(): void { - console.log("This is a message from the Unicoderns ORM demo package"); - }; - -} \ No newline at end of file +* Unicoderns ORM central module +*/ +export * from "./connection"; +export * from "./model"; +export * from "./decorators"; +export * from "./datatypes"; +export * from "./interfaces/config"; +export * from "./interfaces/db/fields"; +export * from "./interfaces/db/defaults"; +export * from "./interfaces/db/models"; +export * from "./interfaces/db/types"; \ No newline at end of file diff --git a/interfaces/config.ts b/interfaces/config.ts new file mode 100644 index 0000000..a9adebe --- /dev/null +++ b/interfaces/config.ts @@ -0,0 +1,45 @@ +//////////////////////////////////////////////////////////////////////////////////////////// +// The MIT License (MIT) // +// // +// Copyright (C) 2018 Unicoderns SA - info@unicoderns.com - unicoderns.com // +// // +// Permission is hereby granted, free of charge, to any person obtaining a copy // +// of this software and associated documentation files (the "Software"), to deal // +// in the Software without restriction, including without limitation the rights // +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // +// copies of the Software, and to permit persons to whom the Software is // +// furnished to do so, subject to the following conditions: // +// // +// The above copyright notice and this permission notice shall be included in all // +// copies or substantial portions of the Software. // +// // +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // +// SOFTWARE. // +//////////////////////////////////////////////////////////////////////////////////////////// + +/*** Main configuration interface */ +export interface Config { + dev: boolean; + connection: Connection; +} + +/*** Connection configuration interface. */ +export interface Connection { + user: string; + password: string; + database: string; + port: number; + host: string; + connectionLimit: number; + validations: ValidationSettings; +} + +/*** Validation settings interface. */ +export interface ValidationSettings { + fields: boolean; +} \ No newline at end of file diff --git a/interfaces/db/defaults.ts b/interfaces/db/defaults.ts new file mode 100644 index 0000000..fe5c7fd --- /dev/null +++ b/interfaces/db/defaults.ts @@ -0,0 +1,28 @@ +//////////////////////////////////////////////////////////////////////////////////////////// +// The MIT License (MIT) // +// // +// Copyright (C) 2018 Unicoderns SA - info@unicoderns.com - unicoderns.com // +// // +// Permission is hereby granted, free of charge, to any person obtaining a copy // +// of this software and associated documentation files (the "Software"), to deal // +// in the Software without restriction, including without limitation the rights // +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // +// copies of the Software, and to permit persons to whom the Software is // +// furnished to do so, subject to the following conditions: // +// // +// The above copyright notice and this permission notice shall be included in all // +// copies or substantial portions of the Software. // +// // +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // +// SOFTWARE. // +//////////////////////////////////////////////////////////////////////////////////////////// + +export namespace Defaults { + export enum Timestamp { "NULL", "ZERO", "CURRENT_TIMESTAMP", "NULL ON UPDATE CURRENT_TIMESTAMP", "CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" }; + export enum Binary { "FALSE", "TRUE" }; +} \ No newline at end of file diff --git a/interfaces/db/fields.ts b/interfaces/db/fields.ts new file mode 100644 index 0000000..b4dab4a --- /dev/null +++ b/interfaces/db/fields.ts @@ -0,0 +1,91 @@ +//////////////////////////////////////////////////////////////////////////////////////////// +// The MIT License (MIT) // +// // +// Copyright (C) 2018 Unicoderns SA - info@unicoderns.com - unicoderns.com // +// // +// Permission is hereby granted, free of charge, to any person obtaining a copy // +// of this software and associated documentation files (the "Software"), to deal // +// in the Software without restriction, including without limitation the rights // +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // +// copies of the Software, and to permit persons to whom the Software is // +// furnished to do so, subject to the following conditions: // +// // +// The above copyright notice and this permission notice shall be included in all // +// copies or substantial portions of the Software. // +// // +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // +// SOFTWARE. // +//////////////////////////////////////////////////////////////////////////////////////////// + +import { Defaults } from "./defaults"; +import { Model } from "../../model"; + +// export enum Privacy {"PUBLIC", "PRIVATE", "PROTECTED"}; + +export namespace Fields { + + // JSloth internal flags + export interface SystemTypes { + type?: string; + alias?: string; + protected?: boolean; + private?: boolean; + } + + export interface CommonTypes extends SystemTypes { + primaryKey?: boolean; + notNull?: boolean; + unique?: boolean; + // binary?: boolean; + unsigned?: boolean; + zeroFill?: boolean; + autoincrement?: boolean; + generated?: boolean; + } + + /*** Datatype interface. */ + export interface DataType extends CommonTypes { + size?: number; + } + + export interface VarCharType extends CommonTypes { + size: number; + } + + export interface FloatType extends CommonTypes { + size?: number; + precision?: number; + } + + export interface BoolType extends CommonTypes { + default: Defaults.Binary; + } + + export interface DataTimestampType extends CommonTypes { + default: Defaults.Timestamp; + } + + /** + * Foreign key to model + */ + + export interface ForeignKey extends DataType { + localField: string; + linkedField: string; + model: Model; + } + + /** + * Foreign key to static enum model + */ + + export interface StaticKey extends DataType { + keys: any; + } + +} \ No newline at end of file diff --git a/interfaces/db/models.ts b/interfaces/db/models.ts new file mode 100644 index 0000000..f168696 --- /dev/null +++ b/interfaces/db/models.ts @@ -0,0 +1,107 @@ +//////////////////////////////////////////////////////////////////////////////////////////// +// The MIT License (MIT) // +// // +// Copyright (C) 2018 Unicoderns SA - info@unicoderns.com - unicoderns.com // +// // +// Permission is hereby granted, free of charge, to any person obtaining a copy // +// of this software and associated documentation files (the "Software"), to deal // +// in the Software without restriction, including without limitation the rights // +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // +// copies of the Software, and to permit persons to whom the Software is // +// furnished to do so, subject to the following conditions: // +// // +// The above copyright notice and this permission notice shall be included in all // +// copies or substantial portions of the Software. // +// // +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // +// SOFTWARE. // +//////////////////////////////////////////////////////////////////////////////////////////// + +import { Fields } from "../../interfaces/db/fields"; + +export namespace Models { + + /** + * Parent model row interface + */ + export interface Row { + [key: string]: any; // Wildcard to avoid compilator errors + } + + /** + * Key/Value object + */ + export interface KeyValue { + [key: string]: string | number; + } + + /** + * Plain query + * @var sql MySQL string query code + * @var values Parameters to replace in the query + */ + export interface Query { + sql: string; + values: string[]; + } + + /** + * Join declaration + * + * @var keyField Model foreign key + * @var fields String array with names of fields to join + * @var kind Type of Join to apply E.g.: INNER, LEFT + */ + export interface Join { + keyField: Fields.ForeignKey; + fields: string[]; + kind: string; + } + + /** + * Select declaration + * + * @var fields If is NOT set "*" will be used, if there's a string then it will be used as is, a plain query will be + * executed, if in the other hand an array is provided (Recommended), then it will filter the keys and run the query. + * @var where Key/Value object used to filter the query, an array of Key/Value objects will generate a multiple filter separated by an "OR". + * @var orderBy String with column_name and direction E.g.: "id, name ASC" + * @var groupBy String with column_name E.g.: "id, name" + */ + export interface Select { + fields?: string[]; + where?: KeyValue | KeyValue[]; + groupBy?: string; + orderBy?: string; + } + + /** + * Select declaration with limit + * + * @var fields If is NOT set "*" will be used, if there's a string then it will be used as is, a plain query will be + * executed, if in the other hand an array is provided (Recommended), then it will filter the keys and run the query. + * @var where Key/Value object used to filter the query, an array of Key/Value objects will generate a multiple filter separated by an "OR". + * @var orderBy String with column_name and direction E.g.: "id, name ASC" + * @var groupBy String with column_name E.g.: "id, name" + * @var limit Number of rows to retrieve + */ + export interface SelectLimit extends Select { + limit?: number + } + + /** + * Update declaration + * + * @var data object data to be update in the table + * @var where Key/Value object used to filter the query, an array of Key/Value objects will generate a multiple filter separated by an "OR", a "*" string wildcard is required for security reasons if you want to match all rows. + */ + export interface Update { + data: Row; + where: string | KeyValue | KeyValue[]; + } + +} \ No newline at end of file diff --git a/interfaces/db/types.ts b/interfaces/db/types.ts new file mode 100644 index 0000000..0d83aa4 --- /dev/null +++ b/interfaces/db/types.ts @@ -0,0 +1,42 @@ +//////////////////////////////////////////////////////////////////////////////////////////// +// The MIT License (MIT) // +// // +// Copyright (C) 2018 Unicoderns SA - info@unicoderns.com - unicoderns.com // +// // +// Permission is hereby granted, free of charge, to any person obtaining a copy // +// of this software and associated documentation files (the "Software"), to deal // +// in the Software without restriction, including without limitation the rights // +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // +// copies of the Software, and to permit persons to whom the Software is // +// furnished to do so, subject to the following conditions: // +// // +// The above copyright notice and this permission notice shall be included in all // +// copies or substantial portions of the Software. // +// // +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // +// SOFTWARE. // +//////////////////////////////////////////////////////////////////////////////////////////// + +import { Fields } from "./fields"; +import { Defaults } from "./defaults"; + +export namespace Types { + + export interface General extends Fields.CommonTypes { + size?: number; + } + + export interface Bool extends Fields.CommonTypes { + default?: Defaults.Binary; + } + + export interface Timestamp extends Fields.CommonTypes { + default?: Defaults.Timestamp; + } + +} \ No newline at end of file diff --git a/model.ts b/model.ts new file mode 100644 index 0000000..b6802ad --- /dev/null +++ b/model.ts @@ -0,0 +1,525 @@ +//////////////////////////////////////////////////////////////////////////////////////////// +// The MIT License (MIT) // +// // +// Copyright (C) 2018 Unicoderns SA - info@unicoderns.com - unicoderns.com // +// // +// Permission is hereby granted, free of charge, to any person obtaining a copy // +// of this software and associated documentation files (the "Software"), to deal // +// in the Software without restriction, including without limitation the rights // +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // +// copies of the Software, and to permit persons to whom the Software is // +// furnished to do so, subject to the following conditions: // +// // +// The above copyright notice and this permission notice shall be included in all // +// copies or substantial portions of the Software. // +// // +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // +// SOFTWARE. // +//////////////////////////////////////////////////////////////////////////////////////////// + +import * as clc from "cli-color"; +import * as mysql from "mysql"; + +import { Promise } from "es6-promise"; +import { getList } from "./decorators"; +import { Models } from "./interfaces/db/models"; + +import { Config } from "./interfaces/config"; +import { DB } from "./connection"; + +/** + * Model Abstract + */ +export class Model { + protected tableName: string = ((this).constructor.name).charAt(0).toLowerCase() + ((this).constructor.name).slice(1); // Get the table name from the model name in camelcase. + protected DB: DB; + public unsafe: boolean = false; + public fields: Map | undefined = undefined; + public joins: Models.Join[] = []; + + + + /** + * Create a table object. + * + * TODO: it shouldn't take DB | any, following this thread: + * https://stackoverflow.com/questions/52376742/npm-package-with-classes-not-compatible-between-typescript-and-transpiled-versio + * + * @param jsloth Core library + * @param privacy To get all fields (secrets included), you need to set privacy as "unsafe" explicitly, in that way we ensure that this will not be a security breach in any wrong future upgrade. + */ + constructor(DB: DB | any, privacy?: string) { + this.DB = DB; + if (privacy == "unsafe") { + this.unsafe = true; + } + } + + /** + * Create cache and return the model field list. + * + * If this.unsafe is set then merge public with secret fields. + * + * @return Fields Mapped + */ + public getFields(): Map | undefined { + let fields = this.fields; + if (typeof fields == "undefined") { + let tmp: Map> = getList(this.tableName); + fields = tmp.get("public"); + if (this.unsafe) { + var secret: Map | undefined = tmp.get("secret"); + if ((secret) && (secret.size)) { + secret.forEach(function (value, key) { + if (typeof fields != "undefined") { + fields.set(key, value); + } + }); + } + } + this.fields = fields; + } + return fields; + } + + /** + * Convert a map in a array. + */ + private mapInArray(target: Map | undefined): string[] { + let keys: string[] = []; + + if (typeof target !== "undefined") { + target.forEach(item => { + keys.push(item); + }); + } else { + console.error(clc.red("No fields in the model")); + } + return keys; + } + + /** + * Filter one array if keys don't exists in other array. + */ + private filterArrayInArray(target: string[], scope: Map | undefined): string[] { + let keys: string[] = []; + + if (typeof scope !== "undefined") { + target.forEach(item => { + if (scope.has(item)) { + keys.push(item); + } + }); + } else { + console.error(clc.red("No fields in the model")); + } + return keys; + } + + /** + * Log if keys don't exists in other array. + */ + private logArrayInArray(target: string[], scope: Map | undefined): void { + if (typeof scope !== "undefined") { + target.forEach(item => { + if (!scope.has(item)) { + console.error(item + " field doesn't exists!"); + } + }); + } else { + console.error(clc.red("No fields in the model")); + } + } + + /* + ///////////////////////////////////////////////////////////////////// + // Generate a report and filter fields + ///////////////////////////////////////////////////////////////////// + private selectFieldsReport(select: string[]): Fields { + let report: Fields; + let fields = this.getFields(); + + report.all = this.filterArrayInArray(select, fields.all); + report.public = this.filterArrayInArray(select, fields.public); + report.protected = this.filterArrayInArray(select, fields.protected); + report.private = this.filterArrayInArray(select, fields.private); + + return this.getFields(); + } + */ + + /** + * Clean and validate a select if is need it + * + * @var fields String array with field names. + * @return Object cointaining the SQL and a field report + */ + private getSelectFieldsSQL(fields: string[] | undefined, prefix?: boolean): string { + let fieldsSQL = ""; + let selectableFields: string[] = []; + let modelFields = this.getFields(); + let config: Config = this.DB.config; + + // Check if is an array or just SQL code + if ((Array.isArray(fields)) && (fields.length)) { + + // Log missing fields in dev mode + if (config.dev) { + this.logArrayInArray(fields, modelFields); + } + + // Check if the validations of fields is on and then filter (Always disallowed in dev mode) + if (config.connection.validations.fields) { + selectableFields = this.filterArrayInArray(fields, modelFields); + } else { + selectableFields = this.mapInArray(modelFields); + } + + } else { + selectableFields = this.mapInArray(modelFields); + } + + if (typeof prefix == "undefined") { + fieldsSQL = "`" + this.tableName + "`.`"; + fieldsSQL = fieldsSQL + selectableFields.join("`, `" + this.tableName + "`.`") + "`"; + } else { + let formatedFields: string[] = []; + selectableFields.forEach((field: string) => { + formatedFields.push("`" + this.tableName + "`.`" + field + "` AS `" + this.tableName + "__" + field + "`"); + }); + fieldsSQL = formatedFields.join(", ") + } + + return fieldsSQL; + } + + /** + * Generates a select string from the Join configuration + * + * @var fields String array with field names. + * @return Object cointaining the SQL and a field report + */ + private getJoinSelectFieldsSQL(): string { + let joins = this.joins; + let joinsStringArray: string[] = []; + let joinsSQL = ""; + if (joins.length) { + joins.forEach(function (join: Models.Join) { + joinsStringArray.push(join.keyField.model.getSelectFieldsSQL(join.fields, true)); + }); + joinsSQL = joinsStringArray.join(", ") + joinsSQL = ", " + joinsSQL; + } + return joinsSQL; + } + + /** + * Generate join sql code + * + * @return String with the where sql code + */ + private generateJoinCode(): string { + let joins = this.joins; + let joinsStringArray: string[] = []; + let joinsSQL = ""; + if (joins.length) { + joins.forEach((join: Models.Join) => { + let linkedTableName = join.keyField.model.tableName; + joinsStringArray.push( + " " + join.kind.toUpperCase() + " JOIN " + + "`" + linkedTableName + "`" + + " ON `" + this.tableName + "`.`" + join.keyField.localField + "` = " + + "`" + linkedTableName + "`.`" + join.keyField.linkedField + "`" + ); + }); + joinsSQL = joinsStringArray.join(" "); + } + return joinsSQL; + } + + ///////////////////////////////////////////////////////////////////// + // Generate "AND" chained where sql code + // @return string + ///////////////////////////////////////////////////////////////////// + private generateWhereCodeChain(where: any): { sql: string, values: string[] } { + let values: string[] = []; + let keys: string[] = []; + let filteredKeys: string[] = []; + let modelFields = this.getFields(); + let config: Config = this.DB.config; + + for (let key in where) { + keys.push(key); + } + + // Check if the validations of fields is on and then filter (Always disallowed in dev mode) + if ((config.connection.validations.fields) && (!config.dev)) { + filteredKeys = this.filterArrayInArray(keys, modelFields); + } else { + if (config.dev) { + this.logArrayInArray(keys, modelFields); + } + filteredKeys = keys; + } + + if (typeof where !== "undefined") { + let sql: string = "`" + this.tableName + "`.`"; + sql = sql + filteredKeys.join("` = ? AND `" + this.tableName + "`.`"); + sql = sql + "` = ?"; + // getting values + filteredKeys.forEach((item: string) => { + values.push(where[item]); + }); + return { + sql: sql, + values: values + }; + } else { + return { + sql: "", + values: [] + }; + } + } + + /** + * Generate where sql code + * + * @var where Array of key/value objects with the conditions + * @return String with the where sql code + */ + private generateWhereCode(where?: any): { sql: string, values: string[] } { + if (where == "*") { + return { + sql: "", + values: [] + }; + } else { + let generated: { sql: string, values: string[] } = { + sql: "", + values: [] + }; + if (Array.isArray(where)) { + let values: string[] = []; + let SQLChains: string[] = []; + where.forEach((chain: any) => { + let localChain = this.generateWhereCodeChain(chain); + values = values.concat(localChain.values); + SQLChains.push(localChain.sql); + }); + generated.sql = "(" + SQLChains.join(") OR (") + ")"; + generated.values = values; + } else { + generated = this.generateWhereCodeChain(where); + } + + if (generated.sql) { + generated.sql = " WHERE " + generated.sql; + return generated; + } else { + return generated; + } + } + } + + /** + * Plain query + * + * Any query over any table can be done here + * + * Warnings: + * - Field privacity or data integrity will not apply to a direct query, you are responsable for the data security. + * + * @var sql MySQL query + * @var values Values to replace in the query + * @return Promise with query result + */ + public query(query: Models.Query): Promise { + return this.DB.query(query); + } + + /** + * Select private query + * + * @var fields If is NOT set "*" will be used, if there's a string then it will be used as is, a plain query will be + * executed, if in the other hand an array is provided (Recommended), then it will filter the keys and run the query. + * @var where Key/Value object used to filter the query, an array of Key/Value objects will generate a multiple filter separated by an "OR". + * @var orderBy String with column_name and direction E.g.: "id, name ASC" + * @var groupBy String with column_name E.g.: "id, name" + * @var limit Number of rows to retrieve + * @return Promise with query result + * + * TODO: + * @var orderBy should be an array of fields, then they can be tested + * @var groupBy should be an array of fields, then they can be tested + * Join at least 2 tables is important + * Group this using functions like select("").orderBy() is just easier to understand + */ + private select(select: Models.SelectLimit): Promise { + let fieldsSQL = this.getSelectFieldsSQL(select.fields); + let joinFieldsSQL = this.getJoinSelectFieldsSQL(); + let joinCode = this.generateJoinCode(); + let whereCode = this.generateWhereCode(select.where); + let groupBy = select.groupBy; + let orderBy = select.orderBy; + let limit = select.limit; + let extra = ""; + if ((typeof groupBy !== "undefined") && (groupBy !== null)) { + extra += " GROUP BY " + groupBy; + } + if ((typeof orderBy !== "undefined") && (orderBy !== null)) { + extra += " ORDER BY " + orderBy; + } + if ((typeof limit !== "undefined") && (limit !== null)) { + extra += " LIMIT " + limit; + } + let sql = "SELECT " + fieldsSQL + joinFieldsSQL + " FROM `" + this.tableName + "`" + joinCode + whereCode.sql + extra + ";"; + this.joins = []; + return this.query({ sql: sql, values: whereCode.values }); + } + + /** + * Get item - Select query + * + * @var fields If is NOT set "*" will be used, if there's a string then it will be used as is, a plain query will be + * executed, if in the other hand an array is provided (Recommended), then it will filter the keys and run the query. + * @var where Key/Value object used to filter the query, an array of Key/Value objects will generate a multiple filter separated by an "OR". + * @var orderBy String with column_name and direction E.g.: "id, name ASC" + * @var groupBy String with column_name E.g.: "id, name" + * @return Promise with query result + */ + public get(select: Models.Select): Promise { + // Create promise + const p: Promise = new Promise( + (resolve: (data: any) => void, reject: (err: mysql.MysqlError) => void) => { + let sqlPromise = this.select({ + fields: select.fields, + where: select.where, + groupBy: select.groupBy, + orderBy: select.orderBy, + limit: 1 + }); + sqlPromise.then((data) => { + resolve(data[0]); + }).catch(err => { + reject(err); + }); + } + ); + return p; + } + + /** + * Get some item - Select query + * + * @var fields If is NOT set "*" will be used, if there's a string then it will be used as is, a plain query will be + * executed, if in the other hand an array is provided (Recommended), then it will filter the keys and run the query. + * @var where Key/Value object used to filter the query, an array of Key/Value objects will generate a multiple filter separated by an "OR". + * @var orderBy String with column_name and direction E.g.: "id, name ASC" + * @var groupBy String with column_name E.g.: "id, name" + * @var limit Number of rows to retrieve + * @return Promise with query result + */ + public getSome(select: Models.SelectLimit): Promise { + return this.select(select); + } + + /** + * Get all items - Select query + * + * @var fields If is NOT set "*" will be used, if there's a string then it will be used as is, a plain query will be + * executed, if in the other hand an array is provided (Recommended), then it will filter the keys and run the query. + * @var where Key/Value object used to filter the query, an array of Key/Value objects will generate a multiple filter separated by an "OR". + * @var orderBy String with column_name and direction E.g.: "id, name ASC" + * @var groupBy String with column_name E.g.: "id, name" + * @return Promise with query result + */ + public getAll(select: Models.Select): Promise { + return this.select(select); + } + + /** + * Join a table + * + * Specify a field that needs to be joined + * + * Warning: It works only with select requests + * + * @var keyField Model foreign key + * @var fields String array with names of fields to join + * @var kind Type of Join to apply E.g.: INNER, LEFT + * @return Model + */ + public join(join: Models.Join): Model { + this.joins.push({ + keyField: join.keyField, + fields: join.fields, + kind: join.kind + }) + return this; + } + + /** + * Insert query + * + * @var data object to be inserted in the table + * @return Promise with query result + */ + public insert(data: Models.Row): Promise { + let fields = []; + let wildcards = []; + let values: string[] = []; + for (let key in data) { + fields.push(key); + wildcards.push("?"); + values.push(data[key]); + } + let query = "INSERT INTO `" + this.tableName + "` (`" + fields.join("`, `") + "`) VALUES (" + wildcards.join(", ") + ");"; + return this.query({ sql: query, values: values }); + } + + /** + * Update query + * + * @var data object data to be update in the table + * @var where Key/Value object used to filter the query, an array of Key/Value objects will generate a multiple filter separated by an "OR". + * @return Promise with query result + */ + public update(update: Models.Update): Promise { + let fields = []; + let values = []; + let unifiedValues = []; + let data = update.data; + let where = update.where; + for (let key in data) { + if (data[key] == "now()") { + fields.push("`" + key + "` = now()"); + } else { + fields.push("`" + key + "` = ?"); + values.push(data[key]); + } + } + let whereCode = this.generateWhereCode(where); + let query = "UPDATE `" + this.tableName + "` SET " + fields.join(", ") + whereCode.sql + ";"; + unifiedValues = values.concat(whereCode.values); + return this.query({ sql: query, values: unifiedValues }); + } + + /** + * Delete query + * + * @var where Key/Value object used to filter the query, an array of Key/Value objects will generate a multiple filter separated by an "OR", a "*" string wildcard is required for security reasons if you want to match all rows. + * @return Promise with query result + */ + public delete(where: string | Models.KeyValue | Models.KeyValue[]): Promise { + let whereCode = this.generateWhereCode(where); + let query = "DELETE FROM `" + this.tableName + "`" + whereCode.sql + ";"; + return this.query({ sql: query, values: whereCode.values }); + } + +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index a9339a9..49f78a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,14 +1,230 @@ { "name": "@unicoderns/orm", - "version": "0.0.1", + "version": "0.0.5", "lockfileVersion": 1, "requires": true, "dependencies": { + "@types/cli-color": { + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@types/cli-color/-/cli-color-0.3.29.tgz", + "integrity": "sha1-yDpx/gLIx+HM7ASN1qJFjR9sluo=", + "dev": true + }, + "@types/mysql": { + "version": "2.15.5", + "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.5.tgz", + "integrity": "sha512-4QAISTUGZbcFh7bqdndo08xRdES5OTU+JODy8VCZbe1yiXyGjqw1H83G43XjQ3IbC10wn9xlGd44A5RXJwNh0Q==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/node": { + "version": "10.9.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.9.4.tgz", + "integrity": "sha512-fCHV45gS+m3hH17zgkgADUSi2RR1Vht6wOZ0jyHP8rjiQra9f+mIcgwPQHllmDocYOstIEbKlxbFDYlgrTPYqw==", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "bignumber.js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-4.1.0.tgz", + "integrity": "sha512-eJzYkFYy9L4JzXsbymsFn3p54D+llV27oTQ+ziJG7WFRheJcNZilgVXMG0LoZtlQSKBsJdWtLFqOD0u+U0jZKA==" + }, + "cli-color": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-1.3.0.tgz", + "integrity": "sha512-XmbLr8MzgOup/sPHF4nOZerCOcL7rD7vKWpEl0axUsMAY+AEimOhYva1ksskWqkLGY/bjR9h7Cfbr+RrJRfmTQ==", + "requires": { + "ansi-regex": "^2.1.1", + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "memoizee": "^0.4.14", + "timers-ext": "^0.1.5" + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "d": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", + "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", + "requires": { + "es5-ext": "^0.10.9" + } + }, + "es5-ext": { + "version": "0.10.46", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.46.tgz", + "integrity": "sha512-24XxRvJXNFwEMpJb3nOkiRJKRoupmjYmOPVlI65Qy2SrtxwOTB+g6ODjBKOtwEHbYrhWRty9xxOWLNdClT2djw==", + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.1", + "next-tick": "1" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-promise": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.5.tgz", + "integrity": "sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg==" + }, + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "es6-weak-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", + "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", + "requires": { + "d": "1", + "es5-ext": "^0.10.14", + "es6-iterator": "^2.0.1", + "es6-symbol": "^3.1.1" + } + }, + "event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "lru-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", + "integrity": "sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM=", + "requires": { + "es5-ext": "~0.10.2" + } + }, + "memoizee": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.14.tgz", + "integrity": "sha512-/SWFvWegAIYAO4NQMpcX+gcra0yEZu4OntmUdrBaWrJncxOqAziGFlHxc7yjKVK2uu3lpPW27P27wkR82wA8mg==", + "requires": { + "d": "1", + "es5-ext": "^0.10.45", + "es6-weak-map": "^2.0.2", + "event-emitter": "^0.3.5", + "is-promise": "^2.1", + "lru-queue": "0.1", + "next-tick": "1", + "timers-ext": "^0.1.5" + } + }, + "mysql": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.16.0.tgz", + "integrity": "sha512-dPbN2LHonQp7D5ja5DJXNbCLe/HRdu+f3v61aguzNRQIrmZLOeRoymBYyeThrR6ug+FqzDL95Gc9maqZUJS+Gw==", + "requires": { + "bignumber.js": "4.1.0", + "readable-stream": "2.3.6", + "safe-buffer": "5.1.2", + "sqlstring": "2.3.1" + } + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "sqlstring": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", + "integrity": "sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A=" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "timers-ext": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.5.tgz", + "integrity": "sha512-tsEStd7kmACHENhsUPaxb8Jf8/+GZZxyNFQbZD07HQOyooOa6At1rQqjffgvg7n+dxscQa9cjjMdWhJtsP2sxg==", + "requires": { + "es5-ext": "~0.10.14", + "next-tick": "1" + } + }, "typescript": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.0.3.tgz", "integrity": "sha512-kk80vLW9iGtjMnIv11qyxLqZm20UklzuR2tL0QAnDIygIUIemcZMxlMWudl9OOt76H3ntVzcTiddQ1/pAAJMYg==", "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" } } } diff --git a/package.json b/package.json index eec3c7c..d2f04ea 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@unicoderns/orm", - "version": "0.0.5", + "version": "0.0.6", "description": "Unicoderns Object/Relational Mapping", "author": { "name": "Unicoderns SA", @@ -11,7 +11,7 @@ "types": "dist/index.d.ts", "scripts": { "build": "tsc", - "prepublish": "tsc", + "prepare": "tsc", "test": "echo \"Error: no test specified\" && exit 1" }, "bugs": { @@ -26,6 +26,13 @@ ], "license": "MIT", "devDependencies": { + "@types/cli-color": "^0.3.29", + "@types/mysql": "^2.15.5", "typescript": "^3.0.3" + }, + "dependencies": { + "cli-color": "^1.3.0", + "es6-promise": "^4.2.5", + "mysql": "^2.16.0" } } diff --git a/tsconfig.json b/tsconfig.json index 7a23590..82fa9de 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { /* Basic Options */ - "target": "es5", + "target": "ES2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ @@ -50,7 +50,7 @@ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ - // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */