@knorm/knorm
Version:
A JavaScript ORM written using ES6 classes
272 lines (271 loc) • 9.88 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const Model_1 = require("./Model");
const Field_1 = require("./Field");
const Connection_1 = require("./Connection");
const Query_1 = require("./Query");
const Transaction_1 = require("./Transaction");
const KnormError_1 = require("./KnormError");
/**
* Creates and configures ORMs.
*/
class Knorm {
/**
* Creates a new {@link Knorm} (ORM) instance. Each instance has it's own set
* of classes and configuration, which enables having multiple {@link Knorm}
* instances in a single application.
*
* @param {object} [config] The ORM's configuration.
* @param {function} [config.fieldToColumn] A function to convert all
* field-names to column names, for example
* [snakeCase](https://lodash.com/docs/4.17.10#snakeCase).
* @param {boolean} [config.debug] Whether or not to enable debug mode. See
* [the debugging guide](/guides/debugging) for more info.
*
*/
constructor(config = {}) {
/**
* The {@link Knorm} instance's model registry. Stores models added via
* {@link Knorm#addModel}.
*
* @type {object}
*/
this.models = {};
/**
* The {@link Knorm} instance's plugin registry. Stores plugins added via
* {@link Knorm#use}.
*
* @type {object}
*/
this.plugins = {};
/**
* The {@link Knorm} instance's config. Stores config passed in via the
* constructor.
*
* @type {object}
*/
this.config = config;
class Field extends this.constructor.Field {
}
class Model extends this.constructor.Model {
}
class Query extends this.constructor.Query {
}
class Connection extends this.constructor.Connection {
}
class Transaction extends this.constructor.Transaction {
}
// TODO: move fieldToColumn to a separate plugin
const { fieldToColumn } = config;
if (typeof fieldToColumn === 'function') {
Field.prototype.getColumnName = function (fieldName) {
return fieldToColumn.call(this, fieldName);
};
}
this.updateTransaction(Transaction);
this.updateModel(Model);
this.updateQuery(Query);
this.updateField(Field);
this.updateConnection(Connection);
[Field, Model, Connection, Query, Transaction].forEach((scopedClass) => {
scopedClass.prototype.knorm = scopedClass.knorm = this;
});
[Model, Query, Transaction].forEach((scopedClass) => {
scopedClass.prototype.models = scopedClass.models = this.models;
});
}
/**
* Loads a plugin into the ORM.
*
* @param {object|function} plugin The plugin to load. If passed as a
* function, the function is called with the ORM instance for initialisation.
* Note that if an object is passed, it should have `name` and `init`
* properties.
* @param {string} plugin.name The name of the plugin. This allows later
* accessing the plugin via the ORM's `plugins` object. Note that for
* functions, the plugin's `name` is the function's name.
* @param {function} plugin.init The function called to initialise the plugin.
*
* @throws {KnormError} if the plugin provided is not a function or is an
* object without an `init` method.
* @throws {KnormError} if the plugin has no `name` property.
* @throws {KnormError} if the plugin has already been added.
*
* @returns {Knorm} the ORM instance.
*/
use(plugin) {
if (!plugin) {
throw new this.constructor.KnormError('no plugin provided');
}
if (typeof plugin !== 'function' && typeof plugin.init !== 'function') {
throw new this.constructor.KnormError('invalid plugin provided');
}
const { name } = plugin;
if (!name) {
throw new this.constructor.KnormError('plugins should have a `name`');
}
if (this.plugins[name] !== undefined) {
throw new this.constructor.KnormError(`plugin \`${name}\` has already been added`);
}
this.plugins[name] = plugin;
if (plugin.init) {
plugin.init(this);
}
else {
plugin(this);
}
return this;
}
/**
* Adds a model to a {@link Knorm} instance.
*
* @param {Model} Model The model to add to the ORM. Note that models are
* automatically added to a {@link Knorm} instance when they are configured,
* therefore you will only need to call this method to add models that are not
* configured. See {@link /guides/models.html#model-registry} for more info.
*
* @throws {KnormError} if the model passed does not inherit the ORM's
* {@link Model} instance. This prevents one from (perhaps accidentally)
* adding a model from ORM instance X to ORM instance Y.
* @throws {KnormError} if the model has already been added.
*
* @returns {Knorm} The same {@link Knorm} instance to allow chaining.
*/
addModel(Model) {
if (!Model) {
throw new this.constructor.KnormError(`no model provided`);
}
if (!(Model.prototype instanceof this.Model) && Model !== this.Model) {
throw new this.constructor.KnormError(`model should be a subclass of \`knorm.Model\``);
}
const { name } = Model;
if (this.models[name]) {
throw new this.constructor.KnormError(`model \`${name}\` has already been added`);
}
this.models[name] = Model;
return this;
}
/**
* Creates a clone of an existing {@link Knorm} instance, copying all the
* models and plugins loaded into the original orm into it.
*
* @returns {Knorm} The newly cloned {@link Knorm} instance.
*/
clone() {
const clone = new Knorm(this.config);
Object.values(this.plugins).forEach((plugin) => {
clone.use(plugin);
});
Object.entries(this.models).forEach(([name, model]) => {
clone.models[name] = model;
});
return clone;
}
/**
* Updates the {@link Transaction} class used in the {@link Knorm} instance.
* This ensures all references to the new class are updated accordingly.
*
* @param {Transaction} Transaction The new class. This could be a class that
* extends the current {@link Knorm#Transaction} class or an entirely new
* class.
*
* @returns {Knorm} The same {@link Knorm} instance to allow chaining.
*/
updateTransaction(Transaction) {
/**
* The {@link Knorm} instance's {@link Transaction} class.
*/
this.Transaction = Transaction;
return this;
}
/**
* Updates the {@link Model} class used in the {@link Knorm} instance. This
* ensures all references to the new class are updated accordingly.
*
* @param {Model} Model The new class. This could be a class that extends the
* current {@link Knorm#Model} class or an entirely new class.
*
* @returns {Knorm} The same {@link Knorm} instance to allow chaining.
*/
updateModel(Model) {
/**
* The {@link Knorm} instance's {@link Model} class.
*/
this.Model = Model;
return this;
}
/**
* Updates the {@link Field} class used in the {@link Knorm} instance. This
* ensures all references to the new class are updated accordingly.
*
* @param {Field} Field The new class. This could be a class that extends the
* current {@link Knorm#Field} class or an entirely new class.
*
* @returns {Knorm} The same {@link Knorm} instance to allow chaining.
*/
updateField(Field) {
/**
* The {@link Knorm} instance's {@link Field} class.
*/
this.Field = this.Model.Field = Field;
return this;
}
/**
* Updates the {@link Query} class used in the {@link Knorm} instance. This
* ensures all references to the new class are updated accordingly.
*
* @param {Query} Query The new class. This could be a class that extends the
* current {@link Knorm#Query} class or an entirely new class.
*
* @returns {Knorm} The same {@link Knorm} instance to allow chaining.
*/
updateQuery(Query) {
/**
* The {@link Knorm} instance's {@link Query} class.
*/
this.Query = this.Model.Query = Query;
return this;
}
/**
* Updates the {@link Connection} class used in the {@link Knorm} instance.
* This ensures all references to the new class are updated accordingly.
*
* @param {Connection} Connection The new class. This could be a class that
* extends the current {@link Knorm#Connection} class or an entirely new
* class.
*
* @returns {Knorm} The same {@link Knorm} instance to allow chaining.
*/
updateConnection(Connection) {
/**
* The {@link Knorm} instance's {@link Connection} class.
*/
this.Connection = this.Query.Connection = this.Transaction.Connection = Connection;
return this;
}
}
exports.Knorm = Knorm;
/**
* A reference to {@link Connection}.
*/
Knorm.Connection = Connection_1.Connection;
/**
* A reference to {@link Model}.
*/
Knorm.Model = Model_1.Model;
/**
* A reference to {@link Query}.
*/
Knorm.Query = Query_1.Query;
/**
* A reference to {@link Field}.
*/
Knorm.Field = Field_1.Field;
/**
* A reference to {@link Transaction}.
*/
Knorm.Transaction = Transaction_1.Transaction;
/**
* A reference to {@link KnormError}.
*/
Knorm.KnormError = KnormError_1.KnormError;