@palmares/databases
Version:
Add support for working with databases with palmares framework
1,255 lines (1,218 loc) • 297 kB
JavaScript
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/defaults/index.ts
var defaults_exports = {};
__export(defaults_exports, {
defaultMigrations: () => defaultMigrations,
defaultModels: () => models_exports
});
module.exports = __toCommonJS(defaults_exports);
// src/defaults/models.ts
var models_exports = {};
__export(models_exports, {
PalmaresMigrations: () => PalmaresMigrations
});
// src/models/model.ts
var import_core10 = require("@palmares/core");
// src/models/exceptions.ts
var ModelNoUniqueFieldsError = class _ModelNoUniqueFieldsError extends Error {
static {
__name(this, "ModelNoUniqueFieldsError");
}
constructor(modelName) {
super(`Model ${modelName} has no unique fields, it should have at least one unique field. If it's an abstract model, you need to set "abstract" to true in the model options.`);
this.name = _ModelNoUniqueFieldsError.name;
}
};
var ModelNoPrimaryKeyFieldError = class _ModelNoPrimaryKeyFieldError extends Error {
static {
__name(this, "ModelNoPrimaryKeyFieldError");
}
constructor(modelName) {
super(`Model ${modelName} has no primary key field, it should have at least one primary key. If it's an abstract model, you need to set "abstract" to true in the model options.`);
this.name = _ModelNoPrimaryKeyFieldError.name;
}
};
var ModelCircularAbstractError = class extends Error {
static {
__name(this, "ModelCircularAbstractError");
}
constructor(originalModelName, abstractModelName) {
super(`Model ${originalModelName} have a circular abstract dependency with ${abstractModelName}`);
}
};
var ManagerEngineInstanceNotFoundError = class _ManagerEngineInstanceNotFoundError extends Error {
static {
__name(this, "ManagerEngineInstanceNotFoundError");
}
constructor(engineName) {
super(`The engine ${engineName} is not found in the manager. Make sure that this model is available for that engine.`);
this.name = _ManagerEngineInstanceNotFoundError.name;
}
};
var ShouldAssignAllInstancesException = class _ShouldAssignAllInstancesException extends Error {
static {
__name(this, "ShouldAssignAllInstancesException");
}
constructor() {
super("You have translated the model before. And you have assigned `instance` to the model options. You should assign `instance` to all model options.");
this.name = _ShouldAssignAllInstancesException.name;
}
};
var EngineDoesNotSupportFieldTypeException = class _EngineDoesNotSupportFieldTypeException extends Error {
static {
__name(this, "EngineDoesNotSupportFieldTypeException");
}
constructor(engineName, fieldType) {
super(`The engine '${engineName}' does not support the field of type: '${fieldType}'. If you are using a custom field, make sure that you are using the 'TranslatableField' class.`);
this.name = _EngineDoesNotSupportFieldTypeException.name;
}
};
var RelatedModelFromForeignKeyIsNotFromEngineException = class _RelatedModelFromForeignKeyIsNotFromEngineException extends Error {
static {
__name(this, "RelatedModelFromForeignKeyIsNotFromEngineException");
}
constructor(engineName, modelName, foreignKeyFieldName, foreignKeyFieldModelName, fieldName) {
super(`The related model '${modelName}' from the foreign key field '${foreignKeyFieldName}' of the model '${foreignKeyFieldModelName}' is not from the engine '${engineName}' that is being used. This is not a problem, but you need to make sure that the field '${fieldName}' it is relating to exists on the model '${modelName}' it is related to.`);
this.name = _RelatedModelFromForeignKeyIsNotFromEngineException.name;
}
};
var ModelMissingException = class _ModelMissingException extends Error {
static {
__name(this, "ModelMissingException");
}
constructor(modelName) {
super(`The model ${modelName} was not found in the engine.`);
this.name = _ModelMissingException.name;
}
};
var FieldFromModelMissingException = class _FieldFromModelMissingException extends Error {
static {
__name(this, "FieldFromModelMissingException");
}
constructor(modelName, fieldName) {
super(`The field ${fieldName} was not found in the model ${modelName}.`);
this.name = _FieldFromModelMissingException.name;
}
};
// src/models/manager.ts
var import_core9 = require("@palmares/core");
// src/databases.ts
var import_core8 = require("@palmares/core");
// src/logging.ts
var import_logging = require("@palmares/logging");
var databaseLogger = new import_logging.Logger({
domainName: "@palmares/databases"
}, {
MODELS_NOT_FOUND: {
category: "warn",
handler: /* @__PURE__ */ __name(({ domainName }) => `Looks like the domain ${domainName} did not define any models.
If that's not intended behavior, you should create the 'models.ts'/'models.js' file in the ${domainName} domain or add the 'getModels' to the domain class.`, "handler")
},
DATABASE_CLOSING: {
category: "info",
handler: /* @__PURE__ */ __name(({ databaseName }) => `Closing the '${databaseName}' database connection.`, "handler")
},
DATABASE_IS_NOT_CONNECTED: {
category: "info",
handler: /* @__PURE__ */ __name(({ databaseName }) => `Couldn't connect to the '${databaseName}' database.`, "handler")
},
FAILED_TO_GET_LAST_MIGRATION: {
category: "error",
handler: /* @__PURE__ */ __name(({ databaseName, reason, stack }) => `Failed to get the last migration for the '${databaseName}' database.
\x1B[1mReason:\x1B[0m ${reason}
\x1B[1mStack:\x1B[0m ${stack}`, "handler")
},
FAILED_TO_COMMIT_MIGRATION: {
category: "error",
handler: /* @__PURE__ */ __name(({ migrationName, databaseName, reason, stack }) => `Failed to get insert ran migration '${migrationName}' for the '${databaseName}' database.
\x1B[1mReason:\x1B[0m ${reason}
\x1B[1mStack:\x1B[0m ${stack}`, "handler")
},
MIGRATIONS_NOT_FOUND: {
category: "warn",
handler: /* @__PURE__ */ __name(({ domainName }) => `No migrations were found for the '${domainName}', if this is your first time running this command, you can safely ignore this message.
You can fully dismiss this message by setting 'DATABASES_DISMISS_NO_MIGRATIONS_LOG = true;' in 'settings.(ts/js)'`, "handler")
},
MIGRATIONS_FILE_TITLE: {
category: "info",
handler: /* @__PURE__ */ __name(({ title }) => `- \x1B[36m${title}`, "handler")
},
MIGRATIONS_FILE_DESCRIPTION: {
category: "info",
handler: /* @__PURE__ */ __name(({ database, lastMigrationName, lastDomainPath }) => `Generating migration on the \x1B[1m'${database}'\x1B[0m database` + (lastMigrationName !== "" && lastDomainPath !== "" ? ` that depends on the migration \x1B[1m'${lastMigrationName}'\x1B[0m that exists on \x1B[1m'${lastDomainPath}'\x1B[0m` : ""), "handler")
},
MIGRATIONS_NO_NEW_MIGRATIONS: {
category: "info",
handler: /* @__PURE__ */ __name(({ databaseName }) => `There are no migrations to run for '${databaseName}'. If you made changes to your models, please run\x1B[1m makemigrations\x1B[0m command first.`, "handler")
},
MIGRATIONS_RUNNING_FILE_NAME: {
category: "info",
handler: /* @__PURE__ */ __name(({ title }) => `Running migration: \x1B[36m${title}\x1B[0m`, "handler")
},
MIGRATION_RUNNING_IN_BATCH: {
category: "info",
handler: /* @__PURE__ */ __name(({ databaseName }) => `The engine that you are using for '${databaseName}' implements a batch migrations, this means that instead of running each migration file one by one, we will let the chosen engine handle the migration runner.`, "handler")
},
MIGRATIONS_ACTION_DESCRIPTION: {
category: "info",
handler: /* @__PURE__ */ __name(({ description }) => ` \u2022 ${description}`, "handler")
},
NO_CHANGES_MADE_FOR_MIGRATIONS: {
category: "info",
handler: /* @__PURE__ */ __name(() => `No changes were found in your models.`, "handler")
},
QUERY_NOT_PROPERLY_SET: {
category: "warn",
handler: /* @__PURE__ */ __name(({ modelName, invalidFields }) => {
const errorsByField = Array.from(invalidFields).map(([_, isValidObject]) => {
return `- ${isValidObject.reason}`;
}).join("\n");
return `The fields on the query to retrieve '${modelName}' data was not set properly and contain wrong or missing data
${errorsByField}`;
}, "handler")
},
CREATE_PALMARES_DB_APP: {
category: "info",
handler: /* @__PURE__ */ __name(({ name, template }) => `Creating Palmares database app '${name}' using the template '${template}'`, "handler")
},
DONE_CREATING_PALMARES_DB_APP: {
category: "info",
handler: /* @__PURE__ */ __name(({ name, template }) => `Done creating Palmares database app '${name}' using the template '${template}'
Next steps:
1. cd ${name}
2. Install the dependencies with your favorite package manager:
- $ pnpm i
- $ yarn
- $ npm i
- $ bun i
3. Create the migrations:
- $ pnpm run makemigrations
- $ yarn run makemigrations
- $ npm run makemigrations
- $ bun run makemigrations
4. Apply the migrations:
- $ pnpm run migrate
- $ yarn run migrate
- $ npm run migrate
- $ bun run migrate
5. Start the application:
- $ pnpm run dev
- $ yarn run dev
- $ npm run dev
- $ bun run dev`, "handler")
}
});
// src/migrations/index.ts
var import_core7 = require("@palmares/core");
// src/migrations/makemigrations/index.ts
var import_core6 = require("@palmares/core");
// src/migrations/makemigrations/asker.ts
var import_core = require("@palmares/core");
var Asker = class Asker2 {
static {
__name(this, "Asker");
}
async theNewAttributeCantHaveNullDoYouWishToContinue(modelName, fieldName) {
const question = `\x1B[0mIf the model \x1B[33m${modelName}\x1B[0m already have data, it can cause issues when migrating the new \x1B[36m${fieldName}\x1B[0m column because you didn't set a \x1B[33mdefaultValue\x1B[0m or \x1B[33mallowNull \x1B[0mis set to \x1B[33mfalse\x1B[0m.
You can safely ignore this message if you didn't add any data to the table.
Press any key to continue or 'CTRL+C' to stop and define the attributes yourself.
`;
const answer = await import_core.std.asker.ask(question);
if (answer.toLowerCase() === "n") return false;
else return true;
}
async didUserRename(modelOrFieldThatWasRenamed, renamedTo) {
const question = `
Did you rename '${modelOrFieldThatWasRenamed}' to '${renamedTo}'? [y/n]
`;
const answer = await import_core.std.asker.ask(question);
if ([
"y",
"n"
].includes(answer)) return answer === "y";
else return false;
}
async didUserRenameToOneOption(valueThatWasRenamed, renamedToOptions) {
const toOptions = renamedToOptions.map((renamedTo, index) => `${index + 1}. ${renamedTo}`);
const explanation = "\nPlease type the corresponding number or leave blank if you have not renamed";
const question = `
Did you rename '${valueThatWasRenamed}' to one of the following options?
${toOptions.join("\n")}
${explanation}
`;
const answer = await import_core.std.asker.ask(question);
if (answer === "") return null;
else {
try {
return renamedToOptions[parseInt(answer) - 1];
} catch {
return null;
}
}
}
};
var asker = new Asker();
// src/utils/constants.ts
var PACKAGE_NAME = "@palmares/databases";
// src/utils/hash.ts
function hashString(stringToHash) {
const p = 53;
const m = 1e9 + 9;
let powerOfP = 1;
let hashValue = 0;
for (let i = 0; i < stringToHash.length; i++) {
hashValue = (hashValue + (stringToHash[i].charCodeAt(0) - "a".charCodeAt(0) + 1) * powerOfP) % m;
powerOfP = powerOfP * p % m;
}
return hashValue.toString();
}
__name(hashString, "hashString");
// src/utils/index.ts
function getUniqueCustomImports(customImports, customImportsToAppendDataTo = []) {
for (const customImport of customImports || []) {
const doesNotExistYet = customImportsToAppendDataTo.find((alreadyExistingCustom) => alreadyExistingCustom.packageName === customImport.packageName && alreadyExistingCustom.value === customImport.value) === void 0;
if (doesNotExistYet) customImportsToAppendDataTo.push(customImport);
}
return customImportsToAppendDataTo;
}
__name(getUniqueCustomImports, "getUniqueCustomImports");
function generateUUID() {
let date2 = (/* @__PURE__ */ new Date()).getTime();
import("perf_hooks");
const performance = globalThis.performance;
let performanceDate = performance && performance.now && performance.now() * 1e3 || 0;
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (character) => {
let randomNumber = Math.random() * 16;
if (date2 > 0) {
randomNumber = (date2 + randomNumber) % 16 | 0;
date2 = Math.floor(date2 / 16);
} else {
randomNumber = (performanceDate + randomNumber) % 16 | 0;
performanceDate = Math.floor(performanceDate / 16);
}
return (character === "x" ? randomNumber : randomNumber & 3 | 8).toString(16);
});
}
__name(generateUUID, "generateUUID");
// src/migrations/actions/operation.ts
var Operation = class {
static {
__name(this, "Operation");
}
/**
* Function that will be used to construct and build the state of all of the models in the application so we
* can compare to the original ones.
*
* @param state - A state instance that holds all of the models of the application before.
* @param domainName - The name of the domain where this model was defined.
* @param domainPath - The path of the domain where this model exists so we can add the migration file there.
*/
async stateForwards(_state, _domainName, _domainPath) {
}
/**
* Method that runs when a migration is running on a migration file, when this happens we will call the exact
* function of the engine migrations.
*
* We also have the fromState (which will be state when the state) which will be the state of the models
* before running the migration and `toState` will be state AFTER running the migration
*/
async run(_migration, _engineInstance, _fromState, _toState, _returnOfInit) {
}
// eslint-disable-next-line ts/require-await
static async defaultToGenerate(domainName, domainPath, modelName, data) {
return {
operation: this,
domainName,
domainPath,
modelName,
order: 0,
dependsOn: [],
data
};
}
// eslint-disable-next-line ts/require-await
static async toString(_engine, indentation = 0, data) {
return {
asString: ""
};
}
// eslint-disable-next-line ts/require-await
static async defaultToString(indentation = 0, customAttributesOfAction = "") {
const ident = " ".repeat(indentation);
return `${ident}new actions.${this.name}(${customAttributesOfAction !== "" ? `
${customAttributesOfAction}
${ident}` : ""})`;
}
// eslint-disable-next-line ts/require-await
static async describe(data) {
return "";
}
};
// src/migrations/actions/fields.ts
var CreateField = class extends Operation {
static {
__name(this, "CreateField");
}
modelName;
fieldName;
fieldDefinition;
constructor(modelName, fieldName, fieldDefinition) {
super();
this.modelName = modelName;
this.fieldName = fieldName;
this.fieldDefinition = fieldDefinition;
}
async stateForwards(state, domainName, domainPath) {
const model2 = await state.get(this.modelName);
const modelConstructor = model2.constructor;
modelConstructor["__domainName"] = domainName;
modelConstructor["__domainPath"] = domainPath;
model2.fields[this.fieldName] = this.fieldDefinition;
await state.set(this.modelName, model2);
}
async run(migration, engineInstance, fromState, toState, returnOfInit) {
const toModel = toState[this.modelName];
const fromModel = fromState[this.modelName];
await engineInstance.migrations?.addField(engineInstance, toModel, fromModel, this.fieldName, migration, returnOfInit);
}
static async toGenerate(domainName, domainPath, modelName, data) {
return super.defaultToGenerate(domainName, domainPath, modelName, data);
}
static async toString(engine, indentation = 0, data) {
const ident = " ".repeat(indentation);
return {
asString: await super.defaultToString(indentation - 1, `${ident}"${data.modelName}",
${ident}"${data.data.fieldName}",
${await data.data.fieldDefinition["__toString"](engine)}`),
customImports: await data.data.fieldDefinition["__getCustomImports"]()
};
}
// eslint-disable-next-line ts/require-await
static async describe(data) {
return `Created the field '${data.data.fieldName}' on the '${data.modelName}' model`;
}
};
var ChangeField = class extends Operation {
static {
__name(this, "ChangeField");
}
modelName;
fieldName;
fieldDefinitionBefore;
fieldDefinitionAfter;
constructor(modelName, fieldName, fieldDefinitionBefore, fieldDefinitionAfter) {
super();
this.modelName = modelName;
this.fieldName = fieldName;
this.fieldDefinitionBefore = fieldDefinitionBefore;
this.fieldDefinitionAfter = fieldDefinitionAfter;
}
async stateForwards(state, domainName, domainPath) {
const model2 = await state.get(this.modelName);
const modelConstructor = model2.constructor;
modelConstructor["__domainName"] = domainName;
modelConstructor["__domainPath"] = domainPath;
model2.fields[this.fieldName] = this.fieldDefinitionAfter;
await state.set(this.modelName, model2);
}
async run(migration, engineInstance, fromState, toState, returnOfInit) {
const fromModel = fromState[this.modelName];
const toModel = toState[this.modelName];
await engineInstance.migrations?.changeField(engineInstance, toModel, fromModel, this.fieldDefinitionBefore, this.fieldDefinitionAfter, migration, returnOfInit);
}
static async toGenerate(domainName, domainPath, modelName, data) {
return super.defaultToGenerate(domainName, domainPath, modelName, data);
}
static async toString(engine, indentation = 0, data) {
const ident = " ".repeat(indentation);
return {
asString: await super.defaultToString(indentation - 1, `${ident}"${data.modelName}",
${ident}"${data.data.fieldName}",
${await data.data.fieldDefinitionBefore["__toString"](engine)},
${await data.data.fieldDefinitionAfter["__toString"](engine)}`),
customImports: (await data.data.fieldDefinitionBefore["__getCustomImports"]()).concat(await data.data.fieldDefinitionAfter["__getCustomImports"]())
};
}
// eslint-disable-next-line ts/require-await
static async describe(data) {
return `Changed the ${`attribute${data.data.changedAttributes.length > 1 ? "s" : ""} ${data.data.changedAttributes.map((attribute) => `'${attribute}'`).join(", ").replace(/,(?!.*,)/, " and")}`} of the '${data.data.fieldName}' field on the '${data.modelName}' model`;
}
};
var RenameField = class extends Operation {
static {
__name(this, "RenameField");
}
modelName;
fieldNameBefore;
fieldNameAfter;
fieldDefinition;
constructor(modelName, fieldNameBefore, fieldNameAfter, fieldDefinition) {
super();
this.modelName = modelName;
this.fieldNameBefore = fieldNameBefore;
this.fieldNameAfter = fieldNameAfter;
this.fieldDefinition = fieldDefinition;
}
async stateForwards(state, domainName, domainPath) {
const model2 = await state.get(this.modelName);
const modelConstructor = model2.constructor;
modelConstructor["__domainName"] = domainName;
modelConstructor["__domainPath"] = domainPath;
const hasNamesReallyChanged = this.fieldNameAfter !== this.fieldNameBefore;
if (hasNamesReallyChanged) {
model2.fields[this.fieldNameAfter] = model2.fields[this.fieldNameBefore];
delete model2.fields[this.fieldNameBefore];
}
model2.fields[this.fieldNameAfter] = this.fieldDefinition;
await state.set(this.modelName, model2);
}
async run(migration, engineInstance, fromState, toState, returnOfInit) {
const fromModel = fromState[this.modelName];
const toModel = toState[this.modelName];
await engineInstance.migrations?.renameField(engineInstance, toModel, fromModel, this.fieldNameBefore, this.fieldNameAfter, migration, returnOfInit);
}
static async toGenerate(domainName, domainPath, modelName, data) {
return super.defaultToGenerate(domainName, domainPath, modelName, data);
}
static async toString(engine, indentation = 0, data) {
const ident = " ".repeat(indentation);
return {
asString: await super.defaultToString(indentation - 1, `${ident}"${data.modelName}",
${ident}"${data.data.fieldNameBefore}",
${ident}"${data.data.fieldNameAfter}",
${await data.data.fieldDefinition["__toString"](engine)}`),
customImports: await data.data.fieldDefinition["__getCustomImports"]()
};
}
// eslint-disable-next-line ts/require-await
static async describe(data) {
return `Renamed the field '${data.data.fieldNameBefore}' to '${data.data.fieldNameAfter}' on the '${data.modelName}' model`;
}
};
var DeleteField = class extends Operation {
static {
__name(this, "DeleteField");
}
modelName;
fieldName;
constructor(modelName, fieldName) {
super();
this.modelName = modelName;
this.fieldName = fieldName;
}
async stateForwards(state, domainName, domainPath) {
const model2 = await state.get(this.modelName);
const modelConstructor = model2.constructor;
modelConstructor["__domainName"] = domainName;
modelConstructor["__domainPath"] = domainPath;
delete model2.fields[this.fieldName];
await state.set(this.modelName, model2);
}
async run(migration, engineInstance, fromState, toState, returnOfInit) {
const fromModel = fromState[this.modelName];
const toModel = toState[this.modelName];
await engineInstance.migrations?.removeField(engineInstance, toModel, fromModel, this.fieldName, migration, returnOfInit);
}
static async toGenerate(domainName, domainPath, modelName, data) {
return super.defaultToGenerate(domainName, domainPath, modelName, data);
}
static async toString(_engine, indentation = 0, data) {
const ident = " ".repeat(indentation);
return {
asString: await super.defaultToString(indentation - 1, `${ident}"${data.modelName}",
${ident}"${data.data.fieldName}"`)
};
}
// eslint-disable-next-line ts/require-await
static async describe(data) {
return `Removed the field '${data.data.fieldName}' on the '${data.modelName}' model`;
}
};
// src/migrations/actions/models.ts
var CreateModel = class extends Operation {
static {
__name(this, "CreateModel");
}
modelName;
fields;
options;
constructor(modelName, fields, options = {}) {
super();
this.modelName = modelName;
this.fields = fields;
this.options = options;
}
async stateForwards(state, domainName, domainPath) {
const model2 = await state.get(this.modelName);
const modelConstructor = model2.constructor;
modelConstructor["__domainName"] = domainName;
modelConstructor["__domainPath"] = domainPath;
modelConstructor["__lazyFields"] = this.fields;
modelConstructor["__lazyOptions"] = this.options;
state.set(this.modelName, model2);
}
async run(migration, engineInstance, _, toState, returnOfInit) {
const toModel = toState[this.modelName];
await engineInstance.migrations?.addModel(engineInstance, toModel, migration, returnOfInit);
}
static async toGenerate(domainName, domainPath, modelName, data) {
return await super.defaultToGenerate(domainName, domainPath, modelName, data);
}
static async toString(engine, indentation = 0, data) {
const ident = " ".repeat(indentation);
const { asString: fieldsAsString, customImports } = await BaseModel["__fieldsToString"](engine, data.data.fields, indentation);
return {
asString: await super.defaultToString(indentation - 1, `${ident}"${data.modelName}",
${fieldsAsString},
${await BaseModel["__optionsToString"](engine, indentation, data.data.options)}`),
customImports
};
}
// eslint-disable-next-line ts/require-await
static async describe(data) {
return `Create the model '${data.modelName}'`;
}
};
var DeleteModel = class extends Operation {
static {
__name(this, "DeleteModel");
}
modelName;
constructor(modelName) {
super();
this.modelName = modelName;
}
async stateForwards(state, _domainName, _domainPath) {
await state.remove(this.modelName);
}
async run(migration, engineInstance, fromState, _toState, returnOfInit) {
const fromModel = fromState[this.modelName];
await engineInstance.migrations?.removeModel(engineInstance, fromModel, migration, returnOfInit);
}
static async toGenerate(domainName, domainPath, modelName) {
return super.defaultToGenerate(domainName, domainPath, modelName, null);
}
static async toString(_engine, indentation = 0, data) {
const ident = " ".repeat(indentation);
return {
asString: await super.defaultToString(indentation - 1, `${ident}"${data.modelName}"`)
};
}
// eslint-disable-next-line ts/require-await
static async describe(data) {
return `Remove the model '${data.modelName}'`;
}
};
var ChangeModel = class extends Operation {
static {
__name(this, "ChangeModel");
}
modelName;
optionsBefore;
optionsAfter;
constructor(modelName, optionsBefore, optionsAfter) {
super();
this.modelName = modelName;
this.optionsBefore = optionsBefore;
this.optionsAfter = optionsAfter;
}
async stateForwards(state, domainName, domainPath) {
const model2 = await state.get(this.modelName);
const modelConstructor = model2.constructor;
modelConstructor["__domainName"] = domainName;
modelConstructor["__domainPath"] = domainPath;
model2.options = this.optionsAfter;
}
async run(migration, engineInstance, fromState, toState, returnOfInit) {
const toModel = toState[this.modelName];
const fromModel = fromState[this.modelName];
await engineInstance.migrations?.changeModel(engineInstance, toModel, fromModel, migration, returnOfInit);
}
static async toGenerate(domainName, domainPath, modelName, data) {
return super.defaultToGenerate(domainName, domainPath, modelName, data);
}
static async toString(engine, indentation = 0, data) {
const ident = " ".repeat(indentation);
return {
asString: await super.defaultToString(indentation - 1, `${ident}"${data.modelName}",
${await BaseModel["__optionsToString"](engine, indentation, data.data.optionsBefore)},
${await BaseModel["__optionsToString"](engine, indentation, data.data.optionsAfter)}`)
};
}
// eslint-disable-next-line ts/require-await
static async describe(data) {
return `Changed one or more options of the model '${data.modelName}' options`;
}
};
var RenameModel = class extends Operation {
static {
__name(this, "RenameModel");
}
oldModelName;
newModelName;
constructor(oldModelName, newModelName) {
super();
this.oldModelName = oldModelName;
this.newModelName = newModelName;
}
async stateForwards(state, domainName, domainPath) {
const model2 = await state.get(this.oldModelName);
const modelConstructor = model2.constructor;
modelConstructor.__cachedName = this.newModelName;
modelConstructor["__domainName"] = domainName;
modelConstructor["__domainPath"] = domainPath;
await Promise.all([
state.set(this.newModelName, model2),
state.remove(this.oldModelName)
]);
}
static async toGenerate(domainName, domainPath, modelName, data) {
return super.defaultToGenerate(domainName, domainPath, modelName, data);
}
static async toString(_engine, indentation = 0, data) {
const ident = " ".repeat(indentation);
return {
asString: await super.defaultToString(indentation - 1, `${ident}"${data.data.modelNameBefore}",
${ident}"${data.data.modelNameBefore}"`)
};
}
// eslint-disable-next-line ts/require-await
static async describe(data) {
return `Renamed the model '${data.data.modelNameBefore}' to '${data.data.modelNameAfter}'`;
}
};
// src/migrations/exceptions.ts
var DefaultDuplicateFunctionNotCalledOnEngine = class _DefaultDuplicateFunctionNotCalledOnEngine extends Error {
static {
__name(this, "DefaultDuplicateFunctionNotCalledOnEngine");
}
constructor() {
super("Default duplicate function was not called by engine.");
this.name = _DefaultDuplicateFunctionNotCalledOnEngine.name;
}
};
// src/engine/utils.ts
function defaultEngineDuplicate(engine, wasCalled = {
value: false
}) {
return async () => {
const engineConstructor = engine.constructor;
const [argsForNewInstance, newInstanceCallback] = await engineConstructor.new(engine.__argumentsUsed);
const newInstance = newInstanceCallback();
newInstance.__argumentsUsed = argsForNewInstance;
newInstance.initializedModels = {
...engine.initializedModels
};
newInstance.__modelsOfEngine = {
...engine.__modelsOfEngine
};
newInstance.__modelsFilteredOutOfEngine = {
...engine.__modelsFilteredOutOfEngine
};
newInstance.__indirectlyRelatedModels = {
...engine.__indirectlyRelatedModels
};
wasCalled.value = true;
return newInstance;
};
}
__name(defaultEngineDuplicate, "defaultEngineDuplicate");
// src/models/utils.ts
var import_core5 = require("@palmares/core");
// src/models/fields/field.ts
var import_core2 = require("@palmares/core");
// src/models/fields/utils.ts
async function defaultToStringCallback(engine, field, _, customParams = void 0) {
let customImports = [];
let stringifiedCustomAttributes = "{}";
if (engine.fields.fieldToString) {
const stringifiedField = engine.fields.fieldToString(field["__customAttributes"]);
customImports = stringifiedField.imports;
stringifiedCustomAttributes = stringifiedField.result;
}
const stringfieldDefaultValue = field["__defaultValue"] === void 0 ? void 0 : JSON.stringify(field["__defaultValue"]);
return {
stringfied: `models.fields.${field["__typeName"]}.new(${customParams?.constructorParams ? `${customParams.constructorParams}` : ""})${typeof field["__primaryKey"] === "boolean" ? `.primaryKey(${field["__primaryKey"]})` : ""}${stringfieldDefaultValue !== void 0 ? `.default(${typeof field["__defaultValue"] === "string" ? `"${stringfieldDefaultValue}"` : stringfieldDefaultValue})` : ""}${typeof field["__allowNull"] === "boolean" ? `.allowNull(${field["__allowNull"]})` : ""}${typeof field["__unique"] === "boolean" ? `.unique(${field["__unique"]})` : ""}${typeof field["__dbIndex"] === "boolean" ? `.dbIndex(${field["__dbIndex"]})` : ""}${typeof field["__databaseName"] === "string" ? `.databaseName("${field["__databaseName"]}")` : ""}${typeof field["__underscored"] === "boolean" ? `.underscored(${field["__underscored"]})` : ""}${typeof stringifiedCustomAttributes === "string" ? `.setCustomAttributes(${stringifiedCustomAttributes})` : ""}${customParams?.builderParams ? `${customParams.builderParams}` : ""}`,
customImports
};
}
__name(defaultToStringCallback, "defaultToStringCallback");
function defaultCompareCallback(engine, existingField, newField, _) {
let isCustomAttributesEqual = true;
if (engine.fields.compare) {
const areCustomAttributesEqual = engine.fields.compare(existingField["__customAttributes"], newField["__customAttributes"]);
isCustomAttributesEqual = areCustomAttributesEqual;
}
const isTypeNameEqual = existingField["__typeName"] === newField["__typeName"];
const isAllowNullEqual = existingField["__allowNull"] === newField["__allowNull"];
const isPrimaryKeyEqual = existingField["__primaryKey"] === newField["__primaryKey"];
const isDefaultValueEqual = existingField["__defaultValue"] === newField["__defaultValue"];
const isUniqueEqual = existingField["__unique"] === newField["__unique"];
const isDbIndexEqual = existingField["__dbIndex"] === newField["__dbIndex"];
const isDatabaseNameEqual = existingField["__databaseName"] === newField["__databaseName"];
const isUnderscoredEqual = existingField["__underscored"] === newField["__underscored"];
const changedAttributes = [
!isTypeNameEqual && "typeName",
!isAllowNullEqual && "allowNull",
!isCustomAttributesEqual && "customAttributes",
!isPrimaryKeyEqual && "primaryKey",
!isDefaultValueEqual && "defaultValue",
!isUniqueEqual && "unique",
!isDbIndexEqual && "dbIndex",
!isDatabaseNameEqual && "databaseName",
!isUnderscoredEqual && "underscored"
].filter((attr) => typeof attr === "string");
return [
changedAttributes.length === 0,
changedAttributes
];
}
__name(defaultCompareCallback, "defaultCompareCallback");
function defaultOptionsCallback(setFieldValue, oldField, _) {
setFieldValue("__allowNull", "allowNull", oldField["__allowNull"]);
setFieldValue("__customAttributes", "customAttributes", oldField["__customAttributes"]);
setFieldValue("__defaultValue", "defaultValue", oldField["__defaultValue"]);
setFieldValue("__dbIndex", "dbIndex", oldField["__dbIndex"]);
setFieldValue("__databaseName", "databaseName", oldField["__databaseName"]);
setFieldValue("__primaryKey", "primaryKey", oldField["__primaryKey"]);
setFieldValue("__underscored", "underscored", oldField["__underscored"]);
setFieldValue("__unique", "unique", oldField["__unique"]);
setFieldValue("__isAuto", "isAuto", oldField["__isAuto"]);
setFieldValue("__fieldName", "fieldName", oldField["__fieldName"]);
setFieldValue("__model", void 0, oldField["__model"]);
}
__name(defaultOptionsCallback, "defaultOptionsCallback");
function defaultNewInstanceArgumentsCallback(_field, _defaultNewInstanceArgumentsCallback) {
return [];
}
__name(defaultNewInstanceArgumentsCallback, "defaultNewInstanceArgumentsCallback");
function getRelatedToAsString(field) {
const relatedTo = field["__relatedTo"];
const relatedToAsString = field["__relatedToAsString"];
if (typeof relatedToAsString !== "string") {
if (typeof relatedTo === "function" && relatedTo["$$type"] !== "$PModel") field["__relatedToAsString"] = relatedTo()["__getName"]();
else if (typeof relatedTo === "string") field["__relatedToAsString"] = relatedTo;
else field["__relatedToAsString"] = relatedTo["__getName"]();
}
}
__name(getRelatedToAsString, "getRelatedToAsString");
function defaultGetArgumentsCallback(field, _) {
return {
$field: field,
$model: field["__model"],
typeName: field["__typeName"],
fieldName: field["__fieldName"],
modelName: field["__model"]?.["__getName"]?.() || "",
isAuto: field["__isAuto"],
primaryKey: field["__primaryKey"],
defaultValue: field["__defaultValue"],
allowNull: field["__allowNull"],
unique: field["__unique"],
dbIndex: field["__dbIndex"],
databaseName: field["__databaseName"],
underscored: field["__underscored"],
customAttributes: field["__customAttributes"]
};
}
__name(defaultGetArgumentsCallback, "defaultGetArgumentsCallback");
// src/models/fields/field.ts
var Field = class {
static {
__name(this, "Field");
}
$$type = "$PField";
__typeName = "Field";
__isAuto = false;
__hasDefaultValue = false;
__primaryKey = false;
__defaultValue = void 0;
__allowNull = false;
__unique = false;
__dbIndex = false;
__databaseName = void 0;
__underscored = true;
__customAttributes;
__allowedQueryOperations = /* @__PURE__ */ new Set([
"eq",
"is",
"greaterThan",
"lessThan",
"like",
"between",
"and",
"or"
]);
// eslint-disable-next-line ts/require-await
__toStringCallback = defaultToStringCallback;
// eslint-disable-next-line ts/require-await
__compareCallback = defaultCompareCallback;
__optionsCallback = defaultOptionsCallback;
__newInstanceCallback = defaultNewInstanceArgumentsCallback;
__customImports = [];
__getArgumentsCallback = defaultGetArgumentsCallback;
__inputParsers = /* @__PURE__ */ new Map();
__outputParsers = /* @__PURE__ */ new Map();
__model;
__fieldName;
constructor(..._args) {
}
/**
* Supposed to be used by library maintainers.
*
* When you custom create a field, you might want to take advantage of the builder pattern we already support.
* This let's you create functions that can be chained together to create a new field. It should be used
* alongside the `_setPartialAttributes` method like
*
* @example
* ```ts
* const customBigInt = TextField.overrideType<
* { create: bigint; read: bigint; update: bigint },
* {
* customAttributes: { name: string };
* unique: boolean;
* auto: boolean;
* allowNull: true;
* dbIndex: boolean;
* isPrimaryKey: boolean;
* defaultValue: any;
* typeName: string;
* engineInstance: DatabaseAdapter;
* }
* >({
* typeName: 'CustomBigInt'
* });
*
* const customBuilder = <TParams extends { name: string }>(params: TParams) => {
* const field = customBigInt.new(params);
* return field._setNewBuilderMethods({
* test: <TTest extends { age: number }>(param: TTest) =>
* // This will union the type `string` with what already exists in the field 'create' type
* field._setPartialAttributes<{ create: string }, { create: 'union' }>(param)
* });
* };
*
* // Then your user can use it like:
*
* const field = customBuilder({ name: 'test' }).test({ age: 2 });
* ```
*
* **Important**: `customBuilder` will be used by the end user and you are responsible for documenting it.
*/
_setNewBuilderMethods(functions) {
if (functions === void 0) return this;
const propertiesOfBase = Object.getOwnPropertyNames(Object.getPrototypeOf(functions));
for (const key of propertiesOfBase) {
if (key === "constructor") continue;
this[key] = functions[key].bind(this);
}
return this;
}
/**
* FOR LIBRARY MAINTAINERS ONLY
*
* Focused for library maintainers that want to support a custom field type not supported by palmares.
* This let's them partially update the custom attributes of the field. By default setCustomAttributes
* will override the custom attributes entirely.
*/
_setPartialAttributes() {
return (partialCustomAttributes) => {
if (partialCustomAttributes !== void 0) {
if (this.__customAttributes === void 0) this.__customAttributes = {};
this.__customAttributes = {
...this.__customAttributes,
...partialCustomAttributes
};
}
return this;
};
}
setCustomAttributes(customAttributes) {
this.__customAttributes = customAttributes;
return this;
}
unique(isUnique) {
if (typeof isUnique !== "boolean") isUnique = true;
this.__unique = isUnique;
return this;
}
allowNull(isNull) {
if (typeof isNull !== "boolean") isNull = true;
this.__allowNull = isNull;
return this;
}
/**
* This method is used to create an index on the database for this field.
*/
dbIndex(dbIndex) {
if (typeof dbIndex !== "boolean") dbIndex = true;
this.__dbIndex = dbIndex;
return this;
}
underscored(isUnderscored) {
if (typeof isUnderscored !== "boolean") isUnderscored = true;
this.__underscored = isUnderscored;
return this;
}
primaryKey(isPrimaryKey) {
if (typeof isPrimaryKey !== "boolean") isPrimaryKey = true;
this.__primaryKey = isPrimaryKey;
return this;
}
auto(isAuto) {
if (typeof isAuto !== "boolean") isAuto = true;
this.__isAuto = isAuto;
return this;
}
default(defaultValue) {
this.__defaultValue = defaultValue;
return this;
}
databaseName(databaseName) {
this.__databaseName = databaseName;
return this;
}
/**
* This method can be used to override the type of a field. This is useful for library
* maintainers that want to support the field type but the default type provided by palmares
* is not the one that the user want to use.
*
* @example
* ```ts
* const MyCustomDatabaseAutoField = AutoField.overrideType<{ input: string; output: string }>();
*
* // then the user can use as normal:
*
* const autoField = MyCustomDatabaseAutoField.new();
*
* // now the type inferred for the field will be a string instead of a number.
* ```
*
* ### Note
*
* Your library should provide documentation of the fields that are supported.
*/
static _overrideType(args) {
this.new = (params) => {
const newInstance = new this(params);
newInstance.__customImports = args.customImports || [];
newInstance.__toStringCallback = args.toStringCallback || defaultToStringCallback;
newInstance.__compareCallback = args.compareCallback || defaultCompareCallback;
newInstance.__optionsCallback = args.optionsCallback || defaultOptionsCallback;
newInstance.__newInstanceCallback = args.newInstanceCallback || defaultNewInstanceArgumentsCallback;
newInstance.__typeName = args.typeName;
newInstance.__allowedQueryOperations = /* @__PURE__ */ new Set([
"is",
"eq",
"greaterThan",
"lessThan",
"like",
"between"
]);
newInstance["__customAttributes"] = params;
return newInstance;
};
return this;
}
/**
* This method enables the framework to automatically import the files when generating the migrations.
*
* This is generally useful for custom field types.
*
* @return - Returns a list of packages that we want to import in the migration file.
*/
// eslint-disable-next-line ts/require-await
async __getCustomImports() {
return this["__customImports"];
}
__init(fieldName, model2) {
const isAlreadyInitialized = this.__model !== void 0 && typeof this.__fieldName === "string";
if (isAlreadyInitialized) return;
const isUnderscored = (this.__underscored || model2.__cachedOptions?.underscored) === true;
this.__fieldName = fieldName;
this.__model = model2;
if (this.__primaryKey) model2["__primaryKeys"].push(this.__fieldName);
if (isUnderscored) this.__databaseName = import_core2.utils.camelCaseToHyphenOrSnakeCase(this.__fieldName);
else this.__databaseName = this.__fieldName;
}
/**
* Gets all of the arguments to pass to the field during migration, translation, etc.
*
* Everything in the field is protected, this way end users don't access the internal implementation.
*/
__getArguments() {
return this.__getArgumentsCallback(this, defaultGetArgumentsCallback);
}
// eslint-disable-next-line ts/require-await
async __toString(engine) {
return this.__toStringCallback(engine, this, defaultToStringCallback, void 0);
}
/**
* Used for comparing one field with the other so we are able to tell if they are different or not.
*
* This is obligatory to add if you create any custom fields.
*
* For custom fields you will call this super method before continuing, we first check if they are the same type.
*
* @param field - The field to compare to.
*
* @return - Returns true if the fields are equal and false otherwise
*/
// eslint-disable-next-line ts/require-await
__compare(engine, field) {
return this.__compareCallback(engine, this, field, defaultCompareCallback);
}
/**
* Used for cloning the field to a new field.
*
* @param oldField - The field to clone. If not provided it will use the current field.
*
* @returns - Returns the cloned field.
*/
__clone(oldField, args) {
const argumentsToPass = oldField.__newInstanceCallback(oldField, defaultNewInstanceArgumentsCallback);
const overridenArguments = args?.newInstanceOverrideCallback?.(argumentsToPass) || argumentsToPass;
const newInstanceOfField = oldField.constructor.new(...Array.isArray(overridenArguments) ? overridenArguments : []);
oldField.__optionsCallback((hiddenAttributeName, getAttributesAttributeName, value) => {
let actualValueToSet = value;
if (getAttributesAttributeName && args?.optionsOverrideCallback?.[getAttributesAttributeName] !== void 0) actualValueToSet = (args?.optionsOverrideCallback)[getAttributesAttributeName](value);
newInstanceOfField[hiddenAttributeName] = actualValueToSet;
}, oldField, defaultOptionsCallback);
return newInstanceOfField;
}
/**
* You should not use this method directly, it is used internally by the framework.
*
* This method is used to create a new instance of the field with the same options as the old field.
*/
static new(..._args) {
return new this(..._args);
}
};
// src/models/fields/date.ts
var DateField = class extends Field {
static {
__name(this, "DateField");
}
$$type = "$PDateField";
__typeName = "DateField";
__allowedQueryOperations = /* @__PURE__ */ new Set([
"eq",
"is",
"greaterThan",
"lessThan",
"between",
"and",
"or"
]);
__autoNow = false;
__autoNowAdd = false;
__inputParsers = /* @__PURE__ */ new Map();
__outputParsers = /* @__PURE__ */ new Map();
__compareCallback = /* @__PURE__ */ __name((engine, oldField, newField, defaultCompareCallback2) => {
const oldFieldAsTextField = oldField;
const newFieldAsTextField = newField;
const isAutoNowEqual = oldFieldAsTextField["__autoNow"] === newFieldAsTextField["__autoNow"];
const isAutoNowAddEqual = oldFieldAsTextField["__autoNowAdd"] === newFieldAsTextField["__autoNowAdd"];
const [isEqual, changedAttributes] = defaultCompareCallback2(engine, oldField, newField, defaultCompareCallback2);
if (!isAutoNowEqual) changedAttributes.push("autoNow");
if (!isAutoNowAddEqual) changedAttributes.push("autoNowAdd");
return [
isAutoNowAddEqual && isAutoNowEqual && isEqual,
changedAttributes
];
}, "__compareCallback");
__optionsCallback = /* @__PURE__ */ __name((setFieldValue, oldField, defaultOptionsCallback2) => {
const oldFieldAsTextField = oldField;
defaultOptionsCallback2(setFieldValue, oldField, defaultOptionsCallback2);
setFieldValue("__autoNow", "autoNow", oldFieldAsTextField["__autoNow"]);
setFieldValue("__autoNowAdd", "autoNowAdd", oldFieldAsTextField["__autoNowAdd"]);
}, "__optionsCallback");
__getArgumentsCallback = /* @__PURE__ */ __name((field, defaultCallback) => {
const fieldAsDateField = field;
const autoNow = fieldAsDateField["__autoNow"];
const autoNowAdd = fieldAsDateField["__autoNowAdd"];
return {
...defaultCallback(field, defaultCallback),
autoNow,
autoNowAdd
};
}, "__getArgumentsCallback");
/**
* This is used internally by the engine to convert the field to string.
* You can override this if you want to extend the ForeignKeyField class.
*/
__toStringCallback = /* @__PURE__ */ __name(async (engine, field, defaultToStringCallback2, _customParams = void 0) => {
const fieldAsDateField = field;
return await defaultToStringCallback2(engine, field, defaultToStringCallback2, {
builderParams: `${typeof fieldAsDateField["__autoNow"] === "boolean" ? `.autoNow(${fieldAsDateField["__autoNow"]})` : ""}${typeof fieldAsDateField["__autoNowAdd"] === "boolean" ? `.autoNowAdd(${fieldAsDateField["__autoNowAdd"]})` : ""}`
});
}, "__toStringCallback");
/**
* Supposed to be used by library maintainers.
*
* When you custom create a field, you might want to take advantage of the builder pattern we already support.
* This let's you create functions that can be chained together to create a new field. It should be used
* alongside the `_setPartialAttributes` method like
*
* @example
* ```ts
* const customBigInt = DateField.overrideType<
* { create: bigint; read: bigint; update: bigint },
* {
* customAttributes: { name: string };
* unique: boolean;
* auto: boolean;
* allowNull: true;
* dbIndex: boolean;
* isPrimaryKey: boolean;
* defaultValue: any;
* typeName: string;
* engineInstance: DatabaseAdapter;
* }
* >({
* typeName: 'CustomBigInt'
* });
*
* const customBuilder = <TParams extends { name: string }>(params: TParams) => {
* const field = customBigInt.new(params);
* return field._setNewBuilderMethods({
* test: <TTest extends { age: number }>(param: TTest) =>
* // This will union the type `string` with what already exists in the field 'create' type
* field._setPartialAttributes<{ create: string }, { create: 'union' }>(param)
* });
* };
*
* // Then your user can use it like:
*
* const field = customBuilder({ name: 'test' }).test({ age: 2 });
* ```
*
* **Important**: `customBuilder` will be used by the end user and you are responsible