{"version":3,"file":"index.cjs","names":["AccessibleFields"],"sources":["../../src/accessibleBy.ts","../../src/plugins/accessible_records.ts","../../src/accessibleFieldsBy.ts"],"sourcesContent":["import type { AnyMongoAbility, Generics, SubjectType, Abilities, AbilityTuple, ExtractSubjectType } from '@casl/ability';\nimport { rulesToCondition } from '@casl/ability/extra';\n\nfunction convertToMongoQuery(rule: AnyMongoAbility['rules'][number]) {\n  const conditions = rule.conditions!;\n  return rule.inverted ? { $nor: [conditions] } : conditions;\n}\n\nconst MONGO_QUERY_AGGREGATION = {\n  and: (conditions: unknown[]) => ({ $and: conditions }),\n  or: (conditions: unknown[]) => ({ $or: conditions }),\n  empty: () => ({})\n};\n\nexport const EMPTY_RESULT_QUERY = { $expr: { $eq: [0, 1] } };\nexport class AccessibleRecords<T extends SubjectType> {\n  constructor(\n    private readonly _ability: AnyMongoAbility,\n    private readonly _action: string\n  ) {}\n\n  /**\n   * In case action is not allowed, it returns `{ $expr: { $eq: [0, 1] } }`\n   */\n  ofType(subjectType: T): Record<string, unknown> {\n    const rules = this._ability.rulesFor(this._action, subjectType);\n    const query = rulesToCondition(rules, convertToMongoQuery, MONGO_QUERY_AGGREGATION);\n    return query === null ? EMPTY_RESULT_QUERY : query as Record<string, unknown>;\n  }\n}\n\ntype SubjectTypes<T extends Abilities> = T extends AbilityTuple\n  ? ExtractSubjectType<T[1]>\n  : never;\n\n/**\n * Returns accessible records Mongo query per record type (i.e., entity type) based on provided Ability and action.\n */\nexport function accessibleBy<T extends AnyMongoAbility>(\n  ability: T,\n  action: Parameters<T['rulesFor']>[0] = 'read'\n): AccessibleRecords<SubjectTypes<Generics<T>['abilities']>> {\n  return new AccessibleRecords(ability, action);\n}\n","import type { AnyMongoAbility, Generics, Normalize } from '@casl/ability';\nimport type { Document as Doc, HydratedDocument, Model, Query, QueryWithHelpers, Schema } from 'mongoose';\nimport { accessibleBy } from '../accessibleBy';\n\nfunction accessibleRecords<T extends AnyMongoAbility>(\n  baseQuery: Query<any, any>,\n  ability: T,\n  action?: Normalize<Generics<T>['abilities']>[0]\n): QueryWithHelpers<Doc, Doc> {\n  const subjectType = ability.detectSubjectType({\n    constructor: baseQuery.model\n  });\n\n  if (!subjectType) {\n    throw new TypeError(`Cannot detect subject type of \"${baseQuery.model.modelName}\" to return accessible records`);\n  }\n\n  const query = accessibleBy(ability, action).ofType(subjectType);\n\n  return baseQuery.and([query]);\n}\n\ntype GetAccessibleRecords<T, TQueryHelpers, TMethods, TVirtuals> = <U extends AnyMongoAbility>(\n  ability: U,\n  action?: Normalize<Generics<U>['abilities']>[0]\n) => QueryWithHelpers<T[], T, AccessibleRecordQueryHelpers<T, TQueryHelpers, TMethods, TVirtuals>>;\n\nexport type AccessibleRecordQueryHelpers<T, TQueryHelpers = {}, TMethods = {}, TVirtuals = {}> = {\n  accessibleBy: GetAccessibleRecords<\n  HydratedDocument<T, TMethods, TVirtuals>,\n  TQueryHelpers,\n  TMethods,\n  TVirtuals\n  >\n};\nexport interface AccessibleRecordModel<\n  T,\n  TQueryHelpers = {},\n  TMethods = {},\n  TVirtuals = {}\n> extends Model<T,\n  TQueryHelpers & AccessibleRecordQueryHelpers<T, TQueryHelpers, TMethods, TVirtuals>,\n  TMethods,\n  TVirtuals> {\n  accessibleBy: GetAccessibleRecords<\n  HydratedDocument<T, TMethods, TVirtuals>,\n  TQueryHelpers,\n  TMethods,\n  TVirtuals\n  >\n}\n\nfunction modelAccessibleBy(this: Model<unknown>, ability: AnyMongoAbility, action?: string) {\n  return accessibleRecords(this.where(), ability, action);\n}\n\nfunction queryAccessibleBy(\n  this: Query<unknown, unknown>,\n  ability: AnyMongoAbility,\n  action?: string\n) {\n  return accessibleRecords(this, ability, action);\n}\n\nexport function accessibleRecordsPlugin(schema: Schema<any>): void {\n  (schema.query as Record<string, unknown>).accessibleBy = queryAccessibleBy;\n  schema.statics.accessibleBy = modelAccessibleBy;\n}\n","import type { AnyMongoAbility, Generics } from \"@casl/ability\";\nimport { AccessibleFields, type GetSubjectTypeAllFieldsExtractor } from \"@casl/ability/extra\";\nimport mongoose from 'mongoose';\n\nconst getSubjectTypeAllFieldsExtractor: GetSubjectTypeAllFieldsExtractor = (type) => {\n  const Model = typeof type === 'string' ? mongoose.models[type] : type;\n  if (!Model) throw new Error(`Unknown mongoose model \"${type}\"`);\n  return 'schema' in Model ? Object.keys((Model.schema as any).paths) : [];\n};\n\nexport function accessibleFieldsBy<T extends AnyMongoAbility>(\n  ability: T,\n  action: Parameters<T['rulesFor']>[0] = 'read'\n): AccessibleFields<Extract<Generics<T>['abilities'], unknown[]>[1]> {\n  return new AccessibleFields(ability, action, getSubjectTypeAllFieldsExtractor);\n}\n"],"mappings":";;;;;;;;;;AAGA,SAAS,EAAoB;IAC3B,MAAM,IAAa,EAAK;IACxB,OAAO,EAAK,WAAW;QAAE,MAAM,EAAC;QAAgB;;;;;;;;;;;;;;;AAGlD,MAAM,IAA0B;IAC9B,KAAM,MAAA;QAA6B,MAAM;;IACzC,IAAK,MAAA;QAA6B,KAAK;;IACvC,OAAA,OAAA,CAAgB;GAGL,IAAqB;IAAE,OAAO;QAAE,KAAK,EAAC,GAAG;;;;AACtD,IAAa,IAAb;IACE,WAAA,CACE,GACA;QADiB,KAAA,IAAA,GACA,KAAA,IAAA;;IAMnB,MAAA,CAAO;QACL,MAAM,IAAQ,KAAK,EAAS,SAAS,KAAK,GAAS,IAC7C,KAAA,GAAA,EAAA,kBAAyB,GAAO,GAAqB;QAC3D,OAAiB,SAAV,IAAiB,IAAqB;;;;AAWjD,SAAgB,EACd,GACA,IAAuC;IAEvC,OAAO,IAAI,EAAkB,GAAS;;;ACtCxC,SAAS,EACP,GACA,GACA;IAEA,MAAM,IAAc,EAAQ,kBAAkB;QAC5C,aAAa,EAAU;;IAGzB,KAAK,GACH,MAAM,IAAI,UAAU,kCAAkC,EAAU,MAAM;IAGxE,MAAM,IAAQ,EAAa,GAAS,GAAQ,OAAO;IAEnD,OAAO,EAAU,IAAI,EAAC;;;AAiCxB,SAAS,EAAwC,GAA0B;IACzE,OAAO,EAAkB,KAAK,SAAS,GAAS;;;AAGlD,SAAS,EAEP,GACA;IAEA,OAAO,EAAkB,MAAM,GAAS;;;ACzD1C,MAAM,IAAsE;IAC1E,MAAM,IAAwB,mBAAT,IAAoB,EAAA,QAAS,OAAO,KAAQ;IACjE,KAAK,GAAO,MAAM,IAAI,MAAM,2BAA2B;IACvD,OAAO,YAAY,IAAQ,OAAO,KAAM,EAAM,OAAe,SAAS;;;uDAGxE,SACE,GACA,IAAuC;IAEvC,OAAO,IAAIA,EAAAA,iBAAiB,GAAS,GAAQ;qCDkD/C,SAAwC;IACrC,EAAO,MAAkC,eAAe,GACzD,EAAO,QAAQ,eAAe"}