diff --git a/lib/types/index.js b/lib/types/index.js deleted file mode 100644 index d2bb02f6..00000000 --- a/lib/types/index.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict'; - -exports.Mixed = require('../schematype'); -exports.String = require('./string'); -exports.Number = require('./number'); -exports.Boolean = require('./boolean'); -exports.Array = require('./array'); -exports.Object = require('./object'); -exports.Date = require('./date'); -exports.Virtual = require('./virtual'); -exports.CUID = require('./cuid'); -exports.Enum = require('./enum'); -exports.Integer = require('./integer'); -exports.Buffer = require('./buffer'); diff --git a/lib/util.js b/lib/util.js deleted file mode 100644 index 596e2acd..00000000 --- a/lib/util.js +++ /dev/null @@ -1,190 +0,0 @@ -'use strict'; - -exports.shuffle = array => { - if (!Array.isArray(array)) throw new TypeError('array must be an Array!'); - const $array = array.slice(); - const { length } = $array; - const { random, floor } = Math; - - for (let i = 0; i < length; i++) { - // @see https://github.com/lodash/lodash/blob/4.17.10/lodash.js#L6718 - // @see https://github.com/lodash/lodash/blob/4.17.10/lodash.js#L3884 - const rand = i + floor(random() * (length - i)); - - const temp = $array[i]; - $array[i] = $array[rand]; - $array[rand] = temp; - } - - return $array; -}; - -function extractPropKey(key) { - return key.split('.'); -} - -exports.getProp = (obj, key) => { - if (typeof obj !== 'object') throw new TypeError('obj must be an object!'); - if (!key) throw new TypeError('key is required!'); - - if (!key.includes('.')) { - return obj[key]; - } - - const token = extractPropKey(key); - let result = obj; - const len = token.length; - - for (let i = 0; i < len; i++) { - result = result[token[i]]; - } - - return result; -}; - -exports.setProp = (obj, key, value) => { - if (typeof obj !== 'object') throw new TypeError('obj must be an object!'); - if (!key) throw new TypeError('key is required!'); - - if (!key.includes('.')) { - obj[key] = value; - return; - } - - const token = extractPropKey(key); - const lastKey = token.pop(); - const len = token.length; - - let cursor = obj; - - for (let i = 0; i < len; i++) { - const name = token[i]; - cursor[name] = cursor[name] || {}; - cursor = cursor[name]; - } - - cursor[lastKey] = value; -}; - -exports.delProp = (obj, key) => { - if (typeof obj !== 'object') throw new TypeError('obj must be an object!'); - if (!key) throw new TypeError('key is required!'); - - if (!key.includes('.')) { - delete obj[key]; - return; - } - - const token = extractPropKey(key); - const lastKey = token.pop(); - const len = token.length; - - let cursor = obj; - - for (let i = 0; i < len; i++) { - const name = token[i]; - - if (cursor[name]) { - cursor = cursor[name]; - } else { - return; - } - } - - delete cursor[lastKey]; -}; - -exports.setGetter = (obj, key, fn) => { - if (typeof obj !== 'object') throw new TypeError('obj must be an object!'); - if (!key) throw new TypeError('key is required!'); - if (typeof fn !== 'function') throw new TypeError('fn must be a function!'); - - if (!key.includes('.')) { - obj.__defineGetter__(key, fn); - return; - } - - const token = extractPropKey(key); - const lastKey = token.pop(); - const len = token.length; - - let cursor = obj; - - for (let i = 0; i < len; i++) { - const name = token[i]; - cursor[name] = cursor[name] || {}; - cursor = cursor[name]; - } - - cursor.__defineGetter__(lastKey, fn); -}; - -exports.arr2obj = (arr, value) => { - if (!Array.isArray(arr)) throw new TypeError('arr must be an array!'); - - const obj = {}; - let i = arr.length; - - while (i--) { - obj[arr[i]] = value; - } - - return obj; -}; - -exports.reverse = arr => { - if (!Array.isArray(arr)) throw new TypeError('arr must be an array!'); - - const len = arr.length; - - if (!len) return arr; - - for (let left = 0, right = len - 1; left < right; left++, right--) { - const tmp = arr[left]; - arr[left] = arr[right]; - arr[right] = tmp; - } - - return arr; -}; - -function parseArgs(args) { - if (typeof args !== 'string') return args; - - const arr = args.split(' '); - const result = {}; - - for (let i = 0, len = arr.length; i < len; i++) { - const key = arr[i]; - - switch (key[0]) { - case '+': - result[key.slice(1)] = 1; - break; - - case '-': - result[key.slice(1)] = -1; - break; - - default: - result[key] = 1; - } - } - - return result; -} - -exports.parseArgs = (orderby, order) => { - let result; - - if (order) { - result = {}; - result[orderby] = order; - } else if (typeof orderby === 'string') { - result = parseArgs(orderby); - } else { - result = orderby; - } - - return result; -}; diff --git a/lib/database.js b/src/database.ts similarity index 68% rename from lib/database.js rename to src/database.ts index 4ab5265b..04f57a93 100644 --- a/lib/database.js +++ b/src/database.ts @@ -1,19 +1,19 @@ -'use strict'; - -const JSONStream = require('JSONStream'); -const Promise = require('bluebird'); -const fs = require('graceful-fs'); -const Model = require('./model'); -const Schema = require('./schema'); -const SchemaType = require('./schematype'); -const WarehouseError = require('./error'); +import JSONStream = require('JSONStream'); +import Bluebird = require('bluebird'); +import { writev, promises as fsPromises, createReadStream } from 'graceful-fs'; +import { pipeline, Stream } from 'stream'; +import Model = require('./model'); +import Schema = require('./schema'); +import SchemaType = require('./schematype'); +import WarehouseError = require('./error'); +// eslint-disable-next-line @typescript-eslint/no-var-requires const pkg = require('../package.json'); -const { open } = fs.promises; -const pipeline = Promise.promisify(require('stream').pipeline); +const { open } = fsPromises; +const pipelineAsync = Bluebird.promisify(pipeline) as (...args: Stream[]) => Bluebird; -let _writev; +let _writev: (handle: fsPromises.FileHandle, buffers: Buffer[]) => Promise; -if (typeof fs.writev === 'function') { +if (typeof writev === 'function') { _writev = (handle, buffers) => handle.writev(buffers); } else { _writev = async (handle, buffers) => { @@ -21,7 +21,7 @@ if (typeof fs.writev === 'function') { }; } -async function exportAsync(database, path) { +async function exportAsync(database: Database, path: string) { const handle = await open(path, 'w'); try { @@ -58,7 +58,17 @@ async function exportAsync(database, path) { } } +type DatabaseOptions = { + version: number, + path: string, + onUpgrade: (...args: any[]) => any, + onDowngrade: (...args: any[]) => any +}; + class Database { + options: DatabaseOptions; + _models: any; + Model: typeof Model; /** * Database constructor. @@ -69,13 +79,15 @@ class Database { * @param {function} [options.onUpgrade] Triggered when the database is upgraded * @param {function} [options.onDowngrade] Triggered when the database is downgraded */ - constructor(options) { - this.options = Object.assign({ + constructor(options: { path: string } & Partial) { + this.options = { version: 0, + // eslint-disable-next-line @typescript-eslint/no-empty-function onUpgrade() {}, - - onDowngrade() {} - }, options); + // eslint-disable-next-line @typescript-eslint/no-empty-function + onDowngrade() {}, + ...options + }; this._models = {}; @@ -93,7 +105,7 @@ class Database { * @param {Schema|object} [schema] * @return {Model} */ - model(name, schema) { + model(name: string, schema?: any) { if (this._models[name]) { return this._models[name]; } @@ -132,9 +144,9 @@ class Database { this.model(data.key)._import(data.value); }); - const rs = fs.createReadStream(path, 'utf8'); + const rs = createReadStream(path, 'utf8'); - return pipeline(rs, parseStream).then(() => { + return pipelineAsync(rs, parseStream).then(() => { if (newVersion > oldVersion) { return onUpgrade(oldVersion, newVersion); } else if (newVersion < oldVersion) { @@ -153,7 +165,7 @@ class Database { const { path } = this.options; if (!path) throw new WarehouseError('options.path is required'); - return Promise.resolve(exportAsync(this, path)).asCallback(callback); + return Bluebird.resolve(exportAsync(this, path)).asCallback(callback); } toJSON() { @@ -171,12 +183,15 @@ class Database { }, models }; } + static Schema = Schema; + Schema: typeof Schema; + static SchemaType = SchemaType; + SchemaType: typeof SchemaType; + static version: number; } Database.prototype.Schema = Schema; -Database.Schema = Database.prototype.Schema; Database.prototype.SchemaType = SchemaType; -Database.SchemaType = Database.prototype.SchemaType; Database.version = pkg.version; -module.exports = Database; +export = Database; diff --git a/lib/document.js b/src/document.ts similarity index 92% rename from lib/document.js rename to src/document.ts index fd8f672b..34fbd074 100644 --- a/lib/document.js +++ b/src/document.ts @@ -1,8 +1,10 @@ -'use strict'; +import rfdc = require('rfdc'); +const cloneDeep = rfdc(); -const cloneDeep = require('rfdc')(); - -class Document { +abstract class Document { + abstract _model; + _id!: any; + abstract _schema; /** * Document constructor. @@ -101,4 +103,4 @@ function isGetter(obj, key) { return Object.getOwnPropertyDescriptor(obj, key).get; } -module.exports = Document; +export = Document; diff --git a/lib/error.js b/src/error.ts similarity index 56% rename from lib/error.js rename to src/error.ts index 1e097423..94bcefa8 100644 --- a/lib/error.js +++ b/src/error.ts @@ -1,6 +1,5 @@ -'use strict'; - class WarehouseError extends Error { + code?: string; /** * WarehouseError constructor @@ -8,18 +7,18 @@ class WarehouseError extends Error { * @param {string} msg * @param {string} code */ - constructor(msg, code) { + constructor(msg: string, code?: string) { super(msg); Error.captureStackTrace(this); this.code = code; } + static ID_EXIST = 'ID_EXIST'; + static ID_NOT_EXIST = 'ID_NOT_EXIST'; + static ID_UNDEFINED = 'ID_UNDEFINED'; } WarehouseError.prototype.name = 'WarehouseError'; -WarehouseError.ID_EXIST = 'ID_EXIST'; -WarehouseError.ID_NOT_EXIST = 'ID_NOT_EXIST'; -WarehouseError.ID_UNDEFINED = 'ID_UNDEFINED'; -module.exports = WarehouseError; +export = WarehouseError; diff --git a/lib/error/population.js b/src/error/population.ts similarity index 52% rename from lib/error/population.js rename to src/error/population.ts index 6e72e928..c97c0fc1 100644 --- a/lib/error/population.js +++ b/src/error/population.ts @@ -1,9 +1,7 @@ -'use strict'; - -const WarehouseError = require('../error'); +import WarehouseError = require('../error'); class PopulationError extends WarehouseError {} PopulationError.prototype.name = 'PopulationError'; -module.exports = PopulationError; +export = PopulationError; diff --git a/lib/error/validation.js b/src/error/validation.ts similarity index 52% rename from lib/error/validation.js rename to src/error/validation.ts index f3f02efe..bdd4ee6b 100644 --- a/lib/error/validation.js +++ b/src/error/validation.ts @@ -1,9 +1,7 @@ -'use strict'; - -const WarehouseError = require('../error'); +import WarehouseError = require('../error'); class ValidationError extends WarehouseError {} ValidationError.prototype.name = 'ValidationError'; -module.exports = ValidationError; +export = ValidationError; diff --git a/lib/model.js b/src/model.ts similarity index 92% rename from lib/model.js rename to src/model.ts index 0e19c4af..072da0f8 100644 --- a/lib/model.js +++ b/src/model.ts @@ -1,18 +1,25 @@ -'use strict'; - -const { EventEmitter } = require('events'); -const cloneDeep = require('rfdc')(); -const Promise = require('bluebird'); -const { parseArgs, getProp, setGetter, shuffle } = require('./util'); -const Document = require('./document'); -const Query = require('./query'); -const Schema = require('./schema'); -const Types = require('./types'); -const WarehouseError = require('./error'); -const PopulationError = require('./error/population'); -const Mutex = require('./mutex'); +import { EventEmitter } from 'events'; +import rfdc = require('rfdc'); +const cloneDeep = rfdc(); +import Promise = require('bluebird'); +import util = require('./util'); +const { parseArgs, getProp, setGetter, shuffle } = util; +import Document = require('./document'); +import Query = require('./query'); +import Schema = require('./schema'); +import Types = require('./types'); +import WarehouseError = require('./error'); +import PopulationError = require('./error/population'); +import Mutex = require('./mutex'); class Model extends EventEmitter { + _mutex = new Mutex(); + data: Record = {}; + schema; + length = 0; + Document; + Query; + _database; /** * Model constructor. @@ -20,7 +27,7 @@ class Model extends EventEmitter { * @param {string} name Model name * @param {Schema|object} [schema] Schema */ - constructor(name, schema_) { + constructor(public name: string, schema_) { super(); let schema; @@ -39,13 +46,11 @@ class Model extends EventEmitter { schema.path('_id', {type: Types.CUID, required: true}); } - this.name = name; - this.data = {}; - this._mutex = new Mutex(); this.schema = schema; - this.length = 0; class _Document extends Document { + _model!: Model; + _schema!: Schema; constructor(data) { super(data); @@ -59,7 +64,10 @@ class Model extends EventEmitter { _Document.prototype._model = this; _Document.prototype._schema = schema; - class _Query extends Query {} + class _Query extends Query { + _model!: Model; + _schema!: Schema; + } this.Query = _Query; @@ -91,7 +99,7 @@ class Model extends EventEmitter { * @param {boolean} [options.lean=false] Returns a plain JavaScript object * @return {Document|object} */ - findById(id, options_) { + findById(id, options_?) { const raw = this.data[id]; if (!raw) return; @@ -116,11 +124,10 @@ class Model extends EventEmitter { /** * Acquires write lock. * - * @param {*} id * @return {Promise} * @private */ - _acquireWriteLock(id) { + _acquireWriteLock(): Promise.Disposer { const mutex = this._mutex; return new Promise((resolve, reject) => { @@ -175,7 +182,7 @@ class Model extends EventEmitter { * @param {function} [callback] * @return {Promise} */ - insertOne(data, callback) { + insertOne(data, callback?) { return Promise.using(this._acquireWriteLock(), () => this._insertOne(data)).asCallback(callback); } @@ -223,7 +230,7 @@ class Model extends EventEmitter { * @return {Promise} * @private */ - _updateWithStack(id, stack) { + _updateWithStack(id, stack): Promise { const schema = this.schema; const data = this.data[id]; @@ -265,7 +272,7 @@ class Model extends EventEmitter { * @param {function} [callback] * @return {Promise} */ - updateById(id, update, callback) { + updateById(id, update, callback?) { return Promise.using(this._acquireWriteLock(), () => { const stack = this.schema._parseUpdate(update); return this._updateWithStack(id, stack); @@ -280,7 +287,7 @@ class Model extends EventEmitter { * @param {function} [callback] * @return {Promise} */ - update(query, data, callback) { + update(query, data, callback?) { return this.find(query).update(data, callback); } @@ -326,7 +333,7 @@ class Model extends EventEmitter { * @param {function} [callback] * @return {Promise} */ - replaceById(id, data, callback) { + replaceById(id, data, callback?) { return Promise.using(this._acquireWriteLock(), () => this._replaceById(id, data)).asCallback(callback); } @@ -338,7 +345,7 @@ class Model extends EventEmitter { * @param {function} [callback] * @return {Promise} */ - replace(query, data, callback) { + replace(query, data, callback?) { return this.find(query).replace(data, callback); } @@ -414,7 +421,7 @@ class Model extends EventEmitter { * @param {function} iterator * @param {object} [options] See {@link Model#findById}. */ - forEach(iterator, options) { + forEach(iterator, options?) { const keys = Object.keys(this.data); let num = 0; @@ -430,7 +437,7 @@ class Model extends EventEmitter { * @param {Object} [options] See {@link Model#findById}. * @return {Array} */ - toArray(options) { + toArray(options?) { const result = new Array(this.length); this.forEach((item, i) => { @@ -450,8 +457,7 @@ class Model extends EventEmitter { * @param {Boolean} [options.lean=false] Returns a plain JavaScript object. * @return {Query|Array} */ - find(query, options_) { - const options = options_ || {}; + find(query, options: { limit?: number; skip?: number; lean?: boolean; } = {}) { const filter = this.schema._execQuery(query); const keys = Object.keys(this.data); const len = keys.length; @@ -486,9 +492,8 @@ class Model extends EventEmitter { * @param {Boolean} [options.lean=false] Returns a plain JavaScript object. * @return {Document|Object} */ - findOne(query, options_) { - const options = options_ || {}; - options.limit = 1; + findOne(query, options_ : { skip?: number; lean?: boolean; } = {}) { + const options = Object.assign(options_, { limit: 1 }); const result = this.find(query, options); return options.lean ? result[0] : result.data[0]; @@ -516,7 +521,7 @@ class Model extends EventEmitter { * @param {Object} [options] See {@link Model#findById}. * @return {Document|Object} */ - eq(i_, options) { + eq(i_, options?) { let index = i_ < 0 ? this.length + i_ : i_; const data = this.data; const keys = Object.keys(data); @@ -562,7 +567,7 @@ class Model extends EventEmitter { * @param {Number} [end] * @return {Query} */ - slice(start_, end_) { + slice(start_: number, end_?: number) { const total = this.length; let start = start_ | 0; @@ -900,7 +905,7 @@ class Model extends EventEmitter { arr[i] = this._populate(item, stack); }); - return new Query(arr); + return new this.Query(arr); } /** @@ -946,12 +951,16 @@ class Model extends EventEmitter { } return result; } + get: Model['findById']; + size: Model['count']; + each: Model['forEach']; + random: Model['shuffle']; } Model.prototype.get = Model.prototype.findById; function execHooks(schema, type, event, data) { - const hooks = schema.hooks[type][event]; + const hooks = schema.hooks[type][event] as ((data: any) => Promise | void)[]; if (!hooks.length) return Promise.resolve(data); return Promise.each(hooks, hook => hook(data)).thenReturn(data); @@ -963,4 +972,4 @@ Model.prototype.each = Model.prototype.forEach; Model.prototype.random = Model.prototype.shuffle; -module.exports = Model; +export = Model; diff --git a/lib/mutex.js b/src/mutex.ts similarity index 76% rename from lib/mutex.js rename to src/mutex.ts index 87725b34..bfb1f358 100644 --- a/lib/mutex.js +++ b/src/mutex.ts @@ -1,12 +1,12 @@ -'use strict'; - class Mutex { + private _locked: boolean; + private readonly _queue: (() => void)[]; constructor() { this._locked = false; this._queue = []; } - lock(fn) { + lock(fn: () => void) { if (this._locked) { this._queue.push(fn); return; @@ -29,4 +29,4 @@ class Mutex { } } -module.exports = Mutex; +export = Mutex; diff --git a/lib/query.js b/src/query.ts similarity index 78% rename from lib/query.js rename to src/query.ts index 53b94881..f547e2f1 100644 --- a/lib/query.js +++ b/src/query.ts @@ -1,17 +1,18 @@ -'use strict'; +import Promise = require('bluebird'); +import util = require('./util'); +const { parseArgs, shuffle } = util; -const Promise = require('bluebird'); -const { parseArgs, shuffle } = require('./util'); - -class Query { +abstract class Query { + length: number; + abstract _model; + abstract _schema; /** * Query constructor. * * @param {Array} data */ - constructor(data) { - this.data = data; + constructor(private data: any[]) { this.length = data.length; } @@ -20,7 +21,7 @@ class Query { * * @return Number */ - count() { + count(): number { return this.length; } @@ -29,7 +30,7 @@ class Query { * * @param {Function} iterator */ - forEach(iterator) { + forEach(iterator: (item: any, index: number) => void): void { const { data, length } = this; for (let i = 0; i < length; i++) { @@ -42,7 +43,7 @@ class Query { * * @return {Array} */ - toArray() { + toArray(): any[] { return this.data; } @@ -53,7 +54,7 @@ class Query { * @param {Number} i * @return {Document|Object} */ - eq(i) { + eq(i: number) { const index = i < 0 ? this.length + i : i; return this.data[index]; } @@ -83,8 +84,8 @@ class Query { * @param {Number} [end] * @return {Query} */ - slice(start, end) { - return new this.constructor(this.data.slice(start, end)); + slice(start: number, end?: number): Query { + return Reflect.construct(this.constructor, [this.data.slice(start, end)]); } /** @@ -93,7 +94,7 @@ class Query { * @param {Number} i * @return {Query} */ - limit(i) { + limit(i: number): Query { return this.slice(0, i); } @@ -103,7 +104,7 @@ class Query { * @param {Number} i * @return {Query} */ - skip(i) { + skip(i: number): Query { return this.slice(i); } @@ -112,8 +113,8 @@ class Query { * * @return {Query} */ - reverse() { - return new this.constructor(this.data.slice().reverse()); + reverse(): Query { + return Reflect.construct(this.constructor, [this.data.slice().reverse()]); } /** @@ -121,8 +122,8 @@ class Query { * * @return {Query} */ - shuffle() { - return new this.constructor(shuffle(this.data)); + shuffle(): Query { + return Reflect.construct(this.constructor, [shuffle(this.data)]); } /** @@ -135,7 +136,7 @@ class Query { * @param {Boolean} [options.lean=false] Returns a plain JavaScript object. * @return {Query|Array} */ - find(query, options = {}) { + find(query: any, options: { limit?: number; skip?: number; lean?: boolean; } = {}): any[] | Query { const filter = this._schema._execQuery(query); const { data, length } = this; const { lean = false } = options; @@ -155,7 +156,7 @@ class Query { } } - return lean ? arr : new this.constructor(arr); + return lean ? arr : Reflect.construct(this.constructor, [arr]); } /** @@ -167,11 +168,11 @@ class Query { * @param {Boolean} [options.lean=false] Returns a plain JavaScript object. * @return {Document|Object} */ - findOne(query, options = {}) { - options.limit = 1; + findOne(query: any, options: { skip?: number; lean?: boolean; } = {}): any { + const _options = Object.assign(options, { limit: 1 }); - const result = this.find(query, options); - return options.lean ? result[0] : result.data[0]; + const result = this.find(query, _options); + return Array.isArray(result) ? result[0] : result.data[0]; } /** @@ -192,11 +193,11 @@ class Query { * @param {String|Number} [order] * @return {Query} */ - sort(orderby, order) { + sort(orderby, order): Query { const sort = parseArgs(orderby, order); const fn = this._schema._execSort(sort); - return new this.constructor(this.data.slice().sort(fn)); + return Reflect.construct(this.constructor, [this.data.slice().sort(fn)]); } /** @@ -205,9 +206,9 @@ class Query { * @param {Function} iterator * @return {Array} */ - map(iterator) { + map(iterator: (item: any, index: number) => T): T[] { const { data, length } = this; - const result = new Array(length); + const result: T[] = new Array(length); for (let i = 0; i < length; i++) { result[i] = iterator(data[i], i); @@ -224,7 +225,7 @@ class Query { * @param {*} [initial] By default, the initial value is the first document. * @return {*} */ - reduce(iterator, initial) { + reduce(iterator, initial?) { const { data, length } = this; let result, i; @@ -251,7 +252,7 @@ class Query { * @param {*} [initial] By default, the initial value is the last document. * @return {*} */ - reduceRight(iterator, initial) { + reduceRight(iterator, initial?) { const { data, length } = this; let result, i; @@ -277,7 +278,7 @@ class Query { * @param {Function} iterator * @return {Query} */ - filter(iterator) { + filter(iterator: (item: any, index: number) => boolean): Query { const { data, length } = this; const arr = []; @@ -286,7 +287,7 @@ class Query { if (iterator(item, i)) arr.push(item); } - return new this.constructor(arr); + return Reflect.construct(this.constructor, [arr]); } /** @@ -296,7 +297,7 @@ class Query { * @param {Function} iterator * @return {Boolean} */ - every(iterator) { + every(iterator: (item: any, index: number) => boolean): boolean { const { data, length } = this; for (let i = 0; i < length; i++) { @@ -313,7 +314,7 @@ class Query { * @param {Function} iterator * @return {Boolean} */ - some(iterator) { + some(iterator: (item: any, index: number) => boolean): boolean { const { data, length } = this; for (let i = 0; i < length; i++) { @@ -330,7 +331,7 @@ class Query { * @param {Function} [callback] * @return {Promise} */ - update(data, callback) { + update(data: any, callback?: (err?: any) => void): Promise { const model = this._model; const stack = this._schema._parseUpdate(data); @@ -344,7 +345,7 @@ class Query { * @param {Function} [callback] * @return {Promise} */ - replace(data, callback) { + replace(data: any, callback?: (err?: any) => void): Promise { const model = this._model; return Promise.map(this.data, item => model.replaceById(item._id, data)).asCallback(callback); @@ -368,7 +369,7 @@ class Query { * @param {String|Object} expr * @return {Query} */ - populate(expr) { + populate(expr: any): Query { const stack = this._schema._parsePopulate(expr); const { data, length } = this; const model = this._model; @@ -379,6 +380,10 @@ class Query { return this; } + + size: Query['count']; + each: Query['forEach']; + random: Query['shuffle']; } Query.prototype.size = Query.prototype.count; @@ -387,4 +392,4 @@ Query.prototype.each = Query.prototype.forEach; Query.prototype.random = Query.prototype.shuffle; -module.exports = Query; +export = Query; diff --git a/lib/schema.js b/src/schema.ts similarity index 90% rename from lib/schema.js rename to src/schema.ts index 73ada186..5c5a1ddc 100644 --- a/lib/schema.js +++ b/src/schema.ts @@ -1,11 +1,12 @@ -'use strict'; - -const SchemaType = require('./schematype'); -const Types = require('./types'); -const Promise = require('bluebird'); -const { getProp, setProp, delProp } = require('./util'); -const PopulationError = require('./error/population'); -const isPlainObject = require('is-plain-object'); +import SchemaType = require('./schematype'); +import Types = require('./types'); +import Promise = require('bluebird'); +import util = require('./util'); +const { getProp, setProp, delProp } = util; +import PopulationError = require('./error/population'); +import SchemaTypeVirtual = require('./types/virtual'); +// eslint-disable-next-line @typescript-eslint/no-var-requires +const isPlainObject: (o: any) => boolean = require('is-plain-object'); /** * @callback queryFilterCallback @@ -101,9 +102,8 @@ class UpdateParser { }; } - constructor(paths) { - this.paths = paths; - } + // eslint-disable-next-line no-useless-constructor + constructor(private paths) {} /** * Parses updating expressions and returns a stack. @@ -159,9 +159,8 @@ class UpdateParser { * @private */ class QueryParser { - constructor(paths) { - this.paths = paths; - } + // eslint-disable-next-line no-useless-constructor + constructor(private paths) {} /** * @@ -384,17 +383,18 @@ class QueryParser { } class Schema { + paths: Record> = {}; + statics: Record = {}; + methods: Record = {}; + hooks; + stacks; /** * Schema constructor. * - * @param {Object} schema + * @param {Object} [schema] */ - constructor(schema) { - this.paths = {}; - this.statics = {}; - this.methods = {}; - + constructor(schema?) { this.hooks = { pre: { save: [], @@ -424,7 +424,7 @@ class Schema { * @param {Object} schema * @param {String} prefix */ - add(schema, prefix = '') { + add(schema: Record, prefix = ''): void { const keys = Object.keys(schema); const len = keys.length; @@ -445,7 +445,9 @@ class Schema { * @param {*} obj * @return {SchemaType | undefined} */ - path(name, obj) { + path(name: string): SchemaType; + path(name: string, obj: SchemaType | ((...args: any[]) => any) | { type: any; } | Record | any[]): void; + path(name: string, obj?: SchemaType | ((...args: any[]) => any) | { type: any; } | Record | any[]): SchemaType | void { if (obj == null) { return this.paths[name]; } @@ -462,12 +464,12 @@ class Schema { break; case 'object': - if (obj.type) { - type = getSchemaType(name, obj); - } else if (Array.isArray(obj)) { + if (Array.isArray(obj)) { type = new Types.Array(name, { child: obj.length ? getSchemaType(name, obj[0]) : new SchemaType(name) }); + } else if (obj.type) { + type = getSchemaType(name, obj); } else { type = new Types.Object(); nested = Object.keys(obj).length > 0; @@ -493,7 +495,7 @@ class Schema { * @param {SchemaType} type * @private */ - _updateStack(name, type) { + _updateStack(name: string, type: SchemaType) { const { stacks } = this; stacks.getter.push(data => { @@ -518,7 +520,7 @@ class Schema { stacks.import.push(data => { const value = getProp(data, name); - const result = type.parse(value, data); + const result = type.parse(value); if (result !== undefined) { setProp(data, name, result); @@ -544,7 +546,7 @@ class Schema { * @param {Function} [getter] * @return {SchemaType.Virtual} */ - virtual(name, getter) { + virtual(name: string, getter?: () => any): SchemaTypeVirtual { const virtual = new Types.Virtual(name, {}); if (getter) virtual.get(getter); @@ -601,7 +603,7 @@ class Schema { * @param {String} name * @param {Function} fn */ - static(name, fn) { + static(name: string, fn) { if (!name) throw new TypeError('Method name is required!'); if (typeof fn !== 'function') { @@ -745,6 +747,7 @@ class Schema { * @return {PopulateResult[]} * @private */ + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types _parsePopulate(expr) { const { paths } = this; const arr = []; @@ -775,6 +778,8 @@ class Schema { if (!item.model) { const path = paths[key]; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore const ref = path.child ? path.child.options.ref : path.options.ref; if (!ref) { @@ -787,9 +792,10 @@ class Schema { return arr; } + Types: typeof Types; + static Types = Types; } Schema.prototype.Types = Types; -Schema.Types = Schema.prototype.Types; -module.exports = Schema; +export = Schema; diff --git a/lib/schematype.js b/src/schematype.ts similarity index 81% rename from lib/schematype.js rename to src/schematype.ts index 06192a30..1c8136cd 100644 --- a/lib/schematype.js +++ b/src/schematype.ts @@ -1,7 +1,6 @@ -'use strict'; - -const { setProp } = require('./util'); -const ValidationError = require('./error/validation'); +import util = require('./util'); +const { setProp } = util; +import ValidationError = require('./error/validation'); /** * This is the basic schema type. @@ -46,7 +45,9 @@ const ValidationError = require('./error/validation'); * * The return value will replace the original data. */ -class SchemaType { +class SchemaType { + options: { required: boolean; default?: (() => T) | T; } + default: () => T; /** * SchemaType constructor. @@ -56,9 +57,7 @@ class SchemaType { * @param {Boolean} [options.required=false] * @param {*} [options.default] */ - constructor(name, options) { - this.name = name || ''; - + constructor(public name = '', options?: { required?: boolean; default?: (() => T) | T; }) { this.options = Object.assign({ required: false }, options); @@ -66,7 +65,7 @@ class SchemaType { const default_ = this.options.default; if (typeof default_ === 'function') { - this.default = default_; + this.default = default_ as unknown as () => T; } else { this.default = () => default_; } @@ -80,7 +79,7 @@ class SchemaType { * @param {Object} data * @return {*} */ - cast(value, data) { + cast(value: unknown, data: unknown): unknown { if (value == null) { return this.default(); } @@ -95,7 +94,7 @@ class SchemaType { * @param {Object} data * @return {*|Error} */ - validate(value, data) { + validate(value: unknown, data: unknown): unknown { if (this.options.required && value == null) { throw new ValidationError(`\`${this.name}\` is required!`); } @@ -110,7 +109,7 @@ class SchemaType { * @param {*} b * @return {Number} */ - compare(a, b) { + compare(a: unknown, b: unknown): number { if (a > b) { return 1; } else if (a < b) { @@ -127,7 +126,7 @@ class SchemaType { * @param {Object} data * @return {*} */ - parse(value, data) { + parse(value: unknown): any { return value; } @@ -138,7 +137,7 @@ class SchemaType { * @param {Object} data * @return {*} */ - value(value, data) { + value(value: unknown, data: unknown): any { return value; } @@ -150,7 +149,7 @@ class SchemaType { * @param {Object} data * @return {Boolean} */ - match(value, query, data) { + match(value: T, query: unknown, data: unknown): boolean { return value === query; } @@ -162,7 +161,7 @@ class SchemaType { * @param {Object} data * @return {Boolean} */ - q$exist(value, query, data) { + q$exist(value: unknown, query: unknown, data: unknown): boolean { return (value != null) === query; } @@ -174,7 +173,7 @@ class SchemaType { * @param {Object} data * @return {boolean} */ - q$ne(value, query, data) { + q$ne(value: T, query: unknown, data: unknown): boolean { return !this.match(value, query, data); } @@ -186,7 +185,7 @@ class SchemaType { * @param {Object} data * @return {Boolean} */ - q$lt(value, query, data) { + q$lt(value: unknown, query: unknown, data: unknown): boolean { return value < query; } @@ -198,7 +197,7 @@ class SchemaType { * @param {Object} data * @return {Boolean} */ - q$lte(value, query, data) { + q$lte(value: unknown, query: unknown, data: unknown): boolean { return value <= query; } @@ -210,7 +209,7 @@ class SchemaType { * @param {Object} data * @return {Boolean} */ - q$gt(value, query, data) { + q$gt(value: unknown, query: unknown, data: unknown): boolean { return value > query; } @@ -222,7 +221,7 @@ class SchemaType { * @param {Object} data * @return {Boolean} */ - q$gte(value, query, data) { + q$gte(value: unknown, query: unknown, data: unknown): boolean { return value >= query; } @@ -234,7 +233,7 @@ class SchemaType { * @param {Object} data * @return {Boolean} */ - q$in(value, query, data) { + q$in(value: unknown, query: unknown[], data: unknown): boolean { return query.includes(value); } @@ -280,10 +279,14 @@ class SchemaType { * @param {Object} data * @return {*} */ - u$rename(value, update, data) { + u$rename(value, update, data): void { if (value !== undefined) setProp(data, update, value); return undefined; } + + q$exists: SchemaType['q$exist']; + q$max: SchemaType['q$lte']; + q$min: SchemaType['q$gte']; } SchemaType.prototype.q$exists = SchemaType.prototype.q$exist; @@ -292,4 +295,4 @@ SchemaType.prototype.q$max = SchemaType.prototype.q$lte; SchemaType.prototype.q$min = SchemaType.prototype.q$gte; -module.exports = SchemaType; +export = SchemaType; diff --git a/lib/types/array.js b/src/types/array.ts similarity index 80% rename from lib/types/array.js rename to src/types/array.ts index 9c7b08ab..3f7354ff 100644 --- a/lib/types/array.js +++ b/src/types/array.ts @@ -1,14 +1,14 @@ -'use strict'; - -const SchemaType = require('../schematype'); -const ValidationError = require('../error/validation'); +import SchemaType = require('../schematype'); +import ValidationError = require('../error/validation'); const { isArray } = Array; /** * Array schema type. */ -class SchemaTypeArray extends SchemaType { +class SchemaTypeArray> extends SchemaType { + options: SchemaType['options'] & { child?: T; }; + child: T; /** * @@ -18,12 +18,12 @@ class SchemaTypeArray extends SchemaType { * @param {Array|Function} [options.default=[]] * @param {SchemaType} [options.child] */ - constructor(name, options) { + constructor(name: string, options?: Partial['options']> & { child?: T; }) { super(name, Object.assign({ default: [] }, options)); - this.child = this.options.child || new SchemaType(name); + this.child = this.options.child || new SchemaType(name) as T; } /** @@ -33,11 +33,11 @@ class SchemaTypeArray extends SchemaType { * @param {Object} data * @return {Array} */ - cast(value_, data) { - let value = super.cast(value_, data); - if (value == null) return value; + cast(value_: unknown, data: unknown): I[] | null | undefined { + value_ = super.cast(value_, data); + if (value_ == null) return value_ as null | undefined; - if (!isArray(value)) value = [value]; + const value = isArray(value_) ? value_ : value_ = [value_]; if (!value.length) return value; const child = this.child; @@ -56,7 +56,7 @@ class SchemaTypeArray extends SchemaType { * @param {Object} data * @return {Array|Error} */ - validate(value_, data) { + validate(value_: unknown, data: unknown): I[] { const value = super.validate(value_, data); if (!isArray(value)) { @@ -68,7 +68,7 @@ class SchemaTypeArray extends SchemaType { const child = this.child; for (let i = 0, len = value.length; i < len; i++) { - value[i] = child.validate(value[i], data); + value[i] = child.validate(value[i], data) as I; } return value; @@ -81,7 +81,7 @@ class SchemaTypeArray extends SchemaType { * @param {Array} b * @return {Number} */ - compare(a, b) { + compare(a: I[], b: I[]): number { if (a) { if (!b) return 1; } else { @@ -105,10 +105,9 @@ class SchemaTypeArray extends SchemaType { * Parses data. * * @param {Array} value - * @param {Object} data * @return {Array} */ - parse(value, data) { + parse(value: unknown[]) { if (!value) return value; const len = value.length; @@ -118,7 +117,7 @@ class SchemaTypeArray extends SchemaType { const child = this.child; for (let i = 0; i < len; i++) { - result[i] = child.parse(value[i], data); + result[i] = child.parse(value[i]); } return result; @@ -131,7 +130,7 @@ class SchemaTypeArray extends SchemaType { * @param {Object} data * @return {Array} */ - value(value, data) { + value(value: unknown[], data: unknown): any[] { if (!value) return value; const len = value.length; @@ -155,7 +154,7 @@ class SchemaTypeArray extends SchemaType { * @param {Object} data * @return {Boolean} */ - match(value, query, data) { + match(value: I[], query: unknown[], data: unknown): boolean { if (!value || !query) { return value === query; } @@ -182,7 +181,7 @@ class SchemaTypeArray extends SchemaType { * @param {Object} data * @return {Boolean} */ - q$size(value, query, data) { + q$size(value: unknown[], query: unknown, data: unknown): boolean { return (value ? value.length : 0) === query; } @@ -194,7 +193,7 @@ class SchemaTypeArray extends SchemaType { * @param {Object} data * @return {Boolean} */ - q$in(value, query, data) { + q$in(value: unknown[], query: unknown[], data: unknown): boolean { if (!value) return false; for (let i = 0, len = query.length; i < len; i++) { @@ -212,7 +211,7 @@ class SchemaTypeArray extends SchemaType { * @param {Object} data * @return {Boolean} */ - q$nin(value, query, data) { + q$nin(value: unknown[], query: unknown[], data: unknown): boolean { if (!value) return true; for (let i = 0, len = query.length; i < len; i++) { @@ -230,7 +229,7 @@ class SchemaTypeArray extends SchemaType { * @param {Object} data * @return {Boolean} */ - q$all(value, query, data) { + q$all(value: unknown[], query: unknown[], data: unknown): boolean { if (!value) return false; for (let i = 0, len = query.length; i < len; i++) { @@ -248,7 +247,7 @@ class SchemaTypeArray extends SchemaType { * @param {Object} data * @return {Array} */ - u$push(value, update, data) { + u$push(value: T[], update: T, data: unknown): T[] { if (isArray(update)) { return value ? value.concat(update) : update; } @@ -269,7 +268,7 @@ class SchemaTypeArray extends SchemaType { * @param {Object} data * @return {Array} */ - u$unshift(value, update, data) { + u$unshift(value: unknown[], update: unknown, data: unknown): any[] { if (isArray(update)) { return value ? update.concat(value) : update; } @@ -290,7 +289,7 @@ class SchemaTypeArray extends SchemaType { * @param {Object} data * @return {Array} */ - u$pull(value, update, data) { + u$pull(value: unknown[], update: unknown, data: unknown): any[] { if (!value) return value; if (isArray(update)) { @@ -350,7 +349,7 @@ class SchemaTypeArray extends SchemaType { * @param {Object} data * @return {Array} */ - u$addToSet(value, update, data) { + u$addToSet(value: any[], update, data) { if (isArray(update)) { if (!value) return update; @@ -370,6 +369,9 @@ class SchemaTypeArray extends SchemaType { return value; } + q$length: SchemaTypeArray['q$size']; + u$append: SchemaTypeArray['u$push']; + u$prepend: SchemaTypeArray['u$unshift']; } SchemaTypeArray.prototype.q$length = SchemaTypeArray.prototype.q$size; @@ -378,4 +380,4 @@ SchemaTypeArray.prototype.u$append = SchemaTypeArray.prototype.u$push; SchemaTypeArray.prototype.u$prepend = SchemaTypeArray.prototype.u$unshift; -module.exports = SchemaTypeArray; +export = SchemaTypeArray; diff --git a/lib/types/boolean.js b/src/types/boolean.ts similarity index 70% rename from lib/types/boolean.js rename to src/types/boolean.ts index 16148b1c..e45ce74c 100644 --- a/lib/types/boolean.js +++ b/src/types/boolean.ts @@ -1,12 +1,10 @@ -'use strict'; - -const SchemaType = require('../schematype'); -const ValidationError = require('../error/validation'); +import SchemaType = require('../schematype'); +import ValidationError = require('../error/validation'); /** * Boolean schema type. */ -class SchemaTypeBoolean extends SchemaType { +class SchemaTypeBoolean extends SchemaType { /** * Casts a boolean. @@ -15,7 +13,7 @@ class SchemaTypeBoolean extends SchemaType { * @param {Object} data * @return {Boolean} */ - cast(value_, data) { + cast(value_: unknown, data: unknown): boolean { const value = super.cast(value_, data); if (value === 'false' || value === '0') return false; @@ -30,14 +28,14 @@ class SchemaTypeBoolean extends SchemaType { * @param {Object} data * @return {Boolean|Error} */ - validate(value_, data) { + validate(value_: unknown, data: unknown): boolean { const value = super.validate(value_, data); if (value != null && typeof value !== 'boolean') { throw new ValidationError(`\`${value}\` is not a boolean!`); } - return value; + return value as boolean; } /** @@ -47,7 +45,7 @@ class SchemaTypeBoolean extends SchemaType { * @param {Object} data * @return {Boolean} */ - parse(value, data) { + parse(value: unknown): boolean { return Boolean(value); } @@ -58,9 +56,9 @@ class SchemaTypeBoolean extends SchemaType { * @param {Object} data * @return {Number} */ - value(value, data) { + value(value: unknown, data: unknown): number { return +value; } } -module.exports = SchemaTypeBoolean; +export = SchemaTypeBoolean; diff --git a/lib/types/buffer.js b/src/types/buffer.ts similarity index 75% rename from lib/types/buffer.js rename to src/types/buffer.ts index 20df46b5..6b2cb9aa 100644 --- a/lib/types/buffer.js +++ b/src/types/buffer.ts @@ -1,12 +1,11 @@ -'use strict'; - -const SchemaType = require('../schematype'); -const ValidationError = require('../error/validation'); +import SchemaType = require('../schematype'); +import ValidationError = require('../error/validation'); /** * Boolean schema type. */ -class SchemaTypeBuffer extends SchemaType { +class SchemaTypeBuffer extends SchemaType { + options: SchemaType['options'] & { encoding: BufferEncoding; }; /** * @param {string} name @@ -15,7 +14,7 @@ class SchemaTypeBuffer extends SchemaType { * @param {boolean|Function} [options.default] * @param {string} [options.encoding=hex] */ - constructor(name, options) { + constructor(name: string, options) { super(name, Object.assign({ encoding: 'hex' }, options)); @@ -28,10 +27,10 @@ class SchemaTypeBuffer extends SchemaType { * @param {Object} data * @return {Buffer} */ - cast(value_, data) { + cast(value_: unknown, data): Buffer | null | undefined { const value = super.cast(value_, data); - if (value == null || Buffer.isBuffer(value)) return value; + if (value == null || Buffer.isBuffer(value)) return value as Buffer | null | undefined; if (typeof value === 'string') return Buffer.from(value, this.options.encoding); if (Array.isArray(value)) return Buffer.from(value); } @@ -43,7 +42,7 @@ class SchemaTypeBuffer extends SchemaType { * @param {Object} data * @return {Buffer} */ - validate(value_, data) { + validate(value_: unknown, data): Buffer { const value = super.validate(value_, data); if (!Buffer.isBuffer(value)) { @@ -60,7 +59,7 @@ class SchemaTypeBuffer extends SchemaType { * @param {Buffer} b * @return {Number} */ - compare(a, b) { + compare(a: Buffer, b: Buffer): number { if (Buffer.isBuffer(a)) { return Buffer.isBuffer(b) ? a.compare(b) : 1; } @@ -72,10 +71,9 @@ class SchemaTypeBuffer extends SchemaType { * Parses data and transform them into buffer values. * * @param {*} value - * @param {Object} data * @return {Boolean} */ - parse(value, data) { + parse(value) { return value ? Buffer.from(value, this.options.encoding) : value; } @@ -86,7 +84,7 @@ class SchemaTypeBuffer extends SchemaType { * @param {Object} data * @return {Number} */ - value(value, data) { + value(value: Buffer, data) { return Buffer.isBuffer(value) ? value.toString(this.options.encoding) : value; } @@ -98,7 +96,7 @@ class SchemaTypeBuffer extends SchemaType { * @param {Object} data * @return {Boolean} */ - match(value, query, data) { + match(value: Buffer, query: Buffer, data): boolean { if (Buffer.isBuffer(value) && Buffer.isBuffer(query)) { return value.equals(query); } @@ -107,4 +105,4 @@ class SchemaTypeBuffer extends SchemaType { } } -module.exports = SchemaTypeBuffer; +export = SchemaTypeBuffer; diff --git a/lib/types/cuid.js b/src/types/cuid.ts similarity index 76% rename from lib/types/cuid.js rename to src/types/cuid.ts index 4e723e89..51b300c5 100644 --- a/lib/types/cuid.js +++ b/src/types/cuid.ts @@ -1,13 +1,11 @@ -'use strict'; - -const SchemaType = require('../schematype'); -const cuid = require('cuid'); -const ValidationError = require('../error/validation'); +import SchemaType = require('../schematype'); +import cuid = require('cuid'); +import ValidationError = require('../error/validation'); /** * [CUID](https://github.com/ericelliott/cuid) schema type. */ -class SchemaTypeCUID extends SchemaType { +class SchemaTypeCUID extends SchemaType { /** * Casts data. Returns a new CUID only if value is null and the field is @@ -41,4 +39,4 @@ class SchemaTypeCUID extends SchemaType { } } -module.exports = SchemaTypeCUID; +export = SchemaTypeCUID; diff --git a/lib/types/date.js b/src/types/date.ts similarity index 81% rename from lib/types/date.js rename to src/types/date.ts index a17e7c05..ee4dc6b2 100644 --- a/lib/types/date.js +++ b/src/types/date.ts @@ -1,12 +1,10 @@ -'use strict'; - -const SchemaType = require('../schematype'); -const ValidationError = require('../error/validation'); +import SchemaType = require('../schematype'); +import ValidationError = require('../error/validation'); /** * Date schema type. */ -class SchemaTypeDate extends SchemaType { +class SchemaTypeDate extends SchemaType { /** * Casts data. @@ -15,12 +13,16 @@ class SchemaTypeDate extends SchemaType { * @param {Object} data * @return {Date} */ - cast(value_, data) { + cast(value: Date, data): Date; + cast(value: null, data): Date | null; + cast(value: undefined, data): Date | undefined; + cast(value: unknown, data): Date | null | undefined; + cast(value_, data): Date | null | undefined { const value = super.cast(value_, data); - if (value == null || value instanceof Date) return value; + if (value == null || value instanceof Date) return value as Date | null | undefined; - return new Date(value); + return new Date(value as any); } /** @@ -48,7 +50,7 @@ class SchemaTypeDate extends SchemaType { * @param {Object} data * @return {Boolean} */ - match(value, query, data) { + match(value: Date, query: Date, data): boolean { if (!value || !query) { return value === query; } @@ -75,10 +77,9 @@ class SchemaTypeDate extends SchemaType { * Parses data and transforms it into a date object. * * @param {*} value - * @param {Object} data * @return {Date} */ - parse(value, data) { + parse(value) { if (value) return new Date(value); } @@ -154,4 +155,4 @@ class SchemaTypeDate extends SchemaType { } } -module.exports = SchemaTypeDate; +export = SchemaTypeDate; diff --git a/lib/types/enum.js b/src/types/enum.ts similarity index 76% rename from lib/types/enum.js rename to src/types/enum.ts index e720507b..df736c0d 100644 --- a/lib/types/enum.js +++ b/src/types/enum.ts @@ -1,12 +1,11 @@ -'use strict'; - -const SchemaType = require('../schematype'); -const ValidationError = require('../error/validation'); +import SchemaType = require('../schematype'); +import ValidationError = require('../error/validation'); /** * Enum schema type. */ -class SchemaTypeEnum extends SchemaType { +class SchemaTypeEnum extends SchemaType { + options: SchemaType['options'] & { elements: any[] }; /** * @@ -41,4 +40,4 @@ class SchemaTypeEnum extends SchemaType { } } -module.exports = SchemaTypeEnum; +export = SchemaTypeEnum; diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 00000000..b6ded3e9 --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,27 @@ +import Mixed = require('../schematype'); +import String = require('./string'); +import Number = require('./number'); +import Boolean = require('./boolean'); +import Array = require('./array'); +import Object = require('./object'); +import Date = require('./date'); +import Virtual = require('./virtual'); +import CUID = require('./cuid'); +import Enum = require('./enum'); +import Integer = require('./integer'); +import Buffer = require('./buffer'); + +export = { + Mixed, + String, + Number, + Boolean, + Array, + Object, + Date, + Virtual, + CUID, + Enum, + Integer, + Buffer +}; diff --git a/lib/types/integer.js b/src/types/integer.ts similarity index 69% rename from lib/types/integer.js rename to src/types/integer.ts index 2c855042..1767ec20 100644 --- a/lib/types/integer.js +++ b/src/types/integer.ts @@ -1,7 +1,5 @@ -'use strict'; - -const SchemaTypeNumber = require('./number'); -const ValidationError = require('../error/validation'); +import SchemaTypeNumber = require('./number'); +import ValidationError = require('../error/validation'); /** * Integer schema type. @@ -15,10 +13,10 @@ class SchemaTypeInteger extends SchemaTypeNumber { * @param {Object} data * @return {Number} */ - cast(value_, data) { + cast(value_, data): number { const value = super.cast(value_, data); - return parseInt(value, 10); + return parseInt(value as any, 10); } /** @@ -28,7 +26,7 @@ class SchemaTypeInteger extends SchemaTypeNumber { * @param {Object} data * @return {Number|Error} */ - validate(value_, data) { + validate(value_, data): number { const value = super.validate(value_, data); if (value % 1 !== 0) { @@ -39,4 +37,4 @@ class SchemaTypeInteger extends SchemaTypeNumber { } } -module.exports = SchemaTypeInteger; +export = SchemaTypeInteger; diff --git a/lib/types/number.js b/src/types/number.ts similarity index 89% rename from lib/types/number.js rename to src/types/number.ts index 90dbffa5..90ffaa6d 100644 --- a/lib/types/number.js +++ b/src/types/number.ts @@ -1,12 +1,10 @@ -'use strict'; - -const SchemaType = require('../schematype'); -const ValidationError = require('../error/validation'); +import SchemaType = require('../schematype'); +import ValidationError = require('../error/validation'); /** * Number schema type. */ -class SchemaTypeNumber extends SchemaType { +class SchemaTypeNumber extends SchemaType { /** * Casts a number. @@ -15,10 +13,10 @@ class SchemaTypeNumber extends SchemaType { * @param {Object} data * @return {Number} */ - cast(value_, data) { + cast(value_, data): number | null | undefined { const value = super.cast(value_, data); - if (value == null || typeof value === 'number') return value; + if (value == null || typeof value === 'number') return value as number | null | undefined; return +value; } @@ -125,4 +123,4 @@ class SchemaTypeNumber extends SchemaType { } } -module.exports = SchemaTypeNumber; +export = SchemaTypeNumber; diff --git a/lib/types/object.js b/src/types/object.ts similarity index 55% rename from lib/types/object.js rename to src/types/object.ts index ff2eb391..9bd88ea3 100644 --- a/lib/types/object.js +++ b/src/types/object.ts @@ -1,22 +1,20 @@ -'use strict'; - -const SchemaType = require('../schematype'); +import SchemaType = require('../schematype'); /** * Object schema type. */ -class SchemaTypeObject extends SchemaType { +class SchemaTypeObject extends SchemaType> { /** * - * @param {String} name + * @param {String} [name] * @param {Object} [options] * @param {Boolean} [options.required=false] * @param {Object|Function} [options.default={}] */ - constructor(name, options) { + constructor(name?, options?) { super(name, Object.assign({ default: {} }, options)); } } -module.exports = SchemaTypeObject; +export = SchemaTypeObject; diff --git a/lib/types/string.js b/src/types/string.ts similarity index 90% rename from lib/types/string.js rename to src/types/string.ts index ac42f089..225f5fe6 100644 --- a/lib/types/string.js +++ b/src/types/string.ts @@ -1,12 +1,10 @@ -'use strict'; - -const SchemaType = require('../schematype'); -const ValidationError = require('../error/validation'); +import SchemaType = require('../schematype'); +import ValidationError = require('../error/validation'); /** * String schema type. */ -class SchemaTypeString extends SchemaType { +class SchemaTypeString extends SchemaType { /** * Casts a string. @@ -100,4 +98,4 @@ class SchemaTypeString extends SchemaType { } } -module.exports = SchemaTypeString; +export = SchemaTypeString; diff --git a/lib/types/virtual.js b/src/types/virtual.ts similarity index 80% rename from lib/types/virtual.js rename to src/types/virtual.ts index adf8748f..fb886f01 100644 --- a/lib/types/virtual.js +++ b/src/types/virtual.ts @@ -1,12 +1,13 @@ -'use strict'; - -const SchemaType = require('../schematype'); -const { setGetter } = require('../util'); +import SchemaType = require('../schematype'); +import util = require('../util'); +const { setGetter } = util; /** * Virtual schema type. */ -class SchemaTypeVirtual extends SchemaType { +class SchemaTypeVirtual extends SchemaType { + getter: (() => any) | undefined; + setter: ((value: any) => void) | undefined; /** * Add a getter. @@ -77,4 +78,4 @@ class SchemaTypeVirtual extends SchemaType { } } -module.exports = SchemaTypeVirtual; +export = SchemaTypeVirtual; diff --git a/src/util.ts b/src/util.ts new file mode 100644 index 00000000..ec613c48 --- /dev/null +++ b/src/util.ts @@ -0,0 +1,183 @@ +function extractPropKey(key) { + return key.split('.'); +} + +function parseArgs(args) { + if (typeof args !== 'string') return args; + + const arr = args.split(' '); + const result = {}; + + for (let i = 0, len = arr.length; i < len; i++) { + const key = arr[i]; + + switch (key[0]) { + case '+': + result[key.slice(1)] = 1; + break; + + case '-': + result[key.slice(1)] = -1; + break; + + default: + result[key] = 1; + } + } + + return result; +} + +export = { + shuffle: array => { + if (!Array.isArray(array)) throw new TypeError('array must be an Array!'); + const $array = array.slice(); + const { length } = $array; + const { random, floor } = Math; + + for (let i = 0; i < length; i++) { + // @see https://github.com/lodash/lodash/blob/4.17.10/lodash.js#L6718 + // @see https://github.com/lodash/lodash/blob/4.17.10/lodash.js#L3884 + const rand = i + floor(random() * (length - i)); + + const temp = $array[i]; + $array[i] = $array[rand]; + $array[rand] = temp; + } + + return $array; + }, + getProp: (obj, key) => { + if (typeof obj !== 'object') throw new TypeError('obj must be an object!'); + if (!key) throw new TypeError('key is required!'); + + if (!key.includes('.')) { + return obj[key]; + } + + const token = extractPropKey(key); + let result = obj; + const len = token.length; + + for (let i = 0; i < len; i++) { + result = result[token[i]]; + } + + return result; + }, + setProp: (obj, key, value) => { + if (typeof obj !== 'object') throw new TypeError('obj must be an object!'); + if (!key) throw new TypeError('key is required!'); + + if (!key.includes('.')) { + obj[key] = value; + return; + } + + const token = extractPropKey(key); + const lastKey = token.pop(); + const len = token.length; + + let cursor = obj; + + for (let i = 0; i < len; i++) { + const name = token[i]; + cursor[name] = cursor[name] || {}; + cursor = cursor[name]; + } + + cursor[lastKey] = value; + }, + delProp: (obj, key) => { + if (typeof obj !== 'object') throw new TypeError('obj must be an object!'); + if (!key) throw new TypeError('key is required!'); + + if (!key.includes('.')) { + delete obj[key]; + return; + } + + const token = extractPropKey(key); + const lastKey = token.pop(); + const len = token.length; + + let cursor = obj; + + for (let i = 0; i < len; i++) { + const name = token[i]; + + if (cursor[name]) { + cursor = cursor[name]; + } else { + return; + } + } + + delete cursor[lastKey]; + }, + setGetter: (obj, key, fn) => { + if (typeof obj !== 'object') throw new TypeError('obj must be an object!'); + if (!key) throw new TypeError('key is required!'); + if (typeof fn !== 'function') throw new TypeError('fn must be a function!'); + + if (!key.includes('.')) { + obj.__defineGetter__(key, fn); + return; + } + + const token = extractPropKey(key); + const lastKey = token.pop(); + const len = token.length; + + let cursor = obj; + + for (let i = 0; i < len; i++) { + const name = token[i]; + cursor[name] = cursor[name] || {}; + cursor = cursor[name]; + } + + cursor.__defineGetter__(lastKey, fn); + }, + arr2obj: (arr, value) => { + if (!Array.isArray(arr)) throw new TypeError('arr must be an array!'); + + const obj = {}; + let i = arr.length; + + while (i--) { + obj[arr[i]] = value; + } + + return obj; + }, + reverse: arr => { + if (!Array.isArray(arr)) throw new TypeError('arr must be an array!'); + + const len = arr.length; + + if (!len) return arr; + + for (let left = 0, right = len - 1; left < right; left++, right--) { + const tmp = arr[left]; + arr[left] = arr[right]; + arr[right] = tmp; + } + + return arr; + }, + parseArgs: (orderby, order) => { + let result; + + if (order) { + result = {}; + result[orderby] = order; + } else if (typeof orderby === 'string') { + result = parseArgs(orderby); + } else { + result = orderby; + } + + return result; + } +} diff --git a/test/fixtures/db.json b/test/fixtures/db.json index 5a796455..32e13d6e 100644 --- a/test/fixtures/db.json +++ b/test/fixtures/db.json @@ -1 +1 @@ -{"meta":{"version":1,"warehouse":"3.0.1"},"models":{"Test":[{"_id":"A"},{"_id":"B"},{"_id":"C"}]}} \ No newline at end of file +{"meta":{"version":1,"warehouse":"4.0.0"},"models":{"Test":[{"_id":"A"},{"_id":"B"},{"_id":"C"}]}} \ No newline at end of file diff --git a/test/scripts/database.js b/test/scripts/database.js index 30001e87..a7b518a5 100644 --- a/test/scripts/database.js +++ b/test/scripts/database.js @@ -10,8 +10,8 @@ const DB_PATH = path.join(path.dirname(__dirname), 'fixtures', 'db.json'); const DB_VERSION = 1; describe('Database', () => { - const Database = require('../..'); - const Model = require('../../lib/model'); + const Database = require('../../built/database'); + const Model = require('../../built/model'); const Schema = Database.Schema; const db = new Database({path: DB_PATH, version: DB_VERSION}); diff --git a/test/scripts/document.js b/test/scripts/document.js index ba9d359c..0a7f680d 100644 --- a/test/scripts/document.js +++ b/test/scripts/document.js @@ -4,7 +4,7 @@ const should = require('chai').should(); // eslint-disable-line describe('Document', () => { const Database = require('../..'); - const Document = require('../../lib/document'); + const Document = require('../../built/document'); const db = new Database(); const Schema = Database.Schema; diff --git a/test/scripts/mutex.js b/test/scripts/mutex.js index 4df3c5af..cf8b1e19 100644 --- a/test/scripts/mutex.js +++ b/test/scripts/mutex.js @@ -1,7 +1,7 @@ 'use strict'; const should = require('chai').should(); // eslint-disable-line -const Mutex = require('../../lib/mutex'); +const Mutex = require('../../built/mutex'); const sinon = require('sinon'); describe('Mutex', () => { diff --git a/test/scripts/query.js b/test/scripts/query.js index 06a99af6..3af8f565 100644 --- a/test/scripts/query.js +++ b/test/scripts/query.js @@ -3,7 +3,7 @@ const should = require('chai').should(); // eslint-disable-line const sortBy = require('lodash/sortBy'); const Promise = require('bluebird'); -const Document = require('../../lib/document'); +const Document = require('../../built/document'); describe('Query', () => { const Database = require('../..'); diff --git a/test/scripts/schematype.js b/test/scripts/schematype.js index 6ed1bf25..b086d00a 100644 --- a/test/scripts/schematype.js +++ b/test/scripts/schematype.js @@ -1,10 +1,10 @@ 'use strict'; const should = require('chai').should(); // eslint-disable-line -const ValidationError = require('../../lib/error/validation'); +const ValidationError = require('../../built/error/validation'); describe('SchemaType', () => { - const SchemaType = require('../../lib/schematype'); + const SchemaType = require('../../built/schematype'); const type = new SchemaType('test'); it('cast()', () => { diff --git a/test/scripts/types/array.js b/test/scripts/types/array.js index 7228343d..2f08cb3a 100644 --- a/test/scripts/types/array.js +++ b/test/scripts/types/array.js @@ -1,13 +1,13 @@ 'use strict'; const should = require('chai').should(); // eslint-disable-line -const ValidationError = require('../../../lib/error/validation'); +const ValidationError = require('../../../built/error/validation'); describe('SchemaTypeArray', () => { - const SchemaTypeArray = require('../../../lib/types/array'); - const SchemaTypeString = require('../../../lib/types/string'); - const SchemaTypeDate = require('../../../lib/types/date'); - const SchemaTypeBoolean = require('../../../lib/types/boolean'); + const SchemaTypeArray = require('../../../built/types/array'); + const SchemaTypeString = require('../../../built/types/string'); + const SchemaTypeDate = require('../../../built/types/date'); + const SchemaTypeBoolean = require('../../../built/types/boolean'); const type = new SchemaTypeArray('test'); it('cast()', () => { diff --git a/test/scripts/types/boolean.js b/test/scripts/types/boolean.js index 07be269f..2a51dae2 100644 --- a/test/scripts/types/boolean.js +++ b/test/scripts/types/boolean.js @@ -1,10 +1,10 @@ 'use strict'; require('chai').should(); -const ValidationError = require('../../../lib/error/validation'); +const ValidationError = require('../../../built/error/validation'); describe('SchemaTypeBoolean', () => { - const SchemaTypeBoolean = require('../../../lib/types/boolean'); + const SchemaTypeBoolean = require('../../../built/types/boolean'); const type = new SchemaTypeBoolean('test'); it('cast()', () => { diff --git a/test/scripts/types/buffer.js b/test/scripts/types/buffer.js index 3c6c62d9..7263b5b0 100644 --- a/test/scripts/types/buffer.js +++ b/test/scripts/types/buffer.js @@ -1,10 +1,10 @@ 'use strict'; const should = require('chai').should(); -const ValidationError = require('../../../lib/error/validation'); +const ValidationError = require('../../../built/error/validation'); describe('SchemaTypeBuffer', () => { - const SchemaTypeBuffer = require('../../../lib/types/buffer'); + const SchemaTypeBuffer = require('../../../built/types/buffer'); const type = new SchemaTypeBuffer('test'); it('cast()', () => { diff --git a/test/scripts/types/cuid.js b/test/scripts/types/cuid.js index f17cd89c..c70830f8 100644 --- a/test/scripts/types/cuid.js +++ b/test/scripts/types/cuid.js @@ -1,10 +1,10 @@ 'use strict'; const should = require('chai').should(); // eslint-disable-line -const ValidationError = require('../../../lib/error/validation'); +const ValidationError = require('../../../built/error/validation'); describe('SchemaTypeCUID', () => { - const SchemaTypeCUID = require('../../../lib/types/cuid'); + const SchemaTypeCUID = require('../../../built/types/cuid'); const type = new SchemaTypeCUID('test'); it('cast()', () => { diff --git a/test/scripts/types/date.js b/test/scripts/types/date.js index 912bec84..b366398d 100644 --- a/test/scripts/types/date.js +++ b/test/scripts/types/date.js @@ -1,10 +1,10 @@ 'use strict'; const should = require('chai').should(); // eslint-disable-line -const ValidationError = require('../../../lib/error/validation'); +const ValidationError = require('../../../built/error/validation'); describe('SchemaTypeDate', () => { - const SchemaTypeDate = require('../../../lib/types/date'); + const SchemaTypeDate = require('../../../built/types/date'); const type = new SchemaTypeDate('test'); it('cast()', () => { diff --git a/test/scripts/types/enum.js b/test/scripts/types/enum.js index 166c3888..94522209 100644 --- a/test/scripts/types/enum.js +++ b/test/scripts/types/enum.js @@ -1,10 +1,10 @@ 'use strict'; require('chai').should(); -const ValidationError = require('../../../lib/error/validation'); +const ValidationError = require('../../../built/error/validation'); describe('SchemaTypeEnum', () => { - const SchemaTypeEnum = require('../../../lib/types/enum'); + const SchemaTypeEnum = require('../../../built/types/enum'); it('validate()', () => { const type = new SchemaTypeEnum('test', {elements: ['foo', 'bar', 'baz']}); diff --git a/test/scripts/types/integer.js b/test/scripts/types/integer.js index 9589fad1..60f22d80 100644 --- a/test/scripts/types/integer.js +++ b/test/scripts/types/integer.js @@ -1,10 +1,10 @@ 'use strict'; require('chai').should(); -const ValidationError = require('../../../lib/error/validation'); +const ValidationError = require('../../../built/error/validation'); describe('SchemaTypeInteger', () => { - const SchemaTypeInteger = require('../../../lib/types/integer'); + const SchemaTypeInteger = require('../../../built/types/integer'); const type = new SchemaTypeInteger('test'); it('cast()', () => { diff --git a/test/scripts/types/number.js b/test/scripts/types/number.js index 6442196e..107b1f64 100644 --- a/test/scripts/types/number.js +++ b/test/scripts/types/number.js @@ -1,10 +1,10 @@ 'use strict'; require('chai').should(); -const ValidationError = require('../../../lib/error/validation'); +const ValidationError = require('../../../built/error/validation'); describe('SchemaTypeNumber', () => { - const SchemaTypeNumber = require('../../../lib/types/number'); + const SchemaTypeNumber = require('../../../built/types/number'); const type = new SchemaTypeNumber('type'); it('cast()', () => { diff --git a/test/scripts/types/object.js b/test/scripts/types/object.js index a5cd9c74..d6ef04e0 100644 --- a/test/scripts/types/object.js +++ b/test/scripts/types/object.js @@ -3,7 +3,7 @@ const should = require('chai').should(); // eslint-disable-line describe('SchemaTypeObject', () => { - const SchemaTypeObject = require('../../../lib/types/object'); + const SchemaTypeObject = require('../../../built/types/object'); const type = new SchemaTypeObject('test'); it('cast() - default', () => { diff --git a/test/scripts/types/string.js b/test/scripts/types/string.js index 2425d012..cbb601f1 100644 --- a/test/scripts/types/string.js +++ b/test/scripts/types/string.js @@ -1,10 +1,10 @@ 'use strict'; require('chai').should(); -const ValidationError = require('../../../lib/error/validation'); +const ValidationError = require('../../../built/error/validation'); describe('SchemaTypeString', () => { - const SchemaTypeString = require('../../../lib/types/string'); + const SchemaTypeString = require('../../../built/types/string'); const type = new SchemaTypeString('test'); it('cast()', () => { diff --git a/test/scripts/types/virtual.js b/test/scripts/types/virtual.js index 515cbbcb..23671158 100644 --- a/test/scripts/types/virtual.js +++ b/test/scripts/types/virtual.js @@ -3,7 +3,7 @@ const should = require('chai').should(); // eslint-disable-line describe('SchemaTypeVirtual', () => { - const SchemaTypeVirtual = require('../../../lib/types/virtual'); + const SchemaTypeVirtual = require('../../../built/types/virtual'); const type = new SchemaTypeVirtual('test'); it('get()', () => { diff --git a/test/scripts/util.js b/test/scripts/util.js index 5a23df2a..9a67c0e2 100644 --- a/test/scripts/util.js +++ b/test/scripts/util.js @@ -3,7 +3,7 @@ const should = require('chai').should(); // eslint-disable-line describe('util', () => { - const util = require('../../lib/util'); + const util = require('../../built/util'); it('shuffle()', () => { const src = Array(100).fill(0).map((_, i) => i);