"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);

// src/engine/query/index.ts
var query_exports = {};
__export(query_exports, {
  AdapterQuery: () => AdapterQuery,
  adapterQuery: () => adapterQuery
});
module.exports = __toCommonJS(query_exports);

// 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;
  }
};

// src/engine/query/get.ts
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<Model>;
  *     search: any;
  *     fields: readonly string[];
  *     ordering?: Order;
  *     limit?: number;
  *     offset?: number | string;
  *   }
  * ) {
  *   const findAllOptions: Parameters<ModelCtor<Model>['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
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<Order> => {
  *   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
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
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 extends OperatorsOfQuery>(
  *    operationType: OperationType,
  *    value?: (OperationType extends 'or' | 'and' | 'in' | 'between' ? unknown[] : unknown) | undefined,
  *    result?: any,
  *    options?: { isNot?: boolean | undefined; ignoreCase?: boolean | undefined } | undefined
  *  ): Promise<any> {
  *    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
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<Model>;
  *     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();
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  AdapterQuery,
  adapterQuery
});
