"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/index.ts var src_exports = {}; __export(src_exports, { AdapterAutoFieldParser: () => AdapterAutoFieldParser, AdapterBigAutoFieldParser: () => AdapterBigAutoFieldParser, AdapterBigIntegerFieldParser: () => AdapterBigIntegerFieldParser, AdapterBooleanFieldParser: () => AdapterBooleanFieldParser, AdapterCharFieldParser: () => AdapterCharFieldParser, AdapterDateFieldParser: () => AdapterDateFieldParser, AdapterDecimalFieldParser: () => AdapterDecimalFieldParser, AdapterEnumFieldParser: () => AdapterEnumFieldParser, AdapterFieldParser: () => AdapterFieldParser, AdapterFields: () => AdapterFields, AdapterForeignKeyFieldParser: () => AdapterForeignKeyFieldParser, AdapterGetQuery: () => AdapterGetQuery, AdapterIntegerFieldParser: () => AdapterIntegerFieldParser, AdapterMigrations: () => AdapterMigrations, AdapterModels: () => AdapterModels, AdapterOrderingQuery: () => AdapterOrderingQuery, AdapterQuery: () => AdapterQuery, AdapterRemoveQuery: () => AdapterRemoveQuery, AdapterSearchQuery: () => AdapterSearchQuery, AdapterSetQuery: () => AdapterSetQuery, AdapterTextFieldParser: () => AdapterTextFieldParser, AdapterUuidFieldParser: () => AdapterUuidFieldParser, AutoField: () => AutoField, BigAutoField: () => BigAutoField, BigIntegerField: () => BigIntegerField, BooleanField: () => BooleanField, CharField: () => CharField, DatabaseAdapter: () => DatabaseAdapter, Databases: () => Databases, DatabasesDomain: () => databasesDomain, DateField: () => DateField, DecimalField: () => DecimalField, EnumField: () => EnumField, Field: () => Field, ForeignKeyField: () => ForeignKeyField, GetQuerySet: () => GetQuerySet, IntegerField: () => IntegerField, InternalModelClass_DoNotUse: () => BaseModel, Manager: () => Manager, Migration: () => Migration, Model: () => model, ModelBaseClass: () => Model, ON_DELETE: () => ON_DELETE, QuerySet: () => QuerySet, RemoveQuerySet: () => RemoveQuerySet, SetQuerySet: () => SetQuerySet, TextField: () => TextField, UuidField: () => UuidField, actions: () => actions_exports, adapterAutoFieldParser: () => adapterAutoFieldParser, adapterBigAutoFieldParser: () => adapterBigAutoFieldParser, adapterBigIntegerFieldParser: () => adapterBigIntegerFieldParser, adapterBooleanFieldParser: () => adapterBooleanFieldParser, adapterCharFieldParser: () => adapterCharFieldParser, adapterDateFieldParser: () => adapterDateFieldParser, adapterDecimalFieldParser: () => adapterDecimalFieldParser, adapterEnumFieldParser: () => adapterEnumFieldParser, adapterFieldParser: () => adapterFieldParser, adapterFields: () => adapterFields, adapterForeignKeyFieldParser: () => adapterForeignKeyFieldParser, adapterGetQuery: () => adapterGetQuery, adapterIntegerFieldParser: () => adapterIntegerFieldParser, adapterMigrations: () => adapterMigrations, adapterModels: () => adapterModels, adapterOrderingQuery: () => adapterOrderingQuery, adapterQuery: () => adapterQuery, adapterRemoveQuery: () => adapterRemoveQuery, adapterSearchQuery: () => adapterSearchQuery, adapterSetQuery: () => adapterSetQuery, adapterTextFieldParser: () => adapterTextFieldParser, adapterUuidFieldParser: () => adapterUuidFieldParser, auto: () => auto, bigAuto: () => bigAuto, bigInt: () => bigInt, bool: () => bool, char: () => char, choice: () => choice, databaseAdapter: () => databaseAdapter, databaseDomainModifier: () => databaseDomainModifier, date: () => date, decimal: () => decimal, default: () => src_default, define: () => initialize, fields: () => fields_exports, foreignKey: () => foreignKey, generateUUID: () => generateUUID, getDatabasesWithDefaultAdapter: () => getDatabasesWithDefaultAdapter, int: () => int, models: () => models, queryset: () => queryset, setDatabaseConfig: () => setDatabaseConfig, text: () => text, uuid: () => uuid }); module.exports = __toCommonJS(src_exports); // src/domain.ts var import_core9 = 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/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/index.ts var actions_exports = {}; __export(actions_exports, { ChangeField: () => ChangeField, ChangeModel: () => ChangeModel, CreateField: () => CreateField, CreateModel: () => CreateModel, DeleteField: () => DeleteField, DeleteModel: () => DeleteModel, Operation: () => Operation, RenameField: () => RenameField, RenameModel: () => RenameModel, RunJs: () => RunJs }); // 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/models/model.ts var import_core5 = 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_core4 = require("@palmares/core"); // src/queries/exceptions.ts var RelationNameIsNotPartOfModelException = class _RelationNameIsNotPartOfModelException extends Error { static { __name(this, "RelationNameIsNotPartOfModelException"); } constructor(modelName, relationName) { super(`The relation name '${relationName}' is not part of the model '${modelName}'.`); this.name = _RelationNameIsNotPartOfModelException.name; } }; // src/queries/search.ts async function parseSearchField(engine, key, fieldData, inputFieldParser, translatedModelInstance, result) { if (typeof fieldData === "object" && fieldData !== null) { if (typeof fieldData.like === "object") { if (fieldData.like?.ignoreCase) { await engine.query.search.parseSearchFieldValue("like", key, translatedModelInstance, await inputFieldParser(fieldData.like?.ignoreCase), result, { ignoreCase: true }); } else if ("not" in fieldData.like) { if (typeof fieldData.like.not === "object") { await engine.query.search.parseSearchFieldValue("like", key, translatedModelInstance, await inputFieldParser(fieldData.like.not.ignoreCase), result, { isNot: true, ignoreCase: true }); } else { await engine.query.search.parseSearchFieldValue("like", key, translatedModelInstance, await inputFieldParser(fieldData.like.not), result, { isNot: true }); } } } if (fieldData.like !== void 0) { await engine.query.search.parseSearchFieldValue("like", key, translatedModelInstance, await inputFieldParser(fieldData.like), result); } if (fieldData.and !== void 0) { const isArrayAndAtLeastOneElement = Array.isArray(fieldData.and) && fieldData.and.length > 1; if (isArrayAndAtLeastOneElement) { await engine.query.search.parseSearchFieldValue("and", key, translatedModelInstance, await inputFieldParser(fieldData.and), result); } else return { isValid: false, code: "invalid_in", reason: `The field '${key}' must contain at least two elements when using the 'and' clause` }; } if (fieldData.or !== void 0) { const isArrayAndAtLeastTwoElements = Array.isArray(fieldData.or) && fieldData.or.length > 1; if (isArrayAndAtLeastTwoElements) { await engine.query.search.parseSearchFieldValue("or", key, translatedModelInstance, await inputFieldParser(fieldData.or), result); } else return { isValid: false, code: "invalid_in", reason: `The field '${key}' must contain at least two elements when using the 'or' clause` }; } if (fieldData.in !== void 0) { const isInArrayAndAtLeastOneElement = Array.isArray(fieldData.in) && fieldData.in.length > 0 || Array.isArray(fieldData.in?.not) && (fieldData.in?.not || []).length > 0; const isInNotArrayAndAtLeastOneElement = Array.isArray(fieldData.in?.not) && (fieldData.in?.not || []).length > 0; if (isInArrayAndAtLeastOneElement) { await engine.query.search.parseSearchFieldValue( "in", key, translatedModelInstance, // eslint-disable-next-line ts/no-unnecessary-condition await Promise.all((fieldData.in || []).map((inValue) => inputFieldParser(inValue))), result ); } else if (isInNotArrayAndAtLeastOneElement) { await engine.query.search.parseSearchFieldValue( "in", key, translatedModelInstance, // eslint-disable-next-line ts/no-unnecessary-condition await Promise.all((fieldData.in?.not || []).map((inValue) => inputFieldParser(inValue))), result, { isNot: true } ); } else return { isValid: false, code: "invalid_in", reason: `The field '${key}' must contain at least one element when using the 'in' clause` }; } if (fieldData.between !== void 0) { const isBetweenAnArrayAndExactlyTwoElements = ( // eslint-disable-next-line ts/no-unnecessary-condition Array.isArray(fieldData.between) && fieldData.between.length === 2 ); const isBetweenNotAnArrayAndExactlyTwoElements = Array.isArray(fieldData.between?.not) && (fieldData.between?.not || []).length === 2; if (isBetweenAnArrayAndExactlyTwoElements) { await engine.query.search.parseSearchFieldValue( "between", key, translatedModelInstance, // eslint-disable-next-line ts/no-unnecessary-condition await Promise.all((fieldData.between || []).map((betweenValue) => inputFieldParser(betweenValue))), result ); } else if (isBetweenNotAnArrayAndExactlyTwoElements) { await engine.query.search.parseSearchFieldValue( "between", key, translatedModelInstance, // eslint-disable-next-line ts/no-unnecessary-condition await Promise.all((fieldData.between?.not || []).map((betweenValue) => inputFieldParser(betweenValue))), result, { isNot: true } ); } else return { isValid: false, code: "invalid_between", reason: `The field '${key}' must have exactly two elements when using the 'between' clause` }; } if (fieldData.is && fieldData.is.not !== void 0) { await engine.query.search.parseSearchFieldValue("is", key, translatedModelInstance, await inputFieldParser(fieldData.is.not), result, { isNot: true }); } else if (fieldData.is !== void 0) await engine.query.search.parseSearchFieldValue("is", key, translatedModelInstance, await inputFieldParser(fieldData.is), result); if (fieldData.greaterThan && fieldData.greaterThan.equal !== void 0) { await engine.query.search.parseSearchFieldValue("greaterThan", key, translatedModelInstance, await inputFieldParser(fieldData.greaterThan.equal), result, { equals: true }); } else if (fieldData.greaterThan !== void 0) await engine.query.search.parseSearchFieldValue("greaterThan", key, translatedModelInstance, await inputFieldParser(fieldData.greaterThan), result); if (fieldData.lessThan && fieldData.lessThan.equal !== void 0) { await engine.query.search.parseSearchFieldValue("lessThan", key, translatedModelInstance, await inputFieldParser(fieldData.lessThan), result, { equals: true }); } else if (fieldData.lessThan !== void 0) await engine.query.search.parseSearchFieldValue("lessThan", key, translatedModelInstance, await inputFieldParser(fieldData.lessThan.equal), result); return; } return await engine.query.search.parseSearchFieldValue("eq", key, translatedModelInstance, await inputFieldParser(fieldData), result); } __name(parseSearchField, "parseSearchField"); // src/models/utils.ts var import_core3 = require("@palmares/core"); // src/models/fields/index.ts var fields_exports = {}; __export(fields_exports, { AutoField: () => AutoField, BigAutoField: () => BigAutoField, BigIntegerField: () => BigIntegerField, BooleanField: () => BooleanField, CharField: () => CharField, DateField: () => DateField, DecimalField: () => DecimalField, EnumField: () => EnumField, Field: () => Field, ForeignKeyField: () => ForeignKeyField, IntegerField: () => IntegerField, ON_DELETE: () => ON_DELETE, TextField: () => TextField, UuidField: () => UuidField, auto: () => auto, bigAuto: () => bigAuto, bigInt: () => bigInt, bool: () => bool, char: () => char, choice: () => choice, date: () => date, decimal: () => decimal, foreignKey: () => foreignKey, int: () => int, text: () => text, uuid: () => uuid }); // 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 = (params: TParams) => { * const field = customBigInt.new(params); * return field._setNewBuilderMethods({ * test: (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 function date() { return DateField.new(); } __name(date, "date"); 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 = (params: TParams) => { * const field = customBigInt.new(params); * return field._setNewBuilderMethods({ * test: (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) { return super.unique(isUnique); } allowNull(isNull) { return super.allowNull(isNull); } /** * This method is used to create an index on the database for this field. */ dbIndex(isDbIndex) { return super.dbIndex(isDbIndex); } underscored(isUnderscored) { return super.underscored(isUnderscored); } primaryKey(isPrimaryKey) { return super.primaryKey(isPrimaryKey); } auto(isAuto) { return super.auto(isAuto); } default(defaultValue) { return super.default(defaultValue); } databaseName(databaseName) { return super.databaseName(databaseName); } autoNow(isAutoNow) { isAutoNow = typeof isAutoNow === "boolean" ? isAutoNow : true; this.__autoNow = isAutoNow; return this; } autoNowAdd(isAutoNowAdd) { isAutoNowAdd = typeof isAutoNowAdd === "boolean" ? isAutoNowAdd : true; this.__autoNowAdd = isAutoNowAdd; return this; } /** * This method can be used to override the type of a field. This is useful for library * maintainers that want to support a custom field type not supported by palmares. * * ### Note * - Your library should provide documentation of the fields that are supported. * - This should be used alongside the `_setPartialAttributes` method and `_setNewBuilderMethods`. * Instead of making users type NameOfYourField.new(), you should create a function that calls .new and creates * a new instance of the field for them. * - TDefinitions exists on type-level only, in runtime, it's not a guarantee of nothing. If TDefinitions * sets unique to true, it's up to you to do `NameOfYourField.new().unique()`, because otherwise it will be false */ static _overrideType(args) { return super._overrideType(args); } static new(..._args) { return new this(..._args); } }; // src/models/fields/decimal.ts function decimal(params) { return DecimalField.new(params); } __name(decimal, "decimal"); var DecimalField = class extends Field { static { __name(this, "DecimalField"); } $$type = "$PDecimalField"; __typeName = "DecimalField"; __maxDigits; __decimalPlaces; __allowedQueryOperations = /* @__PURE__ */ new Set([ "lessThan", "greaterThan", "between", "and", "in", "or", "eq", "is" ]); __inputParsers = /* @__PURE__ */ new Map(); __outputParsers = /* @__PURE__ */ new Map(); __compareCallback = /* @__PURE__ */ __name((engine, oldField, newField, defaultCompareCallback2) => { const oldFieldAsTextField = oldField; const newFieldAsTextField = newField; const isMaxDigitsEqual = oldFieldAsTextField["__maxDigits"] === newFieldAsTextField["__maxDigits"]; const isDecimalPlacesEqual = oldFieldAsTextField["__decimalPlaces"] === newFieldAsTextField["__decimalPlaces"]; const [isEqual, changedAttributes] = defaultCompareCallback2(engine, oldField, newField, defaultCompareCallback2); if (!isMaxDigitsEqual) changedAttributes.push("maxDigits"); if (!isDecimalPlacesEqual) changedAttributes.push("decimalPlaces"); return [ isMaxDigitsEqual && isDecimalPlacesEqual && isEqual, changedAttributes ]; }, "__compareCallback"); /** * 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 fieldAsDecimalField = field; return await defaultToStringCallback2(engine, field, defaultToStringCallback2, { constructorParams: `{ maxDigits: ${fieldAsDecimalField["__maxDigits"]}, decimalPlaces: ${fieldAsDecimalField["__decimalPlaces"]} }` }); }, "__toStringCallback"); /** * This is used internally by the engine for cloning the field to a new instance. * By doing that you are able to get the constructor options of the field when using Field.new() */ __newInstanceCallback = /* @__PURE__ */ __name((oldField, defaultNewInstanceArgumentsCallback2) => { const defaultData = defaultNewInstanceArgumentsCallback2(oldField, defaultNewInstanceArgumentsCallback2); const position0 = defaultData[0] || {}; const otherPositions = defaultData.slice(1); const asDecimalField = oldField; return [ { ...position0, maxDigits: asDecimalField["__maxDigits"], decimalPlaces: asDecimalField["__decimalPlaces"] }, ...otherPositions ]; }, "__newInstanceCallback"); __getArgumentsCallback = /* @__PURE__ */ __name((field, defaultCallback) => { const fieldAsDateField = field; const maxDigits = fieldAsDateField["__maxDigits"]; const decimalPlaces = fieldAsDateField["__decimalPlaces"]; return { ...defaultCallback(field, defaultCallback), decimalPlaces, maxDigits }; }, "__getArgumentsCallback"); constructor(params) { super(params); this.__maxDigits = params.maxDigits; this.__decimalPlaces = params.decimalPlaces; } /** * 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 = DecimalField.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 = (params: TParams) => { * const field = customBigInt.new(params); * return field._setNewBuilderMethods({ * test: (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) { return super.unique(isUnique); } allowNull(isNull) { return super.allowNull(isNull); } /** * This method is used to create an index on the database for this field. */ dbIndex(isDbIndex) { return super.dbIndex(isDbIndex); } underscored(isUnderscored) { return super.underscored(isUnderscored); } primaryKey(isPrimaryKey) { return super.primaryKey(isPrimaryKey); } auto(isAuto) { return super.auto(isAuto); } default(defaultValue) { return super.default(defaultValue); } databaseName(databaseName) { return super.databaseName(databaseName); } /** * This method can be used to override the type of a field. This is useful for library * maintainers that want to support a custom field type not supported by palmares. * * ### Note * - Your library should provide documentation of the fields that are supported. * - This should be used alongside the `_setPartialAttributes` method and `_setNewBuilderMethods`. * Instead of making users type NameOfYourField.new(), you should create a function that calls .new and creates * a new instance of the field for them. * - TDefinitions exists on type-level only, in runtime, it's not a guarantee of nothing. If TDefinitions * sets unique to true, it's up to you to do `NameOfYourField.new().unique()`, because otherwise it will be false */ static _overrideType(args) { return super._overrideType(args); } static new(params) { return new this(params); } }; // src/models/fields/integer.ts function int() { return IntegerField.new(); } __name(int, "int"); var IntegerField = class extends Field { static { __name(this, "IntegerField"); } $$type = "$PIntegerField"; __typeName = "IntegerField"; __allowedQueryOperations = /* @__PURE__ */ new Set([ "lessThan", "greaterThan", "between", "and", "in", "or", "eq", "is" ]); __inputParsers = /* @__PURE__ */ new Map(); __outputParsers = /* @__PURE__ */ new Map(); /** * 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 = () => { * const field = textField.new(); * class Builder { * test(param: TTest) { * return field * ._setPartialAttributes<{ create: TTest }, { create: 'union' }>()(param) * ._setNewBuilderMethods(); * } * value(value: TValue) { * return field * ._setPartialAttributes<{ create: TValue }, { create: 'replace' }>()(value) * ._setNewBuilderMethods(); * } * } * * const builder = new Builder(); * return field._setNewBuilderMethods(builder); * }; * * // 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) { return super.unique(isUnique); } allowNull(isNull) { return super.allowNull(isNull); } /** * This method is used to create an index on the database for this field. */ dbIndex(isDbIndex) { return super.dbIndex(isDbIndex); } underscored(isUnderscored) { return super.underscored(isUnderscored); } primaryKey(isPrimaryKey) { return super.primaryKey(isPrimaryKey); } auto(isAuto) { return super.auto(isAuto); } default(defaultValue) { return super.default(defaultValue); } databaseName(databaseName) { return super.databaseName(databaseName); } /** * This method can be used to override the type of a field. This is useful for library * maintainers that want to support a custom field type not supported by palmares. * * ### Note * Your library should provide documentation of the fields that are supported. */ static _overrideType(args) { return super._overrideType(args); } static new(..._args) { return new this(..._args); } }; // src/models/fields/big-integer.ts function bigInt() { return BigIntegerField.new(); } __name(bigInt, "bigInt"); var BigIntegerField = class extends Field { static { __name(this, "BigIntegerField"); } $$type = "$PBigIntegerField"; __typeName = "BigIntegerField"; __allowedQueryOperations = /* @__PURE__ */ new Set([ "lessThan", "greaterThan", "and", "in", "or", "eq", "is", "between" ]); __inputParsers = /* @__PURE__ */ new Map(); __outputParsers = /* @__PURE__ */ new Map(); /** * 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 = () => { * const field = textField.new(); * class Builder { * test(param: TTest) { * return field * ._setPartialAttributes<{ create: TTest }, { create: 'union' }>()(param) * ._setNewBuilderMethods(); * } * value(value: TValue) { * return field * ._setPartialAttributes<{ create: TValue }, { create: 'replace' }>()(value) * ._setNewBuilderMethods(); * } * } * * const builder = new Builder(); * return field._setNewBuilderMethods(builder); * }; * * // 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) { return super.unique(isUnique); } allowNull(isNull) { return super.allowNull(isNull); } /** * This method is used to create an index on the database for this field. */ dbIndex(isDbIndex) { return super.dbIndex(isDbIndex); } underscored(isUnderscored) { return super.underscored(isUnderscored); } primaryKey(isPrimaryKey) { return super.primaryKey(isPrimaryKey); } auto(isAuto) { return super.auto(isAuto); } default(defaultValue) { return super.default(defaultValue); } databaseName(databaseName) { return super.databaseName(databaseName); } /** * This method can be used to override the type of a field. This is useful for library * maintainers that want to support a custom field type not supported by palmares. * * ### Note * Your library should provide documentation of the fields that are supported. */ static _overrideType(args) { return super._overrideType(args); } static new(..._args) { return new this(..._args); } }; // src/models/fields/text.ts function text() { return TextField.new(); } __name(text, "text"); var TextField = class extends Field { static { __name(this, "TextField"); } $$type = "$PTextField"; __typeName = "TextField"; __allowedQueryOperations = /* @__PURE__ */ new Set([ "and", "in", "or", "eq", "is", "like" ]); __allowBlank = false; __inputParsers = /* @__PURE__ */ new Map(); __outputParsers = /* @__PURE__ */ new Map(); __compareCallback = /* @__PURE__ */ __name((engine, oldField, newField, defaultCompareCallback2) => { const oldFieldAsTextField = oldField; const newFieldAsTextField = newField; const isAllowBlankEqual = oldFieldAsTextField["__allowBlank"] === newFieldAsTextField["__allowBlank"]; const [isEqual, changedAttributes] = defaultCompareCallback2(engine, oldField, newField, defaultCompareCallback2); if (!isAllowBlankEqual) changedAttributes.push("allowBlank"); return [ isAllowBlankEqual && isEqual, changedAttributes ]; }, "__compareCallback"); __optionsCallback = /* @__PURE__ */ __name((setFieldValue, oldField, defaultOptionsCallback2) => { const oldFieldAsTextField = oldField; defaultOptionsCallback2(setFieldValue, oldField, defaultOptionsCallback2); setFieldValue("__allowBlank", "allowBlank", oldFieldAsTextField["__allowBlank"]); }, "__optionsCallback"); __getArgumentsCallback = /* @__PURE__ */ __name((field, defaultGetArgumentsCallback2) => { const fieldAsTextField = field; const defaultData = defaultGetArgumentsCallback2(field, defaultGetArgumentsCallback2); return { ...defaultData, allowBlank: fieldAsTextField["__allowBlank"] }; }, "__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 fieldAsTextField = field; return await defaultToStringCallback2(engine, field, defaultToStringCallback2, { builderParams: `${typeof fieldAsTextField["__allowBlank"] === "boolean" ? `.allowBlank(${fieldAsTextField["__allowBlank"]})` : ""}` }); }, "__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 = 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 = (params: TParams) => { * const field = customBigInt.new(params); * return field._setNewBuilderMethods({ * test: (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) { return super.unique(isUnique); } allowNull(isNull) { return super.allowNull(isNull); } allowBlank(isBlank) { if (isBlank === void 0) isBlank = true; this.__allowBlank = isBlank; return this; } /** * This method is used to create an index on the database for this field. */ dbIndex(isDbIndex) { return super.dbIndex(isDbIndex); } underscored(isUnderscored) { return super.underscored(isUnderscored); } primaryKey(isPrimaryKey) { return super.primaryKey(isPrimaryKey); } auto(isAuto) { return super.auto(isAuto); } default(defaultValue) { return super.default(defaultValue); } databaseName(databaseName) { return super.databaseName(databaseName); } /** * This method can be used to override the type of a field. This is useful for library * maintainers that want to support a custom field type not supported by palmares. * * ### Note * - Your library should provide documentation of the fields that are supported. * - This should be used alongside the `_setPartialAttributes` method and `_setNewBuilderMethods`. * Instead of making users type NameOfYourField.new(), you should create a function that calls .new and creates * a new instance of the field for them. * - TDefinitions exists on type-level only, in runtime, it's not a guarantee of nothing. If TDefinitions * sets unique to true, it's up to you to do `NameOfYourField.new().unique()`, because otherwise it will be false */ static overrideType(args) { return super._overrideType(args); } static new(..._args) { return new this(..._args); } }; // src/models/fields/char.ts function char(args) { return CharField.new(args); } __name(char, "char"); var CharField = class extends TextField { static { __name(this, "CharField"); } $$type = "$PCharField"; __typeName = "CharField"; __allowedQueryOperations = /* @__PURE__ */ new Set([ "and", "in", "or", "eq", "is", "like" ]); __maxLength; __inputParsers = /* @__PURE__ */ new Map(); __outputParsers = /* @__PURE__ */ new Map(); __getArgumentsCallback = /* @__PURE__ */ __name((field, defaultGetArgumentsCallback2) => { const fieldAsTextField = field; const defaultData = defaultGetArgumentsCallback2(field, defaultGetArgumentsCallback2); return { ...defaultData, allowBlank: fieldAsTextField["__allowBlank"], maxLength: fieldAsTextField["__maxLength"] }; }, "__getArgumentsCallback"); __compareCallback = /* @__PURE__ */ __name((engine, oldField, newField, defaultCompareCallback2) => { const oldFieldAsTextField = oldField; const newFieldAsTextField = newField; const isAllowBlankEqual = oldFieldAsTextField["__allowBlank"] === newFieldAsTextField["__allowBlank"]; const isMaxLengthEqual = oldFieldAsTextField["__maxLength"] === newFieldAsTextField["__maxLength"]; const [isEqual, changedAttributes] = defaultCompareCallback2(engine, oldField, newField, defaultCompareCallback2); if (!isAllowBlankEqual) changedAttributes.push("allowBlank"); if (!isMaxLengthEqual) changedAttributes.push("maxLength"); return [ isAllowBlankEqual && isMaxLengthEqual && isEqual, changedAttributes ]; }, "__compareCallback"); __optionsCallback = /* @__PURE__ */ __name((setFieldValue, oldField, defaultOptionsCallback2) => { const oldFieldAsTextField = oldField; defaultOptionsCallback2(setFieldValue, oldField, defaultOptionsCallback2); setFieldValue("__allowBlank", "allowBlank", oldFieldAsTextField["__allowBlank"]); }, "__optionsCallback"); __newInstanceCallback = /* @__PURE__ */ __name((field, defaultNewInstanceArgumentsCallback2) => { const fieldAsCharField = field; const defaultData = defaultNewInstanceArgumentsCallback2(field, defaultNewInstanceArgumentsCallback2); const position0 = defaultData[0] || {}; const otherPositions = defaultData.slice(1); return [ { ...position0, maxLen: fieldAsCharField["__maxLength"] }, ...otherPositions ]; }, "__newInstanceCallback"); /** * 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 fieldAsCharField = field; return await defaultToStringCallback2(engine, field, defaultToStringCallback2, { constructorParams: `{ maxLen: ${fieldAsCharField["__maxLength"]} }`, builderParams: `${typeof fieldAsCharField["__allowBlank"] === "boolean" ? `.allowBlank(${fieldAsCharField["__allowBlank"]})` : ""}` }); }, "__toStringCallback"); constructor(params) { super(params); const maxLength = typeof params.maxLen === "number" ? params.maxLen : 255; this.__maxLength = maxLength; } /** * 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 = CharField._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 = (params: TParams) => { * const field = customBigInt.new(params); * return field._setNewBuilderMethods({ * test: (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) { return super.unique(isUnique); } allowNull(isNull) { return super.allowNull(isNull); } allowBlank(isBlank) { if (isBlank === void 0) isBlank = true; this.__allowBlank = isBlank; return this; } /** * This method is used to create an index on the database for this field. */ dbIndex(isDbIndex) { return super.dbIndex(isDbIndex); } underscored(isUnderscored) { return super.underscored(isUnderscored); } primaryKey(isPrimaryKey) { return super.primaryKey(isPrimaryKey); } auto(isAuto) { return super.auto(isAuto); } default(defaultValue) { return super.default(defaultValue); } databaseName(databaseName) { return super.databaseName(databaseName); } /** * This method can be used to override the type of a field. This is useful for library * maintainers that want to support a custom field type not supported by palmares. * * ### Note * - Your library should provide documentation of the fields that are supported. * - This should be used alongside the `_setPartialAttributes` method and `_setNewBuilderMethods`. * Instead of making users type NameOfYourField.new(), you should create a function that calls .new and creates * a new instance of the field for them. * - TDefinitions exists on type-level only, in runtime, it's not a guarantee of nothing. If TDefinitions * sets unique to true, it's up to you to do `NameOfYourField.new().unique()`, because otherwise it will be false */ static _overrideType(args) { const newField = super._overrideType(args); newField.new = (params) => { const newInstance = new this(params); const keysOfForeignKeyAttributes = /* @__PURE__ */ new Set([ "maxLen" ]); const customAttributes = Object.keys(params).reduce((acc, key) => { if (keysOfForeignKeyAttributes.has(key)) return acc; acc[key] = params[key]; return acc; }, {}); newInstance["__customAttributes"] = customAttributes; return newInstance; }; return newField; } static new(args) { return new this(args); } }; // src/models/fields/uuid.ts function uuid() { return UuidField.new(); } __name(uuid, "uuid"); var UuidField = class extends TextField { static { __name(this, "UuidField"); } $$type = "$PUuidField"; __typeName = "UuidField"; __inputParsers = /* @__PURE__ */ new Map(); __outputParsers = /* @__PURE__ */ new Map(); __compareCallback = /* @__PURE__ */ __name((engine, oldField, newField, defaultCompareCallback2) => { const oldFieldAsUuidField = oldField; const newFieldAsUuidField = newField; const isAllowBlankEqual = oldFieldAsUuidField["__allowBlank"] === newFieldAsUuidField["__allowBlank"]; const [isEqual, changedAttributes] = defaultCompareCallback2(engine, oldField, newField, defaultCompareCallback2); if (!isAllowBlankEqual) changedAttributes.push("allowBlank"); return [ isAllowBlankEqual && isEqual, changedAttributes ]; }, "__compareCallback"); /** * 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 fieldAsUuidField = field; return await defaultToStringCallback2(engine, field, defaultToStringCallback2, { builderParams: `${typeof fieldAsUuidField["__allowBlank"] === "boolean" ? `.allowBlank(${fieldAsUuidField["__allowBlank"]})` : ""}` }); }, "__toStringCallback"); __optionsCallback = /* @__PURE__ */ __name((setFieldValue, oldField, defaultOptionsCallback2) => { const oldFieldAsUuidField = oldField; setFieldValue("__allowBlank", "allowBlank", oldFieldAsUuidField["__allowBlank"]); defaultOptionsCallback2(setFieldValue, oldFieldAsUuidField, defaultOptionsCallback2); }, "__optionsCallback"); __getArgumentsCallback = /* @__PURE__ */ __name((field, defaultCallback) => { const fieldAsUuidField = field; const allowBlank = fieldAsUuidField["__allowBlank"]; return { ...defaultCallback(field, defaultCallback), allowBlank }; }, "__getArgumentsCallback"); /** * 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 = UuidField.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 = (params: TParams) => { * const field = customBigInt.new(params); * return field._setNewBuilderMethods({ * test: (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) { return super.unique(isUnique); } allowNull(isNull) { return super.allowNull(isNull); } allowBlank(isBlank) { return super.allowBlank(isBlank); } /** * This method is used to create an index on the database for this field. */ dbIndex(isDbIndex) { return super.dbIndex(isDbIndex); } underscored(isUnderscored) { return super.underscored(isUnderscored); } primaryKey(isPrimaryKey) { return super.primaryKey(isPrimaryKey); } auto(isAuto) { return super.auto(isAuto); } default(defaultValue) { return super.default(defaultValue); } databaseName(databaseName) { return super.databaseName(databaseName); } /** * This method can be used to override the type of a field. This is useful for library * maintainers that want to support a custom field type not supported by palmares. * * ### Note * - Your library should provide documentation of the fields that are supported. * - This should be used alongside the `_setPartialAttributes` method and `_setNewBuilderMethods`. * Instead of making users type NameOfYourField.new(), you should create a function that calls .new and creates * a new instance of the field for them. * - TDefinitions exists on type-level only, in runtime, it's not a guarantee of nothing. If TDefinitions * sets unique to true, it's up to you to do `NameOfYourField.new().unique()`, because otherwise it will be false */ static _overrideType(args) { return super._overrideType(args); } static new(..._args) { return new this(..._args); } }; // src/models/fields/exceptions.ts var ForeignKeyFieldRelatedOrRelationNameAlreadyExistsError = class extends Error { static { __name(this, "ForeignKeyFieldRelatedOrRelationNameAlreadyExistsError"); } constructor(modelName, fieldName, relatedOrRelationName, otherFieldName, otherModelName, isRelatedName) { super(`The ForeignKeyField with name '${fieldName}' on model '${modelName}' has a ${isRelatedName ? "relatedName" : "relationName"} '${relatedOrRelationName}' that already exists for the field '${otherFieldName}' on model '${otherModelName}'. Please, provide a different value.`); this.name = "ForeignKeyFieldRelatedNameAlreadyExistsError"; } }; var ForeignKeyFieldInvalidRelatedToError = class extends Error { static { __name(this, "ForeignKeyFieldInvalidRelatedToError"); } constructor(fieldName, modelName) { super(`Foreign key field ${fieldName} on model ${modelName} has invalid 'relatedTo' parameter. It should relate to a model, a function that returns a model or a string`); this.name = "ForeignKeyFieldInvalidRelatedToError"; } }; var ForeignKeyFieldRequiredParamsMissingError = class extends Error { static { __name(this, "ForeignKeyFieldRequiredParamsMissingError"); } constructor(fieldName) { super(`Foreign key field ${fieldName} requires a reference to a model with 'relatedTo' and 'onDelete' parameter`); this.name = "ForeignKeyFieldRequiredParamsMissingError"; } }; // src/models/fields/foreign-key.ts function foreignKey(params) { return ForeignKeyField.new(params); } __name(foreignKey, "foreignKey"); var ForeignKeyField = class extends Field { static { __name(this, "ForeignKeyField"); } $$type = "$PForeignKeyField"; __typeName = "ForeignKeyField"; __relatedToAsString; __relatedTo; /** Preferably use __relatedTo, this is just to cache the model if `__relatedTo` is either a function or a string */ __modelRelatedTo; __allowedQueryOperations = /* @__PURE__ */ new Set([]); __onDelete; __relatedName; __relationName; __toField; __originalRelatedName; __inputParsers = /* @__PURE__ */ new Map(); __outputParsers = /* @__PURE__ */ new Map(); /** * This is used internally by the engine to compare if the field is equal to another field. * You can override this if you want to extend the ForeignKeyField class. */ __compareCallback = /* @__PURE__ */ __name((engine, oldField, newField, defaultCompareCallback2) => { const oldFieldAsForeignKey = oldField; const newFieldAsForeignKey = newField; getRelatedToAsString(oldFieldAsForeignKey); getRelatedToAsString(newFieldAsForeignKey); const isRelatedToEqual = oldFieldAsForeignKey["__relatedToAsString"] === newFieldAsForeignKey["__relatedToAsString"]; const isToFieldEqual = oldFieldAsForeignKey["__toField"] === newFieldAsForeignKey["__toField"]; const isOnDeleteEqual = oldFieldAsForeignKey["__onDelete"] === newFieldAsForeignKey["__onDelete"]; const isRelationNameEqual = oldFieldAsForeignKey["__relationName"] === newFieldAsForeignKey["__relationName"]; const isRelatedNameEqual = oldFieldAsForeignKey["__relatedName"] === newFieldAsForeignKey["__relatedName"]; const [isEqual, changedAttributes] = defaultCompareCallback2(engine, oldField, newField, defaultCompareCallback2); if (!isRelatedToEqual) changedAttributes.push("relatedTo"); if (!isToFieldEqual) changedAttributes.push("toField"); if (!isOnDeleteEqual) changedAttributes.push("onDelete"); if (!isRelationNameEqual) changedAttributes.push("relationName"); if (!isRelatedNameEqual) changedAttributes.push("relatedName"); return [ isRelatedNameEqual && isRelatedToEqual && isToFieldEqual && isOnDeleteEqual && isRelationNameEqual && isEqual, changedAttributes ]; }, "__compareCallback"); /** * This is used internally by the engine for cloning the field to a new instance. * By doing that you are able to get the constructor options of the field when using Field.new() */ __newInstanceCallback = /* @__PURE__ */ __name((oldField, defaultNewInstanceArgumentsCallback2) => { const defaultData = defaultNewInstanceArgumentsCallback2(oldField, defaultNewInstanceArgumentsCallback2); const position0 = defaultData[0] || {}; const otherPositions = defaultData.slice(1); const asForeignKeyField = oldField; return [ { ...position0, relatedTo: asForeignKeyField["__relatedTo"], onDelete: asForeignKeyField["__onDelete"], relatedName: asForeignKeyField["__relatedName"], relationName: asForeignKeyField["__relationName"], toField: asForeignKeyField["__toField"] }, ...otherPositions ]; }, "__newInstanceCallback"); /** * 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 fieldAsForeignKey = field; getRelatedToAsString(fieldAsForeignKey); return await defaultToStringCallback2(engine, field, defaultToStringCallback2, { constructorParams: `{relatedTo: "${fieldAsForeignKey["__relatedToAsString"]}", toField: "${fieldAsForeignKey["__toField"]}", onDelete: models.fields.ON_DELETE.${fieldAsForeignKey["__onDelete"].toUpperCase()}, relationName: "${fieldAsForeignKey["__relationName"]}", relatedName: "${fieldAsForeignKey["__originalRelatedName"]}"}` }); }, "__toStringCallback"); __getArgumentsCallback = /* @__PURE__ */ __name((field, defaultCallback) => { const fieldAsForeignKeyField = field; const relatedTo = fieldAsForeignKeyField["__relatedToAsString"]; const toField = fieldAsForeignKeyField["__toField"]; const relationName = fieldAsForeignKeyField["__relationName"]; const relatedName = fieldAsForeignKeyField["__relatedName"]; const onDelete = fieldAsForeignKeyField["__onDelete"]; return { ...defaultCallback(field, defaultCallback), toField, relatedTo, relationName, relatedName, onDelete }; }, "__getArgumentsCallback"); constructor({ onDelete, relatedName, relatedTo, relationName, toField, ...rest }) { super(rest); this.__relatedTo = relatedTo; this.__onDelete = onDelete; this.__relatedName = relatedName; this.__relationName = relationName; this.__toField = toField; } /** * Check if the related model is from the engine instance so we can override the * field creation and change the type of the field to some other field. * * This is useful to manage unmanaged relations. For example, we have a * model in this database and another one in the other database. We can relate both * of them without any issues. What this will do is convert the value of this * field that does not exist on this database to the field it relates to. * * @example * ``` * class Facebook extends models.model() { * fields = { * id: IntegerField.new(), * campaignName: CharField.new() * } * * options = { * managed: false * } * } * * const User = models.initialize('User', { * fields: { * id: AutoField.new(), * firstName: CharField.new(), * facebookId: ForeignKeyField({ * relatedTo: Facebook, * toField: 'id', * relatedName: 'facebookUsers', * relationName: 'facebook' * }) * } * }) * ``` * * Since the `facebookId` field is matching a field in an unmanaged model, the facebookId * field will be translated to `IntegerField` AND NOT * the `ForeignKeyField` * * @param engineInstance - Needs the engine instance to check if the model exists in the engine instance or not. * * @returns - Returns an array where the first item is if the relatedModel is * from the engine instance (false if not) and the field it should change to. */ async isRelatedModelFromEngineInstance(engineInstance) { getRelatedToAsString(this); const relatedToAsString = this["__relatedToAsString"]; const relatedModel = engineInstance.__modelsOfEngine[relatedToAsString]; let modelRelatedTo; if (typeof this["__relatedTo"] === "function" && this["__relatedTo"]?.["$$type"] !== "$PModel") modelRelatedTo = this["__relatedTo"](); else if (typeof this["__relatedTo"] === "string") modelRelatedTo = engineInstance.__modelsOfEngine[this["__relatedTo"]]; else modelRelatedTo = this["__relatedTo"]; if (this["__modelRelatedTo"] === void 0) this["__modelRelatedTo"] = modelRelatedTo; if (relatedModel !== void 0) return [ true, void 0 ]; else { const modelRelatedTo2 = engineInstance.__modelsFilteredOutOfEngine[relatedToAsString]; if (modelRelatedTo2 === void 0) return [ true, void 0 ]; else { const modelInstance = new modelRelatedTo2(); const fieldRelatedTo = modelInstance.fields[this["__toField"]]; let clonedField; if (fieldRelatedTo.$$type === "$PAutoField") clonedField = IntegerField.new(); if (fieldRelatedTo.$$type === "$PBigAutoField") clonedField = BigAutoField.new(); else clonedField = await fieldRelatedTo.clone(); clonedField["__model"] = this["__model"]; clonedField["__fieldName"] = this["__fieldName"]; clonedField["__databaseName"] = this["__databaseName"]; clonedField["__defaultValue"] = this["__defaultValue"]; clonedField["__allowNull"] = this["__allowNull"]; clonedField["__dbIndex"] = this["__dbIndex"]; clonedField["__hasDefaultValue"] = this["__hasDefaultValue"]; clonedField["__underscored"] = this["__underscored"]; clonedField["__unique"] = this["__unique"]; clonedField["__isAuto"] = false; clonedField["__primaryKey"] = false; modelInstance.fields[this["__fieldName"]] = clonedField; return [ false, clonedField ]; } } } /** * This is needed for the state. Some ORMs cannot have the same relatedName twice. * What happens is that when recreating the state * we repeat the models from the database. By doing it this way we able to create * a random relatedName so we guarantee that the same related name will not be * used twice inside inside of the engine to two different models. * * This is a logic that should live here and not on the engine itself because the * engine should not be aware of such errors that might occur. We just want * to keep it simple to develop engines. * * @return - Returns a random relatedName if it is a state model, otherwise returns the normal related name. */ get _relatedName() { const isModelDefined = this["__model"] !== void 0; const modelConstructor = this["__model"] !== void 0 ? this["__model"].constructor : void 0; const isModelAStateModel = isModelDefined && modelConstructor?.["__isState"] === true; if (isModelAStateModel) return `${generateUUID()}-${this.__originalRelatedName}`; else return this.__originalRelatedName; } /** * 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 = (params: TParams) => { * const field = customBigInt.new(params); * return field._setNewBuilderMethods({ * test: (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) { return super.unique(isUnique); } allowNull(isNull) { return super.allowNull(isNull); } /** * This method is used to create an index on the database for this field. */ dbIndex(isDbIndex) { return super.dbIndex(isDbIndex); } underscored(isUnderscored) { return super.underscored(isUnderscored); } primaryKey(isPrimaryKey) { return super.primaryKey(isPrimaryKey); } auto(isAuto) { return super.auto(isAuto); } default(defaultValue) { return super.default(defaultValue); } databaseName(databaseName) { return super.databaseName(databaseName); } /** * 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. * * ### Note * * Your library should provide documentation of the fields that are supported. */ static _overrideType(args) { const newField = super._overrideType(args); newField.new = (params) => { const newInstance = new this(params); const keysOfForeignKeyAttributes = /* @__PURE__ */ new Set([ "relatedTo", "toField", "onDelete", "relatedName", "relationName" ]); const customAttributes = Object.keys(params).reduce((acc, key) => { if (keysOfForeignKeyAttributes.has(key)) return acc; acc[key] = params[key]; return acc; }, {}); newInstance["__customAttributes"] = customAttributes; return newInstance; }; return newField; } /** * This is quite confusing, i know. * * This method is used to keep track of the relation on models. I won't go too far. Let's use an example: * * @example ```typescript * const User = models.initialize('User', { * fields: { * id: AutoField.new(), * name: CharField.new(), * email: CharField.new(), * profileId: ForeignKeyField.new({ * relatedTo: 'Profile', * toField: 'id', * relatedName: 'profileOfUsers', * relationName: 'profile', * onDelete: 'CASCADE' * }) * } * }); * * const Profile = models.initialize('Profile', { * fields: { * id: AutoField.new(), * name: CharField.new(), * } * }); * ``` * * On this example, the `profileId` field on the `User` model is related to the `Profile` model. * __directlyRelatedTo is an object that keeps track of the models that are directly related to the model. * In this case, the `User` model is directly related to the `Profile` model. * This means we should update the `User` model to keep track of the `Profile` model. * * __indirectlyRelatedTo is an object that keeps track of the models that are indirectly related to the model. * In this case, the `Profile` model is indirectly related to the `User` model. * This means we should update the `Profile` model to keep track of the `User` model. * * __associations is an object that keeps track of the fields that are related to the model. It's two way. * So the `Profile` model keeps track of all it's associations with other models. Wether Profile defined the * relation or not. * * With this configuration, no matter which model we have "in hands" we can always keep track of all of the relations. * It's confusing because at the end of the day it'll look like a complex graph. */ __attachRelationsToModel(_field, fieldName, modelOfField, relatedModel) { const modelOfFieldName = modelOfField["__originalName"](); const relatedModelName = typeof relatedModel === "string" ? relatedModel : relatedModel["__originalName"](); const modelAssociations = modelOfField["__associations"]; if (typeof modelAssociations !== "object") modelOfField["__associations"] = {}; modelOfField["__associations"][relatedModelName] = modelAssociations[relatedModelName] || { byRelatedName: /* @__PURE__ */ new Map(), byRelationName: /* @__PURE__ */ new Map() }; const doesRelationAlreadyExistForRelatedName = modelOfField["__associations"][relatedModelName].byRelatedName.has(this["__relatedName"]); const doesRelationAlreadyExistForRelationName = modelOfField["__associations"][relatedModelName].byRelationName.has(this["__relationName"]); const doesRelatedTiesToADifferentField = modelOfField["__associations"][relatedModelName].byRelatedName.get(this["__relatedName"]) !== this; const doesRelationTiesToADifferentField = modelOfField["__associations"][relatedModelName].byRelationName.get(this["__relationName"]) !== this; if (doesRelationAlreadyExistForRelatedName && doesRelatedTiesToADifferentField) { const relatedField = modelOfField["__associations"][relatedModelName].byRelatedName.get(this["__relatedName"]); throw new ForeignKeyFieldRelatedOrRelationNameAlreadyExistsError(modelOfFieldName, fieldName, this["__relatedName"], relatedField["__fieldName"], relatedField["__model"]?.["__getName"]?.(), true); } if (doesRelationAlreadyExistForRelationName && doesRelationTiesToADifferentField) { const relatedField = modelOfField["__associations"][relatedModelName].byRelationName.get(this["__relationName"]); throw new ForeignKeyFieldRelatedOrRelationNameAlreadyExistsError(modelOfFieldName, fieldName, this["__relationName"], relatedField["__fieldName"], relatedField["__model"]?.["__getName"]?.(), false); } modelOfField["__associations"][relatedModelName].byRelatedName.set(this["__relatedName"], this); modelOfField["__associations"][relatedModelName].byRelationName.set(this["__relationName"], this); if (typeof modelOfField["__directlyRelatedTo"] !== "object") modelOfField["__directlyRelatedTo"] = {}; const modelDirectlyRelatedTo = modelOfField["__directlyRelatedTo"]; modelOfField["__directlyRelatedTo"][relatedModelName] = modelDirectlyRelatedTo[relatedModelName] || []; modelOfField["__directlyRelatedTo"][relatedModelName].push(this["__relationName"]); if (typeof relatedModel !== "string") { if (typeof relatedModel["__indirectlyRelatedTo"] !== "object") relatedModel["__indirectlyRelatedTo"] = {}; const modelDirectlyRelatedTo2 = relatedModel["__indirectlyRelatedTo"]; relatedModel["__indirectlyRelatedTo"][relatedModelName] = modelDirectlyRelatedTo2[relatedModelName] || []; relatedModel["__indirectlyRelatedTo"][relatedModelName].push(this["__relatedName"]); if (typeof relatedModel["__associations"] !== "object") relatedModel["__associations"] = {}; const relatedModelAssociations = relatedModel["__associations"]; if (typeof relatedModelAssociations[modelOfFieldName] !== "object") relatedModelAssociations[modelOfFieldName] = { byRelatedName: /* @__PURE__ */ new Map(), byRelationName: /* @__PURE__ */ new Map() }; relatedModelAssociations[modelOfFieldName].byRelatedName.set(this["__relatedName"], this); relatedModelAssociations[modelOfFieldName].byRelationName.set(this["__relationName"], this); } } __init(fieldName, model2) { getRelatedToAsString(this); const isRelatedToAndOnDeleteNotDefined = typeof this["__relatedToAsString"] !== "string" && typeof this["__onDelete"] !== "string"; const relatedToAsString = this["__relatedToAsString"]; const originalNameOfModel = model2["__originalName"](); if (typeof this["__relatedTo"] === "function" && this["__relatedTo"]?.["$$type"] !== "$PModel") this.__attachRelationsToModel(this, fieldName, model2, this["__relatedTo"]()); else if (typeof this["__relatedTo"] === "string") { model2["__callAfterAllModelsAreLoadedToSetupRelations"].set(`${fieldName}${model2["__getName"]()}`, (engineInstance) => { this.__attachRelationsToModel( this, fieldName, model2, // eslint-disable-next-line ts/no-unnecessary-type-assertion engineInstance["__modelsOfEngine"][this["__relatedTo"]] ); }); } else if (this["$$type"] === "$PModel") this.__attachRelationsToModel(this, fieldName, model2, this["__relatedTo"]); else throw new ForeignKeyFieldInvalidRelatedToError(fieldName, model2["__originalName"]()); if (isRelatedToAndOnDeleteNotDefined) throw new ForeignKeyFieldRequiredParamsMissingError(this["__fieldName"]); super.__init(fieldName, model2); const wasRelatedNameDefined = typeof this["__relatedName"] === "string"; if (wasRelatedNameDefined === false) { const relatedToWithFirstStringLower = relatedToAsString.charAt(0).toLowerCase() + relatedToAsString.slice(1); const originalModelNameWithFirstStringUpper = originalNameOfModel.charAt(0).toUpperCase() + originalNameOfModel.slice(1); this["__originalRelatedName"] = `${relatedToWithFirstStringLower}${originalModelNameWithFirstStringUpper}s`; } } static new(params) { return new this(params); } }; // src/models/fields/auto.ts function auto() { return AutoField.new(); } __name(auto, "auto"); var AutoField = class extends Field { static { __name(this, "AutoField"); } $$type = "$PAutoField"; __typeName = "AutoField"; __isAuto = true; __hasDefaultValue = false; __primaryKey = true; __defaultValue = void 0; __allowNull = true; __unique = true; __dbIndex = true; __allowedQueryOperations = /* @__PURE__ */ new Set([ "lessThan", "greaterThan", "and", "in", "or", "eq", "is", "between" ]); __inputParsers = /* @__PURE__ */ new Map(); __outputParsers = /* @__PURE__ */ new Map(); unique = /* @__PURE__ */ __name(() => this, "unique"); auto = /* @__PURE__ */ __name(() => this, "auto"); allowNull = /* @__PURE__ */ __name(() => this, "allowNull"); primaryKey = /* @__PURE__ */ __name(() => this, "primaryKey"); dbIndex = /* @__PURE__ */ __name(() => this, "dbIndex"); default = /* @__PURE__ */ __name(() => this, "default"); constructor(...args) { super(...args); this.__isAuto = true; this.__hasDefaultValue = false; this.__primaryKey = true; this.__defaultValue = void 0; this.__allowNull = true; this.__unique = true; this.__dbIndex = true; } /** * 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 = () => { * const field = textField.new(); * class Builder { * test(param: TTest) { * return field * ._setPartialAttributes<{ create: TTest }, { create: 'union' }>()(param) * ._setNewBuilderMethods(); * } * value(value: TValue) { * return field * ._setPartialAttributes<{ create: TValue }, { create: 'replace' }>()(value) * ._setNewBuilderMethods(); * } * } * * const builder = new Builder(); * return field._setNewBuilderMethods(builder); * }; * * // 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) { return super.setCustomAttributes(customAttributes); } underscored(isUnderscored) { return super.underscored(isUnderscored); } databaseName(databaseName) { return super.databaseName(databaseName); } /** * 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. * * ### Note * * Your library should provide documentation of the fields that are supported. */ static _overrideType(args) { return super._overrideType(args); } static new(..._args) { return new this(..._args); } }; // src/models/fields/big-auto.ts function bigAuto() { return BigAutoField.new(); } __name(bigAuto, "bigAuto"); var BigAutoField = class extends Field { static { __name(this, "BigAutoField"); } $$type = "$PBigAutoField"; __typeName = "BigAutoField"; __allowedQueryOperations = /* @__PURE__ */ new Set([ "lessThan", "greaterThan", "and", "in", "or", "eq", "is", "between" ]); __isAuto = true; __hasDefaultValue = false; __primaryKey = true; __defaultValue = void 0; __allowNull = true; __unique = true; __dbIndex = true; __inputParsers = /* @__PURE__ */ new Map(); __outputParsers = /* @__PURE__ */ new Map(); unique = /* @__PURE__ */ __name(() => this, "unique"); auto = /* @__PURE__ */ __name(() => this, "auto"); allowNull = /* @__PURE__ */ __name(() => this, "allowNull"); primaryKey = /* @__PURE__ */ __name(() => this, "primaryKey"); dbIndex = /* @__PURE__ */ __name(() => this, "dbIndex"); default = /* @__PURE__ */ __name(() => this, "default"); constructor(...args) { super(...args); this.__isAuto = true; this.__hasDefaultValue = false; this.__primaryKey = true; this.__defaultValue = void 0; this.__allowNull = true; this.__unique = true; this.__dbIndex = true; } /** * 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 = () => { * const field = textField.new(); * class Builder { * test(param: TTest) { * return field * ._setPartialAttributes<{ create: TTest }, { create: 'union' }>()(param) * ._setNewBuilderMethods(); * } * value(value: TValue) { * return field * ._setPartialAttributes<{ create: TValue }, { create: 'replace' }>()(value) * ._setNewBuilderMethods(); * } * } * * const builder = new Builder(); * return field._setNewBuilderMethods(builder); * }; * * // 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) { return super.setCustomAttributes(customAttributes); } underscored(isUnderscored) { return super.underscored(isUnderscored); } databaseName(databaseName) { return super.databaseName(databaseName); } /** * 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. * * ### Note * * Your library should provide documentation of the fields that are supported. */ static _overrideType(args) { return super._overrideType(args); } static new(..._args) { return new this(..._args); } }; // src/models/fields/boolean.ts function bool() { return BooleanField.new(); } __name(bool, "bool"); var BooleanField = class extends Field { static { __name(this, "BooleanField"); } $$type = "$PBooleanField"; __typeName = "BooleanField"; __allowedQueryOperations = /* @__PURE__ */ new Set([ "and", "in", "or", "eq", "is" ]); __inputParsers = /* @__PURE__ */ new Map(); __outputParsers = /* @__PURE__ */ new Map(); /** * 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 = () => { * const field = textField.new(); * class Builder { * test(param: TTest) { * return field * ._setPartialAttributes<{ create: TTest }, { create: 'union' }>()(param) * ._setNewBuilderMethods(); * } * value(value: TValue) { * return field * ._setPartialAttributes<{ create: TValue }, { create: 'replace' }>()(value) * ._setNewBuilderMethods(); * } * } * * const builder = new Builder(); * return field._setNewBuilderMethods(builder); * }; * * // 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) { return super.unique(isUnique); } allowNull(isNull) { return super.allowNull(isNull); } /** * This method is used to create an index on the database for this field. */ dbIndex(isDbIndex) { return super.dbIndex(isDbIndex); } underscored(isUnderscored) { return super.underscored(isUnderscored); } primaryKey(isPrimaryKey) { return super.primaryKey(isPrimaryKey); } auto(isAuto) { return super.auto(isAuto); } default(defaultValue) { return super.default(defaultValue); } databaseName(databaseName) { return super.databaseName(databaseName); } /** * This method can be used to override the type of a field. This is useful for library * maintainers that want to support a custom field type not supported by palmares. * * ### Note * Your library should provide documentation of the fields that are supported. */ static _overrideType(args) { return super._overrideType(args); } static new(..._args) { return new this(..._args); } }; // src/models/fields/enum.ts function choice(params) { return EnumField.new(params); } __name(choice, "choice"); var EnumField = class extends Field { static { __name(this, "EnumField"); } $$type = "$PEnumField"; __typeName = "EnumField"; __choices; __allowedQueryOperations = /* @__PURE__ */ new Set([ "and", "in", "or", "eq", "is", "like" ]); __inputParsers = /* @__PURE__ */ new Map(); __outputParsers = /* @__PURE__ */ new Map(); __compareCallback = /* @__PURE__ */ __name((engine, oldField, newField, defaultCompareCallback2) => { const oldFieldAsTextField = oldField; const newFieldAsTextField = newField; const newFieldChoices = Array.isArray(newFieldAsTextField["__choices"]) ? newFieldAsTextField["__choices"] : []; const newFieldChoicesAsSet = new Set(newFieldChoices); const oldFieldChoices = Array.isArray(oldFieldAsTextField["__choices"]) ? oldFieldAsTextField["__choices"] : []; const isChoicesEqual = oldFieldChoices.length === newFieldChoices.length && oldFieldChoices.every((choice2) => newFieldChoicesAsSet.has(choice2)); const [isEqual, changedAttributes] = defaultCompareCallback2(engine, oldField, newField, defaultCompareCallback2); if (!isChoicesEqual) changedAttributes.push("choices"); return [ isChoicesEqual && isEqual, changedAttributes ]; }, "__compareCallback"); __newInstanceCallback = /* @__PURE__ */ __name((field, defaultNewInstanceArgumentsCallback2) => { const fieldAsEnumField = field; const defaultData = defaultNewInstanceArgumentsCallback2(field, defaultNewInstanceArgumentsCallback2); const position0 = defaultData[0] || {}; const otherPositions = defaultData.slice(1); return [ { ...position0, choices: fieldAsEnumField["__choices"] }, ...otherPositions ]; }, "__newInstanceCallback"); __getArgumentsCallback = /* @__PURE__ */ __name((field, defaultCallback) => { const fieldAsDateField = field; const choices = fieldAsDateField["__choices"]; return { ...defaultCallback(field, defaultCallback), choices }; }, "__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 fieldAsEnumField = field; return await defaultToStringCallback2(engine, field, defaultToStringCallback2, { constructorParams: `{ choices: [${fieldAsEnumField["__choices"].map((choice2) => `${JSON.stringify(choice2)}`).join(", ")}], }` }); }, "__toStringCallback"); constructor(params) { super(params); const choices = Array.isArray(params.choices) ? params.choices : []; this.__choices = choices; } /** * 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 = EnumField.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 = (params: TParams) => { * const field = customBigInt.new(params); * return field._setNewBuilderMethods({ * test: (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) { return super.unique(isUnique); } allowNull(isNull) { return super.allowNull(isNull); } /** * This method is used to create an index on the database for this field. */ dbIndex(isDbIndex) { return super.dbIndex(isDbIndex); } underscored(isUnderscored) { return super.underscored(isUnderscored); } primaryKey(isPrimaryKey) { return super.primaryKey(isPrimaryKey); } auto(isAuto) { return super.auto(isAuto); } default(defaultValue) { return super.default(defaultValue); } databaseName(databaseName) { return super.databaseName(databaseName); } /** * This method can be used to override the type of a field. This is useful for library * maintainers that want to support a custom field type not supported by palmares. * * ### Note * - Your library should provide documentation of the fields that are supported. * - This should be used alongside the `_setPartialAttributes` method and `_setNewBuilderMethods`. * Instead of making users type NameOfYourField.new(), you should create a function that calls .new and creates * a new instance of the field for them. * - TDefinitions exists on type-level only, in runtime, it's not a guarantee of nothing. If TDefinitions * sets unique to true, it's up to you to do `NameOfYourField.new().unique()`, because otherwise it will be false */ static _overrideType(args) { const newField = super._overrideType(args); newField.new = (params) => { const newInstance = new this(params); const keysOfForeignKeyAttributes = /* @__PURE__ */ new Set([ "choices" ]); const customAttributes = Object.keys(params).reduce((acc, key) => { if (keysOfForeignKeyAttributes.has(key)) return acc; acc[key] = params[key]; return acc; }, {}); newInstance["__customAttributes"] = customAttributes; return newInstance; }; return newField; } static new(args) { return new this(args); } }; // src/models/fields/types.ts var ON_DELETE = /* @__PURE__ */ function(ON_DELETE2) { ON_DELETE2["CASCADE"] = "cascade"; ON_DELETE2["SET_NULL"] = "set_null"; ON_DELETE2["SET_DEFAULT"] = "set_default"; ON_DELETE2["DO_NOTHING"] = "do_nothing"; ON_DELETE2["RESTRICT"] = "restrict"; return ON_DELETE2; }({}); // src/models/utils.ts var indirectlyRelatedModels = { $set: {} }; var getDefaultModelOptions = /* @__PURE__ */ __name(() => ({ abstract: false, underscored: true, tableName: void 0, managed: true, ordering: [], indexes: [], databases: [ "default" ], customOptions: {} }), "getDefaultModelOptions"); async function foreignKeyFieldParser(engine, field) { const [isRelatedModelFromEngine, fieldToChangeRelationTo] = await field.isRelatedModelFromEngineInstance(engine); if (isRelatedModelFromEngine === false) { if (fieldToChangeRelationTo) return fieldToChangeRelationTo; else { throw new RelatedModelFromForeignKeyIsNotFromEngineException(engine.connectionName, field["__relatedToAsString"] || "", field["__fieldName"], field["__model"]?.["name"] || "", field["__toField"]); } } else return field; } __name(foreignKeyFieldParser, "foreignKeyFieldParser"); function fieldAdapterPerField(engine, field, model2, options) { switch (field["__typeName"]) { case AutoField.name: if (engine.fields.autoFieldParser) return engine.fields.autoFieldParser; else return engine.fields.integerFieldParser; case BigAutoField.name: if (engine.fields.autoFieldParser) return engine.fields.autoFieldParser; else return engine.fields.bigIntegerFieldParser; case BigIntegerField.name: return engine.fields.bigIntegerFieldParser; case CharField.name: return engine.fields.charFieldParser; case DateField.name: return engine.fields.dateFieldParser; case DecimalField.name: return engine.fields.decimalFieldParser; case ForeignKeyField.name: { const fieldAsForeignKeyField = field; if (options?.byPassForeignKey) { let fieldItRelatesTo = void 0; const modelItRelatesTo = fieldAsForeignKeyField["__relatedTo"]; if (typeof modelItRelatesTo === "string") fieldItRelatesTo = engine["__modelsOfEngine"]?.[modelItRelatesTo]?.["_fields"]()[fieldAsForeignKeyField["__toField"]]; else if (modelItRelatesTo["$$type"] === "$PModel") fieldItRelatesTo = modelItRelatesTo?.["_fields"]()[fieldAsForeignKeyField["__toField"]]; else fieldItRelatesTo = modelItRelatesTo()?.["_fields"]()[fieldAsForeignKeyField["__toField"]]; return fieldAdapterPerField(engine, fieldItRelatesTo, model2, options); } return engine.fields.foreignKeyFieldParser; } case IntegerField.name: return engine.fields.integerFieldParser; case TextField.name: return engine.fields.textFieldParser; case UuidField.name: return engine.fields.uuidFieldParser; case EnumField.name: return engine.fields.enumFieldParser; case BooleanField.name: return engine.fields.booleanFieldParser; default: { if (typeof engine.fields.customFieldsParser === "object" && field["__typeName"] in engine.fields.customFieldsParser) return engine.fields.customFieldsParser[field["__typeName"]]; return engine.fields.fieldsParser; } } } __name(fieldAdapterPerField, "fieldAdapterPerField"); function callTranslateAndAppendInputAndOutputParsersToField(connectionName, field, model2, engine, engineFieldParser, args) { if (engineFieldParser && field) { retrieveInputAndOutputParsersFromFieldAndCache(engine, model2, field["__fieldName"], field); return engineFieldParser.translate(args); } else throw new EngineDoesNotSupportFieldTypeException(connectionName, field["__typeName"]); } __name(callTranslateAndAppendInputAndOutputParsersToField, "callTranslateAndAppendInputAndOutputParsersToField"); function retrieveInputAndOutputParsersFromFieldAndCache(engine, model2, fieldName, field) { const existingOutputParser = field["__outputParsers"].get(engine.connectionName); const existingInputParser = field["__inputParsers"].get(engine.connectionName); if (existingOutputParser !== void 0 && existingInputParser !== void 0) { return { input: existingInputParser, output: existingOutputParser }; } const fieldParserAdapter = fieldAdapterPerField(engine, field, model2, { byPassForeignKey: true }); field["__inputParsers"].set(engine.connectionName, fieldParserAdapter.inputParser || null); field["__outputParsers"].set(engine.connectionName, fieldParserAdapter.outputParser || null); const existingFieldParsersByEngine = model2["__fieldParsersByEngine"].get(engine.connectionName) || { input: /* @__PURE__ */ new Set(), output: /* @__PURE__ */ new Set(), toIgnore: /* @__PURE__ */ new Set() }; if (fieldParserAdapter.inputParser) existingFieldParsersByEngine.input.add(fieldName); if (fieldParserAdapter.outputParser) existingFieldParsersByEngine.output.add(fieldName); if (fieldParserAdapter.inputParser === void 0 && fieldParserAdapter.outputParser === void 0) existingFieldParsersByEngine.toIgnore.add(fieldName); model2["__fieldParsersByEngine"].set(engine.connectionName, existingFieldParsersByEngine); return { input: fieldParserAdapter.inputParser, output: fieldParserAdapter.outputParser }; } __name(retrieveInputAndOutputParsersFromFieldAndCache, "retrieveInputAndOutputParsersFromFieldAndCache"); async function parse(engine, engineFields, model2, field, callbackForLazyEvaluation) { let shouldReturnData = true; const callbackForLazyEvaluationInsideDefaultParse = /* @__PURE__ */ __name((translatedField, shouldReturnDataOnDefaultParse) => { if (typeof shouldReturnDataOnDefaultParse === "boolean") shouldReturnData = shouldReturnDataOnDefaultParse; callbackForLazyEvaluation(translatedField, shouldReturnData, field); }, "callbackForLazyEvaluationInsideDefaultParse"); const modelName = field["__model"]?.["__getName"](); const fieldData = field["__getArguments"](); const customAttributes = fieldData["customAttributes"]; delete fieldData["customAttributes"]; const modelAsBaseModel = model2; const args = { engine, field: fieldData, customAttributes, fieldParser: engineFields.fieldsParser, modelName, model: modelAsBaseModel, lazyEvaluate: callbackForLazyEvaluationInsideDefaultParse }; switch (field["__typeName"]) { case ForeignKeyField.name: { if (engineFields.foreignKeyFieldParser) { const fieldToParse = await foreignKeyFieldParser(engine, field); if (fieldToParse?.["$$type"] === "$PForeignKeyField") { return callTranslateAndAppendInputAndOutputParsersToField(engine.connectionName, fieldToParse, modelAsBaseModel.constructor, engine, engineFields.foreignKeyFieldParser, { ...args, field: fieldToParse["__getArguments"]() }); } else return parse(engine, engineFields, model2, fieldToParse, callbackForLazyEvaluation); } else throw new EngineDoesNotSupportFieldTypeException(engine.connectionName, field["__typeName"]); } default: { const fieldParser = fieldAdapterPerField(engine, field, model2.constructor); return callTranslateAndAppendInputAndOutputParsersToField(engine.connectionName, field, modelAsBaseModel.constructor, engine, fieldParser, args); } } } __name(parse, "parse"); async function initializeModels(engine, models2) { const recursiveOptionsToEvaluateModels = [ {} ]; const relationsToCallAfterModelsTranslation = /* @__PURE__ */ new Map(); while (recursiveOptionsToEvaluateModels) { const options = recursiveOptionsToEvaluateModels.shift(); const initializedModels = []; let fieldsToEvaluateAfter = []; const initializeModelPromises = models2.map(async (modelClass, index) => { const modelInstance = new modelClass(); const doesModelIncludesTheConnection = Array.isArray(modelInstance.options?.databases) && typeof engine.connectionName === "string" ? modelInstance.options?.databases.includes(engine.connectionName) : true; const domainName = modelClass["__domainName"]; const domainPath = modelClass["__domainPath"]; modelClass["__callAfterAllModelsAreLoadedToSetupRelations"] = relationsToCallAfterModelsTranslation; if (doesModelIncludesTheConnection) { const initializedModel = await modelClass["__init"](engine, domainName, domainPath, (field, translatedField) => { fieldsToEvaluateAfter.push({ model: modelInstance, field, translatedField, getInitialized: /* @__PURE__ */ __name(() => initializedModel, "getInitialized") }); }, { forceTranslate: options?.forceTranslation === true }); const originalModifyItself = initializedModel.modifyItself; initializedModel.modifyItself = (newModel) => { initializedModels[index].initialized = newModel; originalModifyItself(newModel); }; initializedModels.splice(index, 0, { domainName: domainPath, domainPath, class: modelClass, initialized: initializedModel.instance, modifyItself: initializedModel.modifyItself, original: modelInstance }); } modelClass["__callAfterAllModelsAreLoadedToSetupRelations"] = void 0; }); await Promise.all(initializeModelPromises); const markedFieldsCallbacksToRemove = []; const evaluateLaterFieldsPromises = fieldsToEvaluateAfter.map(async ({ model: model2, field, translatedField, getInitialized }, index) => { const initialized = getInitialized(); const modelConstructor = model2.constructor; const lazyEvaluatedFieldResult = await engine.fields.lazyEvaluateField(engine, modelConstructor["__getName"](), initialized.instance, field["__getArguments"](), translatedField, (args) => { let modelToUse = model2; let fieldToUse = field; if (args) { const modelConstructor2 = engine["__modelsOfEngine"][args.modelName]; if (modelConstructor2 === void 0) throw new ModelMissingException(args.modelName); const modelInstance = new modelConstructor2(); const fields = modelConstructor2["_fields"](); const field2 = fields[args.fieldName]; if (field2 === void 0) throw new FieldFromModelMissingException(args.modelName, args.fieldName); fieldToUse = field2["__clone"](field2, { newInstanceOverrideCallback: args.newInstanceOverrideCallback, optionsOverrideCallback: args.optionsOverrideCallback }); modelToUse = modelInstance; } return parse(engine, engine.fields, modelToUse, fieldToUse, () => { }); }); if (lazyEvaluatedFieldResult !== void 0 && lazyEvaluatedFieldResult !== null) { initialized.modifyItself(lazyEvaluatedFieldResult); markedFieldsCallbacksToRemove.push(index); } }); await Promise.all(evaluateLaterFieldsPromises); fieldsToEvaluateAfter = fieldsToEvaluateAfter.filter((_, index) => !markedFieldsCallbacksToRemove.includes(index)); if (engine.models.afterModelsTranslation) { const { modelEntries, modelsByName } = initializedModels.reduce((acc, model2) => { const optionsOfModel = model2.class["_options"](); if (optionsOfModel?.instance && (options || {}).forceTranslation !== true) return acc; const modelName = model2.class["__getName"](); acc.modelsByName[modelName] = model2; acc.modelEntries.push([ modelName, model2.initialized ]); return acc; }, { modelsByName: {}, modelEntries: [] }); if (modelEntries.length > 0 && modelEntries.length < initializeModels.length && options?.forceTranslation !== true) { const answer = await import_core3.std.asker.ask(` You have translated the model before. And you have assigned 'instance' to the model options. Should we refresh all of the model instances? Type either: 'y' and press Enter to accept or Ctrl+C to make changes before continuing Note: If this engine generate files it will overwrite all the files it has previously generated. Also don't forget to assign the generated models to 'instance' on the model options `); if (answer !== "y") throw new ShouldAssignAllInstancesException(); recursiveOptionsToEvaluateModels.push({ forceTranslation: true }); } if (modelEntries.length > 0) { const returnedValueFromLastTranslation = await engine.models.afterModelsTranslation(engine, modelEntries); if (Array.isArray(returnedValueFromLastTranslation)) { for (const [modelName, returnedValue] of returnedValueFromLastTranslation) modelsByName[modelName].modifyItself(returnedValue); } } } for (const setRelationToCall of relationsToCallAfterModelsTranslation.values()) setRelationToCall(engine); if (recursiveOptionsToEvaluateModels.length <= 0) return initializedModels; } return []; } __name(initializeModels, "initializeModels"); function factoryFunctionForModelTranslate(engine, model2, callbackToParseAfterAllModelsAreTranslated, options) { const modelConstructor = model2.constructor; const modelOptions = modelConstructor["_options"](model2); const fieldEntriesOfModel = Object.entries(modelConstructor["_fields"](model2)); const defaultParseFieldCallback = /* @__PURE__ */ __name((field) => { return parse(engine, engine.fields, model2, field, (translatedField, _, originalField) => { if (originalField) callbackToParseAfterAllModelsAreTranslated(originalField, translatedField); }); }, "defaultParseFieldCallback"); const defaultTranslateFieldsCallback = /* @__PURE__ */ __name(async () => { const translatedFieldDataByFieldName = {}; for (const [fieldName, field] of fieldEntriesOfModel) { const translatedAttributes = typeof engine.fields.translateField === "function" ? await engine.fields.translateField(engine, field, defaultParseFieldCallback) : await defaultParseFieldCallback(field); const isTranslatedAttributeDefined = translatedAttributes !== void 0 && translatedAttributes !== null; if (isTranslatedAttributeDefined) translatedFieldDataByFieldName[fieldName] = translatedAttributes; } return translatedFieldDataByFieldName; }, "defaultTranslateFieldsCallback"); return async () => { const modelName = modelConstructor["__getName"](); const alreadyHasAnInstance = options.forceTranslate !== true && (engine.initializedModels[modelName] !== void 0 || model2.options?.instance !== void 0); const modelInstance = alreadyHasAnInstance ? engine.initializedModels[modelName] !== void 0 ? engine.initializedModels[modelName] : model2.options?.instance : await engine.models.translate(engine, modelName, model2, fieldEntriesOfModel, modelOptions, modelOptions.customOptions, async () => { const options2 = await engine.models.translateOptions.bind(engine.models)(engine, modelName, modelOptions); const fields = typeof engine.models.translateFields === "function" ? await engine.models.translateFields.bind(engine.models)(engine, modelName, fieldEntriesOfModel, model2, defaultParseFieldCallback, defaultTranslateFieldsCallback) : await defaultTranslateFieldsCallback(); return { options: options2, fields }; }, defaultParseFieldCallback, defaultTranslateFieldsCallback); engine.initializedModels[modelConstructor["__getName"]()] = modelInstance; if (modelOptions.applyToTranslatedModel && alreadyHasAnInstance === false) { const translatedModelInstance = typeof engine.models.getModelInstanceForCustomHooks === "function" ? await engine.models.getModelInstanceForCustomHooks(engine, modelName, modelInstance) : modelInstance; modelOptions.applyToTranslatedModel(translatedModelInstance); } return modelInstance; }; } __name(factoryFunctionForModelTranslate, "factoryFunctionForModelTranslate"); // src/queries/queryset.ts var QuerySet = class { static { __name(this, "QuerySet"); } __isJoin; __hasSearch; __markToRemove = false; __markToCreateOrUpdate = false; __type; __query; __cachedData; __model; constructor(model2, type) { this.__model = model2; this.__type = type; this.__query = {}; } static new(model2, type) { if (type === "set") return new SetQuerySet(model2, "set"); if (type === "remove") return new RemoveQuerySet(model2, "remove"); return new GetQuerySet(model2, "get"); } _makeQuery() { const type = this.__type; if (type === "remove") return this.__model.default.remove(() => this); if (type === "set") return this.__model.default.set(() => this); return this.__model.default.get(() => this); } /** * The problem: A user select the fields `name` and `address` to be included on the query. * * When we send the query to the engine, we will get just the fields `name` and `address`, as expected. * But if we have a relation, we might need to include the field that relates the data. Let's use the `id` field as * an example. * * So the actual query should include the fields * `name`, `address` and `id`. * * The problem is that the user don't want the `id` field to be included on the query, but we need it to relate the * data. * * What do we do? We lazy remove the field after we use it to relate the data. * * Notice: We just need this if the user is selecting the fields to be included on the query, if the user selected * the field we need to make the relation, that's unnecessary. * * @param queryset - The query set instance that we are going to append the field to be lazy removed on each record * of the retrieved data. * @param fieldName - The name of the field that we are going to lazy remove after the query. */ __duringQueryAppendFieldToBeLazyRemovedAfterQuery(queryset2, fieldName) { if (queryset2["__query"].fields) { const fieldsOfQueryset = queryset2["__query"].fields(); const doesNotContainFieldOnFields = fieldsOfQueryset.toIncludeAfterQuery.concat(fieldsOfQueryset.toLazyRemoveAfterQuery).includes(fieldName) === false; if (doesNotContainFieldOnFields) queryset2["__query"].fields = () => ({ toIncludeAfterQuery: fieldsOfQueryset.toIncludeAfterQuery, toLazyRemoveAfterQuery: fieldsOfQueryset.toLazyRemoveAfterQuery.concat([ fieldName ]) }); } } async __duringQueryFormatWhereClauseAndDoABasicValidation(model2, translatedModel, engine, search) { if (search) { const invalidFields = /* @__PURE__ */ new Map(); let fieldsInModelInstance = []; const fieldsInSearch = Object.keys(search); const formattedSearch = {}; const promises = fieldsInSearch.map(async (key) => { const modelInstanceFields = model2["_fields"](); fieldsInModelInstance = Object.keys(modelInstanceFields); const field = modelInstanceFields[key]; if (!field) { delete search[key]; return; } const { input: inputFieldParser } = retrieveInputAndOutputParsersFromFieldAndCache(engine, model2, key, field); const fieldInputParserFunction = inputFieldParser ? async (value) => inputFieldParser({ engine, field: modelInstanceFields[key]["__getArguments"](), fieldParser: engine.fields.fieldsParser, translatedModel, modelName: model2["__getName"](), value }) : async (value) => value; if (fieldsInModelInstance.includes(key)) { const isValidObject = await parseSearchField(engine, key, search[key], fieldInputParserFunction, translatedModel, formattedSearch); if (isValidObject?.isValid === false) invalidFields.set(key, isValidObject); } }); await Promise.all(promises); return [ invalidFields, formattedSearch ]; } return [ /* @__PURE__ */ new Set(), search || {} ]; } __duringQueryAppendInsertedOrUpdatedDataToQuery(queryset2, dataInsertedOnParent, fieldNameOnRelationToFilter) { const hasSearch = queryset2["__hasSearch"]; if (queryset2["__query"].data) { const [existingFieldNamesOnSet, existingDataClauseOnChild] = queryset2["__query"].data(); if (existingDataClauseOnChild.length > 0) { queryset2["__query"].data = () => { if (hasSearch) { return [ existingFieldNamesOnSet, existingDataClauseOnChild ]; } const dataToInsert = []; for (const valueToAddOnRelationField of dataInsertedOnParent) { for (const dataToAddOrUpdate of existingDataClauseOnChild) { dataToAddOrUpdate[fieldNameOnRelationToFilter] = valueToAddOnRelationField; dataToInsert.push({ ...dataToAddOrUpdate }); } } existingFieldNamesOnSet.add(fieldNameOnRelationToFilter); return [ existingFieldNamesOnSet, dataToInsert ]; }; } } } /** * The model might not be properly because it's lazy loaded, so we need to check if * we have input and output parsers for all of the fields that we are trying to save or retrieve. * * We load the input and output parsers as we need to use on the fields. This gets appended on searches, * data insertion and data retrieval. It's lazy loaded, this means, we just get the parsers when we are * using the fields. If the search is searching for the 'id' field, we will cache JUST the 'id' field * input and output parsers. You understand? Its progressively adding more data, the more data you need. * * Because of that we need to check if all the data we are trying to save or retrieve has a parser cached. * If not we guarantee to retrieve and cache it. */ __duringQueryCompareObjectWithInputAndOutputParsersOnModelCache(engine, model2, keysYouAreTryingToSaveOrRetrieve, inputOrOutput) { let parsedFieldParsers = model2["__fieldParsersByEngine"].get(engine.connectionName) || { input: /* @__PURE__ */ new Set(), output: /* @__PURE__ */ new Set(), toIgnore: /* @__PURE__ */ new Set() }; const allFieldParsersAsArray = Array.from(parsedFieldParsers.input).concat(Array.from(parsedFieldParsers.output)).concat(Array.from(parsedFieldParsers.toIgnore)); const allFieldParsers = new Set(allFieldParsersAsArray); const differenceBetweenFieldsOnQueryAndFieldsOnModel = new Set(Array.from(keysYouAreTryingToSaveOrRetrieve).filter((fieldName) => !allFieldParsers.has(fieldName))); if (differenceBetweenFieldsOnQueryAndFieldsOnModel.size > 0) { const modelFields = model2["_fields"](); for (const field of differenceBetweenFieldsOnQueryAndFieldsOnModel) { if (!field) continue; retrieveInputAndOutputParsersFromFieldAndCache(engine, model2, field, modelFields[field]); } } parsedFieldParsers = model2["__fieldParsersByEngine"].get(engine.connectionName) || { input: /* @__PURE__ */ new Set(), output: /* @__PURE__ */ new Set(), toIgnore: /* @__PURE__ */ new Set() }; const fieldsWithInputOrOutputParser = new Set(Array.from(keysYouAreTryingToSaveOrRetrieve).filter((field) => parsedFieldParsers[inputOrOutput].has(field))); return fieldsWithInputOrOutputParser; } async __duringQueryActuallyQueryTheDatabase(model2, engine, queryset2, args) { if (this.__cachedData instanceof Promise) return this.__cachedData; const query = {}; const translatedModel = await model2.default.getInstance(engine.connectionName); let fieldsToLazyRemoveAfterQuery = []; if (queryset2["__query"].fields) { const existingFields = queryset2["__query"].fields(); fieldsToLazyRemoveAfterQuery = existingFields.toLazyRemoveAfterQuery; query["fields"] = existingFields.toIncludeAfterQuery.concat(existingFields.toLazyRemoveAfterQuery); } if (queryset2["__query"].data) { const [allFieldsOnDataAsSet, dataToSaveOrUpdate] = queryset2["__query"].data(); const fieldsWithInputParser = this.__duringQueryCompareObjectWithInputAndOutputParsersOnModelCache(engine, model2, allFieldsOnDataAsSet, "input"); if (fieldsWithInputParser.size > 0) { const fieldsOfModel = model2["_fields"](); for (const field of fieldsWithInputParser) { if (!field) continue; const { input } = retrieveInputAndOutputParsersFromFieldAndCache(engine, model2, field, fieldsOfModel[field]); for (const data of dataToSaveOrUpdate) { data[field] = await input({ engine, field: fieldsOfModel[field]["__getArguments"](), fieldParser: engine.fields.fieldsParser, translatedModel, modelName: model2["__getName"](), value: data[field] }); } } } query["data"] = dataToSaveOrUpdate; } if (queryset2["__query"].orderBy) query["ordering"] = queryset2["__query"].orderBy(); if (queryset2["__query"].limit) query["limit"] = queryset2["__query"].limit(); if (queryset2["__query"].offset) query["offset"] = queryset2["__query"].offset(); if (queryset2["__query"].where) { const [invalidFields, formattedSearch] = await this.__duringQueryFormatWhereClauseAndDoABasicValidation(model2, translatedModel, engine, queryset2["__query"].where()); if (invalidFields.size > 0) { this.__cachedData = Promise.resolve([]); databaseLogger.logMessage("QUERY_NOT_PROPERLY_SET", { modelName: model2["__getName"](), invalidFields }); return this.__cachedData; } query["search"] = formattedSearch; } const queryPerOperation = /* @__PURE__ */ __name(async () => { if (queryset2.__type === "set" && queryset2.__markToCreateOrUpdate) { const isUpdate = queryset2["__hasSearch"] === true; const dataToSendToEngine = isUpdate ? Array.isArray(query["data"][0]) ? query["data"][0] : [ query["data"][0] ] : Array.isArray(query["data"]) ? query["data"] : [ query["data"] ]; const allDataAddedOrUpdated = await engine.query.set.queryData(engine, { search: query["search"], modelOfEngineInstance: translatedModel, data: dataToSendToEngine }); return allDataAddedOrUpdated.map((data) => data[1]); } if (queryset2.__type === "remove" && queryset2.__markToRemove) { return engine.query.remove.queryData(engine, { search: query["search"], modelOfEngineInstance: translatedModel }); } return engine.query.get.queryData(engine, { modelOfEngineInstance: translatedModel, search: query["search"], fields: query["fields"], ordering: query["ordering"], limit: query["limit"], offset: query["offset"] }); }, "queryPerOperation"); this.__cachedData = args.cachedData ? args.cachedData : queryPerOperation(); let shouldLoopThroughData = fieldsToLazyRemoveAfterQuery.length > 0 || // eslint-disable-next-line ts/no-unnecessary-condition args.relations?.mappingsFromJoinsWithoutWhereClause?.mapTo instanceof Map || // eslint-disable-next-line ts/no-unnecessary-condition args.relations?.mappingsFromJoinsWithoutWhereClause?.newMap instanceof Map || // eslint-disable-next-line ts/no-unnecessary-condition args.relations?.relationsFromJoinsWithWhereClause?.mapTo instanceof Map || // eslint-disable-next-line ts/no-unnecessary-condition args.relations?.relationsFromJoinsWithWhereClause?.newMap instanceof Map; const awaitedCachedData = await this.__cachedData; let fieldsWithOutputParserAsArray = []; if (Array.isArray(awaitedCachedData) && awaitedCachedData.length > 0) { const fieldsWithOutputParser = this.__duringQueryCompareObjectWithInputAndOutputParsersOnModelCache(engine, model2, new Set(Object.keys(awaitedCachedData[0])), "output"); if (fieldsWithOutputParser.size > 0) { shouldLoopThroughData = true; fieldsWithOutputParserAsArray = Array.from(fieldsWithOutputParser); } } if (shouldLoopThroughData) { for (const dataItem of awaitedCachedData) { await Promise.all(fieldsWithOutputParserAsArray.map(async (field) => { if (!field) return; const { output } = retrieveInputAndOutputParsersFromFieldAndCache(engine, model2, field, model2["_fields"]()[field]); dataItem[field] = await output({ engine, field: model2["_fields"]()[field]["__getArguments"](), fieldParser: engine.fields.fieldsParser, translatedModel, modelName: model2["__getName"](), value: dataItem[field] }); })); for (const fieldToLazyRemove of fieldsToLazyRemoveAfterQuery) { const existingPropertyValue = dataItem[fieldToLazyRemove]; Object.defineProperty(dataItem, fieldToLazyRemove, { get() { delete dataItem[fieldToLazyRemove]; return existingPropertyValue; } }); } if (args.relations) { let valueOfFieldOnRelationToFilter = void 0; const relationOrRelatedName = args.relations.relationOrRelatedName; if (args.relations.fieldNameToGetRelationData) valueOfFieldOnRelationToFilter = dataItem[args.relations.fieldNameToGetRelationData]; args.relations.valuesToFilterOnRelatedModelAsSet?.add(valueOfFieldOnRelationToFilter); if (args.relations?.mappingsFromJoinsWithoutWhereClause?.newMap) { if (!args.relations.mappingsFromJoinsWithoutWhereClause.newMap.has(valueOfFieldOnRelationToFilter)) args.relations.mappingsFromJoinsWithoutWhereClause.newMap.set(valueOfFieldOnRelationToFilter, []); args.relations.mappingsFromJoinsWithoutWhereClause.newMap.get(valueOfFieldOnRelationToFilter)?.push(dataItem); } if (args.relations?.mappingsFromJoinsWithoutWhereClause?.mapTo) { const parentDataToAppendRelationTo = args.relations.mappingsFromJoinsWithoutWhereClause.mapTo.get(valueOfFieldOnRelationToFilter) || []; for (const toAssign of parentDataToAppendRelationTo) { if (args.relations.isUnique) toAssign[relationOrRelatedName] = dataItem; else { if (Array.isArray(toAssign[relationOrRelatedName]) === false) toAssign[relationOrRelatedName] = []; toAssign[relationOrRelatedName].push(dataItem); } } } if (args.relations?.relationsFromJoinsWithWhereClause?.newMap) { const fieldNameWithDataFromCurrentModel = args.relations.fieldNameThatMustBeIncludedInTheQueryToRelateTheData; if (!args.relations.relationsFromJoinsWithWhereClause.newMap.has(fieldNameWithDataFromCurrentModel)) args.relations.relationsFromJoinsWithWhereClause.newMap.set(fieldNameWithDataFromCurrentModel, /* @__PURE__ */ new Map()); if (!args.relations.relationsFromJoinsWithWhereClause.newMap.get(fieldNameWithDataFromCurrentModel)?.has(valueOfFieldOnRelationToFilter)) { const relationForFieldName = args.relations.relationsFromJoinsWithWhereClause.newMap.get(fieldNameWithDataFromCurrentModel); relationForFieldName.set(valueOfFieldOnRelationToFilter, { [relationOrRelatedName]: args.relations.isUnique ? void 0 : [] }); } if (args.relations.isUnique) args.relations.relationsFromJoinsWithWhereClause.newMap.get(fieldNameWithDataFromCurrentModel).get(valueOfFieldOnRelationToFilter)[relationOrRelatedName] = dataItem; else args.relations.relationsFromJoinsWithWhereClause.newMap.get(fieldNameWithDataFromCurrentModel)?.get(valueOfFieldOnRelationToFilter)[relationOrRelatedName].push(dataItem); } if (args.relations?.relationsFromJoinsWithWhereClause?.mapTo) { for (const [field, mapOfRelationsOfThatField] of args.relations.relationsFromJoinsWithWhereClause.mapTo) { const valueOfField = dataItem[field]; const dataToAdd = mapOfRelationsOfThatField.get(valueOfField); if (dataToAdd) { for (const [relationName, data] of Object.entries(dataToAdd)) { dataItem[relationName] = data; } } } } } } } return this.__cachedData; } /** * To Query the database we need to do the hard work ourselves. * * Because the way palmares work, we can't rely on native relations of the database. At the same * time we need to minimize the number of hits we do the database. To do that we do the hardwork ourselves. * This hardwork involves doing a inner join-like query in memory. What we do is: * * - If there is a search on the join we need to fetch first the data from the join before we fetch the data from * the parent model. * - If there is no search on the join we query the parent model first and then we query the joins. * * This is recursive, so more joins you have, more queries will be made. In order to minimize the number of queries * we use the `in` clause on the query to filter the data. At the same time we need to minimize the number of loops * we do on the retrieved data. For that we rely on Maps, there are 2 types: * * - Mappings - Those are used when you query the parent first and then the child. * - Relations - Those are used when you query the child first and then the parent. */ async __queryTheData(model2, engine, args) { const toFilterOnChildModelAsSet = /* @__PURE__ */ new Set(); const mappings = /* @__PURE__ */ new Map(); const baseModelAsModel = model2; baseModelAsModel["_fields"](); const relations = /* @__PURE__ */ new Map(); const toQueryAfterBase = []; const toQueryBeforeBase = []; const joins = this.__query.joins?.(); const joinsEntries = joins ? Object.entries(joins) : []; for (const joinsData of joinsEntries) { const [relationOrRelatedName, { model: model3, querySet }] = joinsData; const joinedModel = model3; const joinedModelName = joinedModel["__getName"](); const directlyRelated = baseModelAsModel["__directlyRelatedTo"]; const indirectlyRelated = baseModelAsModel["__indirectlyRelatedTo"]; const isDirectlyRelated = (directlyRelated?.[joinedModelName] || []).includes(relationOrRelatedName); let isIndirectlyRelated = (indirectlyRelated?.[joinedModelName] || []).includes(relationOrRelatedName); const isNotDirectlyRelatedNorIndirectRelated = !isDirectlyRelated && !isIndirectlyRelated; if (isNotDirectlyRelatedNorIndirectRelated) { const fieldsOfModel = joinedModel["_fields"](); isIndirectlyRelated = (baseModelAsModel["__indirectlyRelatedTo"]?.[joinedModelName] || []).includes(relationOrRelatedName); if (isIndirectlyRelated === false) { for (const [fieldName, probablyAForeignKeyField] of Object.entries(fieldsOfModel)) { const isAForeignKeyFieldAndIsRelatedName = probablyAForeignKeyField["$$type"] === "$PForeignKeyField" && probablyAForeignKeyField["__relatedName"] === relationOrRelatedName; if (isAForeignKeyFieldAndIsRelatedName) { isIndirectlyRelated = true; const foreignKeyField = probablyAForeignKeyField; foreignKeyField["__attachRelationsToModel"](foreignKeyField, fieldName, joinedModel, baseModelAsModel); break; } } } } if (isDirectlyRelated === false && isIndirectlyRelated === false) throw new RelationNameIsNotPartOfModelException(joinedModelName, relationOrRelatedName); if (querySet["__hasSearch"]) this["__hasSearch"] = true; else if (this["__hasSearch"]) querySet["__hasSearch"] = true; const fieldNameOnRelationToFilter = isDirectlyRelated ? baseModelAsModel["__associations"][joinedModelName].byRelationName.get(relationOrRelatedName)?.["__toField"] : baseModelAsModel["__associations"][joinedModelName].byRelatedName.get(relationOrRelatedName)?.["__fieldName"]; const fromCurrentModelsFieldData = isDirectlyRelated ? baseModelAsModel["__associations"][joinedModelName].byRelationName.get(relationOrRelatedName)?.["__fieldName"] : baseModelAsModel["__associations"][joinedModelName].byRelatedName.get(relationOrRelatedName)?.["__toField"]; const isUnique = isDirectlyRelated ? true : baseModelAsModel["__associations"][joinedModelName].byRelatedName.get(relationOrRelatedName)?.["__unique"] || false; const shouldBeQueriedBeforeBase = querySet["__hasSearch"] || // For GET queries querySet["__query"].data !== void 0 && querySet["__hasSearch"] || // For Update SET queries querySet["__query"].data !== void 0 && isDirectlyRelated === true; if (shouldBeQueriedBeforeBase) toQueryBeforeBase.push(async () => { this.__duringQueryAppendFieldToBeLazyRemovedAfterQuery(querySet, fieldNameOnRelationToFilter); const parentWhereClause = this["__query"].where ? this["__query"].where() : {}; const toFilterOnParentModelAsSet = /* @__PURE__ */ new Set(); const hasTheInFilterOnFieldNameOnRelationToFilter = (parentWhereClause[fromCurrentModelsFieldData]?.["in"] || []).length > 0; if (hasTheInFilterOnFieldNameOnRelationToFilter) { querySet["__query"].where = () => ({ ...querySet["__query"].where?.() || {}, [fieldNameOnRelationToFilter]: { in: parentWhereClause[fromCurrentModelsFieldData]["in"] } }); } await querySet["__queryTheData"](joinedModel, engine, { relations: { isUnique, fieldNameToGetRelationData: fieldNameOnRelationToFilter, relationOrRelatedName, fieldNameThatMustBeIncludedInTheQueryToRelateTheData: fromCurrentModelsFieldData, valuesToFilterOnRelatedModelAsSet: toFilterOnParentModelAsSet, relationsFromJoinsWithWhereClause: { newMap: relations, mapTo: args?.relations?.relationsFromJoinsWithWhereClause.mapTo }, mappingsFromJoinsWithoutWhereClause: { mapTo: args?.relations?.mappingsFromJoinsWithoutWhereClause.mapTo } } }); if (querySet["__hasSearch"]) this["__hasSearch"] = true; const formattedToFilterOnParentModelAsSet = Array.from(toFilterOnParentModelAsSet); this.__duringQueryAppendFieldToBeLazyRemovedAfterQuery(this, fromCurrentModelsFieldData); this.__duringQueryAppendInsertedOrUpdatedDataToQuery(this, formattedToFilterOnParentModelAsSet, fromCurrentModelsFieldData); const doesNotHaveADifferentWhereClauseForField = parentWhereClause[fromCurrentModelsFieldData] === void 0 || Array.isArray(parentWhereClause[fromCurrentModelsFieldData]?.["in"]); const shouldAppendWhereClauseOnParent = this["__query"].data !== void 0 && this["__hasSearch"] || this["__query"].data === void 0; if (shouldAppendWhereClauseOnParent && doesNotHaveADifferentWhereClauseForField) { const currentWhereClause = this["__query"].where ? this["__query"].where() : {}; this["__query"].where = () => ({ ...currentWhereClause, [fromCurrentModelsFieldData]: { in: formattedToFilterOnParentModelAsSet } }); } }); else { this.__duringQueryAppendFieldToBeLazyRemovedAfterQuery(this, fromCurrentModelsFieldData); toQueryAfterBase.push(async () => { await this.__duringQueryActuallyQueryTheDatabase(baseModelAsModel, engine, this, { cachedData: void 0, relations: { fieldNameThatMustBeIncludedInTheQueryToRelateTheData: fieldNameOnRelationToFilter, fieldNameToGetRelationData: fromCurrentModelsFieldData, isUnique, mappingsFromJoinsWithoutWhereClause: { newMap: mappings, mapTo: args?.relations?.mappingsFromJoinsWithoutWhereClause.mapTo }, relationsFromJoinsWithWhereClause: { newMap: args?.relations?.relationsFromJoinsWithWhereClause.newMap, mapTo: relations }, valuesToFilterOnRelatedModelAsSet: toFilterOnChildModelAsSet, relationOrRelatedName: args?.relations?.relationOrRelatedName || void 0 } }); const formattedToFilterOnChildModelAsSet = Array.from(toFilterOnChildModelAsSet); this.__duringQueryAppendInsertedOrUpdatedDataToQuery(querySet, formattedToFilterOnChildModelAsSet, fieldNameOnRelationToFilter); const existingWhereClauseOnChild = querySet["__query"].where?.() || {}; const shouldAppendWhereClauseOnChild = querySet["__query"].data !== void 0 && querySet["__hasSearch"] || querySet["__query"].data === void 0; if (shouldAppendWhereClauseOnChild) querySet["__query"].where = () => ({ ...existingWhereClauseOnChild, [fieldNameOnRelationToFilter]: { in: formattedToFilterOnChildModelAsSet } }); this.__duringQueryAppendFieldToBeLazyRemovedAfterQuery(querySet, fieldNameOnRelationToFilter); await querySet["__queryTheData"](joinedModel, engine, { relations: { fieldNameThatMustBeIncludedInTheQueryToRelateTheData: fromCurrentModelsFieldData, fieldNameToGetRelationData: fieldNameOnRelationToFilter, isUnique, relationOrRelatedName, mappingsFromJoinsWithoutWhereClause: { mapTo: mappings }, relationsFromJoinsWithWhereClause: { mapTo: args?.relations?.relationsFromJoinsWithWhereClause.mapTo } } }); }); } } if (toQueryBeforeBase.length > 0) await Promise.all(toQueryBeforeBase.map((queryBeforeBase) => queryBeforeBase())); if (toQueryAfterBase.length > 0) await Promise.all(toQueryAfterBase.map((queryAfterBase) => queryAfterBase())); else { await this.__duringQueryActuallyQueryTheDatabase(baseModelAsModel, engine, this, { cachedData: void 0, relations: { ...args?.relations || {}, relationsFromJoinsWithWhereClause: { mapTo: relations, newMap: args?.relations?.relationsFromJoinsWithWhereClause.newMap } } }); } return this.__cachedData; } }; var CommonQuerySet = class extends QuerySet { static { __name(this, "CommonQuerySet"); } /** * Join a model with another model, you can also pass a callback to make nested queries. * * @param model - The model you want to join with. * @param relationName - The name of the relation you want to join. Let's say the model * you are joining with is called `Profile` on the `User` model, and there are two foreignKeyFields on * the `User` model that points to the `Profile` model, one called `profileId` and the other called `profileId2`. * The relationName would be `profileId` or `profileId2`. * IMPORTANT: If the relationName is not inferred from typescript it's invalid and you cannot relate the data. * @param queryCallback - A callback to make nested queries or to filter through a nested relation */ join(model2, relationName, queryCallback) { const getNewQuerySet = /* @__PURE__ */ __name(() => { if (this.__type === "set") return new SetQuerySet(model2, this.__type); if (this.__type === "remove") return new RemoveQuerySet(model2, this.__type); else return new GetQuerySet(model2, this.__type); }, "getNewQuerySet"); const newParentQuerySet = getNewQuerySet(); const newChildQuerySet = getNewQuerySet(); newChildQuerySet["__isJoin"] = true; const queryCallbackResult = queryCallback ? queryCallback(newChildQuerySet) : newChildQuerySet; if (queryCallbackResult["__hasSearch"]) newParentQuerySet["__hasSearch"] = true; if (this["__isJoin"]) newParentQuerySet["__isJoin"] = true; for (const field of Object.keys(this.__query)) { if (field === "joins") continue; newParentQuerySet["__query"][field] = this.__query[field]; } const existingJoins = this.__query.joins ? this.__query.joins() : {}; newParentQuerySet["__query"].joins = () => ({ ...existingJoins, [relationName]: { model: model2, querySet: queryCallbackResult } }); return newParentQuerySet; } /** * Used for filtering the data on the database. It will guarantee only a subset of the data is returned. * You can chain as many `.where()` methods as you want. This will append the new data to the previous `.where()` * clause. * * @example * ```ts * const users = await User.objects.get((qs) => qs.where({ age: 20 }).where({ name: 'John' })); * ``` * * This will return all users that have the age of '20' and the name of 'John'. * * You can also use some special operators like `in`, `greaterThan`, `lessThan`, * `greaterThanEqual`, etc depending on the data type of the field. * * @example * ```ts * const users = await User.objects.get((qs) => qs.where({ age: { greaterThan: 20 } })); * ``` * * When used in a join, it will do a InnerJoin-like query in memory. For example * * @example * ```ts * const users = await User.objects.get((qs) => * qs.join(Profile, 'profile', (qs) => qs.where({ type: 'admin' })) * ); * ``` * This will return all Users that have the Profile type of 'admin'. * * @param search - The search object to filter the data. * * @returns - A new QuerySet with the search appended. */ where(search) { const getNewQuerySet = /* @__PURE__ */ __name(() => { if (this.__type === "set") return new SetQuerySet(this.__model, this.__type); if (this.__type === "remove") return new RemoveQuerySet(this.__model, this.__type); else if (this.__isJoin) return new GetQuerySetIfSearchOnJoin(this.__model, this.__type); else return new GetQuerySet(this.__model, this.__type); }, "getNewQuerySet"); const newQuerySet = getNewQuerySet(); for (const field of Object.keys(this.__query)) { if (field === "where") continue; newQuerySet["__query"][field] = this.__query[field]; } if (this.__query.where) newQuerySet["__query"].where = () => ({ ...this.__query.where(), ...search }); else newQuerySet["__query"].where = () => search; newQuerySet["__hasSearch"] = true; return newQuerySet; } }; var GetQuerySetIfSearchOnJoin = class extends CommonQuerySet { static { __name(this, "GetQuerySetIfSearchOnJoin"); } /** * Selects only the fields you want to retrieve from the database. This will probably improve the performance * of your query. This can be chained with other `select` clauses. * * @example * ```ts * const users = await User.objects.get((qs) => qs.select('id', 'name')); * * const users = await User.objects.get((qs) => qs.select('id').select('name')); * * // Both of the above examples are the same thing * ``` * * @param fields - The fields you want to retrieve from the database. * * @returns - A new QuerySet with the fields selected. */ select(...fields) { const newQuerySet = new GetQuerySet(this.__model, this.__type); for (const field of Object.keys(this.__query)) { if (field === "fields") continue; newQuerySet["__query"][field] = this.__query[field]; } if (this.__query.fields) { const existingFields = this.__query.fields(); newQuerySet["__query"].fields = () => ({ toIncludeAfterQuery: [ ...existingFields["toIncludeAfterQuery"], ...fields ], toLazyRemoveAfterQuery: existingFields["toLazyRemoveAfterQuery"] }); } else newQuerySet["__query"].fields = () => ({ toIncludeAfterQuery: fields, toLazyRemoveAfterQuery: [] }); newQuerySet["__hasSearch"] = this["__hasSearch"]; newQuerySet["__isJoin"] = this["__isJoin"]; return newQuerySet; } /** * Allows you to order the data you are retrieving from the database. This can be chained * with other `orderBy` clauses. * * A clause with a `-` in front of the field name means it will order in descending order. * Otherwise it will order in ascending order. * * @example * ```ts * const users = await User.objects.get((qs) => qs.orderBy('-name', 'age')); * ``` * * @param ordering - The fields you want to order by. * * @returns - A new QuerySet with the ordering appended. */ orderBy(...ordering) { const newQuerySet = new GetQuerySet(this.__model, this.__type); for (const field of Object.keys(this.__query)) { if (field === "where") continue; newQuerySet["__query"][field] = this.__query[field]; } if (this.__query.orderBy) newQuerySet["__query"].orderBy = () => [ ...this.__query.orderBy(), ...ordering ]; else newQuerySet["__query"].orderBy = () => ordering; newQuerySet["__hasSearch"] = this["__hasSearch"]; newQuerySet["__isJoin"] = this["__isJoin"]; return newQuerySet; } }; var GetQuerySet = class _GetQuerySet extends GetQuerySetIfSearchOnJoin { static { __name(this, "GetQuerySet"); } limit(limit) { const newQuerySet = new _GetQuerySet(this.__model, this.__type); for (const field of Object.keys(this.__query)) { newQuerySet["__query"][field] = this.__query[field]; } newQuerySet["__query"].limit = () => limit; newQuerySet["__hasSearch"] = this["__hasSearch"]; newQuerySet["__isJoin"] = this["__isJoin"]; return newQuerySet; } offset(offset) { const newQuerySet = new _GetQuerySet(this.__model, this.__type); for (const field of Object.keys(this.__query)) { newQuerySet["__query"][field] = this.__query[field]; } newQuerySet["__query"].offset = () => offset; newQuerySet["__hasSearch"] = this["__hasSearch"]; newQuerySet["__isJoin"] = this["__isJoin"]; return newQuerySet; } }; var SetQuerySet = class extends CommonQuerySet { static { __name(this, "SetQuerySet"); } /** * This method is used to set the data to be created or updated. As you've probably seen, we doesn't * offer you too many options, you have `.set`, `remove` and `get` on the Managers. This covers the * `.set` part. A `set` operation creates or updates the data on the database. * * Now you might be asking: How do we know how to differentiate? Easy, by the presence of a `.where()` * clause anywhere on your query (even nested ones). * * If you have a `.where()` clause, it will update the data. If you don't, it will create the data. * * Other differences between the two operations are: * ### Create * - You can set as many data as you want. * - You can set the same data multiple times, it will create multiple entries. * - Obligatory data is required. * * ### Update * - Just one data can be set. * - All fields are optional. * * Here is simple examples: * * @example * ```typescript * // CREATING A USER * const user = await User.objects.set((qs) => qs.data({ name: 'John Doe', email: 'johndoe@example.com' })); * * // CREATING MULTIPLE USERS * const user = await User.objects.set((qs) => qs.data( * { name: 'John Doe', email: 'johndoe@example.com' }, * { name: 'Victor Baptista', email: 'victor@example.com' } * )); * * // UPDATING A USER * const user = await User.objects.set((qs) => qs.where({ id: 1 }).data({ name: 'John Doe' })); * ``` * * Great, now comes the cool part: Relations! * Relations are for querying purposes, most of the time, but you can also set data through them. Palmares * takes care of the rest for you. Let's see an example: * * @example * ```typescript * * // CREATING A PROFILE TYPE WITH TWO ASSOCIATED USERS AND ASSIGNING BOTH USERS TO ONE COMPANY * const company = await Company.default.set((qs) => * qs * .join(User, 'usersOfCompany', (qs) => * qs * .join(ProfileType, 'profileType', (qs) => * qs.data({ * name: 'admin2' * }) * ) * .data({ * age: 10, * name: 'test1', * uuid: 'a417f723-ddb7-4f8c-a42c-0b5975e4cf5f', * userType: 'admin' * }, { * age: 11, * name: 'test2', * uuid: '77ac0c15-09c7-425e-9d77-97c0f973e8e6', * userType: 'user' * }) * ) * .data({ * address: 'test', * name: 'test5' * }) * ); * * // UPDATING A COMPANY, AND ONLY THE COMPANY BY QUERYING BY THE PROFILE TYPE * const company = await Company.default.set((qs) => * qs * .join(User, 'usersOfCompany', (qs) => * qs * .join(ProfileType, 'profileType', (qs) => * qs.where({ id: 5 }) * ) * ) * .data({ * name: 'hello' * }) * ); * ``` * * @param data - The data to be set on the database. * * @returns - Returns a new QuerySet. You cannot set any more chaining method after this one. */ data(...data) { const newQuerySet = new QuerySet(this.__model, "set"); for (const field of Object.keys(this.__query)) { if (field === "data") continue; newQuerySet["__query"][field] = this.__query[field]; } if (this.__query.data) { const [setOfAllFieldNames, existingData] = this.__query.data(); newQuerySet["__query"].data = () => { const newData = []; for (const existingDataToAddOrUpdate of existingData) { for (const dataToAddOrUpdate of data) { for (const field of Object.keys(dataToAddOrUpdate)) setOfAllFieldNames.add(field); newData.push({ ...dataToAddOrUpdate, ...existingDataToAddOrUpdate }); } } return [ setOfAllFieldNames, newData ]; }; } else newQuerySet["__query"].data = () => { const fieldNamesOfData = /* @__PURE__ */ new Set(); for (const dataToAddOrUpdate of data) { for (const field of Object.keys(dataToAddOrUpdate)) fieldNamesOfData.add(field); } return [ fieldNamesOfData, data ]; }; newQuerySet["__hasSearch"] = this["__hasSearch"]; newQuerySet["__isJoin"] = this["__isJoin"]; newQuerySet["__markToCreateOrUpdate"] = true; return newQuerySet; } }; var RemoveQuerySet = class extends CommonQuerySet { static { __name(this, "RemoveQuerySet"); } /** * This methods sets the data to be removed from the database. You must always use * this, otherwise it will not remove the data. We know it's kinda redundant sometimes * but this makes it explicit that you are removing the data from the database. Sometimes * something being a little more verbose is actually a nice thing! * * A simple example: * * @example * ```typescript * const user = await User.objects.remove((qs) => qs.where({ id: 1 }).remove()); * ``` * * This will remove all users with the id of 1. Now let's go to a more complex example: * * @example * ```typescript * const companies = await Company.default.remove((qs) => * qs * .join(User, 'usersOfCompany', (qs) => * qs * .join(ProfileType, 'profileType', (qs) => * qs.where({ * id: 5 * }) * ) * .remove() * ) * .remove() * ); * ``` * * This will remove all companies that have a user with a profile type of 5. And all * the companies related with that user. * * "Will ProfileType be removed?" No, it will not. Because you haven't set .remove() on it's * queryset. So ProfileType is just there for querying purposes but will not be removed, * how cool is that? * * Usually you will want to use this method just on the root queryset, not on its children. * * @returns - Returns a new QuerySet. You cannot set any more chaining method after this one. */ remove() { const newQuerySet = new QuerySet(this.__model, "remove"); for (const field of Object.keys(this.__query)) { newQuerySet["__query"][field] = this.__query[field]; } newQuerySet["__hasSearch"] = this["__hasSearch"]; newQuerySet["__isJoin"] = this["__isJoin"]; newQuerySet["__markToRemove"] = true; return newQuerySet; } }; // src/models/manager.ts var Manager = class { static { __name(this, "Manager"); } $$type = "$PManager"; __instances = {}; __engineInstances; __defaultEngineInstanceName; __models; __modelKls; __isLazyInitializing = false; constructor() { this.__instances = {}; this.__engineInstances = {}; this.__models = {}; this.__defaultEngineInstanceName = ""; this.__isLazyInitializing = false; } __setModel(engineName, initializedModel) { this.__models[engineName] = initializedModel; } /** * This function is used to initialize the models outside of the default Domain lifecycle. Sometimes we might define * the model outside of the domain, because of that we need to initialize the models manually. Usually it's a lot * better to just use the models after they've been initialized but sometimes it might happen that you need to * initialize them before the app is ready. For that we will use this. This will create a new Database instance if it * doesn't exist, load the settings from where we can find them and initialize the domains. After that we are able to * retrieve the data from the model */ async __verifyIfNotInitializedAndInitializeModels(engineName) { const database = new Databases(); const canInitializeTheModels = this.__isLazyInitializing === false && database.isInitialized === false && database.isInitializing === false; this.__isLazyInitializing = true; if (canInitializeTheModels) { const globalDomains = globalThis.$PCachedDatabaseDomains; let domains = globalDomains; let settings = database.settings; if (Array.isArray(domains) === false || settings === void 0) { settings = (0, import_core4.getSettings)(); const { domains: initializedDomains } = await (0, import_core4.initializeDomains)(settings, settings?.$$test ? { ignoreCache: true, ignoreCommands: true } : void 0); domains = initializedDomains; } await database.lazyInitializeEngine(engineName, settings, domains); return true; } return new Promise((resolve) => { const verifyIfInitialized = /* @__PURE__ */ __name(() => { const doesInstanceExists = this.__instances[engineName] !== void 0; if (doesInstanceExists) return resolve(true); setTimeout(() => verifyIfInitialized(), 100); }, "verifyIfInitialized"); verifyIfInitialized(); }); } getModel(engineName) { return this.__models[engineName]; } /** * Retrieves the instance of the model defined in the database. Although you can define the engine instance on * the manager itself, the engine instance in this method can be overridden to retrieve the model of another different * engine instance. * * @throws {ManagerEngineInstanceNotFoundError} - When we cannot find a engine instance for this name. * * @param engineName - The name of the engine defined in `DATABASES` settings in `settings.ts` * * @return - The instance of the the model inside that engine instance */ async getInstance(engineName) { const engineInstanceName = engineName || this.__defaultEngineInstanceName; const doesInstanceExists = this.__instances[engineInstanceName] !== void 0; if (doesInstanceExists) return this.__instances[engineInstanceName].instance; const hasLazilyInitialized = await this.__verifyIfNotInitializedAndInitializeModels(engineInstanceName); if (!hasLazilyInitialized) return this.getInstance(engineName); throw new ManagerEngineInstanceNotFoundError(engineInstanceName); } __setInstance(engineName, instance) { const isDefaultEngineInstanceNameEmpty = this.__defaultEngineInstanceName === ""; if (isDefaultEngineInstanceNameEmpty) this.__defaultEngineInstanceName = engineName; this.__instances[engineName] = instance; } async getEngineInstance(engineName) { const engineInstanceName = engineName || this.__defaultEngineInstanceName; const doesInstanceExists = this.__engineInstances[engineInstanceName] !== void 0; if (doesInstanceExists) return this.__engineInstances[engineInstanceName]; const hasLazilyInitialized = await this.__verifyIfNotInitializedAndInitializeModels(engineInstanceName); if (hasLazilyInitialized) return this.getEngineInstance(engineName); throw new ManagerEngineInstanceNotFoundError(engineInstanceName); } __setEngineInstance(engineName, instance) { const isDefaultEngineInstanceNameEmpty = this.__defaultEngineInstanceName === ""; if (isDefaultEngineInstanceNameEmpty) this.__defaultEngineInstanceName = engineName; this.__engineInstances[engineName] = instance; } /** * A simple get method for retrieving the data of a model. The result will ALWAYS be an array. * * To query you must pass a callback that receives and returns a QuerySet instance. A QuerySet * is an object that contains all of the parameters that you can use to query the database. * * IMPORTANT: `It's not a **REAL** query builder, because there is no way to guarantee where * the data is coming from. It's just a simple and convenient way to make queries across different engines.` * * @example * ```ts * const users = await User.default.get((qs) => qs.fields(['id', 'name']).where({ name: 'John' })); * * // Or you can use like * * * const qs = QuerySet.new('get').fields(['id', 'name']).where({ name: 'John' }); * * const users = await User.default.get(() => qs); * ``` * * @param callback - A callback that receives a QuerySet instance and returns a QuerySet instance. * @param args - Arguments that can be passed to the query. * * @return - An array of instances retrieved by this query. */ async get(callback, args) { const isValidEngineName = typeof args?.engineName === "string" && args.engineName !== ""; const engineInstanceName = isValidEngineName ? args.engineName : this.__defaultEngineInstanceName; const engineInstance = await this.getEngineInstance(engineInstanceName); const initializedDefaultEngineInstanceNameOrSelectedEngineInstanceName = isValidEngineName ? args.engineName : this.__defaultEngineInstanceName; const modelInstance = this.getModel(initializedDefaultEngineInstanceNameOrSelectedEngineInstanceName); const modelConstructor = modelInstance.constructor; return callback(new GetQuerySet(modelConstructor, "get"))["__queryTheData"](modelConstructor, engineInstance); } async set(callback, args) { const isValidEngineName = typeof args?.engineName === "string" && args.engineName !== ""; const engineInstanceName = isValidEngineName ? args.engineName : this.__defaultEngineInstanceName; const engineInstance = await this.getEngineInstance(engineInstanceName); const initializedDefaultEngineInstanceNameOrSelectedEngineInstanceName = isValidEngineName ? args.engineName : this.__defaultEngineInstanceName; const modelInstance = this.getModel(initializedDefaultEngineInstanceNameOrSelectedEngineInstanceName); const modelConstructor = modelInstance.constructor; return callback(new SetQuerySet(modelConstructor, "set"))["__queryTheData"](modelConstructor, engineInstance); } async remove(callback, args) { const isValidEngineName = typeof args?.engineName === "string" && args.engineName !== ""; const engineInstanceName = isValidEngineName ? args.engineName : this.__defaultEngineInstanceName; const engineInstance = await this.getEngineInstance(engineInstanceName); const initializedDefaultEngineInstanceNameOrSelectedEngineInstanceName = isValidEngineName ? args.engineName : this.__defaultEngineInstanceName; const modelInstance = this.getModel(initializedDefaultEngineInstanceNameOrSelectedEngineInstanceName); const modelConstructor = modelInstance.constructor; return callback(new RemoveQuerySet(modelConstructor, "remove"))["__queryTheData"](modelConstructor, engineInstance); } }; var DefaultManager = class extends Manager { static { __name(this, "DefaultManager"); } }; // src/models/model.ts var BaseModel = class { static { __name(this, "BaseModel"); } static $$type = "$PModel"; __className = this.constructor.name; __stringfiedArgumentsOfEvents = /* @__PURE__ */ new Set(); __eventsUnsubscribers = []; static __isState = false; // It would be kinda bad on performance if we always looped through all of the fields of a model to parse them. // So we store the fields that have parsers here. static __fieldParsersByEngine = /* @__PURE__ */ new Map(); static __associations = {}; // This model uses other models as ForeignKey static __directlyRelatedTo = {}; // Other models use this model as ForeignKey static __indirectlyRelatedTo = {}; static __indirectlyRelatedModels = indirectlyRelatedModels; static __primaryKeys = []; static __domainName; static __domainPath; static __callAfterAllModelsAreLoadedToSetupRelations = /* @__PURE__ */ new Map(); static __lazyOptions = {}; static __lazyFields = {}; static __cachedHashedName; static __cachedName; static __cachedOriginalName; static __cachedFields = void 0; static __customOptions = void 0; static __cachedOptions = void 0; static __hasLoadedManagers = false; static __cachedManagers = void 0; static __hasLoadedAbstracts = false; static __instance; static __initialized = {}; constructor() { const baseModelConstructor = this.constructor; if (baseModelConstructor.__instance) return baseModelConstructor.__instance; const newInstance = this; if (newInstance.options?.abstract) newInstance.options.managed = false; this.constructor.__instance = newInstance; return newInstance; } // eslint-disable-next-line ts/require-await async __initializeManagers(engineInstance, modelInstance, translatedModelInstance) { const modelConstructor = this.constructor; const managers = modelConstructor["__getManagers"](); const managerEntries = Object.entries(managers); for (const [managerName, manager] of managerEntries) { manager["__setModel"](engineInstance.connectionName, modelInstance); manager["__setInstance"](engineInstance.connectionName, translatedModelInstance); manager["__setEngineInstance"](engineInstance.connectionName, engineInstance); } } /** * This will add event listeners to the model. So when an event like `.set` or `.remove` is triggered, we will call * the event handler that was defined in the model using the `onSet` or `onRemove` options. * * By default we will take care to prevent the same data being triggered twice. * So we stringify the data and compare it, so for example if a model is trying to save the same data it * received through an event it will not trigger the event again by default. * * @param engineInstance - The current engine instance we are initializing this model instance */ async __initializeEvents(engineInstance) { if (!engineInstance) return; if (!engineInstance.databaseSettings?.events?.emitter) return; const existingEngineInstanceName = engineInstance.connectionName; const modelInstance = this; const modelConstructor = this.constructor; if (!modelInstance.options) return; for (const operationType of [ "onSet", "onRemove" ]) { const eventHandler = typeof modelInstance.options[operationType] === "function" ? modelInstance.options[operationType] : typeof modelInstance.options[operationType] === "object" ? modelInstance.options[operationType].handler : void 0; if (!eventHandler) continue; const isToPreventCallerToBeTheHandled = typeof modelInstance.options[operationType] === "function" ? true : typeof modelInstance.options[operationType] === "object" ? modelInstance.options[operationType].preventCallerToBeTheHandled : void 0; const eventNameToUse = `${modelConstructor["__hashedName"]()}.${operationType}`; const eventEmitter = await Promise.resolve(engineInstance.databaseSettings.events.emitter); this.__eventsUnsubscribers.push(await eventEmitter.addEventListenerWithoutResult(eventNameToUse, async (engineInstanceName, args) => { const isCallerDifferentThanHandler = engineInstanceName !== existingEngineInstanceName; const argsAsString = JSON.stringify(args); this.__stringfiedArgumentsOfEvents.add(argsAsString); if (isToPreventCallerToBeTheHandled && isCallerDifferentThanHandler) await Promise.resolve(eventHandler(args)); else if (!isToPreventCallerToBeTheHandled) await Promise.resolve(eventHandler(args)); this.__stringfiedArgumentsOfEvents.delete(argsAsString); })); } } /** * Initializes the model and returns the model instance for the current engine instance that is being used. */ static async __init(engineInstance, domainName, domainPath, lazyLoadFieldsCallback, options) { if (this.__initialized[engineInstance.connectionName]) return this.__initialized[engineInstance.connectionName]; const currentPalmaresModelInstance = new this(); this.__domainName = domainName; this.__domainPath = domainPath; const functionToCallToTranslateModel = factoryFunctionForModelTranslate(engineInstance, currentPalmaresModelInstance, lazyLoadFieldsCallback, options || {}); const [initializedModelInstance, _] = await Promise.all([ functionToCallToTranslateModel(), currentPalmaresModelInstance.__initializeEvents(engineInstance) ]); const translated = { instance: initializedModelInstance, modifyItself: /* @__PURE__ */ __name((newTranslatedInstance) => { translated.instance = newTranslatedInstance; }, "modifyItself") }; await currentPalmaresModelInstance.__initializeManagers(engineInstance, currentPalmaresModelInstance, translated); this.constructor.__initialized = { [engineInstance.connectionName]: translated }; return translated; } /** * Compare this and another model to see if they are equal so we can create the migrations automatically for them. * You see that we do not compare the fields, for the fields we have a hole set of `CRUD` operations * if something changes there. * So it doesn't matter if two models don't have the same set of fields, if the options are equal, * then they are equal. * * @param model - The model to compare to the current model. * * @returns - Returns true if the models are equal and false otherwise. */ // eslint-disable-next-line ts/require-await static async __compareModels(engine, originalModel, otherModel) { const currentModelOptions = originalModel["_options"](); const otherModelOptions = otherModel["_options"](); let areCustomOptionsEqual = true; if (engine.models.compare && currentModelOptions?.customOptions && otherModelOptions?.customOptions) areCustomOptionsEqual = engine.models.compare(currentModelOptions.customOptions, otherModelOptions.customOptions); return currentModelOptions?.abstract === otherModelOptions?.abstract && currentModelOptions?.underscored === otherModelOptions?.underscored && currentModelOptions?.tableName === otherModelOptions?.tableName && JSON.stringify(currentModelOptions?.ordering) === JSON.stringify(otherModelOptions?.ordering) && JSON.stringify(currentModelOptions?.indexes) === JSON.stringify(otherModelOptions?.indexes) && JSON.stringify(currentModelOptions?.databases) === JSON.stringify(otherModelOptions?.databases) && areCustomOptionsEqual; } /** * Since most data is private, we use this to extract all the data that the model has that might be useful for engine * instances. This way we don't need to expose the model to the engine */ __getModelAttributes() { const modelConstructor = this.constructor; const fields = modelConstructor._fields(); const options = modelConstructor._options(); const fieldsWithAttributes = Object.entries(fields).reduce((accumulator, [fieldName, field]) => { accumulator[fieldName] = field["__getArguments"](); return accumulator; }, {}); return { modelName: modelConstructor.__originalName(), fields: fieldsWithAttributes, options }; } /** * Retrieves the managers from the model constructor. * * This is useful for getting the managers from an abstract model class. */ static __getManagers() { if (this.__hasLoadedManagers !== true) { this.__hasLoadedManagers = true; const managers = {}; let prototype = this; while (prototype) { if (!(prototype.prototype instanceof Model)) break; const propertyNamesOfModel = Object.getOwnPropertyNames(prototype); for (const propName of propertyNamesOfModel) { const value = this[propName]; if (value && value["$$type"] === "$PManager") managers[propName] = value; } prototype = Object.getPrototypeOf(prototype); } this.__cachedManagers = { ...managers, // eslint-disable-next-line ts/no-unnecessary-condition ...this.__cachedManagers || {} }; } return this.__cachedManagers || {}; } /** * This will load all of the abstract instances of the model. The abstracts will append 3 types of * data in the current model: * fields, options, managers and other abstracts * * So for fields we will just accept the ones not already defined in the field, if there * is any clash we will throw an error. * For options, we will only add them if the options are not already defined for the model. * Managers are similar to fields, we will not accept clashing managers with the same manager name. * * @param abstractInstance - The model class that we are instantiating. * @param composedAbstracts - We can have an abstract with an abstract and so on, for that a recursive approach * seems a good solution, this is an array with all of the abstracts that were already loaded for the current model. */ static __loadAbstract(abstractConstructor, composedAbstracts) { const abstractInstance = new abstractConstructor(); const modelInstance = new this(); const modelConstructor = this; const abstractInstanceName = abstractConstructor.name; if (composedAbstracts.includes(abstractInstanceName)) throw new ModelCircularAbstractError(this.name, abstractInstanceName); const abstractManagers = Object.entries(abstractConstructor.__getManagers()); const abstractFieldEntries = Object.entries(abstractConstructor._fields()); for (const [fieldName, field] of abstractFieldEntries) { if (modelInstance.fields[fieldName] || modelConstructor.__lazyFields?.[fieldName]) continue; modelConstructor.__lazyFields ??= {}; modelConstructor.__lazyFields[fieldName] = field; } const areAbstractInstanceOptionsDefined = Object.keys(abstractInstance.options || {}).length > 1; if (areAbstractInstanceOptionsDefined) { const optionsFromAbstract = abstractConstructor._options(); if (optionsFromAbstract) { const duplicatedOptions = structuredClone(optionsFromAbstract); delete duplicatedOptions.abstract; delete duplicatedOptions.managed; modelConstructor.__cachedOptions = { ...duplicatedOptions, ...modelConstructor.__lazyOptions || {} }; } } for (const [managerName, managerInstance] of abstractManagers) { if (modelConstructor[managerName]) continue; modelConstructor[managerName] = managerInstance; } } /** * Initializes all of the abstract classes of the model and loads them to the current model. * * With this we will have the model with all of the fields, options and managers as the other abstracts. */ static __initializeAbstracts() { if (this.__hasLoadedAbstracts) return; const modelInstance = new this(); const alreadyComposedAbstracts = [ this.name ]; for (const abstractModelConstructor of modelInstance["abstracts"]) this.__loadAbstract(abstractModelConstructor, alreadyComposedAbstracts); this.__hasLoadedAbstracts = true; } /** * Get the options of the model. Use this to get the options of the model since here we will use the cached data * if it exists. */ static _options(modelInstance) { if (!modelInstance) modelInstance = new this(); const defaultOptions = getDefaultModelOptions(); this.__initializeAbstracts(); if (this.__cachedOptions === void 0) { const keysOfDefaultOptions = Object.keys(defaultOptions); for (const defaultModelOptionKey of keysOfDefaultOptions) { if (defaultModelOptionKey in (modelInstance.options || {}) === false) { if (typeof modelInstance.options !== "object") modelInstance.options = {}; modelInstance.options[defaultModelOptionKey] = defaultOptions[defaultModelOptionKey]; } } this.__cachedOptions = { // eslint-disable-next-line ts/no-unnecessary-condition ...modelInstance.options || {}, ...this.__lazyOptions || {}, customOptions: this.__customOptions || modelInstance.options?.customOptions || (this.__cachedOptions || {}).customOptions }; if (this.__cachedOptions?.tableName === void 0) { const modelName = this.__originalName(); const firstLetterLowerCase = modelName.charAt(0).toLowerCase() + modelName.slice(1); this.__cachedOptions.tableName = import_core5.utils.camelCaseToHyphenOrSnakeCase(firstLetterLowerCase); } } return this.__cachedOptions; } static _fields(modelInstance) { if (!modelInstance) modelInstance = new this(); const modelInstanceAsModel = modelInstance; this.__initializeAbstracts(); if (this.__cachedFields === void 0) { let modelHasNoUniqueFields = true; let modelHasNoPrimaryKey = true; let fieldsDefinedOnModel = modelInstanceAsModel.fields; if (this.__lazyFields) fieldsDefinedOnModel = { ...fieldsDefinedOnModel, ...this.__lazyFields }; const allFields = Object.entries(fieldsDefinedOnModel); if (this._options()?.abstract !== true) { for (const [fieldName, field] of allFields) { if (field["__unique"]) modelHasNoUniqueFields = false; if (field["__primaryKey"]) modelHasNoPrimaryKey = false; field["__init"](fieldName, this); } if (modelHasNoUniqueFields) throw new ModelNoUniqueFieldsError(this.__cachedName); if (modelHasNoPrimaryKey) throw new ModelNoPrimaryKeyFieldError(this.__cachedName); } modelInstance.fields = fieldsDefinedOnModel; this.__cachedFields = fieldsDefinedOnModel; } return this.__cachedFields; } static __originalName() { if (typeof this.__cachedOriginalName === "string") return this.__cachedOriginalName; if (this.__isState) this.__cachedOriginalName = this.name; else this.__cachedOriginalName = typeof this.__originalName === "string" ? this.__originalName : this.name; return this.__cachedOriginalName; } /** * We use this so the name of the models does not clash with the original ones during migration. * During migration we will have 2 instances of the same model running at the * same time: * * 1. The state model, built from the migration files. * 2. The original model. */ static __getName() { if (typeof this.__cachedName === "string") return this.__cachedName; if (this.__isState) this.__cachedName = `State${this.name}`; else this.__cachedName = this.name; return this.__cachedName; } /** * We use the original model name to create a hash name of the model, a hash name of the model is used so * we can send events back and forth for the model between * multiple palmares instances. * * @returns - The hashed name of the model. */ static __hashedName() { const originalModelName = this.__originalName(); if (this.__cachedHashedName === void 0) { this.__cachedHashedName = hashString(originalModelName); } return this.__cachedHashedName; } static async __fieldsToString(engine, fields, indentation = 0) { const customImportsOfModel = []; const allFields = Object.entries(fields); const ident = " ".repeat(indentation); const fieldsIdent = " ".repeat(indentation + 1); const stringifiedFields = []; for (let i = 0; i < allFields.length; i++) { const fieldName = allFields[i][0]; const field = allFields[i][1]; const isLastField = i === allFields.length - 1; const { stringfied, customImports } = await field["__toString"](engine); stringifiedFields.push(`${fieldsIdent}${fieldName}: ${stringfied}${isLastField ? "" : ",\n"}`); getUniqueCustomImports(customImports, customImportsOfModel); } return { asString: `${ident}{ ${stringifiedFields.join("")} ${ident}}`, customImports: customImportsOfModel }; } // eslint-disable-next-line ts/require-await static async __optionsToString(engine, indentation = 0, options) { const ident = " ".repeat(indentation); const optionsIndent = " ".repeat(indentation + 1); const newOptions = { ...getDefaultModelOptions(), ...options }; let stringfiedCustomOptions = "{}"; if (engine.models.modelToString) { const { result } = engine.models.modelToString(newOptions); stringfiedCustomOptions = result; } return `${ident}{ ${optionsIndent}abstract: ${newOptions.abstract}, ${optionsIndent}underscored: ${newOptions.underscored}, ${optionsIndent}tableName: ${typeof newOptions.tableName === "string" ? `"${newOptions.tableName}"` : newOptions.tableName}, ${optionsIndent}managed: ${newOptions.managed}, ${optionsIndent}ordering: [${options.ordering ? newOptions.ordering.map((field) => `"${field}"`) : ""}], ${optionsIndent}indexes: [${options.indexes ? newOptions.indexes.map((dbIndex, i) => `{ unique: ${dbIndex.unique}, fields: ${dbIndex.fields.map((field) => `"${field}"`)} }${i === (newOptions.indexes.length || 1) - 1 ? "" : ","}`) : ""}], ${optionsIndent}databases: [${options.databases ? newOptions.databases.map((database) => `"${database}"`) : ""}], ${optionsIndent}customOptions: ${stringfiedCustomOptions} ${ident}}`; } }; var BaseModelWithoutMethods = BaseModel; var Model = class extends BaseModelWithoutMethods { static { __name(this, "Model"); } static $$type = "$PModel"; //static [managers: string]: Manager | ((...args: any) => any) | ModelFieldsType; fields = {}; options = void 0; abstracts = []; }; function model() { let DefaultModel = class DefaultModel extends Model { static { __name(this, "DefaultModel"); } static $$type = "$PModel"; static __isState = false; // It would be kinda bad on performance if we always looped through all of the fields of a model to parse them. // So we store the fields that have parsers here and we will // loop through it here. static __fieldParsersByEngine = /* @__PURE__ */ new Map(); static __associations = {}; // This model uses other models as ForeignKey static __directlyRelatedTo = {}; // Other models use this model as ForeignKey static __indirectlyRelatedTo = {}; static __primaryKeys = []; static __callAfterAllModelsAreLoadedToSetupRelations = []; static __lazyFields = {}; static __hasLoadedManagers = false; static __hasLoadedAbstracts = false; static __initialized = {}; static default = new DefaultManager(); /** * This will append fields to the current model. It is useful for extending the models so you can * lazy load the fields. It */ static appendFields(fields) { const modelConstructor = this; const allFieldEntries = Object.entries(fields); for (const [fieldName, field] of allFieldEntries) { if (modelConstructor["__lazyFields"]?.[fieldName]) { modelConstructor["__lazyFields"][fieldName] = field; } } return this; } static setCustomOptions(customOptions) { this["__customOptions"] = customOptions; return this; } static setManagers(managers) { for (const [managerName, managerInstance] of Object.entries(managers)) { this[managerName] = managerInstance; } return this; } }; return DefaultModel; } __name(model, "model"); function initialize(modelName, args) { let ModelConstructor = class ModelConstructor extends model() { static { __name(this, "ModelConstructor"); } static __isState = false; // It would be kinda bad on performance if we always looped through all of the fields of a model to parse them. // So we store the fields that have parsers here and we will // loop through it here. static __fieldParsersByEngine = /* @__PURE__ */ new Map(); static __associations = {}; // This model uses other models as ForeignKey static __directlyRelatedTo = {}; // Other models use this model as ForeignKey static __indirectlyRelatedTo = {}; static __primaryKeys = []; static __callAfterAllModelsAreLoadedToSetupRelations = []; static __lazyFields = {}; static __hasLoadedManagers = false; static __hasLoadedAbstracts = false; static __initialized = {}; static __cachedName = modelName; static __cachedOriginalName = modelName; static __cachedManagers = {}; static __originalName() { if (typeof this.__cachedOriginalName === "string") return this.__cachedOriginalName; if (this.__isState) this.__cachedOriginalName = modelName; else this.__cachedOriginalName = typeof this.__originalName === "string" ? this.__originalName : modelName; return this.__cachedOriginalName; } /** * We use this so the name of the models does not clash with the original ones during migration. * During migration we will have 2 instances of the same model running at the * same time: * * 1. The state model, built from the migration files. * 2. The original model. */ static __getName() { if (typeof this.__cachedName === "string") return this.__cachedName; if (this.__isState) this.__cachedName = `State${modelName}`; else this.__cachedName = modelName; return this.__cachedName; } fields = args.fields; options = args.options; abstracts = args.abstracts || []; }; for (const [managerName, managerFunctions] of Object.entries(args.managers || {})) { let managerInstance; if (managerFunctions?.["$$type"] !== "$PManager") { let NewManagerInstance = class NewManagerInstance extends Manager { static { __name(this, "NewManagerInstance"); } static __lazyFields = {}; }; managerInstance = new NewManagerInstance(); for (const [managerFunctionName, managerFunction] of Object.entries(managerFunctions)) { managerInstance[managerFunctionName] = managerFunction.bind(managerInstance); } } else managerInstance = managerFunctions; ModelConstructor[managerName] = managerInstance; ModelConstructor.__cachedManagers[managerName] = managerInstance; } return ModelConstructor; } __name(initialize, "initialize"); // 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/actions/misc.ts var RunJs = class extends Operation { static { __name(this, "RunJs"); } code; constructor(code) { super(); this.code = code; } async run(migration, engineInstance, _, toState, returnOfInit) { const stateModels = {}; const modelNamesAlreadyAvailable = Object.keys(toState); for (const modelName of modelNamesAlreadyAvailable) { stateModels[modelName] = toState[modelName].class; } await Promise.resolve(this.code(migration, engineInstance, stateModels, returnOfInit)); } }; // 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/migrations/state.ts var State = class { static { __name(this, "State"); } modelsByName = {}; initializedModelsByName = {}; stateNumber = 0; constructor() { } /** * Gets the model instance by a given model name. If the model does not exist * we will create a new model. * * @param modelName - The name of the model to get the model instance for. * * @returns - Returns a model instance with all of the fields and options. */ async get(modelName) { const modelInstance = this.modelsByName[modelName]; const doesModelExist = modelInstance !== void 0; if (doesModelExist) return modelInstance; else return await this.newModel(modelName); } /** * Sets a new model in the state. Probably this is not needed because we are changing the values in place. * But since problems might occur we will keep it. * * @param modelName - The name of the model to set. * @param modifiedModel - The modified model instance to set. */ // eslint-disable-next-line ts/require-await async set(modelName, modifiedModel) { this.modelsByName[modelName] = modifiedModel; } /** * Creates a new model with a different name than the original one. Instead of it being `${nameOfTheModel}` * it becomes `State${nameOfTheModel}` so, like `Users` will be `StateUsers`, this because the state will * clash with the original ones when being initialized. * * After that we add this to the `modelsByModelName` object with the ORIGINAL model name. And then call `.get()` * to retrieve the model to the user. * * @param modelName - The name of the model that is being created. * * @return - Returns the newly created model. We use the .get method because if we have any side effects to the * model we will also run them. */ async newModel(modelName) { const ModelClass = class StateModel extends model() { static { __name(this, "StateModel"); } static isState = true; static __cachedOriginalName = modelName; static __cachedName = `State${modelName}`; fields = {}; options = {}; }; const newModelInstance = new ModelClass(); this.modelsByName[modelName] = newModelInstance; return this.get(modelName); } /** * Removes a specific model from the state. * * @param modelName - The name of the model to remove. */ // eslint-disable-next-line ts/require-await async remove(modelName) { delete this.modelsByName[modelName]; } /** * This method is used to initialized the models created inside the state, so what we do is that * first we recreate the state of the database using this model and after that we translate all of the * models to something that the engine instance is able to understand and interpret. * * @param engineInstance - The engine instance to use to initialize the models. */ async initializeStateModels(engineInstance) { const modelsInState = Object.values(this.modelsByName); const stateNumber = this.stateNumber; return initializeModels(engineInstance, modelsInState.map((stateModel) => { const ModelClass = class StateModel extends model() { static { __name(this, "StateModel"); } static __isState = true; static __stateNumber = stateNumber; static _initialized = {}; static __domainName = stateModel.constructor["__domainName"]; static __domainPath = stateModel.constructor["__domainPath"]; static __cachedOriginalName = stateModel.constructor["__cachedOriginalName"]; static __cachedName = stateModel.constructor["__cachedName"]; static __lazyFields = stateModel.constructor["__lazyFields"]; static __lazyOptions = stateModel.constructor["__lazyOptions"]; static __cachedOptions = void 0; }; return ModelClass; })); } /** * Retrieves all of the models that were initialized in the state by it's original name. * By default we will duplicate the engine instance so we can initialize the models in the engine * instance without worrying if the names will crash with one another. * * For example: if we have a `StateUsers` model, we will return `Users` as the original name. * * Generally this should be used to send the models when running the migration files. We also send * a function called closeEngineInstance so we are able to close the connection to the database * after we are done with the migration. * * @param engineInstance - The engine instance that is being used, so we are able to duplicate it, * and close the connection afterwards. * * @return - Returns an object with the models that were initialized in the state and the engine * instance. */ async geInitializedModelsByName(engineInstance) { let duplicatedEngineInstance = void 0; const wasDefaultDuplicateCalled = { value: false }; if (engineInstance.duplicate === void 0) duplicatedEngineInstance = await defaultEngineDuplicate(engineInstance, wasDefaultDuplicateCalled)(); else duplicatedEngineInstance = await engineInstance.duplicate(defaultEngineDuplicate(engineInstance, wasDefaultDuplicateCalled)); if (wasDefaultDuplicateCalled.value === false) throw new DefaultDuplicateFunctionNotCalledOnEngine(); const closeEngineInstance = duplicatedEngineInstance.close?.bind(duplicatedEngineInstance); const wasInitialized = Object.keys(this.initializedModelsByName).length > 0; if (wasInitialized) return { initializedModels: this.initializedModelsByName, closeEngineInstance }; const initializedModels = await this.initializeStateModels(duplicatedEngineInstance); for (const initializedModel of initializedModels) { this.initializedModelsByName[initializedModel.class["__originalName"]()] = initializedModel; } return { initializedModels: this.initializedModelsByName, closeEngineInstance }; } /** * The factory method that should be called to create a new instance of the state. * The state is used to keep track how the models were for every migration file. On the migration files * we know how the model was at a specific moment in time. The original models defined by the user in the * `models.ts` file will keep track of the state of the models at the current time, it's like a "picture" * of the models right now. * * When creating a new migration file automatically we need to know what the state were, and how it will be. * How it were we can get from the previous migrations, how it will be we can get from the "picture" of how it * is right now. * * @param foundMigrations - All of the migration files that were found for a specific engine instance. * @param untilMigration - Until what migration file we want to retrieve the state? * @param untilOperationIndex - Until what migration operation index we want to retrieve the state? For example * we can run the migration until the migration file `002_auto_create_users_table.ts` but on this migration we can * have multiple operations. This enables us to run the migration until the operation index `1` which is the * second operation in the migration file. This granularity is used for recreating the state when running the * migrations. * * @return - Returns the state instance that was created by traversing each migration file and operation in order. * */ static async buildState(foundMigrations, untilMigration, untilOperationIndex) { const state = new this(); for (const foundMigration of foundMigrations) { const isToBuildStateUntilThisMigration = foundMigration.migration.name === untilMigration; for (let i = 0; i < foundMigration.migration.operations.length; i++) { const isToBuildUntilOperationIndex = i === untilOperationIndex; if (isToBuildStateUntilThisMigration && isToBuildUntilOperationIndex) return state; const operation = foundMigration.migration.operations[i]; await operation.stateForwards(state, foundMigration.domainName, foundMigration.domainPath); } if (isToBuildStateUntilThisMigration) return state; } return state; } }; // src/migrations/makemigrations/index.ts var MakeMigrations = class { static { __name(this, "MakeMigrations"); } #originalModelsByName; #stateModelsByName; settings; database; filteredMigrationsOfDatabase; optionalArgs; engine; constructor(engine, database, settings, originalModels, stateModels, filteredMigrationsOfDatabase, optionalArgs) { this.engine = engine; this.database = database; this.filteredMigrationsOfDatabase = filteredMigrationsOfDatabase; this.optionalArgs = optionalArgs; this.settings = settings; this.modelsArrayToObjectByName(originalModels, stateModels); } /** * Adds the initialized models array to an object where the keys are the model name and the values are the * initialized model. We do this to both the original ones (defined in the application) and the models. * * @param originalModels - The models inside of the application, the ones defined in the models.(js/ts) file * or retrieved in the `getModels` method inside of the domain. * @param stateModels - The models built by the state, we run each migration in order and retrieve the state * of it, one after the other. */ modelsArrayToObjectByName(originalModels, stateModels) { this.#originalModelsByName = {}; this.#stateModelsByName = {}; for (const originalModel of originalModels) { this.#originalModelsByName[originalModel.class["__originalName"]()] = originalModel; } for (const stateModel of stateModels) { this.#stateModelsByName[stateModel.class["__originalName"]()] = stateModel; } } /** * This is used to verify if the models or the fields have been renamed, changed, created or removed. * We do it in a single and big function but probably you could separate it in less methods. * * Sometimes we cannot infer things so we need to ask the user if he really changed or not a model or a field. * That's because we might have cases where we might not be sure if the model or field was renamed or not. To * make it clear for the user it's better to ask so we use the 'Asker' instance to do this. * * How we find if anything was changed, created, removed or renamed is the same for models and fields so we use the * same method for both. * * Last but not least the operations are updated by reference (you know what it means to pass a value by value and * by reference?) We pass an array to operations we update it as we look to changes between the state and the models * defined in your application. * * @param originalModelsByNameOrFields - This param are the models or fields defined by the user, this is how the user * wants the data to be. It is an object where the key is the name of the field or of the model and the value is the * initialized model or the field instance. * @param stateModelsByNameOrFields - This param are the models or fields reconstructed by running each migration * in order. So this is how those models were before any change. * @param fieldOrModel - Are you running this on an object of fields or on an object of models? * @param operations - The array where we will hold all of the operations that we need to do in our migration. */ async #getOperationsFromModelsOrFields(originalModelsByNameOrFields, stateModelsByNameOrFields, fieldOrModel = "model", operations = []) { const appendOperation = /* @__PURE__ */ __name((operation) => operation ? operations.push(operation) : null, "appendOperation"); const originalModelOrFieldEntries = Object.entries(originalModelsByNameOrFields); const stateModelOrFieldEntries = Object.entries(stateModelsByNameOrFields); const modelsOrFieldsInOriginalButNotDefinedInState = []; const modelsOrFieldsInStateButNotDefinedInOriginal = []; for (const [stateFieldOrModelName, stateFieldOrModelObject] of stateModelOrFieldEntries) { const didRenamedFieldNameOrModelName = originalModelsByNameOrFields[stateFieldOrModelName] === void 0; if (didRenamedFieldNameOrModelName) { if (originalModelOrFieldEntries.length === stateModelOrFieldEntries.length) { let renamedTo = ""; for (let i = 0; i < originalModelOrFieldEntries.length; i++) { const originalModelOrFieldName = originalModelOrFieldEntries[i][0]; const hasTheUserRenamedTheModel = stateModelsByNameOrFields[originalModelOrFieldName] === void 0; if (hasTheUserRenamedTheModel) { renamedTo = originalModelOrFieldName; break; } } if (await asker.didUserRename(stateFieldOrModelName, renamedTo)) { const originalModelOrField = originalModelsByNameOrFields[renamedTo]; appendOperation(await this.#callbackIfRenamed(fieldOrModel, stateFieldOrModelName, renamedTo, originalModelOrField)); stateModelsByNameOrFields[renamedTo] = stateFieldOrModelObject; delete stateModelsByNameOrFields[stateFieldOrModelName]; } else { appendOperation(await this.#callbackIfDeleted(fieldOrModel, stateFieldOrModelName, stateFieldOrModelObject)); } } else { modelsOrFieldsInStateButNotDefinedInOriginal.push(stateFieldOrModelName); } } } for (const [originalFieldOrModelName, originalFieldOrModelObject] of originalModelOrFieldEntries) { const stateFieldOrModelObject = stateModelsByNameOrFields[originalFieldOrModelName]; const hasCreatedANewFieldOrModel = stateFieldOrModelObject === void 0; if (hasCreatedANewFieldOrModel) { const wereAModelOrFieldCreated = originalModelOrFieldEntries.length === stateModelOrFieldEntries.length; if (wereAModelOrFieldCreated) { appendOperation(await this.#callbackIfCreated(fieldOrModel, originalFieldOrModelName, originalFieldOrModelObject)); } else { modelsOrFieldsInOriginalButNotDefinedInState.push(originalFieldOrModelName); } } else { appendOperation(await this.#callbackIfChanged(operations, fieldOrModel, originalFieldOrModelName, stateFieldOrModelObject, originalFieldOrModelObject)); } } const wereModelsOrFieldsCreated = modelsOrFieldsInOriginalButNotDefinedInState.length > 0 && modelsOrFieldsInStateButNotDefinedInOriginal.length === 0; const wereModelsOrFieldsDeleted = modelsOrFieldsInStateButNotDefinedInOriginal.length > 0 && modelsOrFieldsInOriginalButNotDefinedInState.length === 0; if (wereModelsOrFieldsCreated) { for (const originalFieldOrModelNameToAdd of modelsOrFieldsInOriginalButNotDefinedInState) { const originalFieldOrModelObject = originalModelsByNameOrFields[originalFieldOrModelNameToAdd]; appendOperation(await this.#callbackIfCreated(fieldOrModel, originalFieldOrModelNameToAdd, originalFieldOrModelObject)); } } else if (wereModelsOrFieldsDeleted) { for (const stateFieldOrModelNameToRemove of modelsOrFieldsInStateButNotDefinedInOriginal) { const stateFieldOrModelObject = stateModelsByNameOrFields[stateFieldOrModelNameToRemove]; appendOperation(await this.#callbackIfDeleted(fieldOrModel, stateFieldOrModelNameToRemove, stateFieldOrModelObject)); } } else { const nonRenamedFieldsOrModels = [ ...modelsOrFieldsInOriginalButNotDefinedInState ]; for (const fieldOrModelNameInState of modelsOrFieldsInStateButNotDefinedInOriginal) { let answer = null; const didTheUserRenamedToOneOfTheOptions = nonRenamedFieldsOrModels.length !== 0; if (didTheUserRenamedToOneOfTheOptions) { answer = await asker.didUserRenameToOneOption(fieldOrModelNameInState, nonRenamedFieldsOrModels); } const stateFieldOrModelObject = stateModelsByNameOrFields[fieldOrModelNameInState]; const didTheUserDeletedTheFieldOrTheModel = answer === null; if (didTheUserDeletedTheFieldOrTheModel) { appendOperation(await this.#callbackIfDeleted(fieldOrModel, fieldOrModelNameInState, stateFieldOrModelObject)); } else { const answerAsString = answer; const originalModelOrFieldObject = originalModelsByNameOrFields[answerAsString]; appendOperation(await this.#callbackIfRenamed(fieldOrModel, fieldOrModelNameInState, answerAsString, originalModelOrFieldObject)); const indexOfSelectedAnswer = nonRenamedFieldsOrModels.indexOf(answerAsString); nonRenamedFieldsOrModels.splice(indexOfSelectedAnswer, 1); stateModelsByNameOrFields[answerAsString] = stateFieldOrModelObject; delete stateModelsByNameOrFields[fieldOrModelNameInState]; } } for (const fieldOrModelNameInOriginal of modelsOrFieldsInOriginalButNotDefinedInState) { const originalFieldOrModelObject = originalModelsByNameOrFields[fieldOrModelNameInOriginal]; const stateFieldOrModelObject = stateModelsByNameOrFields[fieldOrModelNameInOriginal]; if (stateFieldOrModelObject === void 0) { appendOperation(await this.#callbackIfCreated(fieldOrModel, fieldOrModelNameInOriginal, originalFieldOrModelObject)); } else { appendOperation(await this.#callbackIfChanged(operations, fieldOrModel, fieldOrModelNameInOriginal, stateFieldOrModelObject, originalFieldOrModelObject)); } } } } async #callbackIfRenamed(fieldOrModel, fieldOrModelName, renamedTo, originalModelOrField) { switch (fieldOrModel) { case "field": const originalField = originalModelOrField; return RenameField.toGenerate(originalField["__model"]?.["__domainName"], originalField["__model"]?.["__domainPath"], originalField["__model"]?.["__originalName"](), { fieldDefinition: originalField["__getArguments"](), fieldNameAfter: renamedTo, fieldNameBefore: fieldOrModelName }); case "model": const originalInitializedModel = originalModelOrField; return RenameModel.toGenerate(originalInitializedModel.domainName, originalInitializedModel.domainPath, renamedTo, { modelNameAfter: renamedTo, modelNameBefore: fieldOrModelName }); } } async #callbackIfDeleted(fieldOrModel, fieldOrModelName, stateFieldOrModel) { switch (fieldOrModel) { case "field": const stateField = stateFieldOrModel; return DeleteField.toGenerate(stateField["__model"]?.["__domainName"], stateField["__model"]?.["__domainPath"], stateField["__model"]?.["__originalName"](), { fieldName: fieldOrModelName }); case "model": const stateInitializedModel = stateFieldOrModel; return DeleteModel.toGenerate(stateInitializedModel.domainName, stateInitializedModel.domainPath, fieldOrModelName); } } async #callbackIfChanged(operations, fieldOrModel, fieldOrModelName, stateFieldOrModel, originalFieldOrModel) { switch (fieldOrModel) { case "field": return await this.#fieldWasUpdated(fieldOrModelName, stateFieldOrModel, originalFieldOrModel); case "model": return await this.#modelWasUpdated(operations, fieldOrModelName, stateFieldOrModel, originalFieldOrModel); } } async #callbackIfCreated(fieldOrModel, fieldOrModelName, originalFieldOrModel) { switch (fieldOrModel) { case "field": const originalField = originalFieldOrModel; const fieldName = fieldOrModelName; const isDefaultValueNotDefinedAndFieldDoesNotAllowNull = ( // eslint-disable-next-line ts/no-unnecessary-condition originalField["__defaultValue"] === void 0 && originalField["__allowNull"] === false ); if (isDefaultValueNotDefinedAndFieldDoesNotAllowNull) { const answer = await asker.theNewAttributeCantHaveNullDoYouWishToContinue(originalField["__model"]?.["__originalName"]?.(), fieldName); if (answer === false) { return process.exit(1); } } return CreateField.toGenerate(originalField["__model"]?.["__domainName"], originalField["__model"]?.["__domainPath"], originalField["__model"]?.["__originalName"]?.(), { fieldDefinition: originalField, fieldName }); case "model": const originalInitializedModel = originalFieldOrModel; return CreateModel.toGenerate(originalInitializedModel["domainName"], originalInitializedModel["domainPath"], fieldOrModelName, { fields: originalInitializedModel.class["_fields"](), options: originalInitializedModel.class["_options"]() || {} }); } } async #modelWasUpdated(operations, modelName, stateInitializedModel, originalInitializedModel) { let response = void 0; const areModelsEqual = await originalInitializedModel.class["__compareModels"](this.engine, originalInitializedModel.class, stateInitializedModel.class); if (!areModelsEqual) { response = ChangeModel.toGenerate(originalInitializedModel.domainName, originalInitializedModel.domainPath, modelName, { optionsAfter: originalInitializedModel.class["_options"]() || {}, optionsBefore: stateInitializedModel.class["_options"]() || {} }); } await this.#getOperationsFromModelsOrFields(originalInitializedModel.class["_fields"](), stateInitializedModel.class["_fields"](), "field", operations); return response; } async #fieldWasUpdated(fieldName, stateField, originalField) { const [areFieldsEqual, changedAttributes] = originalField["__compare"](this.engine, stateField); if (areFieldsEqual === false) { return ChangeField.toGenerate(originalField["__model"]?.["__domainName"], originalField["__model"]?.["__domainPath"], originalField["__model"]?.["__originalName"]?.(), { fieldDefinitionAfter: originalField, fieldDefinitionBefore: stateField, fieldName, changedAttributes }); } } /** * Method for reordering the migrations so no dependent migration will come first than the other. For example: * We have two models: `Post` and `User`. `Post` have`a dependency of `User` with an user_id field. * When we are creating this model in the database we must guarantee that the `User` will be created in the database * before creating `Post`. * * This dependencies occur even if we are changing a model, for example, if User didn't exist before and we are * creating the `User` model at the same time we are adding `user_id` to the `Post` model this dependency will exist. * * For that we need to reorder the operations. We do this in a while loop so we can guarantee all of the dependencies * are satisfied before creating the model. * * @param operations - The operations list unordered that you want to be ordered respecting all of the dependencies * between models. * * @returns - The operations but ordered, respecting the dependencies of the models. */ // eslint-disable-next-line ts/require-await async #reorderOperations(operations) { const reorderedOperations = []; let pendingOperations = operations; let previousNumberOfReorderedOperations = void 0; while (pendingOperations.length > 0) { const newPendingOperations = []; for (let i = 0; i < pendingOperations.length; i++) { const operationToProcess = pendingOperations[i]; const modelOfOperationToProcess = ( // eslint-disable-next-line ts/no-unnecessary-condition this.#originalModelsByName[operationToProcess.modelName] !== void 0 ? this.#originalModelsByName[operationToProcess.modelName] : this.#stateModelsByName[operationToProcess.modelName] ); const hasNoDependencies = Object.keys(modelOfOperationToProcess.class["__directlyRelatedTo"]).length === 0; const addedModels = new Set(reorderedOperations.map((operation) => operation.modelName)); const dependenciesAlreadyAdded = Object.keys(modelOfOperationToProcess.class["__directlyRelatedTo"]).every((dependencyOfModel) => ( // For circular relations. addedModels.has(dependencyOfModel) || dependencyOfModel === modelOfOperationToProcess.class["__originalName"]() )); const didNotAddAnyPendingOperationInThePreviousRun = previousNumberOfReorderedOperations === reorderedOperations.length && i === pendingOperations.length - 1; if (hasNoDependencies || dependenciesAlreadyAdded || didNotAddAnyPendingOperationInThePreviousRun) { operationToProcess.order = reorderedOperations.length; reorderedOperations.push(operationToProcess); } else { newPendingOperations.push(operationToProcess); } } previousNumberOfReorderedOperations = reorderedOperations.length; pendingOperations = newPendingOperations; } return reorderedOperations; } /** * Method used for generating the migration file from the operations. We will make a migration file for each database. * So if you use multiple databases you will end up with multiple migration files, one for each database. This was * better because we thought about that. The first idea was to add the migrations from multiple databases in a * single file. This is not good because you can't have easy access from which database this migration is from. * On the other hand, by splitting into multiple files we are able to see directly in the file name from which file * this migration is from. * * Every migration has a description so the user can know what we are doing in this automatically generated migration. * * The automatic migrations are composed like so: * `{ordered number}_{database name}_auto_migration_{time when it was generated}` * * Sometimes the user might define something outside of the default application scope. For that use cases we can * import stuff dynamically. For example, if you create a customField supposed to work specifically for sequelize- * engine, you can add a `customImports` function for that field so we are able to add that import on the top of the * migration file. * * @param operationsOfFile - All of the operations, in order, that we want to run inside of this migration. * @param domainPath - Where are we going to create this migration file. * @param numberOfMigrations - (optional) - The number of migrations that were created in this single makemigrations * run. This is useful so we can append the numbers accordingly. * @param lastMigrationName - (optional) - The last migration name that this depends on, most of them will have * dependencies on each one, except for the first one. * @param lastDomainPath - (optional) - Where the dependent migration was generated so we can look at it * if we want to. * * @returns - Returns the migration name automatically generated here in this function. */ async #generateMigrationFile(operationsOfFile, domainPath, numberOfMigrations = 0, lastMigrationName = "", lastDomainPath = "") { const customImportsOfCustomData = []; const operationsAsString = []; const currentDate = /* @__PURE__ */ new Date(); const migrationNumber = numberOfMigrations + 1; const migrationNumberToString = migrationNumber < 10 ? `00${migrationNumber}` : migrationNumber < 100 ? `0${migrationNumber}` : migrationNumber; const migrationName = `${migrationNumberToString}_${this.database}_auto_migration_${Date.now().toString()}`; const settingsIsTs = (this.settings?.settingsLocation || "").endsWith(".ts") || this.optionalArgs.useTs; databaseLogger.logMessage("MIGRATIONS_FILE_TITLE", { title: migrationName }); databaseLogger.logMessage("MIGRATIONS_FILE_DESCRIPTION", { database: this.database, lastMigrationName, lastDomainPath }); for (const operation of operationsOfFile) { databaseLogger.logMessage("MIGRATIONS_ACTION_DESCRIPTION", { description: await operation.operation.describe(operation) }); const { asString, customImports } = await operation.operation.toString(this.engine, 3, operation); operationsAsString.push(asString); if (Array.isArray(customImports)) { getUniqueCustomImports(customImports, customImportsOfCustomData); } } const operationsToString = operationsAsString.join(",\n"); const customImportsAsString = customImportsOfCustomData.map(({ value, packageName }) => { if (settingsIsTs) return `import ${value} from "${packageName}";`; if (value.startsWith("* as ")) return `const ${value.replace("* as ", "")} = require('${packageName}');`; if (value.startsWith("{ default as ")) { return `const ${value.replace("{ default as ", "").replace(" }", "")} = require('${packageName}');`; } return `const { ${value} } = require(${packageName});`; }).join("\n") + ` `; const file = `/* prettier-ignore-start */ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols /** * Automatically generated by ${import_core6.FRAMEWORK_NAME} on ${currentDate.toISOString()} */ ` + (settingsIsTs ? `import { models, actions } from '${PACKAGE_NAME}';` : `const { models, actions } = require('${PACKAGE_NAME}');`) + ` ${customImportsAsString} ` + (settingsIsTs ? `export default { ` : `module.exports = { `) + ` name: '${migrationName}', database: '${this.database}', dependsOn: '${lastMigrationName}', operations: [ ${operationsToString} ] }; `; const indexFileOfMigrations = `index.${settingsIsTs ? "ts" : "js"}`; const pathToWriteMigrations = await import_core6.std.files.join(domainPath, "migrations"); const existsFile = await import_core6.std.files.exists(pathToWriteMigrations); if (!existsFile) { await import_core6.std.files.makeDirectory(pathToWriteMigrations); await import_core6.std.files.writeFile([ pathToWriteMigrations, indexFileOfMigrations ], ""); } await import_core6.std.files.writeFile([ pathToWriteMigrations, `${migrationName}.${settingsIsTs ? "ts" : "js"}` ], file); const files = await import_core6.std.files.readDirectory(pathToWriteMigrations); const filteredFilesWithoutIndex = files.filter((fileName) => fileName !== indexFileOfMigrations); const contentsOfIndex = filteredFilesWithoutIndex.map((fileName, index) => { const fileNameWithoutExtension = fileName.replace(settingsIsTs ? ".ts" : ".js", ""); return settingsIsTs ? `export { default as M${fileNameWithoutExtension} } from "./${fileNameWithoutExtension}";` : `require('./${fileNameWithoutExtension}')${index === filteredFilesWithoutIndex.length - 1 ? "" : ","}`; }).join("\n"); await import_core6.std.files.writeFile([ pathToWriteMigrations, indexFileOfMigrations ], contentsOfIndex); return migrationName; } /** * Method responsible for condensing the operations in a single file. For example, let's suppose we have two Domains: * `auth` and `posts`, on the `posts` domain we have the `Post` model and on the `auth` domain we have the `User` * model. So let's suppose we did 3 operations after the last migrations: * * 1. We've added a new field in 'Post' called `dateOfPost`. * 2. We've set in the 'Post' model, on the the existing field `description` as `allowBlank = true`. * 3. On the `User` model we've added a new `dateJoined` field. * * operations 1 and 2 are related to the model `Post` that is located in the `posts` domain. * The operation 3 is related to the model `User` that is located in the `auth` domain. * * This means that operations 1 and 2 must exist in a single file, and the operation 3 should exist in other. * THat's exactly what we do in this function we merge operations related to the same domain in a single file so we * don't need one file for each operation. * * @param operations - All of the operations that we need to run for this database. We will separate them to their * files inside of this method. * @param emptyOptions - (optional) - Those are the custom options if the value is an empty migration. An empty * migration is a migration file without any operations. This is preferred instead of creating the file by hand. * * @returns - Returns the last migration name. */ async generateFiles(operations, emptyOptions) { const lastMigrationIndex = this.filteredMigrationsOfDatabase.length - 1; const hasALastMigration = this.filteredMigrationsOfDatabase[lastMigrationIndex] !== void 0; let previousDomainPath = operations[0] ? operations[0].domainPath : ""; let lastDomainPath = hasALastMigration ? this.filteredMigrationsOfDatabase[lastMigrationIndex].domainPath : ""; let lastMigrationName = hasALastMigration ? this.filteredMigrationsOfDatabase[lastMigrationIndex].migration.name : ""; let numberOfMigrationFilesCreated = 0; let operationsOnFile = []; for (let i = 0; i < operations.length + 1; i++) { const operation = operations[i]; const isLastOperationOrFromADifferentDomain = i >= operations.length || operation.domainPath !== previousDomainPath; if (isLastOperationOrFromADifferentDomain) { const domainPath = operationsOnFile[0] ? operationsOnFile[0].domainPath : previousDomainPath; const totalNumberOfMigrations = this.filteredMigrationsOfDatabase.length + numberOfMigrationFilesCreated; const migrationName = await this.#generateMigrationFile(operationsOnFile, emptyOptions?.onDomain || domainPath, totalNumberOfMigrations, emptyOptions?.previousMigrationName || lastMigrationName, emptyOptions?.onDomain || lastDomainPath); numberOfMigrationFilesCreated++; previousDomainPath = operation ? operation.domainPath : ""; lastDomainPath = domainPath; lastMigrationName = migrationName; operationsOnFile = []; } operationsOnFile.push(operation); } return lastMigrationName; } /** * This will handle when the user wants to create an empty migration file without any operations in it. * * To create an empty migration file the user must define the domain name were he wants to add the migration, * for example `AuthDomain`. By default this will create this empty migration file for all of the databases * that he has but the user can define for which database does he want this empty migration to be created * for by defining it like: `default:AuthDomain`. This guarantees that this migration will only exist for * the `default` database. * * @param previousMigrationName - The last run migration so we can add the dependencies accordingly. */ async #handleGenerateEmptyMigration(previousMigrationName) { const isArgsAString = typeof this.optionalArgs.empty === "string"; const isToGenerateMigration = /* @__PURE__ */ __name((domain3) => { const separatedArg = this.optionalArgs.empty.split(":"); if (separatedArg.length > 1) { return this.database === separatedArg[0] && domain3 === separatedArg[1]; } return domain3 === this.optionalArgs.empty; }, "isToGenerateMigration"); if (isArgsAString) { const domainClasses = await (0, import_core6.retrieveDomains)(this.settings); for (const domainClass of domainClasses) { const domain3 = new domainClass(); if (isToGenerateMigration(domain3.name)) { await this.generateFiles([], { onDomain: domain3.path, previousMigrationName }); break; } } } } /** * This is the main entrypoint for the `makemigrations` command. We run this function and the * side effects run in order so we are able to retrieve the operations that we need to do in the database * to match the changes that were made to the models. * * First we need to retrieve all of the operations, second we need to reorder those operations, and last but not least * we generate the file. * If the `makemigrations` has the `--empty` flag in it then we create an empty migration after every migration were * created. */ async _run() { let previousMigrationName = void 0; const operations = []; await this.#getOperationsFromModelsOrFields(this.#originalModelsByName, this.#stateModelsByName, "model", operations); const didNotChangeAnythingInTheModels = operations.length === 0; if (didNotChangeAnythingInTheModels) { if (!this.optionalArgs.empty) databaseLogger.logMessage("NO_CHANGES_MADE_FOR_MIGRATIONS", void 0); } else { const reorderedMigrations = await this.#reorderOperations(operations); previousMigrationName = await this.generateFiles(reorderedMigrations); } if (this.optionalArgs.empty) await this.#handleGenerateEmptyMigration(previousMigrationName); } static async buildAndRun(settings, migrations, initializedEngineInstances, optionalArgs) { const initializedEngineInstancesEntries = Object.entries(initializedEngineInstances); for (const [database, { engineInstance, projectModels }] of initializedEngineInstancesEntries) { const filteredMigrationsOfDatabase = migrations.filter((migration) => [ database, "*" ].includes(migration.migration.database)); const state = await State.buildState(filteredMigrationsOfDatabase); const initializedState = await state.initializeStateModels(engineInstance); const makemigrations = new this(engineInstance, database, settings, projectModels, initializedState, filteredMigrationsOfDatabase, optionalArgs); await makemigrations._run(); } } }; // src/migrations/migrate/migration.ts var Migration = class { static { __name(this, "Migration"); } domainName; databaseName; name; dependsOn; engineInstance; operations = []; allMigrations; transaction = null; constructor(engineInstance, migrationFile, domainName, allMigrations) { this.engineInstance = engineInstance; this.name = migrationFile.name; this.databaseName = migrationFile.database; this.dependsOn = migrationFile.dependsOn; this.operations = migrationFile.operations; this.domainName = domainName; this.allMigrations = allMigrations; } /** * The method that is used to run the migration on the transaction. * This method will recreate the state FOR EVERY action. We know this is suboptimal but since this code * is only running on a CI environment then it's fine. It is also fine to flush your migrations from now * and then and recreate the migrations from scratch. * * @param transaction - The transaction that is being used to run the migration on. * @param allMigrations - All of the migrations that are available to be used inside of this database. With * this we reconstruct the state AFTER the migration has been run and BEFORE the migration has been run. * @param returnOfInit - The return of the init function that is run before the migration is run. If it's * implemented we will pass it to all of the operations. */ async #runOnTransaction(transaction, allMigrations, returnOfInit) { this.transaction = transaction; const connectionsToClose = []; for (let i = 0; i < this.operations.length; i++) { const operation = this.operations[i]; const fromState = await State.buildState(allMigrations, this.name, i); const { initializedModels: fromStateModelsByModelName, closeEngineInstance: closeFromEngineInstance } = await fromState.geInitializedModelsByName(this.engineInstance); const toState = await State.buildState(allMigrations, this.name, i + 1); const { initializedModels: toStateModelsByModelName, closeEngineInstance: closeToEngineInstance } = await toState.geInitializedModelsByName(this.engineInstance); await operation.run(this, this.engineInstance, fromStateModelsByModelName, toStateModelsByModelName, returnOfInit); if (closeToEngineInstance) connectionsToClose.push(() => closeToEngineInstance(this.engineInstance)); if (closeFromEngineInstance) connectionsToClose.push(() => closeFromEngineInstance(this.engineInstance)); } return connectionsToClose; } /** * The method that is used to effectively run the migration. By default we will run the migration * files in a transaction, so we guarantee that if the migration fails for some reason all of the * actions and changes will be rolled back. */ async run() { let returnOfInit = void 0; if (this.engineInstance.migrations?.init) returnOfInit = await this.engineInstance.migrations.init(this.engineInstance); const connectionsToClose = await this.engineInstance.useTransaction(this.#runOnTransaction.bind(this), this.allMigrations, returnOfInit); if (this.engineInstance.migrations?.finish) await this.engineInstance.migrations.finish(this.engineInstance, returnOfInit); return connectionsToClose; } /** * The static method that is used to build the migration from the file. We load the file data into memory * and then build the migration from that data. * * For running the migration we do need the engine instance that is being used to run this migration, the file * to build the migration from, and all of the migrations that is available for this engine instance. We need * them to be able to recreate the state. * * @param engineInstance - The engine instance that is being used to run all of the migrations in the database. * @param migrationFile - The current migration file that is running. * @param allMigrations - All of the migration files that are available to be used inside of this database. With * this we are able to recreate the state of the database. */ static async buildFromFile(engineInstance, migrationFile, allMigrations) { const migration = new this(engineInstance, migrationFile.migration, migrationFile.domainName, allMigrations); return migration.run(); } }; // src/defaults/models.ts var models_exports = {}; __export(models_exports, { PalmaresMigrations: () => PalmaresMigrations }); // src/defaults/managers.ts var PalmaresMigrationsManager = class extends Manager { static { __name(this, "PalmaresMigrationsManager"); } /** * Creates a new migration in the database. This way we can know what migrations have was evaluated and what migration * still needs to be evaluated. * * @param migrationName - The name of the migration file that was evaluated. * @param engineName - The name of the engine from which this migration was created. */ async createMigration(migrationName, engineName) { return this.set((qs) => qs.data({ migrationName, engineName }), { useTransaction: true, engineName }); } /** * Retrieves the last migration that was evaluated for a given engine. * * @param engineName - The name of the engine to create this migration for. * * @return - An empty '' string or the name of the last migration. */ async getLastMigrationName(engineName) { const allMigrations = await this.get((qs) => qs.where({ engineName }).select("migrationName"), { engineName }); return allMigrations.length > 0 ? allMigrations[0].migrationName : ""; } }; // src/defaults/models.ts var PalmaresMigrations = class extends model() { static { __name(this, "PalmaresMigrations"); } fields = { id: BigAutoField.new(), migrationName: CharField.new({ maxLen: 150 }), engineName: CharField.new({ maxLen: 150 }) }; options = { tableName: "palmares_migrations", ordering: [ "-id" ] }; static migrations = new PalmaresMigrationsManager(); }; // src/migrations/migrate/index.ts var Migrate = class { static { __name(this, "Migrate"); } settings; migrationsToAddAfterIteration = []; constructor(settings) { this.settings = settings; } /** * Instead of passing the values to save here, you see that we look through the `migrationsToAddAfterIteration` * because although we recommend adding the '@palmares/database' as the first domain in the project, there might * have use cases where users set other stuff first. * * Because of that, we hold the data to save in PalmaresMigration until it is created in the database so when it is * created we can save all of the data. * * So what you do is that you append a data to `migrationsToAddAfterIteration`, and only after that you can iterate * over. To save to the database that the migration was added. * * @param migrationName - The name of the migration file that was evaluated. * @param engineName - The name of the user defined engine that was created in `DATABASES` */ async saveMigration(migrationName, engineName) { this.migrationsToAddAfterIteration.push({ migrationName, engineName }); const newMigrationsToAddAfterIteration = []; for (const migrationToAddAfterIteration of this.migrationsToAddAfterIteration) { try { const createdMigration = await PalmaresMigrations.migrations.createMigration(migrationToAddAfterIteration.migrationName, migrationToAddAfterIteration.engineName); if (!createdMigration) newMigrationsToAddAfterIteration.push(migrationToAddAfterIteration); } catch (e) { databaseLogger.logMessage("FAILED_TO_COMMIT_MIGRATION", { migrationName: migrationToAddAfterIteration.migrationName, databaseName: migrationToAddAfterIteration.engineName, reason: e.message, stack: e.stack || "" }); newMigrationsToAddAfterIteration.push(migrationToAddAfterIteration); } } this.migrationsToAddAfterIteration = newMigrationsToAddAfterIteration; } /** * Retrieve the last migration name that was created in the database. This is the last migration file name * that was evaluated by palmares. This way we can filter only the migrations after that. * * @param engineName - The name of the engine to use, usually it will be the `default`. */ async getLastMigration(engineName) { try { const lastMigrationName = await PalmaresMigrations.migrations.getLastMigrationName(engineName); const isAValidMigrationName = typeof lastMigrationName === "string" && lastMigrationName !== ""; if (isAValidMigrationName) return lastMigrationName; } catch (e) { databaseLogger.logMessage("FAILED_TO_GET_LAST_MIGRATION", { databaseName: engineName, reason: e.message, stack: e.stack || "" }); return null; } return null; } /** * This is the main method that is used to run the migrations in the specific database. * * There are 2 ways to run the migrations: * - Either we batch them into a single migration that just gets the current state and let the ORM handle the rest. * - Run each migration file individually and each migration action one by one. * * @param engineInstance - The engine instance that is being used to run all of the migrations * in the database. * @param allMigrationsOfDatabase - All of the migration files that are available to be used inside of * this database. */ async _run(engineInstance, allMigrationsOfDatabase) { const lastMigrationName = await this.getLastMigration(engineInstance.connectionName); const startIndexOfFilter = allMigrationsOfDatabase.findIndex((migration) => migration.migration.name === lastMigrationName) + 1; const filteredMigrationsOfDatabase = allMigrationsOfDatabase.slice(startIndexOfFilter, allMigrationsOfDatabase.length); if (filteredMigrationsOfDatabase.length > 0) { if (engineInstance.migrations?.batchAll) { databaseLogger.logMessage("MIGRATION_RUNNING_IN_BATCH", { databaseName: engineInstance.connectionName }); let returnOfInit = void 0; if (engineInstance.migrations.init) returnOfInit = await engineInstance.migrations.init(engineInstance); const currentState = await State.buildState(filteredMigrationsOfDatabase); const initializedModelsByName = await currentState.geInitializedModelsByName(engineInstance); const formattedModelsByName = {}; for (const [modelName, model2] of Object.entries(initializedModelsByName.initializedModels)) formattedModelsByName[modelName] = model2.initialized; await engineInstance.migrations.batchAll(engineInstance, formattedModelsByName, returnOfInit); } let connectionsToClose = []; for (const migrationFile of filteredMigrationsOfDatabase) { const migrationName = migrationFile.migration.name; if (engineInstance.migrations?.batchAll === void 0) { databaseLogger.logMessage("MIGRATIONS_RUNNING_FILE_NAME", { title: migrationName }); connectionsToClose = connectionsToClose.concat(await Migration.buildFromFile(engineInstance, migrationFile, allMigrationsOfDatabase)); } await this.saveMigration(migrationName, engineInstance.connectionName); } await Promise.allSettled(connectionsToClose.map((closeConnection) => closeConnection())); } else { databaseLogger.logMessage("MIGRATIONS_NO_NEW_MIGRATIONS", { databaseName: engineInstance.connectionName }); } } /** * This is a factory method that MUST be called to create a new instance of the `Migrate` class. * * @param settings - The settings that is being used. * @param migrations - All of the migrations that are available to be used for this database. * @param initializedEngineInstances - The engine instances that were initialized so we can run the migrations * in each database. */ static async buildAndRun(settings, migrations, initializedEngineInstances) { const initializedEngineInstancesEntries = Object.entries(initializedEngineInstances); for (const [database, { engineInstance }] of initializedEngineInstancesEntries) { const filteredMigrationsOfDatabase = migrations.filter((migration) => [ database, "*" ].includes(migration.migration.database)); const migrate2 = new this(settings); await migrate2._run(engineInstance, filteredMigrationsOfDatabase); } } }; // src/migrations/index.ts var Migrations = class { static { __name(this, "Migrations"); } settings; domains; constructor(settings, domains) { this.settings = settings; this.domains = domains; } async makeMigrations(initializedEngineInstances, optionalArgs) { const migrations = await this.#getMigrations(); await MakeMigrations.buildAndRun(this.settings, migrations, initializedEngineInstances, optionalArgs); } async migrate(initializedEngineInstances) { const migrations = await this.#getMigrations(); await Migrate.buildAndRun(this.settings, migrations, initializedEngineInstances); } // eslint-disable-next-line ts/require-await async #reorderMigrations(migrations) { const reorderedMigrations = []; const reference = {}; for (const migration of migrations) { const dependsOn = migration.migration.dependsOn; const migrationName = migration.migration.name; const indexToAddValue = reference[dependsOn] ? reference[dependsOn] + 1 : reorderedMigrations.length; if (reference[dependsOn]) { reorderedMigrations.splice(indexToAddValue, 0, migration); } else { reorderedMigrations.push(migration); } reference[migrationName] = indexToAddValue; } return reorderedMigrations; } async #getMigrations() { const foundMigrations = []; const promises = this.domains.map(async (domain3) => { if (domain3.getMigrations) { let domainMigrations = await Promise.resolve(domain3.getMigrations()); if (typeof domainMigrations === "object" && domainMigrations !== null) domainMigrations = Object.values(domainMigrations); for (const domainMigration of domainMigrations) { foundMigrations.push({ domainPath: domain3.path, domainName: domain3.name, migration: domainMigration }); } } else { const fullPath = await import_core7.std.files.join(domain3.path, "migrations"); try { const directoryFiles = await import_core7.std.files.readDirectory(fullPath); const promises2 = directoryFiles.map(async (element) => { const file = element; const pathOfMigration = await import_core7.std.files.join(fullPath, file); const pathToGetMigration = import_core7.std.files.getPathToFileURL(pathOfMigration); const migrationFile = (await (await import_core7.std.os.platform() === "windows" && pathOfMigration.startsWith("file:") === false ? import(`file:/${pathToGetMigration}`) : import(pathToGetMigration))).default; const isAValidMigrationFile = typeof migrationFile === "object" && // eslint-disable-next-line ts/no-unnecessary-condition migrationFile !== void 0 && typeof migrationFile.database === "string" && Array.isArray(migrationFile.operations) && typeof migrationFile.name === "string"; if (isAValidMigrationFile) { foundMigrations.push({ domainName: domain3.name, domainPath: domain3.path, migration: migrationFile }); } }); await Promise.all(promises2); } catch (e) { const error = e; const couldNotFindFileOrDirectory = error.message.startsWith("ENOENT: no such file or directory, scandir"); if (error.code === import_core7.ERR_MODULE_NOT_FOUND || couldNotFindFileOrDirectory) { if (this.settings.dismissNoMigrationsLog !== false) databaseLogger.logMessage("MIGRATIONS_NOT_FOUND", { domainName: domain3.name }); } else { throw e; } } } }); await Promise.all(promises); return this.#reorderMigrations(foundMigrations); } }; // src/databases.ts var Databases = class { static { __name(this, "Databases"); } settings; isInitializing = false; isInitialized = false; initializedEngineInstances = {}; obligatoryModels = []; managers = /* @__PURE__ */ new Map(); #cachedModelsByModelName = {}; constructor() { if (globalThis.$PDatabaseInstance) return globalThis.$PDatabaseInstance; globalThis.$PDatabaseInstance = this; } /** * This will lazy initialize the hole engine instance with all of the models before using it. * Generally this is not needed but for example on cases like serverless. We need to guarantee that * the database will work without the need of the default domain lifecycle. That's because * on certain environments we can't guarantee that the hole domain lifecycle will be called and executed, * this is why we need to lazy initialize it. * * We initialize the hole engine AND NOT JUST THE MODELS because there is no way to know before hand about relations. * Yeah we can guarantee direct relations, for example `Post` that are related to a `User`. But we cannot * guarantee indirect relations, for example, that `User` is related to `Post`. This is because of the architecture * that we choose to keep all relations in the models themselves. If we change this architecture we are able to lazy * load just certain models as well as their relations so it can be even more efficient. Right now we thinks * that this is efficient enough. * * @param engineName - The name of the engine that we want to lazy initialize. * @param settings - The settings that we want to use. * @param domains - The domains of the application. */ async lazyInitializeEngine(engineName, settings, domains) { if (this.isInitialized === false && this.isInitializing === false) { const isDatabaseDefined = settings.databases !== void 0 && typeof settings.databases === "object"; const engineNameToUse = ( // eslint-disable-next-line ts/no-unnecessary-condition engineName === "" ? Object.keys(settings.databases || {})[0] : engineName ); const isEngineNameDefined = engineNameToUse in (settings.databases || {}); if (isDatabaseDefined && isEngineNameDefined) { const databaseSettings = settings.databases[engineNameToUse]; await this.initializeDatabase(engineNameToUse, databaseSettings, domains); } } } /** * Initializes the database connection and load the models to their respective engines. * * @param settings - The settings object from the file itself. */ async init(settings, domains) { if (this.isInitialized === false && this.isInitializing === false) { this.settings = settings; this.isInitializing = true; const isDatabaseDefined = ( // eslint-disable-next-line ts/no-unnecessary-condition this.settings.databases !== void 0 && typeof settings.databases === "object" ); if (isDatabaseDefined) { const databaseEntries = Object.entries(settings.databases); const existsEventEmitterForAllEngines = ( // eslint-disable-next-line ts/no-unnecessary-condition settings.eventEmitter?.["$$type"] === "$PEventEmitter" || (settings.eventEmitter || {}) instanceof Promise ); for (const [databaseName, databaseSettings] of databaseEntries) { const existsEventEmitterForSpecificEngine = databaseSettings.events?.emitter?.["$$type"] === "$PEventEmitter" || databaseSettings.events?.emitter instanceof Promise; if (existsEventEmitterForSpecificEngine === false && existsEventEmitterForAllEngines) { databaseSettings.events = { emitter: settings.eventEmitter }; } await this.initializeDatabase(databaseName, databaseSettings, domains); } this.isInitialized = true; } this.isInitializing = false; } } /** * Responsible for handling the `makemigrations` command. For this command we must initialize the database first. * The user can pass --empty to create a new empty migration file. * * @param settings - The settings defined by the user in settings.js/ts file. * @param domains - The domains defined by the user so we can fetch all of the models and migrations. */ async makeMigrations(settings, domains, optionalArgs) { await this.init(settings, domains); const migrations = new Migrations(settings, domains); await migrations.makeMigrations(this.initializedEngineInstances, optionalArgs); } /** * Responsible for handling the `migrate` command. For this command we must initialize the database first. * * @param settings - The settings defined by the user in settings.js/ts file. * @param domains - The domains defined by the user so we can fetch all of the models and migrations. */ async migrate(settings, domains) { await this.init(settings, domains); const migrations = new Migrations(settings, domains); await migrations.migrate(this.initializedEngineInstances); } /** * Closes the database connection on all of the initialized engine instances. */ async close() { const initializedEngineEntries = Object.values(this.initializedEngineInstances); const promises = initializedEngineEntries.map(async (initializedEngine) => { if (!initializedEngine?.engineInstance) return; databaseLogger.logMessage("DATABASE_CLOSING", { databaseName: initializedEngine.engineInstance.connectionName }); if (initializedEngine.engineInstance.close) await initializedEngine.engineInstance.close(initializedEngine.engineInstance); }); await Promise.all(promises); } /** * Initializes the database connection and load the models to their respective engines. * * @param engineName - A custom name of the engine that we are using. * @param databaseSettings - The settings object for the database. */ async initializeDatabase(engineName, databaseSettings, domains) { let engineInstance; let argumentsToPassOnNew; const doesAnEngineInstanceAlreadyExist = engineName in this.initializedEngineInstances && this.initializedEngineInstances[engineName]?.engineInstance !== void 0; const isProbablyAnEngineInstanceDefinedForDatabase = databaseSettings.engine !== void 0; if (doesAnEngineInstanceAlreadyExist && this.initializedEngineInstances[engineName]?.engineInstance) engineInstance = this.initializedEngineInstances[engineName].engineInstance; else { const engineArgs = databaseSettings.engine; argumentsToPassOnNew = engineArgs[0]; engineInstance = engineArgs[1](); const isAnEngineInstanceDefinedForDatabase = isProbablyAnEngineInstanceDefinedForDatabase ? engineInstance?.["$$type"] === "$PDatabaseAdapter" : false; if (!isAnEngineInstanceDefinedForDatabase) throw new Error("You must define an engine for the database."); } engineInstance.__argumentsUsed = argumentsToPassOnNew; engineInstance.connectionName = engineName; engineInstance.databaseSettings = databaseSettings; const models2 = Object.values(await this.getModels(engineInstance, domains)); const onlyTheModelsFiltered = {}; const onlyTheModelsNotOnTheEngine = {}; const modelsFilteredForDatabase = []; models2.forEach((foundModel) => { const modelInstance = new foundModel.model(); const options = foundModel.model["_options"](modelInstance); const isModelManagedByEngine = ( // eslint-disable-next-line ts/no-unnecessary-condition options?.abstract !== true && // eslint-disable-next-line ts/no-unnecessary-condition options?.managed !== false && // eslint-disable-next-line ts/no-unnecessary-condition (Array.isArray(options?.databases) === false || options.databases.includes(engineName) === true) ); const modelName = foundModel.model["__getName"]() || modelInstance.constructor.name; if (isModelManagedByEngine) onlyTheModelsFiltered[modelName] = foundModel.model; else onlyTheModelsNotOnTheEngine[modelName] = foundModel.model; if (isModelManagedByEngine) modelsFilteredForDatabase.push(foundModel); }); const isDatabaseConnected = await Promise.resolve(engineInstance.isConnected ? engineInstance.isConnected(engineInstance) : true); engineInstance.__modelsOfEngine = onlyTheModelsFiltered; engineInstance.__modelsFilteredOutOfEngine = onlyTheModelsNotOnTheEngine; if (isDatabaseConnected) { const { projectModels } = await this.initializeModels(engineInstance, modelsFilteredForDatabase); const mergedProjectModels = (this.initializedEngineInstances[engineName]?.projectModels || []).concat(projectModels); this.initializedEngineInstances[engineName] = { engineInstance, projectModels: mergedProjectModels }; } else { databaseLogger.logMessage("DATABASE_IS_NOT_CONNECTED", { databaseName: engineInstance.connectionName }); throw new Error(`The database engine ${engineName} was not able to connect to the database.`); } } /** * Initializes the models to the engine instance, the engine instance will convert the models to something it * can understand. For example on sequelize engine we will convert the models to a sequelize model. On a Prisma * engine for example we could interpret the models as a Prisma schema, and we could build the file after. * * @param engineInstance - The engine instance that we will be using. * @param projectModels - The models from the project (not the default ones that we create). * * @returns - Returns the engine instance that we are using to build run everything over returns the project models * and the internal models. */ async initializeModels(engineInstance, projectModels) { const initializedProjectModels = await initializeModels( engineInstance, // eslint-disable-next-line no-shadow projectModels.map(({ domainPath, domainName, model: model2 }) => { model2["__domainName"] = domainName; model2["__domainPath"] = domainPath; return model2; }) ); return { engineInstance, projectModels: initializedProjectModels }; } /** * Retrieves the models on all of the installed domains. By default we will look for the models * in the `models` file in the path of the domain. You can also define your domain app implementing * the `DatabaseDomainInterface` interface. With this type of domain you are able to export your models by defining * the `getModels` method. When this method is defined we bypass the lookup of the models in the `models` * file or folder, for complex projects you might want to use this method. * * @param domains - The domains where we want to retrieve the models from. Those are all of the * domains installed with INSTALLED_DOMAINS. * * @returns - Returns an array of models. */ async getModels(engineInstance, domains) { const settings = (0, import_core8.getSettings)(); if (domains === void 0 && settings) domains = (await (0, import_core8.retrieveDomains)(settings)).map((domainClass) => new domainClass()); const cachedFoundModels = Object.values(this.#cachedModelsByModelName); const existsCachedFoundModels = cachedFoundModels.length > 0; if (existsCachedFoundModels === false && domains) { const promises = domains.map(async (domain3) => { const hasGetModelsMethodDefined = typeof domain3.getModels === "function"; if (hasGetModelsMethodDefined) { const models2 = await Promise.resolve(domain3.getModels(engineInstance)); if (Array.isArray(models2)) { for (const modelOfModels of models2) { this.#cachedModelsByModelName[modelOfModels["__getName"]()] = { domainPath: domain3.path, domainName: domain3.name, model: modelOfModels }; } } else { const modelEntries = Object.entries(models2); for (const [modelName, modelKls] of modelEntries) { this.#cachedModelsByModelName[modelName] = { domainName: domain3.path, domainPath: domain3.path, model: modelKls }; } } } }); await Promise.all(promises); } return this.#cachedModelsByModelName; } }; // src/defaults/migrations.ts var defaultMigrations = [ { name: "create_palmares_migration_table", database: "*", dependsOn: "", operations: [ new CreateModel("PalmaresMigrations", { id: BigAutoField.new().databaseName("id").underscored(), migrationName: CharField.new({ maxLen: 150 }).databaseName("migration_name").underscored(), engineName: CharField.new({ maxLen: 150 }).databaseName("engine_name").underscored() }, { abstract: false, underscored: true, tableName: "palmares_migrations", managed: true, ordering: [ "-id" ], indexes: [], databases: [ "default" ], customOptions: {} }) ] } ]; // src/domain.ts function loadDatabases(databaseDomains) { if (Array.isArray(databaseDomains)) globalThis.$PCachedDatabaseDomains = databaseDomains; if (!globalThis.$PDatabases) globalThis.$PDatabases = new Databases(); return [ globalThis.$PDatabases, globalThis.$PCachedDatabaseDomains ]; } __name(loadDatabases, "loadDatabases"); var databaseDomainModifier = (0, import_core9.domain)("@palmares/database", "", {}); var databasesDomain = (0, import_core9.domain)("@palmares/database", "", { modifiers: [ databaseDomainModifier ], commands: { makemigrations: { description: "Create the migrations automatically based on your created models", positionalArgs: void 0, keywordArgs: { empty: { description: "Creates an empty migration", hasFlag: true, type: "string", canBeMultiple: true } }, handler: /* @__PURE__ */ __name(async (options) => { const [databases] = loadDatabases(); await makeMigrations(databases, options); }, "handler") }, migrate: { description: "Run the pending migrations on your database", positionalArgs: void 0, keywordArgs: void 0, handler: /* @__PURE__ */ __name(async (options) => { const [databases] = loadDatabases(); await migrate(databases, options); }, "handler") }, ["load-models"]: { description: "Load the databases. For some engines, it will just create the models locally", positionalArgs: void 0, keywordArgs: void 0, handler: /* @__PURE__ */ __name(async (options) => { const settingsAsDatabaseSettings = options.settings; const [databases] = loadDatabases(); const settingsWithDefault = defaultSettings(settingsAsDatabaseSettings); await databases.init(settingsWithDefault, options.domains); if (databases) await Promise.all([ databases.close() ]); }, "handler") } }, // eslint-disable-next-line ts/require-await load: /* @__PURE__ */ __name(async (_) => { return async (options) => { const databaseDomains = options.domains; loadDatabases(databaseDomains); }; }, "load"), ready: /* @__PURE__ */ __name(async (options) => { const [databases, databaseDomains] = loadDatabases(); const settingsWithDefault = defaultSettings(options.settings); if (databases && databaseDomains) await databases.init(settingsWithDefault, databaseDomains); }, "ready"), close: /* @__PURE__ */ __name(async () => { const [databases] = loadDatabases(); if (databases) await Promise.all([ databases.close() ]); }, "close"), // eslint-disable-next-line ts/require-await getMigrations: /* @__PURE__ */ __name(async () => defaultMigrations, "getMigrations"), // eslint-disable-next-line ts/require-await getModels: /* @__PURE__ */ __name(async (engineInstance) => { if (engineInstance.migrations) return models_exports; else return []; }, "getModels") }); // src/engine/exceptions.ts var NotImplementedAdapterException = class _NotImplementedAdapterException extends Error { static { __name(this, "NotImplementedAdapterException"); } constructor(methodName) { super(`Method ${methodName} was not implemented in your Adapter, it should be in order to fully work.`); this.name = _NotImplementedAdapterException.name; } }; var NotImplementedAdapterFieldsException = class _NotImplementedAdapterFieldsException extends Error { static { __name(this, "NotImplementedAdapterFieldsException"); } constructor(methodName) { super(`Method ${methodName} was not implemented in your Adapter fields and it should be implemented in order to work properly.`); this.name = _NotImplementedAdapterFieldsException.name; } }; // src/engine/index.ts function databaseAdapter(args) { let CustomDatabaseAdapter = class CustomDatabaseAdapter extends DatabaseAdapter { static { __name(this, "CustomDatabaseAdapter"); } fields = args.fields; models = args.models; query = args.query; migrations = args.migrations; static new = args.new; duplicate = args.duplicate; isConnected = args.isConnected; close = args.close; transaction = args.transaction; }; return CustomDatabaseAdapter; } __name(databaseAdapter, "databaseAdapter"); var DatabaseAdapter = class { static { __name(this, "DatabaseAdapter"); } $$type = "$PDatabaseAdapter"; connectionName; databaseSettings; initializedModels = {}; models; fields; query; migrations; ModelType; instance; __argumentsUsed; __ignoreNotImplementedErrors = false; __modelsFilteredOutOfEngine; __modelsOfEngine; __indirectlyRelatedModels = {}; /** * Factory function for creating a new DatabaseAdapter instance. Your engine should always implement this function * as static and return a new instance of your engine. * * @returns - Will return a new engine instance. */ static new(..._args) { throw new NotImplementedAdapterException("new"); } /** * Duplicates this instance to a new instance so we can work on it instead of the default one. Generally * you will not need to worry too much about this, this is used more on migrations so we can keep the state * models separated from the original models. * * @example * ```ts * async duplicate(getNewEngine: () => Promise) { * const duplicatedEngine = await getNewEngine(); * await duplicatedEngine.connection.close(); * await duplicatedEngine.connection.connect(); * return duplicatedEngine; * } * ``` * * @param _getNewEngine - Default duplicate function, this should be called or it will throw an error. * * @returns - A new engine instance after calling `.new` static method. */ // eslint-disable-next-line ts/require-await async duplicate(_getNewEngine) { throw new NotImplementedAdapterException("duplicate"); } /** * We use this to see check if we have a connection to the database or not. We will only translate the models if we * have a connection to the database. If your orm does not rely on a connection to create the models you can return * true by default. * * @return - Return true if the database is connected or false otherwise. */ // eslint-disable-next-line ts/require-await async isConnected(_engine) { throw new NotImplementedAdapterException("isConnected"); } /** * Called when we want to close all of the connections to the database, if your engine can close the connection * automatically this don't need to be used. * * @example * ```ts * * ``` */ // eslint-disable-next-line ts/require-await async close(_engine) { throw new NotImplementedAdapterException("close"); } /** * A transaction is a database transaction, this is used to guarantee that all of the queries we do will run inside * of a transaction. * * @param callback - The callback that will be called to run inside of a transaction. * @param args - The arguments of the callback. * * @return - The return value of the callback. */ async transaction(_databaseAdapter, callback, ...args) { const transact = void 0; return await Promise.resolve(callback(transact, ...args)); } /** * A transaction is kinda strange, but it's a function that will run another function inside of it. With this * we can guarantee that a given piece of code will run inside of the transaction. After it finishes it returns * the value normally. * * @example * ``` * function transactionMultiply(transaction: SequelizeTransaction, a: number, b: number) { * return a * b; * } * * const result = await engineInstance.useTransaction(transactionMultiply, 2, 2) * * result // 4 * ``` * * On the example above, transactionMultiply run in a transaction and we pass the variables of this function on the * other arguments. The first argument is always the callback. The rest are the arguments of the callback function. * * @param callback - The callback that will be called to run inside of a transaction. * @param args - The arguments of the callback. * * @return - The return value of the callback. */ async useTransaction(callback, ...args) { return await this.transaction(this, callback, ...args); } }; // src/engine/query/get.ts function adapterGetQuery(args) { let CustomAdapterGetQuery = class CustomAdapterGetQuery extends AdapterGetQuery { static { __name(this, "CustomAdapterGetQuery"); } queryData = args.queryData; }; return CustomAdapterGetQuery; } __name(adapterGetQuery, "adapterGetQuery"); var AdapterGetQuery = class { static { __name(this, "AdapterGetQuery"); } /** * This is a simple query, by default you should always implement this function in your AdapterGetQuery. * * This will guarantee that you are able to retrieve the data, it's not much performatic because it will do * many small queries to the database, which might slow things down, but you will be guaranteed to work 100% * with the types. * * For a more performatic approach you should implement `queryDataNatively`. That will translate the query to the * native query, but the second can be harder to implement since it relies on knowing about palmares objects and * model structure. * * A simple Sequelize example: * @example * ```ts * async queryData( * _engine: DatabaseAdapter, * args: { * modelOfEngineInstance: ModelCtor; * search: any; * fields: readonly string[]; * ordering?: Order; * limit?: number; * offset?: number | string; * } * ) { * const findAllOptions: Parameters['findAll']>[0] = { * attributes: args.fields as string[], * where: args.search, * nest: true, * raw: true, * }; * if (args.ordering) findAllOptions.order = args.ordering; * if (args.limit) findAllOptions.limit = args.limit; * if (args.offset) findAllOptions.offset = Number(args.offset); * return args.modelOfEngineInstance.findAll(findAllOptions); * } * ``` * * @param _engine - The engine instance that is running the query. * @param _args - The arguments of the query. * @param _args.modelOfEngineInstance - The model instance to query, this is what your ORM has translated on * `AdapterModel.translate` function. * @param _args.fields - The fields to retrieve from the database, sometimes the user doesn't want to retrieve * all of the fields from the database. * @param _args.ordering - The ordering to use on the query, this ordering is translated from the `parseOrdering` * inside {@link AdapterQueryOrdering} * @param _args.search - The search argument to search on the database. This was translated from the `parseSearch` * inside {@link AdapterQuerySearch} * @param _args.limit - The limit of the query, this is used for pagination. * @param _args.offset - The offset of the query, this is used for pagination. * * @returns - Returns an array, always should return an array, if the data doesn't exist, return an empty array * instead of undefined or null. */ // eslint-disable-next-line ts/require-await async queryData(_engine, _args) { return []; } // eslint-disable-next-line ts/require-await async queryDataNatively(_engine, _modelConstructor, _search, _fields, _includes, _defaultParseSearch) { throw new NotImplementedAdapterException("queryDataNatively"); } }; // src/engine/query/ordering.ts function adapterOrderingQuery(args) { let CustomAdapterOrderingQuery = class CustomAdapterOrderingQuery extends AdapterOrderingQuery { static { __name(this, "CustomAdapterOrderingQuery"); } parseOrdering = args.parseOrdering; }; return CustomAdapterOrderingQuery; } __name(adapterOrderingQuery, "adapterOrderingQuery"); var AdapterOrderingQuery = class { static { __name(this, "AdapterOrderingQuery"); } /** * Ordering the query is as simple as passing an array of string, each string contains the name of the field alongside * a `-` if it's descending. * * - `['name']` - Order by name ascending. * - `['-name']` - Order by name descending. * * A simple Sequelize example would be: * @example * ```ts * import { Order } from 'sequelize'; * * function async parseOrdering(ordering): Promise => { * return ordering.map((order) => { * const isDescending = order.startsWith('-'); * return [isDescending ? order.slice(1) : order, isDescending ? 'DESC' : 'ASC']; * }); * } * ``` * * @param _ordering - The ordering to parse. * * @returns - Returns the parsed ordering to be used on your query. */ // eslint-disable-next-line ts/require-await async parseOrdering(_modelInstance, _ordering) { throw new NotImplementedAdapterException("parseOrdering"); } }; // src/engine/query/remove.ts function adapterRemoveQuery(args) { let CustomAdapterRemoveQuery = class CustomAdapterRemoveQuery extends AdapterRemoveQuery { static { __name(this, "CustomAdapterRemoveQuery"); } queryData = args.queryData; }; return CustomAdapterRemoveQuery; } __name(adapterRemoveQuery, "adapterRemoveQuery"); var AdapterRemoveQuery = class { static { __name(this, "AdapterRemoveQuery"); } /** * This query is used to remove a certain data from the database. * * An example of how you can implement it on sequelize * @example * ```typescript * queryData: async (_, args) => { * async function remove() { * return args.modelOfEngineInstance.destroy({ * where: args.search, * transaction: args.transaction, * }); * } * * if (args.shouldReturnData) { * const deleted = await args.modelOfEngineInstance.findAll({ * where: args.search, * transaction: args.transaction, * }); * await remove(); * * return deleted.map((data: any) => data.toJSON()); * } * * await remove(); * return []; * } */ // eslint-disable-next-line ts/require-await async queryData(_engine, _args) { return [ {} ]; } }; // src/engine/query/search.ts function adapterSearchQuery(args) { let CustomAdapterQuerySearch = class CustomAdapterQuerySearch extends AdapterSearchQuery { static { __name(this, "CustomAdapterQuerySearch"); } parseSearchFieldValue = args.parseSearchFieldValue; }; return CustomAdapterQuerySearch; } __name(adapterSearchQuery, "adapterSearchQuery"); var AdapterSearchQuery = class { static { __name(this, "AdapterSearchQuery"); } /** * This will pretty much receive the search value and parse it and translate that into an object for each field. * The nicest thing is that we send you the `result`. This way you can organize the `search` object the way that * you want and makes sense for your engine. * * @example * ```ts * async parseSearchFieldValue( * operationType: OperationType, * value?: (OperationType extends 'or' | 'and' | 'in' | 'between' ? unknown[] : unknown) | undefined, * result?: any, * options?: { isNot?: boolean | undefined; ignoreCase?: boolean | undefined } | undefined * ): Promise { * switch (operationType) { * case 'like': * if (options?.ignoreCase) result[Op.iLike] = value; * else if (options?.isNot && options.ignoreCase) result[Op.notILike] = value; * else if (options?.isNot) result[Op.notLike] = value; * else result[Op.like] = value; * return; * case 'is': * if (value === null && options?.isNot) result[Op.not] = value; * else if (value === null) result[Op.is] = value; * else if (options?.isNot) result[Op.ne] = value; * else result[Op.eq] = value; * return; * case 'in': * if (options?.isNot) result[Op.notIn] = value; * else result[Op.in] = value; * return; * case 'between': * if (options?.isNot) result[Op.notBetween] = value; * else result[Op.between] = value; * return; * case 'and': * result[Op.and] = value; * return; * case 'or': * result[Op.or] = value; * return; * case 'greaterThan': * result[Op.gt] = value; * return; * case 'greaterThanOrEqual': * result[Op.gte] = value; * return; * case 'lessThan': * result[Op.lt] = value; * return; * case 'lessThanOrEqual': * result[Op.lte] = value; * return; * default: * return; * } * } * ``` * * @param operationType - The operation type of the query, this can be `like`, `in`, `is`, `between` and so on. * @param key - The key of the query, this is the field name that we are querying. * @param modelInstance - The model instance that we are querying. * @param value - The value of the query, if the operation is `or`, `and`, `in` or `between` this will be an array of * values, otherwise this can be a string, a number and such. Be aware that this respects the `inputParser` of the * field if you have implemented it on {@link AdapterFieldParser}. This is nice because we can maintain the types of * the fields through the framework and you can parse the way that makes sense for your engine. * @param result - This is the result we will use on the query. It's an object that you can append the result of the * parsing. * @param options - This is an object with some options that you can use to parse the query, like if it's a negative * query, if it's case insensitive and so on. * * @returns - Values are changed in-place, so you should not return anything. */ // eslint-disable-next-line ts/require-await async parseSearchFieldValue(_operationType, _key, _modelInstance, _value, _result, _options) { return Array.isArray(_value) ? _value[0] : _value; } }; // src/engine/query/set.ts function adapterSetQuery(args) { let CustomAdapterSetQuery = class CustomAdapterSetQuery extends AdapterSetQuery { static { __name(this, "CustomAdapterSetQuery"); } queryData = args.queryData; }; return CustomAdapterSetQuery; } __name(adapterSetQuery, "adapterSetQuery"); var AdapterSetQuery = class { static { __name(this, "AdapterSetQuery"); } /** * This is a simple upsert query, by default you should always implement this function in your AdapterSetQuery. * * _Note_: If `args.search` is not null or undefined, you should update the data, otherwise you should create it. * _Note 2_: You should return an array, the first argument is true if the data was created, false otherwise. The * second argument is the data that was created or updated. * * @example * ```ts * async queryData( * _: DatabaseAdapter, * args: { * modelOfEngineInstance: ModelCtor; * search: any; * data?: any; * transaction?: Transaction; * } * ): Promise<[boolean, any][]> { * return Promise.all( * args.data.map(async (eachData: any) => { * if (args.search === undefined) * return [ * true, * ( * await args.modelOfEngineInstance.create(eachData, { * transaction: args.transaction, * }) * ).toJSON(), * ]; * const [instance, hasCreated] = await args.modelOfEngineInstance.upsert(eachData, { * transaction: args.transaction, * returning: true, * }); * return [hasCreated ? hasCreated : false, instance.toJSON()]; * }) * ); * } * ``` * * @param _engine - The engine instance that is running the query. * @param _args - The arguments of the query. * @param _args.modelOfEngineInstance - The model instance to query, this is what your ORM has translated * on `AdapterModel.translate` function. * @param _args.search - The search argument to search on the database. * @param _args.data - The data to be inserted or updated. * @param _args.transaction - The transaction to use to run the query, That's what you pass to the callback * function on `DatabaseAdapter.transaction`. * * @returns - Returns an array of tuples, the first argument is true if the data was created, false otherwise. * The second argument is the data that was created or updated. */ // eslint-disable-next-line ts/require-await async queryData(_engine, _args) { return _args.data.map((eachData) => [ true, { ...eachData } ]); } }; // src/engine/query/index.ts function adapterQuery(args) { let CustomAdapterQuery = class CustomAdapterQuery extends AdapterQuery { static { __name(this, "CustomAdapterQuery"); } get = args.get; set = args.set; remove = args.remove; search = args.search; ordering = args.ordering; }; return CustomAdapterQuery; } __name(adapterQuery, "adapterQuery"); var AdapterQuery = class { static { __name(this, "AdapterQuery"); } get = new AdapterGetQuery(); set = new AdapterSetQuery(); remove = new AdapterRemoveQuery(); search = new AdapterSearchQuery(); ordering = new AdapterOrderingQuery(); }; // src/engine/model.ts function adapterModels(args) { let CustomAdapterModel = class CustomAdapterModel extends AdapterModels { static { __name(this, "CustomAdapterModel"); } translateOptions = args.translateOptions; translateFields = args.translateFields; translate = args.translate; afterModelsTranslation = args.afterModelsTranslation; compare = args.compare; modelToString = args.modelToString; static customOptions = args.customOptions; }; return CustomAdapterModel; } __name(adapterModels, "adapterModels"); var AdapterModels = class { static { __name(this, "AdapterModels"); } /** * Used for translating the options of the model. Options of the model are things like the `tableName`, `indexes`, * `timestamps`, etc. If your engine does not offer the option to implement the options, just return the `options` * argument as is. * * @example * ```ts * async translateOptions( * _engine: SequelizeEngine, * modelName: string, * options: ModelOptionsType * ): Promise { * const indexes = this.#indexes[modelName] ? this.#indexes[modelName] : []; * return { * underscored: options?.underscored || true, * indexes: indexes, * timestamps: false, * tableName: options?.tableName, * ...options?.customOptions, * }; * } * ``` * * @param engine - Your custom engine instance. * @param modelName - The name of the model that is being translated. * @param modelOptions - The options of the model that is being translated. */ // eslint-disable-next-line ts/require-await async translateOptions(_engine, _modelName, _modelOptions) { throw new NotImplementedAdapterException("translateOptions"); } /** * This method is completely optional, we already try to solve that for you. What this method does is that it is * used to translate the fields of the model. We already give you the field entries of the model on * `_fieldEntriesOfModel`. Since we already has a default implementation you can opt to use it by * calling `_defaultTranslateFieldsCallback`. * * If you opt to NOT use it, you should call `engine.fields.translateField` for each field of the model. If * `translateField` is not implemented on EngineFields, you can call `_defaultTranslateFieldCallback` that * already has a default implementation for you. * * This should return an object with the fields translated to something that YOUR ORM can understand. Each * key of the object is the field name and the value is the translated field. * * **IMPORTANT:** By default, if the `translate` method on any of your FieldsParser returns `undefined` or * `null`, it will **NOT** be added to the object. That's useful if you want to add it later and lazy evaluate that. * * _Note: All examples below are considering that we are translating to sequelize._ * * - **If you opt out of the default implementation of both the `_defaultTranslateFieldCallback` and * `_defaultTranslateFieldsCallback`, this is how you can do it:** * * @example * ```ts * async function translateFields( * engine: DatabaseAdapter, * modelName: string, * fieldEntriesOfModel: [string, Field][], * model: Model, * defaultTranslateFieldCallback: (field: Field) => Promise, * _: () => Promise<{ [key: string]: any }> * ) { * const fieldAttributes: { [key: string]: ModelAttributeColumnOptions } = {}; * for (const [fieldName, field] of fieldEntriesOfModel) { * const translatedAttributes = await engine.fields.translateField(engine, field, defaultTranslateFieldCallback); * const isTranslatedAttributeDefined = translatedAttributes !== undefined && * translatedAttributes !== null && typeof translatedAttributes === 'object'; * if (isTranslatedAttributeDefined) fieldAttributes[fieldName] = translatedAttributes; * } * * return fieldAttributes; * } * ``` * * - **If you opt in of the default implementation of just the `_defaultTranslateFieldCallback`, * this is how you can do it: * (this assumes that `translateField` was not defined on your _EngineFields_ implementation)** * * @example * ```ts * async function translateFields( * engine: DatabaseAdapter, * modelName: string, * fieldEntriesOfModel: [string, Field][], * model: Model, * defaultTranslateFieldCallback: (field: Field) => Promise, * _: () => Promise<{ [key: string]: any }> * ) { * const fieldAttributes: { [key: string]: ModelAttributeColumnOptions } = {}; * for (const [fieldName, field] of fieldEntriesOfModel) { * const translatedAttributes = await defaultTranslateFieldCallback(field); * const isTranslatedAttributeDefined = translatedAttributes !== undefined && * translatedAttributes !== null && typeof translatedAttributes === 'object'; * if (isTranslatedAttributeDefined) fieldAttributes[fieldName] = translatedAttributes; * } * * return fieldAttributes; * } * ``` * * - **If you opt in of the default implementation of just the `_defaultTranslateFieldsCallback`, this * is how you can do it: * (assuming that you want to let it translate first and then do anything with the fields afterwards)** * * @example * ```ts * async function translateFields( * _engine: DatabaseAdapter, * _modelName: string, * _fieldEntriesOfModel: [string, Field][], * _model: Model, * _: (field: Field) => Promise, * defaultTranslateFieldsCallback: () => Promise<{ [key: string]: any }> * ) { * const fieldAttributes: { [key: string]: ModelAttributeColumnOptions } = await defaultTranslateFieldsCallback(); * * // Do something with the fields after they were translated * * return fieldAttributes; * } * ``` * * Last but not least, you can totally opt out of using it. If that's your choice, just don't implement it and we will * use the default implementation. * On your `translate` method you should see that `fields` object will be an object where the keys are the field names * and the values are the translated fields. * * @param engine - Your custom engine instance. * @param modelName - The name of the model that is being translated. * @param fieldEntriesOfModel - The field entries of the model. It's an array of tuples where the first element is the * field name and the second is the field. * @param model - The model that is being translated. * @param defaultTranslateFieldCallback - The default implementation of the `translateField` method that Palmares * provides. If you have a `translateField` implementation on your `EngineFields` implementation, you need to make * sure that you pass it to this method. * @param defaultTranslateFieldsCallback - The default implementation of the `translateFields` method that Palmares * provides. * * @returns - An object where the keys are the field names and the values are the translated values. */ // eslint-disable-next-line ts/require-await async translateFields(_engine, _modelName, _fieldEntriesOfModel, _model, _defaultTranslateFieldCallback, _defaultTranslateFieldsCallback) { throw new NotImplementedAdapterException("translateFields"); } /** * The `translate` method will be called to translate the model to a instance of something that your engine/ORM can * understand. In other words, we will transform a Palmares model to YOUR model. * * ## first, a little explanation what it does: * * On Palmares, we DO NOT OFFER an ORM by default, we are really bad coders and we trust others (like you) to do that * for us. Translating a model means taking what we offer for them and passing all that data to you. You will decide * what to do with that. Some ORMs like DrizzleORM, Sequelize, TypeORM, etc. Will have a default implementation of * how a model should be implemented. That's what this method does, it will take the palmares model and translate * to your own ORM. * * - On Sequelize this would be the `User` on this example: * * @example * ```ts * const { Sequelize, Model, DataTypes } = require("sequelize"); * const sequelize = new Sequelize("sqlite::memory:"); * * const User = sequelize.define("user", { * name: DataTypes.TEXT, * favoriteColor: { * type: DataTypes.TEXT, * defaultValue: 'green' * }, * age: DataTypes.INTEGER, * cash: DataTypes.INTEGER * }); * ``` * * - On prisma, this would be `prisma.user` * * @example * ```ts * const { PrismaClient } = require('@prisma/client') * * const prisma = new PrismaClient() * * const users = await prisma.user.findMany() // here prisma.user is what we would need you to return. * ``` * * Prisma, actually has a `gotcha` there. Because you might want to transform the data to a string before actually * returning the actual model implementation. That's why we have the {@link AdapterModels['afterModelsTranslation']} * method. You can return a string from here, and on the `afterModelsTranslation` method you can build the schema * file and run the `prisma generate` command to generate the models. And just after that return the models. * * **This is an example assuming that you are translating sequelize** * @example * ```ts * async translate( * engine: SequelizeEngine, * modelName: string, * model: ModelBaseClass, * defaultTranslateCallback: () => Promise<{ options: ModelOptions; fields: ModelAttributes }>, * _: (_field: Field) => Promise, * __: () => Promise<{ [key: string]: ModelAttributeColumnOptions }> * ): Promise | undefined> { * const { options: translatedOptions, fields: translatedAttributes } = await defaultTranslateCallback(); * * translatedOptions.indexes = getIndexes(engine.connectionName, modelName); * * const sequelizeModel = new Function('sequelizeModel', `return class ${modelName} extends sequelizeModel {}`)( * Model * ); * * const translatedModel = sequelizeModel.init(translatedAttributes, { * sequelize: engine.instance, * ...translatedOptions, * }); * * if (translatedModel !== undefined) await this.#translateOrdering(model, translatedModel); * return translatedModel; * } * ``` * * @param engine - The instance of your DatabaseAdapter. * @param modelName - The name of the model that is being translated. * @param model - The Palmares model instance so we can translate it. * @param fieldEntriesOfModel - The field entries of the model. It's an array of tuples where the first element is * the field name and the second is the field. * @param modelOptions - The options of the model that is being translated. * @param customOptions - Custom options that you can pass for your model. * @param defaultTranslateCallback - Instead of manually calling the `translateFields` and `translateOptions` methods, * you can call this function and it will do that for you. It will return an object * with the `options` and `fields` keys. The `options` key will be the return of the `translateOptions` method and the * `fields` key will be the return of the `translateFields` method. * @param defaultTranslateFieldCallback - This is passed here so you can pass to `translateFields` if you wish to call * it manually. * @param defaultTranslateFieldsCallback - This is passed here so you can pass to `translateFields` if you wish to * call it manually. * * @returns - The instance of the translated model. */ // eslint-disable-next-line ts/require-await async translate(_engine, _modelName, _model, _fieldEntriesOfModel, _modelOptions, _customOptions, _defaultTranslateCallback, _defaultTranslateFieldCallback, _defaultTranslateFieldsCallback) { throw new NotImplementedAdapterException("translate"); } /** * Some ORMs like Prisma requires you to run a generator command to generate the models to something that can be used * inside Typescript. With this method you can run this generator command. * This is called just once after all your models were translated. * * You have to options to return: * 1. You can return an array with all your models translated again (that's useful if you want to do one last change * to your models). * 2. You can return undefined and we will use the returned models from the `translate` method. * * @example * ```ts * async afterModelsTranslation( * _engine: DatabaseAdapter, _models: [string, any][] * ): Promise<[string, any][] | undefined> { * spawn('npx', ['prisma', 'generate'], { stdio: 'inherit' }); * return undefined; * } * ``` * * @param engine - The engine instance. * @param models - An array of tuples where the first value is the modelName and the second is the value returned from * `translate` method. * * @returns - An array of tuples where the first value is the modelName and the second is the value returned from * `translate` method, or undefined if you don't want to modify the models. */ // eslint-disable-next-line ts/require-await async afterModelsTranslation(_engine, _models) { throw new NotImplementedAdapterException("afterModelsTranslation"); } // eslint-disable-next-line ts/require-await async getModelInstanceForCustomHooks(_engine, _modelName, _translatedModel) { throw new NotImplementedAdapterException("getModelInstanceForCustomHooks"); } getTranslatedModels() { return void 0; } /** * Used for comparing two custom arguments so we can know if we need to update the field or not. * * This is part of the migration, don't need to implement if you are not using Palmares Migrations. */ compare(_oldCustomArguments, _newCustomArguments) { throw new NotImplementedAdapterException("compare"); } /** * Used for stringfying the custom arguments so we can store them in the database. If you do not implement this * and implement compare we will throw an error, otherwise we will just ignore the custom arguments. * * This is part of the migration, don't need to implement if you are not using Palmares Migrations. */ modelToString(_customArguments) { throw new NotImplementedAdapterException("modelToString"); } /** * This method is used just for giving typesafety. If you implement this method, those are custom options that you can * pass for your model. * * For example, if you are using sequelize, those would be the `third` argument from `sequelize.define`. */ static customOptions(args) { return args; } }; // src/engine/fields/auto.ts function adapterAutoFieldParser(args) { let CustomAdapterAutoFieldParser = class CustomAdapterAutoFieldParser extends AdapterAutoFieldParser { static { __name(this, "CustomAdapterAutoFieldParser"); } translate = args.translate; inputParser = args.inputParser; outputParser = args.outputParser; }; return CustomAdapterAutoFieldParser; } __name(adapterAutoFieldParser, "adapterAutoFieldParser"); var AdapterAutoFieldParser = class { static { __name(this, "AdapterAutoFieldParser"); } /** * @description * Used to translate the field to something that the database can understand. The `{@link AdapterFieldParser}` * instance will be injected by default in the `translate` method. * The core idea is that for every field type we will have a parser that will be used to translate the field to * something that the database can understand. It's nice if all of the configuration options are supported by * your ORM, but if that's not the case it's nice to notify the users through documentation. * * - _Note_: **If you return undefined, we will not consider that field on the object we build on `translateFields` * under {@link AdapterModels} instance.** * - _Note2_: **Use the `lazyEvaluate` function to evaluate something after the model was translated.** * * Imagine that you translating to sequelize field: * @example * ```ts * async translate({ * engine, * field, * modelName, * }: { * engine: SequelizeEngine; * field: Field; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * lazyEvaluate: (translatedField: any) => void; * }): Promise { * const defaultOptions = {} as ModelAttributeColumnOptions; * const isFieldAIndexOrIsFieldUnique = field.dbIndex === true || (field.unique as boolean) === true; * * if (isFieldAIndexOrIsFieldUnique) appendIndexes(engine.connectionName, modelName, field); * * const hasNotYetSetDefaultValueForField = defaultOptions.defaultValue === undefined; * if (hasNotYetSetDefaultValueForField) defaultOptions.defaultValue = field.defaultValue; * * defaultOptions.autoIncrement = field.isAuto; * defaultOptions.autoIncrementIdentity = field.isAuto; * defaultOptions.primaryKey = field.primaryKey; * defaultOptions.allowNull = field.allowNull; * defaultOptions.unique = field.unique; * defaultOptions.validate = {}; * defaultOptions.validate.notNull = !field.allowNull; * defaultOptions.field = field.databaseName; * * const customAttributesOfFieldEntries = Object.entries(field.customAttributes); * for (const [key, value] of customAttributesOfFieldEntries) { * const keyAsTypeofModelColumnOption = key as keyof ModelAttributeColumnOptions; * defaultOptions[keyAsTypeofModelColumnOption] = value as never; * } * * const isFieldOfTypeText = * field.typeName === TextField.name || field.typeName === CharField.name || field.typeName === UuidField.name; * * if (isFieldOfTypeText) this.textFieldValidations(field as TextField); * * return defaultOptions; * } * ``` * * @description Or you can lazy evaluate (useful for ForeignKeys): * * @example * ```ts * async translate(args: { * engine: SequelizeEngine; * field: AutoField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * lazyEvaluate: (translatedField: TranslatedFieldToEvaluateAfterType) => void; * }): Promise { * const defaultOptions = await args.fieldParser.translate(args); * * args.lazyEvaluate({ * fieldAttributes: defaultOptions, * type: 'foreign-key', * } as TranslatedFieldToEvaluateAfterType); * } * ``` * * @description * As we discussed before, the `lazyEvaluate` function is used to evaluate something after the model was translated. * So what you pass to the `lazyEvaluate` function will be passed to `lazyEvaluateField` method on the * `{@link AdapterFields}` instance under `fieldTranslated` argument * * @returns - The translated field. */ // eslint-disable-next-line ts/require-await async translate(args) { throw new EngineDoesNotSupportFieldTypeException(args.engine.constructor.name, AutoField.name); } /** * @description * This is used to parse the input value before you save it. For example, let's say that palmares by default accept * `Date` objects for `DateField`. But your database does not support saving `Date` instances. What you can do is that * you can implement this method on `DateFieldParser` and return a iSO string. With that, you can be 100% sure that * the data on your `queryData` is something valid for your database. * * This parses the value for each data. * * @example * ```ts * async inputParser(args: { * engine: SequelizeEngine; * field: DateField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * value: Date | string; * }): Promise { * if (args.value instanceof Date) return args.value.toISOString(); * return args.value; * } * ``` * * @returns - The parsed value. */ // eslint-disable-next-line ts/require-await async inputParser(args) { return args.value; } /** * @description * This is used to parse the output value before you send it to the user. For example, if the user is fetching a * `DateField` from the database, you can parse the value to a `Date` object. * This can be useful so you can guarantee that the user will receive the data in the format that it's expected. * * This parses the value for each data that is retrieved. * * @example * ```ts * async outputParser(args: { * engine: SequelizeEngine; * field: DateField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * value: string; * }): Promise { * if (typeof value === 'string') return new Date(value); * return args.value; * } * ``` * * @returns - The parsed value for the user for that specific field. */ // eslint-disable-next-line ts/require-await async outputParser(args) { return args.value; } }; // src/engine/fields/big-auto.ts function adapterBigAutoFieldParser(args) { let CustomAdapterBigAutoFieldParser = class CustomAdapterBigAutoFieldParser extends AdapterBigAutoFieldParser { static { __name(this, "CustomAdapterBigAutoFieldParser"); } translate = args.translate; inputParser = args.inputParser; outputParser = args.outputParser; }; return CustomAdapterBigAutoFieldParser; } __name(adapterBigAutoFieldParser, "adapterBigAutoFieldParser"); var AdapterBigAutoFieldParser = class { static { __name(this, "AdapterBigAutoFieldParser"); } /** * @description * Used to translate the field to something that the database can understand. The `{@link AdapterFieldParser}` * instance will be injected by default in the `translate` method. * The core idea is that for every field type we will have a parser that will be used to translate the field * to something that the database can understand. It's nice if all of the configuration options are supported * by your ORM, but if that's not the case it's nice to notify the users through documentation. * * - _Note_: **If you return undefined, we will not consider that field on the object we build on `translateFields` * under {@link AdapterModels} instance.** * - _Note2_: **Use the `lazyEvaluate` function to evaluate something after the model was translated.** * * Imagine that you translating to sequelize field: * @example * ```ts * async translate({ * engine, * field, * modelName, * }: { * engine: SequelizeEngine; * field: Field; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * lazyEvaluate: (translatedField: any) => void; * }): Promise { * const defaultOptions = {} as ModelAttributeColumnOptions; * const isFieldAIndexOrIsFieldUnique = field.dbIndex === true || (field.unique as boolean) === true; * * if (isFieldAIndexOrIsFieldUnique) appendIndexes(engine.connectionName, modelName, field); * * const hasNotYetSetDefaultValueForField = defaultOptions.defaultValue === undefined; * if (hasNotYetSetDefaultValueForField) defaultOptions.defaultValue = field.defaultValue; * * defaultOptions.autoIncrement = field.isAuto; * defaultOptions.autoIncrementIdentity = field.isAuto; * defaultOptions.primaryKey = field.primaryKey; * defaultOptions.allowNull = field.allowNull; * defaultOptions.unique = field.unique; * defaultOptions.validate = {}; * defaultOptions.validate.notNull = !field.allowNull; * defaultOptions.field = field.databaseName; * * const customAttributesOfFieldEntries = Object.entries(field.customAttributes); * for (const [key, value] of customAttributesOfFieldEntries) { * const keyAsTypeofModelColumnOption = key as keyof ModelAttributeColumnOptions; * defaultOptions[keyAsTypeofModelColumnOption] = value as never; * } * * const isFieldOfTypeText = * field.typeName === TextField.name || field.typeName === CharField.name || field.typeName === UuidField.name; * * if (isFieldOfTypeText) this.textFieldValidations(field as TextField); * * return defaultOptions; * } * ``` * * @description Or you can lazy evaluate (useful for ForeignKeys): * * @example * ```ts * async translate(args: { * engine: SequelizeEngine; * field: BigAutoField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * lazyEvaluate: (translatedField: TranslatedFieldToEvaluateAfterType) => void; * }): Promise { * const defaultOptions = await args.fieldParser.translate(args); * * args.lazyEvaluate({ * fieldAttributes: defaultOptions, * type: 'foreign-key', * } as TranslatedFieldToEvaluateAfterType); * } * ``` * * @description * As we discussed before, the `lazyEvaluate` function is used to evaluate something after the model was translated. * So what you pass to the `lazyEvaluate` function will be passed to `lazyEvaluateField` method on the * `{@link AdapterFields}` instance under `fieldTranslated` argument * * @returns - The translated field. */ // eslint-disable-next-line ts/require-await async translate(args) { throw new EngineDoesNotSupportFieldTypeException(args.engine.constructor.name, BigAutoField.name); } /** * @description * This is used to parse the input value before you save it. For example, let's say that palmares by default accept * `Date` objects for `DateField`. But your database does not support saving `Date` instances. What you can do is * that you can implement this method on `DateFieldParser` and return a iSO string. With that, you can be 100% sure * that the data on your `queryData` is something valid for your database. * * This parses the value for each data. * * @example * ```ts * async inputParser(args: { * engine: SequelizeEngine; * field: DateField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * value: Date | string; * }): Promise { * if (args.value instanceof Date) return args.value.toISOString(); * return args.value; * } * ``` * * @returns - The parsed value. */ // eslint-disable-next-line ts/require-await async inputParser(args) { return args.value; } /** * @description * This is used to parse the output value before you send it to the user. For example, if the user is fetching a * `DateField` from the database, you can parse the value to a `Date` object. * This can be useful so you can guarantee that the user will receive the data in the format that it's expected. * * This parses the value for each data that is retrieved. * * @example * ```ts * async outputParser(args: { * engine: SequelizeEngine; * field: DateField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * value: string; * }): Promise { * if (typeof value === 'string') return new Date(value); * return args.value; * } * ``` * * @returns - The parsed value for the user for that specific field. */ // eslint-disable-next-line ts/require-await async outputParser(args) { return args.value; } }; // src/engine/fields/big-integer.ts function adapterBigIntegerFieldParser(args) { let CustomAdapterBigIntegerFieldParser = class CustomAdapterBigIntegerFieldParser extends AdapterBigIntegerFieldParser { static { __name(this, "CustomAdapterBigIntegerFieldParser"); } translate = args.translate; inputParser = args.inputParser; outputParser = args.outputParser; }; return CustomAdapterBigIntegerFieldParser; } __name(adapterBigIntegerFieldParser, "adapterBigIntegerFieldParser"); var AdapterBigIntegerFieldParser = class { static { __name(this, "AdapterBigIntegerFieldParser"); } /** * @description * Used to translate the field to something that the database can understand. The `{@link AdapterFieldParser}` * instance will be injected by default in the `translate` method. * The core idea is that for every field type we will have a parser that will be used to translate the field to * something that the database can understand. It's nice if all of the configuration options are supported by * your ORM, but if that's not the case it's nice to notify the users through documentation. * * - _Note_: **If you return undefined, we will not consider that field on the object we build on `translateFields` * under {@link AdapterModels} instance.** * - _Note2_: **Use the `lazyEvaluate` function to evaluate something after the model was translated.** * * Imagine that you translating to sequelize field: * @example * ```ts * async translate({ * engine, * field, * modelName, * }: { * engine: SequelizeEngine; * field: Field; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * lazyEvaluate: (translatedField: any) => void; * }): Promise { * const defaultOptions = {} as ModelAttributeColumnOptions; * const isFieldAIndexOrIsFieldUnique = field.dbIndex === true || (field.unique as boolean) === true; * * if (isFieldAIndexOrIsFieldUnique) appendIndexes(engine.connectionName, modelName, field); * * const hasNotYetSetDefaultValueForField = defaultOptions.defaultValue === undefined; * if (hasNotYetSetDefaultValueForField) defaultOptions.defaultValue = field.defaultValue; * * defaultOptions.autoIncrement = field.isAuto; * defaultOptions.autoIncrementIdentity = field.isAuto; * defaultOptions.primaryKey = field.primaryKey; * defaultOptions.allowNull = field.allowNull; * defaultOptions.unique = field.unique; * defaultOptions.validate = {}; * defaultOptions.validate.notNull = !field.allowNull; * defaultOptions.field = field.databaseName; * * const customAttributesOfFieldEntries = Object.entries(field.customAttributes); * for (const [key, value] of customAttributesOfFieldEntries) { * const keyAsTypeofModelColumnOption = key as keyof ModelAttributeColumnOptions; * defaultOptions[keyAsTypeofModelColumnOption] = value as never; * } * * const isFieldOfTypeText = * field.typeName === TextField.name || field.typeName === CharField.name || field.typeName === UuidField.name; * * if (isFieldOfTypeText) this.textFieldValidations(field as TextField); * * return defaultOptions; * } * ``` * * @description Or you can lazy evaluate (useful for ForeignKeys): * * @example * ```ts * async translate(args: { * engine: SequelizeEngine; * field: BigIntegerField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * lazyEvaluate: (translatedField: TranslatedFieldToEvaluateAfterType) => void; * }): Promise { * const defaultOptions = await args.fieldParser.translate(args); * * args.lazyEvaluate({ * fieldAttributes: defaultOptions, * type: 'foreign-key', * } as TranslatedFieldToEvaluateAfterType); * } * ``` * * @description * As we discussed before, the `lazyEvaluate` function is used to evaluate something after the model was translated. * So what you pass to the `lazyEvaluate` function will be passed to `lazyEvaluateField` method on the * `{@link AdapterFields}` instance under `fieldTranslated` argument * * @returns - The translated field. */ // eslint-disable-next-line ts/require-await async translate(args) { throw new EngineDoesNotSupportFieldTypeException(args.engine.constructor.name, BigIntegerField.name); } /** * @description * This is used to parse the input value before you save it. For example, let's say that palmares by default accept * `Date` objects for `DateField`. But your database does not support saving `Date` instances. What you can do is that * you can implement this method on `DateFieldParser` and return a iSO string. With that, you can be 100% sure that * the data on your `queryData` is something valid for your database. * * This parses the value for each data. * * @example * ```ts * async inputParser(args: { * engine: SequelizeEngine; * field: DateField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * value: Date | string; * }): Promise { * if (args.value instanceof Date) return args.value.toISOString(); * return args.value; * } * ``` * * @returns - The parsed value. */ // eslint-disable-next-line ts/require-await async inputParser(args) { return args.value; } /** * @description * This is used to parse the output value before you send it to the user. For example, if the user is fetching a * `DateField` from the database, you can parse the value to a `Date` object. * This can be useful so you can guarantee that the user will receive the data in the format that it's expected. * * This parses the value for each data that is retrieved. * * @example * ```ts * async outputParser(args: { * engine: SequelizeEngine; * field: DateField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * value: string; * }): Promise { * if (typeof value === 'string') return new Date(value); * return args.value; * } * ``` * * @returns - The parsed value for the user for that specific field. */ // eslint-disable-next-line ts/require-await async outputParser(args) { return args.value; } }; // src/engine/fields/boolean.ts function adapterBooleanFieldParser(args) { let CustomAdapterBooleanFieldParser = class CustomAdapterBooleanFieldParser extends AdapterBooleanFieldParser { static { __name(this, "CustomAdapterBooleanFieldParser"); } translate = args.translate; inputParser = args.inputParser; outputParser = args.outputParser; }; return CustomAdapterBooleanFieldParser; } __name(adapterBooleanFieldParser, "adapterBooleanFieldParser"); var AdapterBooleanFieldParser = class { static { __name(this, "AdapterBooleanFieldParser"); } /** * @description * Used to translate the field to something that the database can understand. The `{@link AdapterFieldParser}` * instance will be injected by default in the `translate` method. * The core idea is that for every field type we will have a parser that will be used to translate the field * to something that the database can understand. It's nice if all of the configuration options are supported * by your ORM, but if that's not the case it's nice to notify the users through documentation. * * - _Note_: **If you return undefined, we will not consider that field on the object we build on * `translateFields` under {@link AdapterModels} instance.** * - _Note2_: **Use the `lazyEvaluate` function to evaluate something after the model was translated.** * * Imagine that you translating to sequelize field: * @example * ```ts * async translate({ * engine, * field, * modelName, * }: { * engine: SequelizeEngine; * field: Field; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * lazyEvaluate: (translatedField: any) => void; * }): Promise { * const defaultOptions = {} as ModelAttributeColumnOptions; * const isFieldAIndexOrIsFieldUnique = field.dbIndex === true || (field.unique as boolean) === true; * * if (isFieldAIndexOrIsFieldUnique) appendIndexes(engine.connectionName, modelName, field); * * const hasNotYetSetDefaultValueForField = defaultOptions.defaultValue === undefined; * if (hasNotYetSetDefaultValueForField) defaultOptions.defaultValue = field.defaultValue; * * defaultOptions.autoIncrement = field.isAuto; * defaultOptions.autoIncrementIdentity = field.isAuto; * defaultOptions.primaryKey = field.primaryKey; * defaultOptions.allowNull = field.allowNull; * defaultOptions.unique = field.unique; * defaultOptions.validate = {}; * defaultOptions.validate.notNull = !field.allowNull; * defaultOptions.field = field.databaseName; * * const customAttributesOfFieldEntries = Object.entries(field.customAttributes); * for (const [key, value] of customAttributesOfFieldEntries) { * const keyAsTypeofModelColumnOption = key as keyof ModelAttributeColumnOptions; * defaultOptions[keyAsTypeofModelColumnOption] = value as never; * } * * const isFieldOfTypeText = * field.typeName === TextField.name || field.typeName === CharField.name || field.typeName === UuidField.name; * * if (isFieldOfTypeText) this.textFieldValidations(field as TextField); * * return defaultOptions; * } * ``` * * @description Or you can lazy evaluate (useful for ForeignKeys): * * @example * ```ts * async translate(args: { * engine: SequelizeEngine; * field: BooleanField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * lazyEvaluate: (translatedField: TranslatedFieldToEvaluateAfterType) => void; * }): Promise { * const defaultOptions = await args.fieldParser.translate(args); * * args.lazyEvaluate({ * fieldAttributes: defaultOptions, * type: 'foreign-key', * } as TranslatedFieldToEvaluateAfterType); * } * ``` * * @description * As we discussed before, the `lazyEvaluate` function is used to evaluate something after the model was translated. * So what you pass to the `lazyEvaluate` function will be passed to `lazyEvaluateField` method on the * `{@link AdapterFields}` instance under `fieldTranslated` argument * * @returns - The translated field. */ // eslint-disable-next-line ts/require-await async translate(args) { throw new EngineDoesNotSupportFieldTypeException(args.engine.constructor.name, BooleanField.name); } /** * @description * This is used to parse the input value before you save it. For example, let's say that palmares by default accept * `Date` objects for `DateField`. But your database does not support saving `Date` instances. What you can do is * that you can implement this method on `DateFieldParser` and return a iSO string. With that, you can be 100% sure * that the data on your `queryData` is something valid for your database. * * This parses the value for each data. * * @example * ```ts * async inputParser(args: { * engine: SequelizeEngine; * field: DateField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * value: Date | string; * }): Promise { * if (args.value instanceof Date) return args.value.toISOString(); * return args.value; * } * ``` * * @returns - The parsed value. */ // eslint-disable-next-line ts/require-await async inputParser(args) { return args.value; } /** * @description * This is used to parse the output value before you send it to the user. For example, if the user is fetching a * `DateField` from the database, you can parse the value to a `Date` object. * This can be useful so you can guarantee that the user will receive the data in the format that it's expected. * * This parses the value for each data that is retrieved. * * @example * ```ts * async outputParser(args: { * engine: SequelizeEngine; * field: DateField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * value: string; * }): Promise { * if (typeof value === 'string') return new Date(value); * return args.value; * } * ``` * * @returns - The parsed value for the user for that specific field. */ // eslint-disable-next-line ts/require-await async outputParser(args) { return args.value; } }; // src/engine/fields/char.ts function adapterCharFieldParser(args) { let CustomAdapterCharFieldParser = class CustomAdapterCharFieldParser extends AdapterCharFieldParser { static { __name(this, "CustomAdapterCharFieldParser"); } translate = args.translate; inputParser = args.inputParser; outputParser = args.outputParser; }; return CustomAdapterCharFieldParser; } __name(adapterCharFieldParser, "adapterCharFieldParser"); var AdapterCharFieldParser = class { static { __name(this, "AdapterCharFieldParser"); } /** * @description * Used to translate the field to something that the database can understand. The `{@link AdapterFieldParser}` * instance will be injected by default in the `translate` method. * The core idea is that for every field type we will have a parser that will be used to translate the field * to something that the database can understand. It's nice if all of the configuration options are supported * by your ORM, but if that's not the case it's nice to notify the users through documentation. * * - _Note_: **If you return undefined, we will not consider that field on the object we build on * `translateFields` under {@link AdapterModels} instance.** * - _Note2_: **Use the `lazyEvaluate` function to evaluate something after the model was translated.** * * Imagine that you translating to sequelize field: * @example * ```ts * async translate({ * engine, * field, * modelName, * }: { * engine: SequelizeEngine; * field: Field; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * lazyEvaluate: (translatedField: any) => void; * }): Promise { * const defaultOptions = {} as ModelAttributeColumnOptions; * const isFieldAIndexOrIsFieldUnique = field.dbIndex === true || (field.unique as boolean) === true; * * if (isFieldAIndexOrIsFieldUnique) appendIndexes(engine.connectionName, modelName, field); * * const hasNotYetSetDefaultValueForField = defaultOptions.defaultValue === undefined; * if (hasNotYetSetDefaultValueForField) defaultOptions.defaultValue = field.defaultValue; * * defaultOptions.autoIncrement = field.isAuto; * defaultOptions.autoIncrementIdentity = field.isAuto; * defaultOptions.primaryKey = field.primaryKey; * defaultOptions.allowNull = field.allowNull; * defaultOptions.unique = field.unique; * defaultOptions.validate = {}; * defaultOptions.validate.notNull = !field.allowNull; * defaultOptions.field = field.databaseName; * * const customAttributesOfFieldEntries = Object.entries(field.customAttributes); * for (const [key, value] of customAttributesOfFieldEntries) { * const keyAsTypeofModelColumnOption = key as keyof ModelAttributeColumnOptions; * defaultOptions[keyAsTypeofModelColumnOption] = value as never; * } * * const isFieldOfTypeText = * field.typeName === TextField.name || field.typeName === CharField.name || field.typeName === UuidField.name; * * if (isFieldOfTypeText) this.textFieldValidations(field as TextField); * * return defaultOptions; * } * ``` * * @description Or you can lazy evaluate (useful for ForeignKeys): * * @example * ```ts * async translate(args: { * engine: SequelizeEngine; * field: CharField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * lazyEvaluate: (translatedField: TranslatedFieldToEvaluateAfterType) => void; * }): Promise { * const defaultOptions = await args.fieldParser.translate(args); * * args.lazyEvaluate({ * fieldAttributes: defaultOptions, * type: 'foreign-key', * } as TranslatedFieldToEvaluateAfterType); * } * ``` * * @description * As we discussed before, the `lazyEvaluate` function is used to evaluate something after the model was translated. * So what you pass to the `lazyEvaluate` function will be passed to `lazyEvaluateField` method on the * `{@link AdapterFields}` instance under `fieldTranslated` argument * * @returns - The translated field. */ // eslint-disable-next-line ts/require-await async translate(args) { throw new EngineDoesNotSupportFieldTypeException(args.engine.constructor.name, CharField.name); } /** * @description * This is used to parse the input value before you save it. For example, let's say that palmares by default accept * `Date` objects for `CharField`. But your database does not support saving `Date` instances. What you can do is * that you can implement this method on `CharFieldParser` and return a iSO string. With that, you can be 100% * sure that the data on your `queryData` is something valid for your database. * * This parses the value for each data. * * @example * ```ts * async inputParser(args: { * engine: SequelizeEngine; * field: CharField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * value: Date | string; * }): Promise { * if (args.value instanceof Date) return args.value.toISOString(); * return args.value; * } * ``` * * @returns - The parsed value. */ // eslint-disable-next-line ts/require-await async inputParser(args) { return args.value; } /** * @description * This is used to parse the output value before you send it to the user. For example, if the user is fetching * a `CharField` from the database, you can parse the value to a `Date` object. * This can be useful so you can guarantee that the user will receive the data in the format that it's expected. * * This parses the value for each data that is retrieved. * * @example * ```ts * async outputParser(args: { * engine: SequelizeEngine; * field: CharField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * value: string; * }): Promise { * if (typeof value === 'string') return new Date(value); * return args.value; * } * ``` * * @returns - The parsed value for the user for that specific field. */ // eslint-disable-next-line ts/require-await async outputParser(args) { return args.value; } }; // src/engine/fields/date.ts function adapterDateFieldParser(args) { let CustomAdapterDateFieldParser = class CustomAdapterDateFieldParser extends AdapterDateFieldParser { static { __name(this, "CustomAdapterDateFieldParser"); } translate = args.translate; inputParser = args.inputParser; outputParser = args.outputParser; }; return CustomAdapterDateFieldParser; } __name(adapterDateFieldParser, "adapterDateFieldParser"); var AdapterDateFieldParser = class { static { __name(this, "AdapterDateFieldParser"); } /** * @description * Used to translate the field to something that the database can understand. The `{@link AdapterFieldParser}` * instance will be injected by default in the `translate` method. * The core idea is that for every field type we will have a parser that will be used to translate the field * to something that the database can understand. It's nice if all of the configuration options are supported * by your ORM, but if that's not the case it's nice to notify the users through documentation. * * - _Note_: **If you return undefined, we will not consider that field on the object we build on * `translateFields` under {@link AdapterModels} instance.** * - _Note2_: **Use the `lazyEvaluate` function to evaluate something after the model was translated.** * * Imagine that you translating to sequelize field: * @example * ```ts * async translate({ * engine, * field, * modelName, * }: { * engine: SequelizeEngine; * field: Field; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * lazyEvaluate: (translatedField: any) => void; * }): Promise { * const defaultOptions = {} as ModelAttributeColumnOptions; * const isFieldAIndexOrIsFieldUnique = field.dbIndex === true || (field.unique as boolean) === true; * * if (isFieldAIndexOrIsFieldUnique) appendIndexes(engine.connectionName, modelName, field); * * const hasNotYetSetDefaultValueForField = defaultOptions.defaultValue === undefined; * if (hasNotYetSetDefaultValueForField) defaultOptions.defaultValue = field.defaultValue; * * defaultOptions.autoIncrement = field.isAuto; * defaultOptions.autoIncrementIdentity = field.isAuto; * defaultOptions.primaryKey = field.primaryKey; * defaultOptions.allowNull = field.allowNull; * defaultOptions.unique = field.unique; * defaultOptions.validate = {}; * defaultOptions.validate.notNull = !field.allowNull; * defaultOptions.field = field.databaseName; * * const customAttributesOfFieldEntries = Object.entries(field.customAttributes); * for (const [key, value] of customAttributesOfFieldEntries) { * const keyAsTypeofModelColumnOption = key as keyof ModelAttributeColumnOptions; * defaultOptions[keyAsTypeofModelColumnOption] = value as never; * } * * const isFieldOfTypeText = * field.typeName === TextField.name || field.typeName === CharField.name || field.typeName === UuidField.name; * * if (isFieldOfTypeText) this.textFieldValidations(field as TextField); * * return defaultOptions; * } * ``` * * @description Or you can lazy evaluate (useful for ForeignKeys): * * @example * ```ts * async translate(args: { * engine: SequelizeEngine; * field: DateField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * lazyEvaluate: (translatedField: TranslatedFieldToEvaluateAfterType) => void; * }): Promise { * const defaultOptions = await args.fieldParser.translate(args); * * args.lazyEvaluate({ * fieldAttributes: defaultOptions, * type: 'foreign-key', * } as TranslatedFieldToEvaluateAfterType); * } * ``` * * @description * As we discussed before, the `lazyEvaluate` function is used to evaluate something after the model was translated. * So what you pass to the `lazyEvaluate` function will be passed to `lazyEvaluateField` method on the * `{@link AdapterFields}` instance under `fieldTranslated` argument * * @returns - The translated field. */ // eslint-disable-next-line ts/require-await async translate(args) { throw new EngineDoesNotSupportFieldTypeException(args.engine.constructor.name, DateField.name); } /** * @description * This is used to parse the input value before you save it. For example, let's say that palmares by default accept * `Date` objects for `DateField`. But your database does not support saving `Date` instances. What you can do is * that you can implement this method on `DateFieldParser` and return a iSO string. With that, you can be 100% sure * that the data on your `queryData` is something valid for your database. * * This parses the value for each data. * * @example * ```ts * async inputParser(args: { * engine: SequelizeEngine; * field: DateField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * value: Date | string; * }): Promise { * if (args.value instanceof Date) return args.value.toISOString(); * return args.value; * } * ``` * * @returns - The parsed value. */ // eslint-disable-next-line ts/require-await async inputParser(args) { return args.value; } /** * @description * This is used to parse the output value before you send it to the user. For example, if the user is fetching a * `DateField` from the database, you can parse the value to a `Date` object. * This can be useful so you can guarantee that the user will receive the data in the format that it's expected. * * This parses the value for each data that is retrieved. * * @example * ```ts * async outputParser(args: { * engine: SequelizeEngine; * field: DateField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * value: string; * }): Promise { * if (typeof value === 'string') return new Date(value); * return args.value; * } * ``` * * @returns - The parsed value for the user for that specific field. */ // eslint-disable-next-line ts/require-await async outputParser(args) { return args.value; } }; // src/engine/fields/decimal.ts function adapterDecimalFieldParser(args) { let CustomAdapterDecimalFieldParser = class CustomAdapterDecimalFieldParser extends AdapterDecimalFieldParser { static { __name(this, "CustomAdapterDecimalFieldParser"); } translate = args.translate; inputParser = args.inputParser; outputParser = args.outputParser; }; return CustomAdapterDecimalFieldParser; } __name(adapterDecimalFieldParser, "adapterDecimalFieldParser"); var AdapterDecimalFieldParser = class { static { __name(this, "AdapterDecimalFieldParser"); } /** * @description * Used to translate the field to something that the database can understand. The `{@link AdapterFieldParser}` * instance will be injected by default in the `translate` method. * The core idea is that for every field type we will have a parser that will be used to translate the field to * something that the database can understand. It's nice if all of the configuration options are supported by * your ORM, but if that's not the case it's nice to notify the users through documentation. * * - _Note_: **If you return undefined, we will not consider that field on the object we build on * `translateFields` under {@link AdapterModels} instance.** * - _Note2_: **Use the `lazyEvaluate` function to evaluate something after the model was translated.** * * Imagine that you translating to sequelize field: * @example * ```ts * async translate({ * engine, * field, * modelName, * }: { * engine: SequelizeEngine; * field: Field; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * lazyEvaluate: (translatedField: any) => void; * }): Promise { * const defaultOptions = {} as ModelAttributeColumnOptions; * const isFieldAIndexOrIsFieldUnique = field.dbIndex === true || (field.unique as boolean) === true; * * if (isFieldAIndexOrIsFieldUnique) appendIndexes(engine.connectionName, modelName, field); * * const hasNotYetSetDefaultValueForField = defaultOptions.defaultValue === undefined; * if (hasNotYetSetDefaultValueForField) defaultOptions.defaultValue = field.defaultValue; * * defaultOptions.autoIncrement = field.isAuto; * defaultOptions.autoIncrementIdentity = field.isAuto; * defaultOptions.primaryKey = field.primaryKey; * defaultOptions.allowNull = field.allowNull; * defaultOptions.unique = field.unique; * defaultOptions.validate = {}; * defaultOptions.validate.notNull = !field.allowNull; * defaultOptions.field = field.databaseName; * * const customAttributesOfFieldEntries = Object.entries(field.customAttributes); * for (const [key, value] of customAttributesOfFieldEntries) { * const keyAsTypeofModelColumnOption = key as keyof ModelAttributeColumnOptions; * defaultOptions[keyAsTypeofModelColumnOption] = value as never; * } * * const isFieldOfTypeText = * field.typeName === TextField.name || field.typeName === CharField.name || field.typeName === UuidField.name; * * if (isFieldOfTypeText) this.textFieldValidations(field as TextField); * * return defaultOptions; * } * ``` * * @description Or you can lazy evaluate (useful for ForeignKeys): * * @example * ```ts * async translate(args: { * engine: SequelizeEngine; * field: DecimalField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * lazyEvaluate: (translatedField: TranslatedFieldToEvaluateAfterType) => void; * }): Promise { * const defaultOptions = await args.fieldParser.translate(args); * * args.lazyEvaluate({ * fieldAttributes: defaultOptions, * type: 'foreign-key', * } as TranslatedFieldToEvaluateAfterType); * } * ``` * * @description * As we discussed before, the `lazyEvaluate` function is used to evaluate something after the model was translated. * So what you pass to the `lazyEvaluate` function will be passed to `lazyEvaluateField` method on the * `{@link AdapterFields}` instance under `fieldTranslated` argument * * @returns - The translated field. */ // eslint-disable-next-line ts/require-await async translate(args) { throw new EngineDoesNotSupportFieldTypeException(args.engine.constructor.name, DecimalField.name); } /** * @description * This is used to parse the input value before you save it. For example, let's say that palmares by default accept * `Date` objects for `DateField`. But your database does not support saving `Date` instances. What you can do is * that you can implement this method on `DateFieldParser` and return a iSO string. With that, you can be 100% sure * that the data on your `queryData` is something valid for your database. * * This parses the value for each data. * * @example * ```ts * async inputParser(args: { * engine: SequelizeEngine; * field: DateField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * value: Date | string; * }): Promise { * if (args.value instanceof Date) return args.value.toISOString(); * return args.value; * } * ``` * * @returns - The parsed value. */ // eslint-disable-next-line ts/require-await async inputParser(args) { return args.value; } /** * @description * This is used to parse the output value before you send it to the user. For example, if the user is fetching a * `DateField` from the database, you can parse the value to a `Date` object. * This can be useful so you can guarantee that the user will receive the data in the format that it's expected. * * This parses the value for each data that is retrieved. * * @example * ```ts * async outputParser(args: { * engine: SequelizeEngine; * field: DateField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * value: string; * }): Promise { * if (typeof value === 'string') return new Date(value); * return args.value; * } * ``` * * @returns - The parsed value for the user for that specific field. */ // eslint-disable-next-line ts/require-await async outputParser(args) { return args.value; } }; // src/engine/fields/enum.ts function adapterEnumFieldParser(args) { let CustomAdapterEnumFieldParser = class CustomAdapterEnumFieldParser extends AdapterEnumFieldParser { static { __name(this, "CustomAdapterEnumFieldParser"); } translate = args.translate; inputParser = args.inputParser; outputParser = args.outputParser; }; return CustomAdapterEnumFieldParser; } __name(adapterEnumFieldParser, "adapterEnumFieldParser"); var AdapterEnumFieldParser = class { static { __name(this, "AdapterEnumFieldParser"); } /** * @description * Used to translate the field to something that the database can understand. The `{@link AdapterFieldParser}` * instance will be injected by default in the `translate` method. * The core idea is that for every field type we will have a parser that will be used to translate the field to * something that the database can understand. It's nice if all of the configuration options are supported by * your ORM, but if that's not the case it's nice to notify the users through documentation. * * - _Note_: **If you return undefined, we will not consider that field on the object we build on `translateFields` * under {@link AdapterModels} instance.** * - _Note2_: **Use the `lazyEvaluate` function to evaluate something after the model was translated.** * * Imagine that you translating to sequelize field: * @example * ```ts * async translate({ * engine, * field, * modelName, * }: { * engine: SequelizeEngine; * field: Field; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * lazyEvaluate: (translatedField: any) => void; * }): Promise { * const defaultOptions = {} as ModelAttributeColumnOptions; * const isFieldAIndexOrIsFieldUnique = field.dbIndex === true || (field.unique as boolean) === true; * * if (isFieldAIndexOrIsFieldUnique) appendIndexes(engine.connectionName, modelName, field); * * const hasNotYetSetDefaultValueForField = defaultOptions.defaultValue === undefined; * if (hasNotYetSetDefaultValueForField) defaultOptions.defaultValue = field.defaultValue; * * defaultOptions.autoIncrement = field.isAuto; * defaultOptions.autoIncrementIdentity = field.isAuto; * defaultOptions.primaryKey = field.primaryKey; * defaultOptions.allowNull = field.allowNull; * defaultOptions.unique = field.unique; * defaultOptions.validate = {}; * defaultOptions.validate.notNull = !field.allowNull; * defaultOptions.field = field.databaseName; * * const customAttributesOfFieldEntries = Object.entries(field.customAttributes); * for (const [key, value] of customAttributesOfFieldEntries) { * const keyAsTypeofModelColumnOption = key as keyof ModelAttributeColumnOptions; * defaultOptions[keyAsTypeofModelColumnOption] = value as never; * } * * const isFieldOfTypeText = * field.typeName === TextField.name || field.typeName === CharField.name || field.typeName === UuidField.name; * * if (isFieldOfTypeText) this.textFieldValidations(field as TextField); * * return defaultOptions; * } * ``` * * @description Or you can lazy evaluate (useful for ForeignKeys): * * @example * ```ts * async translate(args: { * engine: SequelizeEngine; * field: EnumField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * lazyEvaluate: (translatedField: TranslatedFieldToEvaluateAfterType) => void; * }): Promise { * const defaultOptions = await args.fieldParser.translate(args); * * args.lazyEvaluate({ * fieldAttributes: defaultOptions, * type: 'foreign-key', * } as TranslatedFieldToEvaluateAfterType); * } * ``` * * @description * As we discussed before, the `lazyEvaluate` function is used to evaluate something after the model was translated. * So what you pass to the `lazyEvaluate` function will be passed to `lazyEvaluateField` method on the * `{@link AdapterFields}` instance under `fieldTranslated` argument * * @returns - The translated field. */ // eslint-disable-next-line ts/require-await async translate(args) { throw new EngineDoesNotSupportFieldTypeException(args.engine.constructor.name, EnumField.name); } /** * @description * This is used to parse the input value before you save it. For example, let's say that palmares by default accept * `Date` objects for `DateField`. But your database does not support saving `Date` instances. What you can do is * that you can implement this method on `DateFieldParser` and return a iSO string. With that, you can be 100% * sure that the data on your `queryData` is something valid for your database. * * This parses the value for each data. * * @example * ```ts * async inputParser(args: { * engine: SequelizeEngine; * field: DateField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * value: Date | string; * }): Promise { * if (args.value instanceof Date) return args.value.toISOString(); * return args.value; * } * ``` * * @returns - The parsed value. */ // eslint-disable-next-line ts/require-await async inputParser(args) { return args.value; } /** * @description * This is used to parse the output value before you send it to the user. For example, if the user is fetching a * `DateField` from the database, you can parse the value to a `Date` object. * This can be useful so you can guarantee that the user will receive the data in the format that it's expected. * * This parses the value for each data that is retrieved. * * @example * ```ts * async outputParser(args: { * engine: SequelizeEngine; * field: DateField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * value: string; * }): Promise { * if (typeof value === 'string') return new Date(value); * return args.value; * } * ``` * * @returns - The parsed value for the user for that specific field. */ // eslint-disable-next-line ts/require-await async outputParser(args) { return args.value; } }; // src/engine/fields/field.ts function adapterFieldParser(args) { let CustomAdapterFieldParser = class CustomAdapterFieldParser extends AdapterFieldParser { static { __name(this, "CustomAdapterFieldParser"); } translate = args.translate; inputParser = args.inputParser; outputParser = args.outputParser; }; return CustomAdapterFieldParser; } __name(adapterFieldParser, "adapterFieldParser"); var AdapterFieldParser = class { static { __name(this, "AdapterFieldParser"); } /** * @description * Used to translate the field to something that the database can understand. The `{@link AdapterFieldParser}` * instance will be injected by default in the `translate` method. * The core idea is that for every field type we will have a parser that will be used to translate the field to * something that the database can understand. It's nice if all of the configuration options are supported by * your ORM, but if that's not the case it's nice to notify the users through documentation. * * - _Note_: **If you return undefined, we will not consider that field on the object we build on `translateFields` * under {@link AdapterModels} instance.** * - _Note2_: **Use the `lazyEvaluate` function to evaluate something after the model was translated.** * * Imagine that you translating to sequelize field: * @example * ```ts * async translate({ * engine, * field, * modelName, * }: { * engine: SequelizeEngine; * field: Field; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * lazyEvaluate: (translatedField: any) => void; * }): Promise { * const defaultOptions = {} as ModelAttributeColumnOptions; * const isFieldAIndexOrIsFieldUnique = field.dbIndex === true || (field.unique as boolean) === true; * * if (isFieldAIndexOrIsFieldUnique) appendIndexes(engine.connectionName, modelName, field); * * const hasNotYetSetDefaultValueForField = defaultOptions.defaultValue === undefined; * if (hasNotYetSetDefaultValueForField) defaultOptions.defaultValue = field.defaultValue; * * defaultOptions.autoIncrement = field.isAuto; * defaultOptions.autoIncrementIdentity = field.isAuto; * defaultOptions.primaryKey = field.primaryKey; * defaultOptions.allowNull = field.allowNull; * defaultOptions.unique = field.unique; * defaultOptions.validate = {}; * defaultOptions.validate.notNull = !field.allowNull; * defaultOptions.field = field.databaseName; * * const customAttributesOfFieldEntries = Object.entries(field.customAttributes); * for (const [key, value] of customAttributesOfFieldEntries) { * const keyAsTypeofModelColumnOption = key as keyof ModelAttributeColumnOptions; * defaultOptions[keyAsTypeofModelColumnOption] = value as never; * } * * const isFieldOfTypeText = * field.typeName === TextField.name || field.typeName === CharField.name || field.typeName === UuidField.name; * * if (isFieldOfTypeText) this.textFieldValidations(field as TextField); * * return defaultOptions; * } * ``` * * @description Or you can lazy evaluate (useful for ForeignKeys): * * @example * ```ts * async translate(args: { * engine: SequelizeEngine; * field: ForeignKeyField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * lazyEvaluate: (translatedField: TranslatedFieldToEvaluateAfterType) => void; * }): Promise { * const defaultOptions = await args.fieldParser.translate(args); * * args.lazyEvaluate({ * fieldAttributes: defaultOptions, * type: 'foreign-key', * } as TranslatedFieldToEvaluateAfterType); * } * ``` * * @description * As we discussed before, the `lazyEvaluate` function is used to evaluate something after the model was translated. * So what you pass to the `lazyEvaluate` function will be passed to `lazyEvaluateField` method on the * `{@link AdapterFields}` instance under `fieldTranslated` argument * * @returns - The translated field. */ // eslint-disable-next-line ts/require-await async translate(args) { throw new EngineDoesNotSupportFieldTypeException(args.engine.constructor.name, Field.name); } /** * @description * This is used to parse the input value before you save it. For example, let's say that palmares by default accept * `Date` objects for `DateField`. But your database does not support saving `Date` instances. What you can do is * that you can implement this method on `DateFieldParser` and return a iSO string. With that, you can be 100% sure * that the data on your `queryData` is something valid for your database. * * This parses the value for each data. * * @example * ```ts * async inputParser(args: { * engine: SequelizeEngine; * field: DateField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * value: Date | string; * }): Promise { * if (args.value instanceof Date) return args.value.toISOString(); * return args.value; * } * ``` * * @returns - The parsed value. */ // eslint-disable-next-line ts/require-await async inputParser(args) { return args.value; } /** * @description * This is used to parse the output value before you send it to the user. For example, if the user is fetching a * `DateField` from the database, you can parse the value to a `Date` object. * This can be useful so you can guarantee that the user will receive the data in the format that it's expected. * * This parses the value for each data that is retrieved. * * @example * ```ts * async outputParser(args: { * engine: SequelizeEngine; * field: DateField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * value: string; * }): Promise { * if (typeof value === 'string') return new Date(value); * return args.value; * } * ``` * * @returns - The parsed value for the user for that specific field. */ // eslint-disable-next-line ts/require-await async outputParser(args) { return args.value; } }; // src/engine/fields/foreign-key.ts function adapterForeignKeyFieldParser(args) { let CustomAdapterForeignKeyFieldParser = class CustomAdapterForeignKeyFieldParser extends AdapterForeignKeyFieldParser { static { __name(this, "CustomAdapterForeignKeyFieldParser"); } translate = args.translate; inputParser = args.inputParser; outputParser = args.outputParser; }; return CustomAdapterForeignKeyFieldParser; } __name(adapterForeignKeyFieldParser, "adapterForeignKeyFieldParser"); var AdapterForeignKeyFieldParser = class { static { __name(this, "AdapterForeignKeyFieldParser"); } /** * @description * Used to translate the field to something that the database can understand. The `{@link AdapterFieldParser}` * instance will be injected by default in the `translate` method. * The core idea is that for every field type we will have a parser that will be used to translate the field to * something that the database can understand. It's nice if all of the configuration options are supported by * your ORM, but if that's not the case it's nice to notify the users through documentation. * * - _Note_: **If you return undefined, we will not consider that field on the object we build on * `translateFields` under {@link AdapterModels} instance.** * - _Note2_: **Use the `lazyEvaluate` function to evaluate something after the model was translated.** * * Imagine that you translating to sequelize field: * @example * ```ts * async translate({ * engine, * field, * modelName, * }: { * engine: SequelizeEngine; * field: Field; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * lazyEvaluate: (translatedField: any) => void; * }): Promise { * const defaultOptions = {} as ModelAttributeColumnOptions; * const isFieldAIndexOrIsFieldUnique = field.dbIndex === true || (field.unique as boolean) === true; * * if (isFieldAIndexOrIsFieldUnique) appendIndexes(engine.connectionName, modelName, field); * * const hasNotYetSetDefaultValueForField = defaultOptions.defaultValue === undefined; * if (hasNotYetSetDefaultValueForField) defaultOptions.defaultValue = field.defaultValue; * * defaultOptions.autoIncrement = field.isAuto; * defaultOptions.autoIncrementIdentity = field.isAuto; * defaultOptions.primaryKey = field.primaryKey; * defaultOptions.allowNull = field.allowNull; * defaultOptions.unique = field.unique; * defaultOptions.validate = {}; * defaultOptions.validate.notNull = !field.allowNull; * defaultOptions.field = field.databaseName; * * const customAttributesOfFieldEntries = Object.entries(field.customAttributes); * for (const [key, value] of customAttributesOfFieldEntries) { * const keyAsTypeofModelColumnOption = key as keyof ModelAttributeColumnOptions; * defaultOptions[keyAsTypeofModelColumnOption] = value as never; * } * * const isFieldOfTypeText = * field.typeName === TextField.name || field.typeName === CharField.name || field.typeName === UuidField.name; * * if (isFieldOfTypeText) this.textFieldValidations(field as TextField); * * return defaultOptions; * } * ``` * * @description Or you can lazy evaluate (useful for ForeignKeys): * * @example * ```ts * async translate(args: { * engine: SequelizeEngine; * field: ForeignKeyField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * lazyEvaluate: (translatedField: TranslatedFieldToEvaluateAfterType) => void; * }): Promise { * const defaultOptions = await args.fieldParser.translate(args); * * args.lazyEvaluate({ * fieldAttributes: defaultOptions, * type: 'foreign-key', * } as TranslatedFieldToEvaluateAfterType); * } * ``` * * @description * As we discussed before, the `lazyEvaluate` function is used to evaluate something after the model was translated. * So what you pass to the `lazyEvaluate` function will be passed to `lazyEvaluateField` method on the * `{@link AdapterFields}` instance under `fieldTranslated` argument * * @returns - The translated field. */ // eslint-disable-next-line ts/require-await async translate(args) { throw new EngineDoesNotSupportFieldTypeException(args.engine.constructor.name, ForeignKeyField.name); } /** * @description * This is used to parse the input value before you save it. For example, let's say that palmares by default accept * `Date` objects for `DateField`. But your database does not support saving `Date` instances. What you can do is * that you can implement this method on `DateFieldParser` and return a iSO string. With that, you can be 100% sure * that the data on your `queryData` is something valid for your database. * * This parses the value for each data. * * @example * ```ts * async inputParser(args: { * engine: SequelizeEngine; * field: DateField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * value: Date | string; * }): Promise { * if (args.value instanceof Date) return args.value.toISOString(); * return args.value; * } * ``` * * @returns - The parsed value. */ // eslint-disable-next-line ts/require-await async inputParser(args) { return args.value; } /** * @description * This is used to parse the output value before you send it to the user. For example, if the user is fetching a * `DateField` from the database, you can parse the value to a `Date` object. * This can be useful so you can guarantee that the user will receive the data in the format that it's expected. * * This parses the value for each data that is retrieved. * * @example * ```ts * async outputParser(args: { * engine: SequelizeEngine; * field: DateField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * value: string; * }): Promise { * if (typeof value === 'string') return new Date(value); * return args.value; * } * ``` * * @returns - The parsed value for the user for that specific field. */ // eslint-disable-next-line ts/require-await async outputParser(args) { return args.value; } }; // src/engine/fields/integer.ts function adapterIntegerFieldParser(args) { let CustomAdapterIntegerFieldParser = class CustomAdapterIntegerFieldParser extends AdapterIntegerFieldParser { static { __name(this, "CustomAdapterIntegerFieldParser"); } translate = args.translate; inputParser = args.inputParser; outputParser = args.outputParser; }; return CustomAdapterIntegerFieldParser; } __name(adapterIntegerFieldParser, "adapterIntegerFieldParser"); var AdapterIntegerFieldParser = class { static { __name(this, "AdapterIntegerFieldParser"); } /** * @description * Used to translate the field to something that the database can understand. The `{@link AdapterFieldParser}` * instance will be injected by default in the `translate` method. * The core idea is that for every field type we will have a parser that will be used to translate the field * to something that the database can understand. It's nice if all of the configuration options are supported * by your ORM, but if that's not the case it's nice to notify the users through documentation. * * - _Note_: **If you return undefined, we will not consider that field on the object we build on * `translateFields` under {@link AdapterModels} instance.** * - _Note2_: **Use the `lazyEvaluate` function to evaluate something after the model was translated.** * * Imagine that you translating to sequelize field: * @example * ```ts * async translate({ * engine, * field, * modelName, * }: { * engine: SequelizeEngine; * field: Field; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * lazyEvaluate: (translatedField: any) => void; * }): Promise { * const defaultOptions = {} as ModelAttributeColumnOptions; * const isFieldAIndexOrIsFieldUnique = field.dbIndex === true || (field.unique as boolean) === true; * * if (isFieldAIndexOrIsFieldUnique) appendIndexes(engine.connectionName, modelName, field); * * const hasNotYetSetDefaultValueForField = defaultOptions.defaultValue === undefined; * if (hasNotYetSetDefaultValueForField) defaultOptions.defaultValue = field.defaultValue; * * defaultOptions.autoIncrement = field.isAuto; * defaultOptions.autoIncrementIdentity = field.isAuto; * defaultOptions.primaryKey = field.primaryKey; * defaultOptions.allowNull = field.allowNull; * defaultOptions.unique = field.unique; * defaultOptions.validate = {}; * defaultOptions.validate.notNull = !field.allowNull; * defaultOptions.field = field.databaseName; * * const customAttributesOfFieldEntries = Object.entries(field.customAttributes); * for (const [key, value] of customAttributesOfFieldEntries) { * const keyAsTypeofModelColumnOption = key as keyof ModelAttributeColumnOptions; * defaultOptions[keyAsTypeofModelColumnOption] = value as never; * } * * const isFieldOfTypeText = * field.typeName === TextField.name || field.typeName === CharField.name || field.typeName === UuidField.name; * * if (isFieldOfTypeText) this.textFieldValidations(field as TextField); * * return defaultOptions; * } * ``` * * @description Or you can lazy evaluate (useful for ForeignKeys): * * @example * ```ts * async translate(args: { * engine: SequelizeEngine; * field: IntegerField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * lazyEvaluate: (translatedField: TranslatedFieldToEvaluateAfterType) => void; * }): Promise { * const defaultOptions = await args.fieldParser.translate(args); * * args.lazyEvaluate({ * fieldAttributes: defaultOptions, * type: 'foreign-key', * } as TranslatedFieldToEvaluateAfterType); * } * ``` * * @description * As we discussed before, the `lazyEvaluate` function is used to evaluate something after the model was translated. * So what you pass to the `lazyEvaluate` function will be passed to `lazyEvaluateField` method on the * `{@link AdapterFields}` instance under `fieldTranslated` argument * * @returns - The translated field. */ // eslint-disable-next-line ts/require-await async translate(args) { throw new EngineDoesNotSupportFieldTypeException(args.engine.constructor.name, IntegerField.name); } /** * @description * This is used to parse the input value before you save it. For example, let's say that palmares by default accept * `Date` objects for `DateField`. But your database does not support saving `Date` instances. What you can do is * that you can implement this method on `DateFieldParser` and return a iSO string. With that, you can be 100% sure * that the data on your `queryData` is something valid for your database. * * This parses the value for each data. * * @example * ```ts * async inputParser(args: { * engine: SequelizeEngine; * field: DateField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * value: Date | string; * }): Promise { * if (args.value instanceof Date) return args.value.toISOString(); * return args.value; * } * ``` * * @returns - The parsed value. */ // eslint-disable-next-line ts/require-await async inputParser(args) { return args.value; } /** * @description * This is used to parse the output value before you send it to the user. For example, if the user is fetching a * `DateField` from the database, you can parse the value to a `Date` object. * This can be useful so you can guarantee that the user will receive the data in the format that it's expected. * * This parses the value for each data that is retrieved. * * @example * ```ts * async outputParser(args: { * engine: SequelizeEngine; * field: DateField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * value: string; * }): Promise { * if (typeof value === 'string') return new Date(value); * return args.value; * } * ``` * * @returns - The parsed value for the user for that specific field. */ // eslint-disable-next-line ts/require-await async outputParser(args) { return args.value; } }; // src/engine/fields/text.ts function adapterTextFieldParser(args) { let CustomAdapterTextFieldParser = class CustomAdapterTextFieldParser extends AdapterTextFieldParser { static { __name(this, "CustomAdapterTextFieldParser"); } translate = args.translate; inputParser = args.inputParser; outputParser = args.outputParser; }; return CustomAdapterTextFieldParser; } __name(adapterTextFieldParser, "adapterTextFieldParser"); var AdapterTextFieldParser = class { static { __name(this, "AdapterTextFieldParser"); } /** * @description * Used to translate the field to something that the database can understand. The `{@link AdapterFieldParser}` * instance will be injected by default in the `translate` method. * The core idea is that for every field type we will have a parser that will be used to translate the field * to something that the database can understand. It's nice if all of the configuration options are supported * by your ORM, but if that's not the case it's nice to notify the users through documentation. * * - _Note_: **If you return undefined, we will not consider that field on the object we build on * `translateFields` under {@link AdapterModels} instance.** * - _Note2_: **Use the `lazyEvaluate` function to evaluate something after the model was translated.** * * Imagine that you translating to sequelize field: * @example * ```ts * async translate({ * engine, * field, * modelName, * }: { * engine: SequelizeEngine; * field: Field; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * lazyEvaluate: (translatedField: any) => void; * }): Promise { * const defaultOptions = {} as ModelAttributeColumnOptions; * const isFieldAIndexOrIsFieldUnique = field.dbIndex === true || (field.unique as boolean) === true; * * if (isFieldAIndexOrIsFieldUnique) appendIndexes(engine.connectionName, modelName, field); * * const hasNotYetSetDefaultValueForField = defaultOptions.defaultValue === undefined; * if (hasNotYetSetDefaultValueForField) defaultOptions.defaultValue = field.defaultValue; * * defaultOptions.autoIncrement = field.isAuto; * defaultOptions.autoIncrementIdentity = field.isAuto; * defaultOptions.primaryKey = field.primaryKey; * defaultOptions.allowNull = field.allowNull; * defaultOptions.unique = field.unique; * defaultOptions.validate = {}; * defaultOptions.validate.notNull = !field.allowNull; * defaultOptions.field = field.databaseName; * * const customAttributesOfFieldEntries = Object.entries(field.customAttributes); * for (const [key, value] of customAttributesOfFieldEntries) { * const keyAsTypeofModelColumnOption = key as keyof ModelAttributeColumnOptions; * defaultOptions[keyAsTypeofModelColumnOption] = value as never; * } * * const isFieldOfTypeText = * field.typeName === TextField.name || field.typeName === CharField.name || field.typeName === UuidField.name; * * if (isFieldOfTypeText) this.textFieldValidations(field as TextField); * * return defaultOptions; * } * ``` * * @description Or you can lazy evaluate (useful for ForeignKeys): * * @example * ```ts * async translate(args: { * engine: SequelizeEngine; * field: TextField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * lazyEvaluate: (translatedField: TranslatedFieldToEvaluateAfterType) => void; * }): Promise { * const defaultOptions = await args.fieldParser.translate(args); * * args.lazyEvaluate({ * fieldAttributes: defaultOptions, * type: 'foreign-key', * } as TranslatedFieldToEvaluateAfterType); * } * ``` * * @description * As we discussed before, the `lazyEvaluate` function is used to evaluate something after the model was translated. * So what you pass to the `lazyEvaluate` function will be passed to `lazyEvaluateField` method on the * `{@link AdapterFields}` instance under `fieldTranslated` argument * * @returns - The translated field. */ // eslint-disable-next-line ts/require-await async translate(args) { throw new EngineDoesNotSupportFieldTypeException(args.engine.constructor.name, TextField.name); } /** * @description * This is used to parse the input value before you save it. For example, let's say that palmares by default accept * `Date` objects for `DateField`. But your database does not support saving `Date` instances. What you can do is that * you can implement this method on `DateFieldParser` and return a iSO string. With that, you can be 100% sure that * the data on your `queryData` is something valid for your database. * * This parses the value for each data. * * @example * ```ts * async inputParser(args: { * engine: SequelizeEngine; * field: DateField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * value: Date | string; * }): Promise { * if (args.value instanceof Date) return args.value.toISOString(); * return args.value; * } * ``` * * @returns - The parsed value. */ // eslint-disable-next-line ts/require-await async inputParser(args) { return args.value; } /** * @description * This is used to parse the output value before you send it to the user. For example, if the user is fetching a * `DateField` from the database, you can parse the value to a `Date` object. * This can be useful so you can guarantee that the user will receive the data in the format that it's expected. * * This parses the value for each data that is retrieved. * * @example * ```ts * async outputParser(args: { * engine: SequelizeEngine; * field: DateField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * value: string; * }): Promise { * if (typeof value === 'string') return new Date(value); * return args.value; * } * ``` * * @returns - The parsed value for the user for that specific field. */ // eslint-disable-next-line ts/require-await async outputParser(args) { return args.value; } }; // src/engine/fields/uuid.ts function adapterUuidFieldParser(args) { let CustomAdapterUuidFieldParser = class CustomAdapterUuidFieldParser extends AdapterUuidFieldParser { static { __name(this, "CustomAdapterUuidFieldParser"); } translate = args.translate; inputParser = args.inputParser; outputParser = args.outputParser; }; return CustomAdapterUuidFieldParser; } __name(adapterUuidFieldParser, "adapterUuidFieldParser"); var AdapterUuidFieldParser = class { static { __name(this, "AdapterUuidFieldParser"); } /** * @description * Used to translate the field to something that the database can understand. The `{@link AdapterFieldParser}` * instance will be injected by default in the `translate` method. * The core idea is that for every field type we will have a parser that will be used to translate the field to * something that the database can understand. It's nice if all of the configuration options are supported by * your ORM, but if that's not the case it's nice to notify the users through documentation. * * - _Note_: **If you return undefined, we will not consider that field on the object we build on `translateFields` * under {@link AdapterModels} instance.** * - _Note2_: **Use the `lazyEvaluate` function to evaluate something after the model was translated.** * * Imagine that you translating to sequelize field: * @example * ```ts * async translate({ * engine, * field, * modelName, * }: { * engine: SequelizeEngine; * field: Field; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * lazyEvaluate: (translatedField: any) => void; * }): Promise { * const defaultOptions = {} as ModelAttributeColumnOptions; * const isFieldAIndexOrIsFieldUnique = field.dbIndex === true || (field.unique as boolean) === true; * * if (isFieldAIndexOrIsFieldUnique) appendIndexes(engine.connectionName, modelName, field); * * const hasNotYetSetDefaultValueForField = defaultOptions.defaultValue === undefined; * if (hasNotYetSetDefaultValueForField) defaultOptions.defaultValue = field.defaultValue; * * defaultOptions.autoIncrement = field.isAuto; * defaultOptions.autoIncrementIdentity = field.isAuto; * defaultOptions.primaryKey = field.primaryKey; * defaultOptions.allowNull = field.allowNull; * defaultOptions.unique = field.unique; * defaultOptions.validate = {}; * defaultOptions.validate.notNull = !field.allowNull; * defaultOptions.field = field.databaseName; * * const customAttributesOfFieldEntries = Object.entries(field.customAttributes); * for (const [key, value] of customAttributesOfFieldEntries) { * const keyAsTypeofModelColumnOption = key as keyof ModelAttributeColumnOptions; * defaultOptions[keyAsTypeofModelColumnOption] = value as never; * } * * const isFieldOfTypeText = * field.typeName === TextField.name || field.typeName === CharField.name || field.typeName === UuidField.name; * * if (isFieldOfTypeText) this.textFieldValidations(field as TextField); * * return defaultOptions; * } * ``` * * @description Or you can lazy evaluate (useful for ForeignKeys): * * @example * ```ts * async translate(args: { * engine: SequelizeEngine; * field: UuidField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * lazyEvaluate: (translatedField: TranslatedFieldToEvaluateAfterType) => void; * }): Promise { * const defaultOptions = await args.fieldParser.translate(args); * * args.lazyEvaluate({ * fieldAttributes: defaultOptions, * type: 'foreign-key', * } as TranslatedFieldToEvaluateAfterType); * } * ``` * * @description * As we discussed before, the `lazyEvaluate` function is used to evaluate something after the model was translated. * So what you pass to the `lazyEvaluate` function will be passed to `lazyEvaluateField` method on the * `{@link AdapterFields}` instance under `fieldTranslated` argument * * @returns - The translated field. */ // eslint-disable-next-line ts/require-await async translate(args) { throw new EngineDoesNotSupportFieldTypeException(args.engine.constructor.name, UuidField.name); } /** * @description * This is used to parse the input value before you save it. For example, let's say that palmares by default accept * `Date` objects for `DateField`. But your database does not support saving `Date` instances. What you can do is * that you can implement this method on `DateFieldParser` and return a iSO string. With that, you can be 100% sure * that the data on your `queryData` is something valid for your database. * * This parses the value for each data. * * @example * ```ts * async inputParser(args: { * engine: SequelizeEngine; * field: DateField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * value: Date | string; * }): Promise { * if (args.value instanceof Date) return args.value.toISOString(); * return args.value; * } * ``` * * @returns - The parsed value. */ // eslint-disable-next-line ts/require-await async inputParser(args) { return args.value; } /** * @description * This is used to parse the output value before you send it to the user. For example, if the user is fetching a * `DateField` from the database, you can parse the value to a `Date` object. * This can be useful so you can guarantee that the user will receive the data in the format that it's expected. * * This parses the value for each data that is retrieved. * * @example * ```ts * async outputParser(args: { * engine: SequelizeEngine; * field: DateField; * fieldParser: SequelizeEngineFieldParser; * modelName: string; * model: InstanceType>; * value: string; * }): Promise { * if (typeof value === 'string') return new Date(value); * return args.value; * } * ``` * * @returns - The parsed value for the user for that specific field. */ // eslint-disable-next-line ts/require-await async outputParser(args) { return args.value; } }; // src/engine/fields/index.ts function adapterFields(args) { let CustomAdapterFields = class CustomAdapterFields extends AdapterFields { static { __name(this, "CustomAdapterFields"); } fieldsParser = args.fieldsParser; autoFieldParser = typeof args.autoFieldParser === "function" ? args.autoFieldParser : args.integerFieldParser; bigAutoFieldParser = typeof args.bigAutoFieldParser === "function" ? args.bigAutoFieldParser : args.bigIntegerFieldParser; bigIntegerFieldParser = args.bigIntegerFieldParser; charFieldParser = args.charFieldParser; compare = args.compare; fieldToString = args.fieldToString; dateFieldParser = args.dateFieldParser; decimalFieldParser = args.decimalFieldParser; foreignKeyFieldParser = args.foreignKeyFieldParser; integerFieldParser = args.integerFieldParser; textFieldParser = args.textFieldParser; uuidFieldParser = args.uuidFieldParser; enumFieldParser = args.enumFieldParser; booleanFieldParser = args.booleanFieldParser; customFieldsParser = args.customFieldsParser; lazyEvaluateField = args.lazyEvaluateField; translateField = args.translateField; }; return CustomAdapterFields; } __name(adapterFields, "adapterFields"); var AdapterFields = class { static { __name(this, "AdapterFields"); } /** * An {@link AdapterFieldParser}, it allows you to have a default translate function for all fields. * By default, the translate function will receive the fields parser */ fieldsParser = new AdapterFieldParser(); autoFieldParser = new AdapterAutoFieldParser(); bigAutoFieldParser = new AdapterBigAutoFieldParser(); bigIntegerFieldParser = new AdapterBigIntegerFieldParser(); charFieldParser = new AdapterCharFieldParser(); dateFieldParser = new AdapterDateFieldParser(); decimalFieldParser = new AdapterDecimalFieldParser(); foreignKeyFieldParser = new AdapterForeignKeyFieldParser(); integerFieldParser = new AdapterIntegerFieldParser(); textFieldParser = new AdapterTextFieldParser(); uuidFieldParser = new AdapterUuidFieldParser(); enumFieldParser = new AdapterEnumFieldParser(); booleanFieldParser = new AdapterBooleanFieldParser(); customFieldsParser = void 0; /** * Stuff like `foreignKeys` can be a little cumbersome to implement. Specially when you are doing a translation * from one ORM to another. We don't really know how your ORM handles stuff. So instead of you trying to get * around our implementation, we already offer you a way to define your own custom implementation of lazy * evaluation of fields. * * This is used alongside the `lazyEvaluate` parameter on the `translate` method of your field parser. * * ### Some examples * * On Sequelize you need to define relations using the `hasOne`, `hasMany`, `belongsTo`, `belongsToMany` methods. * You do not define foreign keys directly on the model. * * So what you need to do is: * @example * ```ts * // First, on your FieldParser you need to call the `lazyEvaluate` function, what you pass to it, will be passed * // to this method on `_fieldTranslated`. * * export class SequelizeAdapterForeignKeyFieldParser extends AdapterForeignKeyFieldParser { * async translate(args: { * Adapter: SequelizeAdapter; * field: ForeignKeyField; * fieldParser: SequelizeAdapterFieldParser; * modelName: string; * model: InstanceType>; * lazyEvaluate: (translatedField: TranslatedFieldToEvaluateAfterType) => void; * }): Promise { * const defaultOptions = await args.fieldParser.translate(args); * * args.lazyEvaluate({ * fieldAttributes: defaultOptions; * type: 'foreign-key', * } as TranslatedFieldToEvaluateAfterType); * } * } * * // Then, on your AdapterFields you need to implement the `lazyEvaluateField` method. * * export class SequelizeAdapterFields extends AdapterFields { * async lazyEvaluateField( * Adapter: SequelizeAdapter, * _modelName: string, * translatedModel: ModelCtor, * field: Field, * fieldTranslated: TranslatedFieldToEvaluateAfterType * ): Promise { * switch (fieldTranslated.type) { * case 'foreign-key': * handleRelatedField(Adapter, field as ForeignKeyField, fieldTranslated); * break; * case 'date': * translatedModel.addHook('beforeSave', `${field.fieldName}AutoNow`, (instance: Model) => { * // eslint-disable-next-line @typescript-eslint/ban-ts-comment * // @ts-ignore * instance[updateDateHook] = new Date(); * }); * break; * } * return translatedModel; * } * } * ``` * * So as you can see, you can use the `lazyEvaluate` function to pass any data you want to the `lazyEvaluateField` * method. * This method already gives you the model translated so if you want to do any custom logic with the model, * you can do it here. * * @param Adapter - The Adapter that is being used. That's your own custom Adapter implementation. * @param modelName - The name of the model that is being translated. * @param translatedModel - The model translated to something that the ORM can understand. * @param field - The field of the model that is being translated. * @param fieldTranslated - The data that is sent when you call `lazyEvaluate` on the `translate` method of * your {@link AdapterFieldParser}. * @param parseAgain - A function that you can call to parse an specific field again. For example, * your engine creates a field that is a foreign key by attaching `.relation()` to the end of the field type. * but the field type is a normal int() or varchar() field. Parsing the field again will call the `translate` * method of the field parser again. * * @returns - Should return the model translated and modified, even if you do not modify the model, you should * return the translated model. */ // eslint-disable-next-line ts/require-await async lazyEvaluateField(_Adapter, _modelName, _translatedModel, _field, _fieldTranslated, _parseAgain) { throw new NotImplementedAdapterFieldsException("lazyEvaluateField"); } /** * This method is completely optional, by default we offer a default implementation that is able to bypass that and * calls the `translate` methods directly. * * This method is used to retrieve the field translated to something that your custom ORM can understand. * * For drizzleORM, that would be something like this: * `integer('int1').default(10)` * or * `integer('int2').default(sql`'10'::int`)` * (It's on their docs: https://orm.drizzle.team/docs/column-types/pg#integer). * * So you are pretty much translating the Palmares model Field to your own ORM field. * * - **If you want to bypass our custom implementation and do everything by your own:** * * @example * ```ts * async translateField( * adapter: Adapter, * field: Field, * defaultTranslateFieldCallback: (field: Field) => Promise * ) { * switch (field.typeName) { * case 'IntegerField': * return integer(field.name).default(field.defaultValue); * case 'CharField': * return char(field.name, { length: field.maxLength }).default(field.defaultValue); * } * } * ``` * * - **If you want to call the `translate` methods on each field parser. But you do not want to call the * `translate` method by hand you can use the `defaultTranslateFieldCallback`:** * * @example * ```ts * async translateField( * adapter: Adapter, * field: Field, * defaultTranslateFieldCallback: (field: Field) => Promise * ) { * const translatedField = await defaultTranslateFieldCallback(field); * * // Do any custom logic that you want to do with the translated field. * // Or translated other field types we do not support by default. * translatedField.default(field.defaultValue); * return translatedField; * } * ``` * * _Note_: **Last but not least, you can also opt to not implement this function and we will handle that for you.** * * @param Adapter - The Adapter that is being used. That's your own custom Adapter implementation. * @param field - The field of the model that is being translated. * @param defaultTranslateFieldCallback - The default callback that you can call to translate the field. It will * use the `translate` method of the field parser. * * @returns The field translated to something that the ORM can understand. */ // eslint-disable-next-line ts/require-await async translateField(_Adapter, _field, _defaultTranslateFieldCallback) { throw new NotImplementedAdapterFieldsException("translateField"); } /** * Used for comparing two custom arguments so we can know if we need to update the field or not. * * This is part of the migration, don't need to implement if you are not using Palmares Migrations. */ compare(_oldCustomArguments, _newCustomArguments) { throw new NotImplementedAdapterFieldsException("compare"); } /** * Used for stringfying the custom arguments so we can store them in the database. If you do not implement this * and implement compare we will throw an error, otherwise we will just ignore the custom arguments. * * This is part of the migration, don't need to implement if you are not using Palmares Migrations. */ fieldToString(_customArguments) { throw new NotImplementedAdapterFieldsException("fieldToString"); } }; // src/engine/migrations.ts function adapterMigrations(args) { let CustomAdapterMigrations = class CustomAdapterMigrations extends AdapterMigrations { static { __name(this, "CustomAdapterMigrations"); } batchAll = args.batchAll; init = args.init; addModel = args.addModel; removeModel = args.removeModel; changeModel = args.changeModel; addField = args.addField; changeField = args.changeField; renameField = args.renameField; removeField = args.removeField; finish = args.finish; }; return CustomAdapterMigrations; } __name(adapterMigrations, "adapterMigrations"); var AdapterMigrations = class { static { __name(this, "AdapterMigrations"); } /** * This function is COMPLETELY optional and allows you to batch all of the migrations on a single function. By * default we run each migration file one by one, but for stuff like Prisma we do not have this option. Prisma * already migrates everything on a single command. So what we do is that instead of running each migration file * one by one we run all of them at once. * * We just generate the current state of the database and pass it to the batch function. Imagine that the state holds * the strings of each model, so with that we just need to create the .schema file and let the ORM do the rest. * * A simple prisma Example (it can be more complicated than that but you get the idea): * @example * ```ts * async currentBatch( * engine: DatabaseAdapter, * toStateModels: OriginalOrStateModelsByNameType, * returnOfInit: any * ): Promise { * for (const model of Object.values(toStateModels)) { * fs.appendFileSync('./prisma/schema.prisma', model.initialized); * } * * execSync('npx prisma migrate dev'); * } * ``` * * @param _engine - The engine instance that is running the migrations. * @param _toStateModels - All of the models on a key/value pair where the key is the name of the model and the * value is the model itself. * @param _returnOfInit - The return of the init function, if you implemented it, otherwise it will be undefined. */ // eslint-disable-next-line ts/require-await async batchAll(_engine, _toStateModels, _returnOfInit) { throw new NotImplementedAdapterException("batch"); } /** * This is called before the migrations are run. If you want to initialize something before the migrations run * you can use this function, but it's totally optional and not required. * * **Be aware: what you return from this function will be passed to all of the other functions as the LAST argument.** * * On Sequelize implementation we use this to initialize the queryInterface that is used on the migrations. * @example * ``` * async init(engine: DatabaseAdapter): Promise { * return engine.instance?.getQueryInterface() as QueryInterface; * } * ``` * * @param _engine - The engine instance that is running the migrations. */ // eslint-disable-next-line ts/require-await async init(_engine) { return; } /** * This is called when we are creating a new column on the database. A model represent a table on the database * (if we are talking about SQL databases). * * @example * ```ts * async addModel( * engine: DatabaseAdapter, * toModel: InitializedModelsType>, * migration: Migration * ): Promise { * const model = toModel.initialized; * * await this.#queryInterface.createTable( * model.options.tableName as string, * model.getAttributes(), * Object.assign(model.options, { * transaction: migration.transaction, * }) * ); * * await this.#handleCircularDependencies(engine, migration.transaction, { toModel }); * await this.#handleIndexes(migration.transaction, { toModel }); * } * ``` * * @param _engine - The engine instance that is running the migrations. * @param _toModel - The model that will be created on the database. * @param _migration - The migration instance that is running the migrations. * @param _returnOfInit - The return of the init function, if you implemented it, otherwise it will be undefined. */ // eslint-disable-next-line ts/require-await async addModel(_engine, _toModel, _migration, _returnOfInit) { return; } /** * Removes a model/table from the database. This removes everything from the database, no worries about the data. * * A simple sequelize implementation: * @example * ```ts * async removeModel( * _: DatabaseAdapter, * fromModel: InitializedModelsType>, * migration: Migration * ): Promise { * const tableNameToRemove = fromModel.initialized.options.tableName as string; * const transaction = migration.transaction; * await this.#queryInterface.dropTable(tableNameToRemove, { transaction }); * } * ``` * * @param _engine - The engine instance that is running the migrations. * @param _fromModel - How the model WAS structured before running the migration. * @param _migration - The migration instance that is running the migrations. * @param _returnOfInit - The return of the init function, if you implemented it, otherwise it will be undefined. */ // eslint-disable-next-line ts/require-await async removeModel(_engine, _fromModel, _migration, _returnOfInit) { return; } /** * Used when the user changes the model. Let's say that the user changed the model name, added a new index, * changed the ordering, changed pretty much any model configuration. * * This is called when the model had changed, not the fields but the options. * * @example * ```ts * async changeModel( * engine: DatabaseAdapter, * toModel: InitializedModelsType>, * fromModel: InitializedModelsType>, * migration: Migration * ): Promise { * const fromTableName = fromModel.initialized.tableName; * const toTableName = toModel.initialized.tableName; * const hasTheNameOfTheTableChanged = fromTableName !== toTableName; * * if (hasTheNameOfTheTableChanged) { * await this.#queryInterface.renameTable(fromTableName, toTableName, { * transaction: migration.transaction, * }); * } * * await this.#handleCircularDependencies(engine, migration, { fromModel, toModel }); * await this.#handleIndexes(migration, { fromModel, toModel }); * } * ``` * * @param _engine - The engine instance that is running the migrations. * @param _toModel - How the model state IS right now when running the migration, this is how the model will be after * running the migration. * @param _fromModel - How the model state WILL BE before running the migration. * @param _migration - The migration instance that is running the migrations. * @param _returnOfInit - The return of the init function, if you implemented it, otherwise it will be undefined. */ // eslint-disable-next-line ts/require-await async changeModel(_engine, _toModel, _fromModel, _migration, _returnOfInit) { return; } /** * When the user already has a model and you add a new field to the model this is used. So, in other words, this is * used to add new fields to existing models. * * Here is a sequelize example: * @example * ```ts * async addField( * engine: DatabaseAdapter, * toModel: InitializedModelsType>, * fromModel: InitializedModelsType>, * fieldName: string, * migration: Migration * ): Promise { * engine = engine as InstanceType; * let sequelizeAttribute = toModel.initialized.getAttributes()[fieldName]; * const doesNotExistSequelizeAttribute = sequelizeAttribute === undefined; * if (doesNotExistSequelizeAttribute) { * const originalFieldName = toModel.original.fields[fieldName]?.fieldName; * sequelizeAttribute = toModel.initialized.rawAttributes[originalFieldName]; * } * * await this.#queryInterface.addColumn( * toModel.initialized.options.tableName as string, * sequelizeAttribute.field as string, * sequelizeAttribute, * { transaction: migration.transaction } * ); * * await this.#handleCircularDependencies(engine, migration, { fromModel, toModel }); * await this.#handleIndexes(migration, { fromModel, toModel }); * } * ``` * * @param _engine - The engine instance that is running the migrations. * @param _toModel - How the model state IS right now when running the migration, this is how the model will be after * running the migration. You can take the field data from the actual model instance. * @param _fromModel - How the model state WAS before running the migration. * @param _fieldName -THe name of the field that is being added to the database. * @param _migration - The migration instance that is running the migrations. * @param _returnOfInit - The return of the init function, if you implemented it, otherwise it will be undefined. */ // eslint-disable-next-line ts/require-await async addField(_engine, _toModel, _fromModel, _fieldName, _migration, _returnOfInit) { return; } /** * When the user already has a model and a field but he makes changes to this field adding another attribute, * changing the type, changing the name, etc. * * This is called when the field had changed, not the model but the field specifically. * * Here is a Sequelize example: * @example * ```ts * async changeField( * engine: DatabaseAdapter, * toModel: InitializedModelsType>, * fromModel: InitializedModelsType>, * fieldBefore: Field, * fieldAfter: Field, * migration: Migration * ): Promise { * engine = engine as InstanceType; * const attributesAsArray = Object.values(toModel.initialized.getAttributes()); * const initializedAttribute = attributesAsArray.find((attribute) => attribute.field === fieldAfter.databaseName); * const tableName = toModel.initialized.options.tableName as string; * if (initializedAttribute) { * const isOfTypeRelation = fieldBefore instanceof ForeignKeyField; * // This removes the constraint, when we change the column sequelize automatically creates a new constraint * // because of that we remove the old one. * if (isOfTypeRelation) { * const constraints: GetForeignKeyReferencesForTableReturnType[] | undefined = * (await this.#queryInterface.getForeignKeyReferencesForTable(tableName, { * transaction: migration.transaction, * })) as GetForeignKeyReferencesForTableReturnType[] | undefined; * if (constraints) { * const constraintsToRemove = constraints?.filter( * (constraint) => constraint.columnName === fieldBefore.databaseName * ); * for (const constraintToRemove of constraintsToRemove) { * await this.#queryInterface.removeConstraint(tableName, constraintToRemove.constraintName as string, { * transaction: migration.transaction, * }); * } * } * } * } * await this.#queryInterface.changeColumn( * tableName, * fieldAfter.databaseName as unknown as string, * initializedAttribute, * { * transaction: migration.transaction, * } * ); * await this.#handleCircularDependencies(engine, migration, { fromModel, toModel }); * await this.#handleIndexes(migration, { toModel, fromModel }); * } * ``` * * @param _engine - The engine instance that is running the migrations. * @param _toModel - How the model state IS right now when running the migration, this is how the model will be * after running the migration. * @param _fromModel - How the model state WAS before running the migration. * @param _fieldBefore - How the field WAS * @param _fieldAfter - How the field WILL BE. * @param _migration - The migration instance that is running the migrations. * @param _returnOfInit - The return of the init function, if you implemented it, otherwise it will be undefined. */ // eslint-disable-next-line ts/require-await async changeField(_engine, _toModel, _fromModel, _fieldBefore, _fieldAfter, _migration, _returnOfInit) { return; } /** * Pretty much called whenever the user renames a field. Why this is done outside of the `changeField`? Because * renaming a field can do change some values on the database. Some databases might prefer to recreate the field * from scratch, we opt for maintaining the data and renaming the field only. * * Here is a Sequelize example: * @example * ```ts * async renameField( * engine: DatabaseAdapter, * toModel: InitializedModelsType>, * fromModel: InitializedModelsType>, * fieldNameBefore: string, * fieldNameAfter: string, * migration: Migration * ): Promise { * engine = engine as InstanceType; * const databaseNameAfter = toModel.initialized.getAttributes()[fieldNameAfter].field as string; * const databaseNameBefore = toModel.initialized.getAttributes()[fieldNameBefore].field as string; * const tableNameWhereRenameHappened = toModel.initialized.options.tableName as string; * * await this.#queryInterface.renameColumn(tableNameWhereRenameHappened, databaseNameBefore, databaseNameAfter, { * transaction: migration.transaction, * }); * await this.#handleCircularDependencies(engine, migration, { fromModel, toModel }); * await this.#handleIndexes(migration, { toModel, fromModel }); * } * ``` * * @param _engine - The engine instance that is running the migrations. * @param _toModel - How the model state IS right now when running the migration, this is how the model will * be after running the migration. * @param _fromModel - How the model state WAS before running the migration. * @param _fieldNameBefore - How the name of the model WAS. * @param _fieldNameAfter - How the name of the model WILL BE. * @param _migration - The migration instance that is running the migrations. * @param _returnOfInit - The return of the init function, if you implemented it, otherwise it will be undefined. */ // eslint-disable-next-line ts/require-await async renameField(_engine, _toModel, _fromModel, _fieldNameBefore, _fieldNameAfter, _migration, _returnOfInit) { return; } /** * When a model already exists but we just want to remove an existing field that was created (if it was renamed * we call renamed. We actually ask the user for what happened) * * Here is a Sequelize example: * @example * ```ts * async removeField( * engine: DatabaseAdapter, * toModel: InitializedModelsType>, * fromModel: InitializedModelsType>, * fieldName: string, * migration: Migration * ): Promise { * engine = engine as InstanceType; * const columnName = fromModel.initialized.getAttributes()[fieldName].field as string; * const tableName = fromModel.initialized.options.tableName as string; * await this.#queryInterface.removeColumn(tableName, columnName, { * transaction: migration.transaction, * }); * await this.#handleIndexes(migration, { toModel, fromModel }); * } * ``` * * @param _engine - The engine instance that is running the migrations. * @param _toModel - How the model state IS right now when running the migration, this is how the model will be * after running the migration. * @param _fromModel - How the model state WAS before running the migration. * @param _fieldName - The name of the field that will be removed (THAT'S NOT THE DB NAME, BUT THE JS NAME) * @param _migration - The migration instance that is running the migrations. * @param _returnOfInit - The return of the init function, if you implemented it, otherwise it will be undefined. */ // eslint-disable-next-line ts/require-await async removeField(_engine, _toModel, _fromModel, _fieldName, _migration, _returnOfInit) { return; } /** * When the migration file finishes running and you want to do some cleanup we call this function. If you don't have * any cleanup to do, don't implement this function. * * @param _engine - The engine instance that is running the migrations. * @param _returnOfInit - The return of the init function, if you implemented it, otherwise it will be undefined. */ // eslint-disable-next-line ts/require-await async finish(_engine, _returnOfInit) { return; } }; // src/queries/utils.ts function queryset(model2, type = "get") { if (type === "set") return new SetQuerySet(model2, "set"); if (type === "remove") return new RemoveQuerySet(model2, "remove"); return new GetQuerySet(model2, "get"); } __name(queryset, "queryset"); // src/standalone.ts var import_core10 = require("@palmares/core"); function setDatabaseConfig(settings) { if (settings.std) (0, import_core10.setDefaultStd)(settings.std); const databaseDomain = (0, import_core10.domain)("@palmares/database", "", { // eslint-disable-next-line ts/require-await getMigrations: /* @__PURE__ */ __name(async () => defaultMigrations, "getMigrations"), // eslint-disable-next-line ts/require-await getModels: /* @__PURE__ */ __name(async (engineInstance) => { if (engineInstance.migrations) return models_exports; else return []; }, "getModels") }); const domains = [ new databaseDomain() ]; for (const location of settings.locations) { const newDomainConstructor = (0, import_core10.domain)(location.name, location.path, { getModels: location.getModels, getMigrations: location.getMigrations }); const initializedDomain = new newDomainConstructor(); domains.push(initializedDomain); } const [databases] = loadDatabases(domains); databases.settings = settings; return { makeMigrations: /* @__PURE__ */ __name((args) => makeMigrations(databases, { settings, commandLineArgs: { keywordArgs: { useTs: typeof args.useTs === "boolean" ? args.useTs : true, empty: args.isEmpty || false }, positionalArgs: {} }, domains }), "makeMigrations"), migrate: /* @__PURE__ */ __name(() => migrate(databases, { settings, domains, commandLineArgs: { keywordArgs: {}, positionalArgs: {} } }), "migrate"), load: /* @__PURE__ */ __name(async () => { const settingsWithDefault = defaultSettings(settings); await databases.init(settingsWithDefault, domains); if (databases) await Promise.all([ databases.close() ]); }, "load") }; } __name(setDatabaseConfig, "setDatabaseConfig"); // src/index.ts var models = { fields: fields_exports, Model: model, ModelBaseClass: Model }; var src_default = databasesDomain; function getDatabasesWithDefaultAdapter() { return { define: /* @__PURE__ */ __name((modelName, args) => { return initialize(modelName, args); }, "define"), Model: /* @__PURE__ */ __name(() => model(), "Model"), fields: { auto: /* @__PURE__ */ __name((..._args) => AutoField.new(..._args), "auto"), bigAuto: /* @__PURE__ */ __name((..._args) => BigAutoField.new(..._args), "bigAuto"), bigInt: /* @__PURE__ */ __name((..._args) => BigIntegerField.new(..._args), "bigInt"), int: /* @__PURE__ */ __name((..._args) => IntegerField.new(..._args), "int"), text: /* @__PURE__ */ __name((..._args) => TextField.new(..._args), "text"), char: /* @__PURE__ */ __name((args) => CharField.new(args), "char"), uuid: /* @__PURE__ */ __name((..._args) => UuidField.new(..._args), "uuid"), date: /* @__PURE__ */ __name((..._args) => DateField.new(..._args), "date"), decimal: /* @__PURE__ */ __name((params) => DecimalField.new(params), "decimal"), boolean: /* @__PURE__ */ __name((...args) => BooleanField.new(...args), "boolean"), enum: /* @__PURE__ */ __name((args) => EnumField.new(args), "enum"), foreignKey: /* @__PURE__ */ __name((params) => ForeignKeyField.new(params), "foreignKey") } }; } __name(getDatabasesWithDefaultAdapter, "getDatabasesWithDefaultAdapter"); // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { AdapterAutoFieldParser, AdapterBigAutoFieldParser, AdapterBigIntegerFieldParser, AdapterBooleanFieldParser, AdapterCharFieldParser, AdapterDateFieldParser, AdapterDecimalFieldParser, AdapterEnumFieldParser, AdapterFieldParser, AdapterFields, AdapterForeignKeyFieldParser, AdapterGetQuery, AdapterIntegerFieldParser, AdapterMigrations, AdapterModels, AdapterOrderingQuery, AdapterQuery, AdapterRemoveQuery, AdapterSearchQuery, AdapterSetQuery, AdapterTextFieldParser, AdapterUuidFieldParser, AutoField, BigAutoField, BigIntegerField, BooleanField, CharField, DatabaseAdapter, Databases, DatabasesDomain, DateField, DecimalField, EnumField, Field, ForeignKeyField, GetQuerySet, IntegerField, InternalModelClass_DoNotUse, Manager, Migration, Model, ModelBaseClass, ON_DELETE, QuerySet, RemoveQuerySet, SetQuerySet, TextField, UuidField, actions, adapterAutoFieldParser, adapterBigAutoFieldParser, adapterBigIntegerFieldParser, adapterBooleanFieldParser, adapterCharFieldParser, adapterDateFieldParser, adapterDecimalFieldParser, adapterEnumFieldParser, adapterFieldParser, adapterFields, adapterForeignKeyFieldParser, adapterGetQuery, adapterIntegerFieldParser, adapterMigrations, adapterModels, adapterOrderingQuery, adapterQuery, adapterRemoveQuery, adapterSearchQuery, adapterSetQuery, adapterTextFieldParser, adapterUuidFieldParser, auto, bigAuto, bigInt, bool, char, choice, databaseAdapter, databaseDomainModifier, date, decimal, define, fields, foreignKey, generateUUID, getDatabasesWithDefaultAdapter, int, models, queryset, setDatabaseConfig, text, uuid });