UNPKG

@palmares/databases

Version:

Add support for working with databases with palmares framework

1,254 lines (1,214 loc) 298 kB
"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/standalone.ts var standalone_exports = {}; __export(standalone_exports, { setDatabaseConfig: () => setDatabaseConfig }); module.exports = __toCommonJS(standalone_exports); var import_core10 = require("@palmares/core"); // src/settings.ts function defaultSettings(settings) { if (settings.databases === void 0) settings.databases = {}; if (typeof settings.dismissNoMigrationsLog !== "boolean") settings.dismissNoMigrationsLog = false; return settings; } __name(defaultSettings, "defaultSettings"); // src/commands/make-migrations.ts async function makeMigrations(databases, { settings, domains, commandLineArgs }) { const databaseSettings = defaultSettings(settings); const databaseDomains = domains; await databases.makeMigrations(databaseSettings, databaseDomains, commandLineArgs.keywordArgs); await databases.close(); process.exit(0); } __name(makeMigrations, "makeMigrations"); // src/commands/migrate.ts async function migrate(databases, { settings, domains }) { const databaseSettings = defaultSettings(settings); const databaseDomains = domains; await databases.migrate(databaseSettings, databaseDomains); await databases.close(); process.exit(0); } __name(migrate, "migrate"); // src/defaults/models.ts var models_exports = {}; __export(models_exports, { PalmaresMigrations: () => PalmaresMigrations }); // src/models/model.ts var import_core9 = 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_core8 = require("@palmares/core"); // src/databases.ts var import_core7 = 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_core6 = require("@palmares/core"); // src/migrations/makemigrations/index.ts var import_core5 = 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_core4 = 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 }, *