UNPKG

better-auth

Version:

The most comprehensive authentication library for TypeScript.

3,079 lines • 93.4 kB
'use strict';

const betterCall = require('better-call');
const zod = require('zod');
const account = require('./better-auth.BzwQYq5t.cjs');
require('./better-auth.DiSjtgs9.cjs');
require('@better-auth/utils/base64');
require('@better-auth/utils/hmac');
require('@better-auth/utils/binary');
require('./better-auth.DcWKCjjf.cjs');
const date = require('./better-auth.C1hdVENX.cjs');
const index = require('./better-auth.ANpbi45u.cjs');
const parser = require('./better-auth.DhsGZ30Q.cjs');
require('./better-auth.GpOOav9x.cjs');
require('defu');
const hasPermission = require('./better-auth.DSVbLSt7.cjs');
const cookies_index = require('../cookies/index.cjs');
const id = require('./better-auth.Bg6iw3ig.cjs');
require('@better-auth/utils/hash');
require('@noble/ciphers/chacha');
require('@noble/ciphers/utils');
require('@noble/ciphers/webcrypto');
require('jose');
require('@noble/hashes/scrypt');
require('@better-auth/utils');
require('@better-auth/utils/hex');
require('@noble/hashes/utils');
require('./better-auth.CYeOI8C-.cjs');
const plugins_organization_access_index = require('../plugins/organization/access/index.cjs');

const shimContext = (originalObject, newContext) => {
  const shimmedObj = {};
  for (const [key, value] of Object.entries(originalObject)) {
    shimmedObj[key] = (ctx) => {
      return value({
        ...ctx,
        context: {
          ...newContext,
          ...ctx.context
        }
      });
    };
    shimmedObj[key].path = value.path;
    shimmedObj[key].method = value.method;
    shimmedObj[key].options = value.options;
    shimmedObj[key].headers = value.headers;
  }
  return shimmedObj;
};

const getOrgAdapter = (context, options) => {
  const adapter = context.adapter;
  return {
    findOrganizationBySlug: async (slug) => {
      const organization = await adapter.findOne({
        model: "organization",
        where: [
          {
            field: "slug",
            value: slug
          }
        ]
      });
      return organization;
    },
    createOrganization: async (data) => {
      const organization = await adapter.create({
        model: "organization",
        data: {
          ...data.organization,
          metadata: data.organization.metadata ? JSON.stringify(data.organization.metadata) : void 0
        }
      });
      return {
        ...organization,
        metadata: organization.metadata ? JSON.parse(organization.metadata) : void 0
      };
    },
    findMemberByEmail: async (data) => {
      const user = await adapter.findOne({
        model: "user",
        where: [
          {
            field: "email",
            value: data.email
          }
        ]
      });
      if (!user) {
        return null;
      }
      const member = await adapter.findOne({
        model: "member",
        where: [
          {
            field: "organizationId",
            value: data.organizationId
          },
          {
            field: "userId",
            value: user.id
          }
        ]
      });
      if (!member) {
        return null;
      }
      return {
        ...member,
        user: {
          id: user.id,
          name: user.name,
          email: user.email,
          image: user.image
        }
      };
    },
    listMembers: async (data) => {
      const members = await adapter.findMany({
        model: "member",
        where: [
          {
            field: "organizationId",
            value: data.organizationId
          }
        ],
        limit: options?.membershipLimit || 100
      });
      return members;
    },
    findMemberByOrgId: async (data) => {
      const [member, user] = await Promise.all([
        await adapter.findOne({
          model: "member",
          where: [
            {
              field: "userId",
              value: data.userId
            },
            {
              field: "organizationId",
              value: data.organizationId
            }
          ]
        }),
        await adapter.findOne({
          model: "user",
          where: [
            {
              field: "id",
              value: data.userId
            }
          ]
        })
      ]);
      if (!user || !member) {
        return null;
      }
      return {
        ...member,
        user: {
          id: user.id,
          name: user.name,
          email: user.email,
          image: user.image
        }
      };
    },
    findMemberById: async (memberId) => {
      const member = await adapter.findOne({
        model: "member",
        where: [
          {
            field: "id",
            value: memberId
          }
        ]
      });
      if (!member) {
        return null;
      }
      const user = await adapter.findOne({
        model: "user",
        where: [
          {
            field: "id",
            value: member.userId
          }
        ]
      });
      if (!user) {
        return null;
      }
      return {
        ...member,
        user: {
          id: user.id,
          name: user.name,
          email: user.email,
          image: user.image
        }
      };
    },
    createMember: async (data) => {
      const member = await adapter.create({
        model: "member",
        data: {
          ...data,
          createdAt: /* @__PURE__ */ new Date()
        }
      });
      return member;
    },
    updateMember: async (memberId, role) => {
      const member = await adapter.update({
        model: "member",
        where: [
          {
            field: "id",
            value: memberId
          }
        ],
        update: {
          role
        }
      });
      return member;
    },
    deleteMember: async (memberId) => {
      const member = await adapter.delete({
        model: "member",
        where: [
          {
            field: "id",
            value: memberId
          }
        ]
      });
      return member;
    },
    updateOrganization: async (organizationId, data) => {
      const organization = await adapter.update({
        model: "organization",
        where: [
          {
            field: "id",
            value: organizationId
          }
        ],
        update: {
          ...data,
          metadata: typeof data.metadata === "object" ? JSON.stringify(data.metadata) : data.metadata
        }
      });
      if (!organization) {
        return null;
      }
      return {
        ...organization,
        metadata: organization.metadata ? parser.parseJSON(organization.metadata) : void 0
      };
    },
    deleteOrganization: async (organizationId) => {
      await adapter.delete({
        model: "member",
        where: [
          {
            field: "organizationId",
            value: organizationId
          }
        ]
      });
      await adapter.delete({
        model: "invitation",
        where: [
          {
            field: "organizationId",
            value: organizationId
          }
        ]
      });
      await adapter.delete({
        model: "organization",
        where: [
          {
            field: "id",
            value: organizationId
          }
        ]
      });
      return organizationId;
    },
    setActiveOrganization: async (sessionToken, organizationId) => {
      const session = await context.internalAdapter.updateSession(
        sessionToken,
        {
          activeOrganizationId: organizationId
        }
      );
      return session;
    },
    findOrganizationById: async (organizationId) => {
      const organization = await adapter.findOne({
        model: "organization",
        where: [
          {
            field: "id",
            value: organizationId
          }
        ]
      });
      return organization;
    },
    /**
     * @requires db
     */
    findFullOrganization: async ({
      organizationId,
      isSlug,
      includeTeams
    }) => {
      const org = await adapter.findOne({
        model: "organization",
        where: [{ field: isSlug ? "slug" : "id", value: organizationId }]
      });
      if (!org) {
        return null;
      }
      const [invitations, members, teams] = await Promise.all([
        adapter.findMany({
          model: "invitation",
          where: [{ field: "organizationId", value: org.id }]
        }),
        adapter.findMany({
          model: "member",
          where: [{ field: "organizationId", value: org.id }],
          limit: options?.membershipLimit || 100
        }),
        includeTeams ? adapter.findMany({
          model: "team",
          where: [{ field: "organizationId", value: org.id }]
        }) : null
      ]);
      if (!org) return null;
      const userIds = members.map((member) => member.userId);
      const users = userIds.length > 0 ? await adapter.findMany({
        model: "user",
        where: [{ field: "id", value: userIds, operator: "in" }],
        limit: options?.membershipLimit || 100
      }) : [];
      const userMap = new Map(users.map((user) => [user.id, user]));
      const membersWithUsers = members.map((member) => {
        const user = userMap.get(member.userId);
        if (!user) {
          throw new index.BetterAuthError(
            "Unexpected error: User not found for member"
          );
        }
        return {
          ...member,
          user: {
            id: user.id,
            name: user.name,
            email: user.email,
            image: user.image
          }
        };
      });
      return {
        ...org,
        invitations,
        members: membersWithUsers,
        teams
      };
    },
    listOrganizations: async (userId) => {
      const members = await adapter.findMany({
        model: "member",
        where: [
          {
            field: "userId",
            value: userId
          }
        ]
      });
      if (!members || members.length === 0) {
        return [];
      }
      const organizationIds = members.map((member) => member.organizationId);
      const organizations = await adapter.findMany({
        model: "organization",
        where: [
          {
            field: "id",
            value: organizationIds,
            operator: "in"
          }
        ]
      });
      return organizations;
    },
    createTeam: async (data) => {
      const team = await adapter.create({
        model: "team",
        data
      });
      return team;
    },
    findTeamById: async ({
      teamId,
      organizationId,
      includeTeamMembers
    }) => {
      const team = await adapter.findOne({
        model: "team",
        where: [
          {
            field: "id",
            value: teamId
          },
          ...organizationId ? [
            {
              field: "organizationId",
              value: organizationId
            }
          ] : []
        ]
      });
      if (!team) {
        return null;
      }
      let members = [];
      if (includeTeamMembers) {
        members = await adapter.findMany({
          model: "member",
          where: [
            {
              field: "teamId",
              value: teamId
            }
          ],
          limit: options?.membershipLimit || 100
        });
        return {
          ...team,
          members
        };
      }
      return team;
    },
    updateTeam: async (teamId, data) => {
      const team = await adapter.update({
        model: "team",
        where: [
          {
            field: "id",
            value: teamId
          }
        ],
        update: {
          ...data
        }
      });
      return team;
    },
    deleteTeam: async (teamId) => {
      const team = await adapter.delete({
        model: "team",
        where: [
          {
            field: "id",
            value: teamId
          }
        ]
      });
      return team;
    },
    listTeams: async (organizationId) => {
      const teams = await adapter.findMany({
        model: "team",
        where: [
          {
            field: "organizationId",
            value: organizationId
          }
        ]
      });
      return teams;
    },
    createTeamInvitation: async ({
      email,
      role,
      teamId,
      organizationId,
      inviterId,
      expiresIn = 1e3 * 60 * 60 * 48
      // Default expiration: 48 hours
    }) => {
      const expiresAt = date.getDate(expiresIn);
      const invitation = await adapter.create({
        model: "invitation",
        data: {
          email,
          role,
          organizationId,
          teamId,
          inviterId,
          status: "pending",
          expiresAt
        }
      });
      return invitation;
    },
    findInvitationsByTeamId: async (teamId) => {
      const invitations = await adapter.findMany({
        model: "invitation",
        where: [
          {
            field: "teamId",
            value: teamId
          }
        ]
      });
      return invitations;
    },
    createInvitation: async ({
      invitation,
      user
    }) => {
      const defaultExpiration = 60 * 60 * 48;
      const expiresAt = date.getDate(
        options?.invitationExpiresIn || defaultExpiration,
        "sec"
      );
      const invite = await adapter.create({
        model: "invitation",
        data: {
          status: "pending",
          expiresAt,
          inviterId: user.id,
          ...invitation
        }
      });
      return invite;
    },
    findInvitationById: async (id) => {
      const invitation = await adapter.findOne({
        model: "invitation",
        where: [
          {
            field: "id",
            value: id
          }
        ]
      });
      return invitation;
    },
    findPendingInvitation: async (data) => {
      const invitation = await adapter.findMany({
        model: "invitation",
        where: [
          {
            field: "email",
            value: data.email
          },
          {
            field: "organizationId",
            value: data.organizationId
          },
          {
            field: "status",
            value: "pending"
          }
        ]
      });
      return invitation.filter(
        (invite) => new Date(invite.expiresAt) > /* @__PURE__ */ new Date()
      );
    },
    findPendingInvitations: async (data) => {
      const invitations = await adapter.findMany({
        model: "invitation",
        where: [
          {
            field: "organizationId",
            value: data.organizationId
          },
          {
            field: "status",
            value: "pending"
          }
        ]
      });
      return invitations.filter(
        (invite) => new Date(invite.expiresAt) > /* @__PURE__ */ new Date()
      );
    },
    listInvitations: async (data) => {
      const invitations = await adapter.findMany({
        model: "invitation",
        where: [
          {
            field: "organizationId",
            value: data.organizationId
          }
        ]
      });
      return invitations;
    },
    updateInvitation: async (data) => {
      const invitation = await adapter.update({
        model: "invitation",
        where: [
          {
            field: "id",
            value: data.invitationId
          }
        ],
        update: {
          status: data.status
        }
      });
      return invitation;
    }
  };
};

const orgMiddleware = account.createAuthMiddleware(async (ctx) => {
  return {};
});
const orgSessionMiddleware = account.createAuthMiddleware(
  {
    use: [account.sessionMiddleware]
  },
  async (ctx) => {
    const session = ctx.context.session;
    return {
      session
    };
  }
);

const ORGANIZATION_ERROR_CODES = {
  YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_ORGANIZATION: "You are not allowed to create a new organization",
  YOU_HAVE_REACHED_THE_MAXIMUM_NUMBER_OF_ORGANIZATIONS: "You have reached the maximum number of organizations",
  ORGANIZATION_ALREADY_EXISTS: "Organization already exists",
  ORGANIZATION_NOT_FOUND: "Organization not found",
  USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION: "User is not a member of the organization",
  YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_ORGANIZATION: "You are not allowed to update this organization",
  YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_ORGANIZATION: "You are not allowed to delete this organization",
  NO_ACTIVE_ORGANIZATION: "No active organization",
  USER_IS_ALREADY_A_MEMBER_OF_THIS_ORGANIZATION: "User is already a member of this organization",
  MEMBER_NOT_FOUND: "Member not found",
  ROLE_NOT_FOUND: "Role not found",
  YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_TEAM: "You are not allowed to create a new team",
  TEAM_ALREADY_EXISTS: "Team already exists",
  TEAM_NOT_FOUND: "Team not found",
  YOU_CANNOT_LEAVE_THE_ORGANIZATION_AS_THE_ONLY_OWNER: "You cannot leave the organization as the only owner",
  YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_MEMBER: "You are not allowed to delete this member",
  YOU_ARE_NOT_ALLOWED_TO_INVITE_USERS_TO_THIS_ORGANIZATION: "You are not allowed to invite users to this organization",
  USER_IS_ALREADY_INVITED_TO_THIS_ORGANIZATION: "User is already invited to this organization",
  INVITATION_NOT_FOUND: "Invitation not found",
  YOU_ARE_NOT_THE_RECIPIENT_OF_THE_INVITATION: "You are not the recipient of the invitation",
  YOU_ARE_NOT_ALLOWED_TO_CANCEL_THIS_INVITATION: "You are not allowed to cancel this invitation",
  INVITER_IS_NO_LONGER_A_MEMBER_OF_THE_ORGANIZATION: "Inviter is no longer a member of the organization",
  YOU_ARE_NOT_ALLOWED_TO_INVITE_USER_WITH_THIS_ROLE: "you are not allowed to invite user with this role",
  FAILED_TO_RETRIEVE_INVITATION: "Failed to retrieve invitation",
  YOU_HAVE_REACHED_THE_MAXIMUM_NUMBER_OF_TEAMS: "You have reached the maximum number of teams",
  UNABLE_TO_REMOVE_LAST_TEAM: "Unable to remove last team",
  YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_MEMBER: "You are not allowed to update this member",
  ORGANIZATION_MEMBERSHIP_LIMIT_REACHED: "Organization membership limit reached",
  YOU_ARE_NOT_ALLOWED_TO_CREATE_TEAMS_IN_THIS_ORGANIZATION: "You are not allowed to create teams in this organization",
  YOU_ARE_NOT_ALLOWED_TO_DELETE_TEAMS_IN_THIS_ORGANIZATION: "You are not allowed to delete teams in this organization",
  YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_TEAM: "You are not allowed to update this team",
  YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_TEAM: "You are not allowed to delete this team",
  INVITATION_LIMIT_REACHED: "Invitation limit reached",
  TEAM_MEMBER_LIMIT_REACHED: "Team member limit reached"
};

const createInvitation = (option) => account.createAuthEndpoint(
  "/organization/invite-member",
  {
    method: "POST",
    use: [orgMiddleware, orgSessionMiddleware],
    body: zod.z.object({
      email: zod.z.string({
        description: "The email address of the user to invite"
      }),
      role: zod.z.union([
        zod.z.string({
          description: "The role to assign to the user"
        }),
        zod.z.array(
          zod.z.string({
            description: "The roles to assign to the user"
          })
        )
      ]),
      organizationId: zod.z.string({
        description: "The organization ID to invite the user to"
      }).optional(),
      resend: zod.z.boolean({
        description: "Resend the invitation email, if the user is already invited"
      }).optional(),
      teamId: zod.z.string({
        description: "The team ID to invite the user to"
      }).optional()
    }),
    metadata: {
      $Infer: {
        body: {}
      },
      openapi: {
        description: "Invite a user to an organization",
        responses: {
          "200": {
            description: "Success",
            content: {
              "application/json": {
                schema: {
                  type: "object",
                  properties: {
                    id: {
                      type: "string"
                    },
                    email: {
                      type: "string"
                    },
                    role: {
                      type: "string"
                    },
                    organizationId: {
                      type: "string"
                    },
                    inviterId: {
                      type: "string"
                    },
                    status: {
                      type: "string"
                    },
                    expiresAt: {
                      type: "string"
                    }
                  },
                  required: [
                    "id",
                    "email",
                    "role",
                    "organizationId",
                    "inviterId",
                    "status",
                    "expiresAt"
                  ]
                }
              }
            }
          }
        }
      }
    }
  },
  async (ctx) => {
    const session = ctx.context.session;
    const organizationId = ctx.body.organizationId || session.session.activeOrganizationId;
    if (!organizationId) {
      throw new betterCall.APIError("BAD_REQUEST", {
        message: ORGANIZATION_ERROR_CODES.ORGANIZATION_NOT_FOUND
      });
    }
    const adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);
    const member = await adapter.findMemberByOrgId({
      userId: session.user.id,
      organizationId
    });
    if (!member) {
      throw new betterCall.APIError("BAD_REQUEST", {
        message: ORGANIZATION_ERROR_CODES.MEMBER_NOT_FOUND
      });
    }
    const canInvite = hasPermission.hasPermission({
      role: member.role,
      options: ctx.context.orgOptions,
      permissions: {
        invitation: ["create"]
      }
    });
    if (!canInvite) {
      throw new betterCall.APIError("FORBIDDEN", {
        message: ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_INVITE_USERS_TO_THIS_ORGANIZATION
      });
    }
    const creatorRole = ctx.context.orgOptions.creatorRole || "owner";
    const roles = parseRoles(ctx.body.role);
    if (member.role !== creatorRole && roles.split(",").includes(creatorRole)) {
      throw new betterCall.APIError("FORBIDDEN", {
        message: ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_INVITE_USER_WITH_THIS_ROLE
      });
    }
    const alreadyMember = await adapter.findMemberByEmail({
      email: ctx.body.email,
      organizationId
    });
    if (alreadyMember) {
      throw new betterCall.APIError("BAD_REQUEST", {
        message: ORGANIZATION_ERROR_CODES.USER_IS_ALREADY_A_MEMBER_OF_THIS_ORGANIZATION
      });
    }
    const alreadyInvited = await adapter.findPendingInvitation({
      email: ctx.body.email,
      organizationId
    });
    if (alreadyInvited.length && !ctx.body.resend) {
      throw new betterCall.APIError("BAD_REQUEST", {
        message: ORGANIZATION_ERROR_CODES.USER_IS_ALREADY_INVITED_TO_THIS_ORGANIZATION
      });
    }
    if (alreadyInvited.length && ctx.context.orgOptions.cancelPendingInvitationsOnReInvite) {
      await adapter.updateInvitation({
        invitationId: alreadyInvited[0].id,
        status: "canceled"
      });
    }
    const organization = await adapter.findOrganizationById(organizationId);
    if (!organization) {
      throw new betterCall.APIError("BAD_REQUEST", {
        message: ORGANIZATION_ERROR_CODES.ORGANIZATION_NOT_FOUND
      });
    }
    const invitationLimit = typeof ctx.context.orgOptions.invitationLimit === "function" ? await ctx.context.orgOptions.invitationLimit(
      {
        user: session.user,
        organization,
        member
      },
      ctx.context
    ) : ctx.context.orgOptions.invitationLimit ?? 100;
    const pendingInvitations = await adapter.findPendingInvitations({
      organizationId
    });
    if (pendingInvitations.length >= invitationLimit) {
      throw new betterCall.APIError("FORBIDDEN", {
        message: ORGANIZATION_ERROR_CODES.INVITATION_LIMIT_REACHED
      });
    }
    if (ctx.context.orgOptions.teams && ctx.context.orgOptions.teams.enabled && typeof ctx.context.orgOptions.teams.maximumMembersPerTeam !== "undefined" && "teamId" in ctx.body && ctx.body.teamId) {
      const team = await adapter.findTeamById({
        teamId: ctx.body.teamId,
        organizationId,
        includeTeamMembers: true
      });
      if (!team) {
        throw new betterCall.APIError("BAD_REQUEST", {
          message: ORGANIZATION_ERROR_CODES.TEAM_NOT_FOUND
        });
      }
      const maximumMembersPerTeam = typeof ctx.context.orgOptions.teams.maximumMembersPerTeam === "function" ? await ctx.context.orgOptions.teams.maximumMembersPerTeam({
        teamId: ctx.body.teamId,
        session,
        organizationId
      }) : ctx.context.orgOptions.teams.maximumMembersPerTeam;
      if (team.members.length >= maximumMembersPerTeam) {
        throw new betterCall.APIError("FORBIDDEN", {
          message: ORGANIZATION_ERROR_CODES.TEAM_MEMBER_LIMIT_REACHED
        });
      }
    }
    const invitation = await adapter.createInvitation({
      invitation: {
        role: roles,
        email: ctx.body.email.toLowerCase(),
        organizationId,
        ..."teamId" in ctx.body ? {
          teamId: ctx.body.teamId
        } : {}
      },
      user: session.user
    });
    await ctx.context.orgOptions.sendInvitationEmail?.(
      {
        id: invitation.id,
        role: invitation.role,
        email: invitation.email.toLowerCase(),
        organization,
        inviter: {
          ...member,
          user: session.user
        },
        invitation
      },
      ctx.request
    );
    return ctx.json(invitation);
  }
);
const acceptInvitation = account.createAuthEndpoint(
  "/organization/accept-invitation",
  {
    method: "POST",
    body: zod.z.object({
      invitationId: zod.z.string({
        description: "The ID of the invitation to accept"
      })
    }),
    use: [orgMiddleware, orgSessionMiddleware],
    metadata: {
      openapi: {
        description: "Accept an invitation to an organization",
        responses: {
          "200": {
            description: "Success",
            content: {
              "application/json": {
                schema: {
                  type: "object",
                  properties: {
                    invitation: {
                      type: "object"
                    },
                    member: {
                      type: "object"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  async (ctx) => {
    const session = ctx.context.session;
    const adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);
    const invitation = await adapter.findInvitationById(ctx.body.invitationId);
    if (!invitation || invitation.expiresAt < /* @__PURE__ */ new Date() || invitation.status !== "pending") {
      throw new betterCall.APIError("BAD_REQUEST", {
        message: ORGANIZATION_ERROR_CODES.INVITATION_NOT_FOUND
      });
    }
    if (invitation.email.toLowerCase() !== session.user.email.toLowerCase()) {
      throw new betterCall.APIError("FORBIDDEN", {
        message: ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_THE_RECIPIENT_OF_THE_INVITATION
      });
    }
    const membershipLimit = ctx.context.orgOptions?.membershipLimit || 100;
    const members = await adapter.listMembers({
      organizationId: invitation.organizationId
    });
    if (members.length >= membershipLimit) {
      throw new betterCall.APIError("FORBIDDEN", {
        message: ORGANIZATION_ERROR_CODES.ORGANIZATION_MEMBERSHIP_LIMIT_REACHED
      });
    }
    const acceptedI = await adapter.updateInvitation({
      invitationId: ctx.body.invitationId,
      status: "accepted"
    });
    if (!acceptedI) {
      throw new betterCall.APIError("BAD_REQUEST", {
        message: ORGANIZATION_ERROR_CODES.FAILED_TO_RETRIEVE_INVITATION
      });
    }
    if (ctx.context.orgOptions.teams && ctx.context.orgOptions.teams.enabled && typeof ctx.context.orgOptions.teams.maximumMembersPerTeam !== "undefined" && "teamId" in acceptedI && acceptedI.teamId) {
      const team = await adapter.findTeamById({
        teamId: acceptedI.teamId,
        organizationId: invitation.organizationId,
        includeTeamMembers: true
      });
      if (!team) {
        throw new betterCall.APIError("BAD_REQUEST", {
          message: ORGANIZATION_ERROR_CODES.TEAM_NOT_FOUND
        });
      }
      const maximumMembersPerTeam = typeof ctx.context.orgOptions.teams.maximumMembersPerTeam === "function" ? await ctx.context.orgOptions.teams.maximumMembersPerTeam({
        teamId: acceptedI.teamId,
        session,
        organizationId: invitation.organizationId
      }) : ctx.context.orgOptions.teams.maximumMembersPerTeam;
      if (team.members.length >= maximumMembersPerTeam) {
        throw new betterCall.APIError("FORBIDDEN", {
          message: ORGANIZATION_ERROR_CODES.TEAM_MEMBER_LIMIT_REACHED
        });
      }
    }
    const member = await adapter.createMember({
      organizationId: invitation.organizationId,
      userId: session.user.id,
      role: invitation.role,
      createdAt: /* @__PURE__ */ new Date(),
      ..."teamId" in acceptedI ? {
        teamId: acceptedI.teamId
      } : {}
    });
    await adapter.setActiveOrganization(
      session.session.token,
      invitation.organizationId
    );
    if (!acceptedI) {
      return ctx.json(null, {
        status: 400,
        body: {
          message: ORGANIZATION_ERROR_CODES.INVITATION_NOT_FOUND
        }
      });
    }
    return ctx.json({
      invitation: acceptedI,
      member
    });
  }
);
const rejectInvitation = account.createAuthEndpoint(
  "/organization/reject-invitation",
  {
    method: "POST",
    body: zod.z.object({
      invitationId: zod.z.string({
        description: "The ID of the invitation to reject"
      })
    }),
    use: [orgMiddleware, orgSessionMiddleware],
    metadata: {
      openapi: {
        description: "Reject an invitation to an organization",
        responses: {
          "200": {
            description: "Success",
            content: {
              "application/json": {
                schema: {
                  type: "object",
                  properties: {
                    invitation: {
                      type: "object"
                    },
                    member: {
                      type: "null"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  async (ctx) => {
    const session = ctx.context.session;
    const adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);
    const invitation = await adapter.findInvitationById(ctx.body.invitationId);
    if (!invitation || invitation.expiresAt < /* @__PURE__ */ new Date() || invitation.status !== "pending") {
      throw new betterCall.APIError("BAD_REQUEST", {
        message: "Invitation not found!"
      });
    }
    if (invitation.email.toLowerCase() !== session.user.email.toLowerCase()) {
      throw new betterCall.APIError("FORBIDDEN", {
        message: ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_THE_RECIPIENT_OF_THE_INVITATION
      });
    }
    const rejectedI = await adapter.updateInvitation({
      invitationId: ctx.body.invitationId,
      status: "rejected"
    });
    return ctx.json({
      invitation: rejectedI,
      member: null
    });
  }
);
const cancelInvitation = account.createAuthEndpoint(
  "/organization/cancel-invitation",
  {
    method: "POST",
    body: zod.z.object({
      invitationId: zod.z.string({
        description: "The ID of the invitation to cancel"
      })
    }),
    use: [orgMiddleware, orgSessionMiddleware],
    openapi: {
      description: "Cancel an invitation to an organization",
      responses: {
        "200": {
          description: "Success",
          content: {
            "application/json": {
              schema: {
                type: "object",
                properties: {
                  invitation: {
                    type: "object"
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  async (ctx) => {
    const session = ctx.context.session;
    const adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);
    const invitation = await adapter.findInvitationById(ctx.body.invitationId);
    if (!invitation) {
      throw new betterCall.APIError("BAD_REQUEST", {
        message: ORGANIZATION_ERROR_CODES.INVITATION_NOT_FOUND
      });
    }
    const member = await adapter.findMemberByOrgId({
      userId: session.user.id,
      organizationId: invitation.organizationId
    });
    if (!member) {
      throw new betterCall.APIError("BAD_REQUEST", {
        message: ORGANIZATION_ERROR_CODES.MEMBER_NOT_FOUND
      });
    }
    const canCancel = hasPermission.hasPermission({
      role: member.role,
      options: ctx.context.orgOptions,
      permissions: {
        invitation: ["cancel"]
      }
    });
    if (!canCancel) {
      throw new betterCall.APIError("FORBIDDEN", {
        message: ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_CANCEL_THIS_INVITATION
      });
    }
    const canceledI = await adapter.updateInvitation({
      invitationId: ctx.body.invitationId,
      status: "canceled"
    });
    return ctx.json(canceledI);
  }
);
const getInvitation = account.createAuthEndpoint(
  "/organization/get-invitation",
  {
    method: "GET",
    use: [orgMiddleware],
    requireHeaders: true,
    query: zod.z.object({
      id: zod.z.string({
        description: "The ID of the invitation to get"
      })
    }),
    metadata: {
      openapi: {
        description: "Get an invitation by ID",
        responses: {
          "200": {
            description: "Success",
            content: {
              "application/json": {
                schema: {
                  type: "object",
                  properties: {
                    id: {
                      type: "string"
                    },
                    email: {
                      type: "string"
                    },
                    role: {
                      type: "string"
                    },
                    organizationId: {
                      type: "string"
                    },
                    inviterId: {
                      type: "string"
                    },
                    status: {
                      type: "string"
                    },
                    expiresAt: {
                      type: "string"
                    },
                    organizationName: {
                      type: "string"
                    },
                    organizationSlug: {
                      type: "string"
                    },
                    inviterEmail: {
                      type: "string"
                    }
                  },
                  required: [
                    "id",
                    "email",
                    "role",
                    "organizationId",
                    "inviterId",
                    "status",
                    "expiresAt",
                    "organizationName",
                    "organizationSlug",
                    "inviterEmail"
                  ]
                }
              }
            }
          }
        }
      }
    }
  },
  async (ctx) => {
    const session = await account.getSessionFromCtx(ctx);
    if (!session) {
      throw new betterCall.APIError("UNAUTHORIZED", {
        message: "Not authenticated"
      });
    }
    const adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);
    const invitation = await adapter.findInvitationById(ctx.query.id);
    if (!invitation || invitation.status !== "pending" || invitation.expiresAt < /* @__PURE__ */ new Date()) {
      throw new betterCall.APIError("BAD_REQUEST", {
        message: "Invitation not found!"
      });
    }
    if (invitation.email.toLowerCase() !== session.user.email.toLowerCase()) {
      throw new betterCall.APIError("FORBIDDEN", {
        message: ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_THE_RECIPIENT_OF_THE_INVITATION
      });
    }
    const organization = await adapter.findOrganizationById(
      invitation.organizationId
    );
    if (!organization) {
      throw new betterCall.APIError("BAD_REQUEST", {
        message: ORGANIZATION_ERROR_CODES.ORGANIZATION_NOT_FOUND
      });
    }
    const member = await adapter.findMemberByOrgId({
      userId: invitation.inviterId,
      organizationId: invitation.organizationId
    });
    if (!member) {
      throw new betterCall.APIError("BAD_REQUEST", {
        message: ORGANIZATION_ERROR_CODES.INVITER_IS_NO_LONGER_A_MEMBER_OF_THE_ORGANIZATION
      });
    }
    return ctx.json({
      ...invitation,
      organizationName: organization.name,
      organizationSlug: organization.slug,
      inviterEmail: member.user.email
    });
  }
);
const listInvitations = account.createAuthEndpoint(
  "/organization/list-invitations",
  {
    method: "GET",
    use: [orgMiddleware, orgSessionMiddleware],
    query: zod.z.object({
      organizationId: zod.z.string({
        description: "The ID of the organization to list invitations for"
      }).optional()
    }).optional()
  },
  async (ctx) => {
    const session = await account.getSessionFromCtx(ctx);
    if (!session) {
      throw new betterCall.APIError("UNAUTHORIZED", {
        message: "Not authenticated"
      });
    }
    const orgId = ctx.query?.organizationId || session.session.activeOrganizationId;
    if (!orgId) {
      throw new betterCall.APIError("BAD_REQUEST", {
        message: "Organization ID is required"
      });
    }
    const adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);
    const isMember = await adapter.findMemberByOrgId({
      userId: session.user.id,
      organizationId: orgId
    });
    if (!isMember) {
      throw new betterCall.APIError("FORBIDDEN", {
        message: "You are not a member of this organization"
      });
    }
    const invitations = await adapter.listInvitations({
      organizationId: orgId
    });
    return ctx.json(invitations);
  }
);

const addMember = () => account.createAuthEndpoint(
  "/organization/add-member",
  {
    method: "POST",
    body: zod.z.object({
      userId: zod.z.coerce.string(),
      role: zod.z.union([zod.z.string(), zod.z.array(zod.z.string())]),
      organizationId: zod.z.string().optional()
    }),
    use: [orgMiddleware],
    metadata: {
      SERVER_ONLY: true,
      $Infer: {
        body: {}
      }
    }
  },
  async (ctx) => {
    const session = ctx.body.userId ? await account.getSessionFromCtx(ctx).catch((e) => null) : null;
    const orgId = ctx.body.organizationId || session?.session.activeOrganizationId;
    if (!orgId) {
      return ctx.json(null, {
        status: 400,
        body: {
          message: ORGANIZATION_ERROR_CODES.NO_ACTIVE_ORGANIZATION
        }
      });
    }
    const teamId = "teamId" in ctx.body ? ctx.body.teamId : void 0;
    if (teamId && !ctx.context.orgOptions.teams?.enabled) {
      ctx.context.logger.error("Teams are not enabled");
      throw new betterCall.APIError("BAD_REQUEST", {
        message: "Teams are not enabled"
      });
    }
    const adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);
    const user = await ctx.context.internalAdapter.findUserById(
      ctx.body.userId
    );
    if (!user) {
      throw new betterCall.APIError("BAD_REQUEST", {
        message: account.BASE_ERROR_CODES.USER_NOT_FOUND
      });
    }
    const alreadyMember = await adapter.findMemberByEmail({
      email: user.email,
      organizationId: orgId
    });
    if (alreadyMember) {
      throw new betterCall.APIError("BAD_REQUEST", {
        message: ORGANIZATION_ERROR_CODES.USER_IS_ALREADY_A_MEMBER_OF_THIS_ORGANIZATION
      });
    }
    if (teamId) {
      const team = await adapter.findTeamById({
        teamId,
        organizationId: orgId
      });
      if (!team || team.organizationId !== orgId) {
        throw new betterCall.APIError("BAD_REQUEST", {
          message: ORGANIZATION_ERROR_CODES.TEAM_NOT_FOUND
        });
      }
    }
    const membershipLimit = ctx.context.orgOptions?.membershipLimit || 100;
    const members = await adapter.listMembers({ organizationId: orgId });
    if (members.length >= membershipLimit) {
      throw new betterCall.APIError("FORBIDDEN", {
        message: ORGANIZATION_ERROR_CODES.ORGANIZATION_MEMBERSHIP_LIMIT_REACHED
      });
    }
    const createdMember = await adapter.createMember({
      organizationId: orgId,
      userId: user.id,
      role: parseRoles(ctx.body.role),
      createdAt: /* @__PURE__ */ new Date(),
      ...teamId ? { teamId } : {}
    });
    return ctx.json(createdMember);
  }
);
const removeMember = account.createAuthEndpoint(
  "/organization/remove-member",
  {
    method: "POST",
    body: zod.z.object({
      memberIdOrEmail: zod.z.string({
        description: "The ID or email of the member to remove"
      }),
      /**
       * If not provided, the active organization will be used
       */
      organizationId: zod.z.string({
        description: "The ID of the organization to remove the member from. If not provided, the active organization will be used"
      }).optional()
    }),
    use: [orgMiddleware, orgSessionMiddleware],
    metadata: {
      openapi: {
        description: "Remove a member from an organization",
        responses: {
          "200": {
            description: "Success",
            content: {
              "application/json": {
                schema: {
                  type: "object",
                  properties: {
                    member: {
                      type: "object",
                      properties: {
                        id: {
                          type: "string"
                        },
                        userId: {
                          type: "string"
                        },
                        organizationId: {
                          type: "string"
                        },
                        role: {
                          type: "string"
                        }
                      },
                      required: ["id", "userId", "organizationId", "role"]
                    }
                  },
                  required: ["member"]
                }
              }
            }
          }
        }
      }
    }
  },
  async (ctx) => {
    const session = ctx.context.session;
    const organizationId = ctx.body.organizationId || session.session.activeOrganizationId;
    if (!organizationId) {
      return ctx.json(null, {
        status: 400,
        body: {
          message: ORGANIZATION_ERROR_CODES.NO_ACTIVE_ORGANIZATION
        }
      });
    }
    const adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);
    const member = await adapter.findMemberByOrgId({
      userId: session.user.id,
      organizationId
    });
    if (!member) {
      throw new betterCall.APIError("BAD_REQUEST", {
        message: ORGANIZATION_ERROR_CODES.MEMBER_NOT_FOUND
      });
    }
    let toBeRemovedMember = null;
    if (ctx.body.memberIdOrEmail.includes("@")) {
      toBeRemovedMember = await adapter.findMemberByEmail({
        email: ctx.body.memberIdOrEmail,
        organizationId
      });
    } else {
      toBeRemovedMember = await adapter.findMemberById(
        ctx.body.memberIdOrEmail
      );
    }
    if (!toBeRemovedMember) {
      throw new betterCall.APIError("BAD_REQUEST", {
        message: ORGANIZATION_ERROR_CODES.MEMBER_NOT_FOUND
      });
    }
    const roles = toBeRemovedMember.role.split(",");
    const creatorRole = ctx.context.orgOptions?.creatorRole || "owner";
    const isOwner = roles.includes(creatorRole);
    if (isOwner) {
      if (member.role !== creatorRole) {
        throw new betterCall.APIError("BAD_REQUEST", {
          message: ORGANIZATION_ERROR_CODES.YOU_CANNOT_LEAVE_THE_ORGANIZATION_AS_THE_ONLY_OWNER
        });
      }
      const members = await adapter.listMembers({
        organizationId
      });
      const owners = members.filter((member2) => {
        const roles2 = member2.role.split(",");
        return roles2.includes(creatorRole);
      });
      if (owners.length <= 1) {
        throw new betterCall.APIError("BAD_REQUEST", {
          message: ORGANIZATION_ERROR_CODES.YOU_CANNOT_LEAVE_THE_ORGANIZATION_AS_THE_ONLY_OWNER
        });
      }
    }
    const canDeleteMember = hasPermission.hasPermission({
      role: member.role,
      options: ctx.context.orgOptions,
      permissions: {
        member: ["delete"]
      }
    });
    if (!canDeleteMember) {
      throw new betterCall.APIError("UNAUTHORIZED", {
        message: ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_MEMBER
      });
    }
    if (toBeRemovedMember?.organizationId !== organizationId) {
      throw new betterCall.APIError("BAD_REQUEST", {
        message: ORGANIZATION_ERROR_CODES.MEMBER_NOT_FOUND
      });
    }
    await adapter.deleteMember(toBeRemovedMember.id);
    if (session.user.id === toBeRemovedMember.userId && session.session.activeOrganizationId === toBeRemovedMember.organizationId) {
      await adapter.setActiveOrganization(session.session.token, null);
    }
    return ctx.json({
      member: toBeRemovedMember
    });
  }
);
const updateMemberRole = (option) => account.createAuthEndpoint(
  "/organization/update-member-role",
  {
    method: "POST",
    body: zod.z.object({
      role: zod.z.union([zod.z.string(), zod.z.array(zod.z.string())]),
      memberId: zod.z.string(),
      organizationId: zod.z.string().optional()
    }),
    use: [orgMiddleware, orgSessionMiddleware],
    metadata: {
      $Infer: {
        body: {}
      },
      openapi: {
        description: "Update the role of a member in an organization",
        responses: {
          "200": {
            description: "Success",
            content: {
              "application/json": {
                schema: {
                  type: "object",
                  properties: {
                    member: {
                      type: "object",
                      properties: {
                        id: {
                          type: "string"
                        },
                        userId: {
                          type: "string"
                        },
                        organizationId: {
                          type: "string"
                        },
                        role: {
                          type: "string"
                        }
                      },
                      required: ["id", "userId", "organizationId", "role"]
                    }
                  },
                  required: ["member"]
                }
              }
            }
          }
        }
      }
    }
  },
  async (ctx) => {
    const session = ctx.context.session;
    if (!ctx.body.role) {
      throw new betterCall.APIError("BAD_REQUEST");
    }
    const organizationId = ctx.body.organizationId || session.session.activeOrganizationId;
    if (!organizationId) {
      throw new betterCall.APIError("BAD_REQUEST", {
        message: ORGANIZATION_ERROR_CODES.NO_ACTIVE_ORGANIZATION
      });
    }
    const adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);
    const roleToSet = Array.isArray(ctx.body.role) ? ctx.body.role : ctx.body.role ? [ctx.body.role] : [];
    const member = await adapter.findMemberByOrgId({
      userId: session.user.id,
      organizationId
    });
    if (!member) {
      throw new betterCall.APIError("BAD_REQUEST", {
        message: ORGANIZATION_ERROR_CODES.MEMBER_NOT_FOUND
      });
    }
    const toBeUpdatedMember = member.id !== ctx.body.memberId ? await adapter.findMemberById(ctx.body.memberId) : member;
    if (!toBeUpdatedMember) {
      throw new betterCall.APIError("BAD_REQUEST", {
        message: ORGANIZATION_ERROR_CODES.MEMBER_NOT_FOUND
      });
    }
    const toBeUpdatedMemberRoles = toBeUpdatedMember.role.split(",");
    const updatingMemberRoles = member.role.split(",");
    const creatorRole = ctx.context.orgOptions?.creatorRole || "owner";
    if (toBeUpdatedMemberRoles.includes(creatorRole) && !updatingMemberRoles.includes(creatorRole) || roleToSet.includes(creatorRole) && !updatingMemberRoles.includes(creatorRole)) {
      throw new betterCall.APIError("FORBIDDEN", {
        message: ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_MEMBER
      });
    }
    const canUpdateMember = hasPermission.hasPermission({
      role: member.role,
      options: ctx.context.orgOptions,
      permissions: {
        member: ["update"]
      }
    });
    if (!canUpdateMember) {
      throw new betterCall.APIError("FORBIDDEN", {
        message: ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_MEMBER
      });
    }
    const updatedMember = await adapter.updateMember(
      ctx.body.memberId,
      parseRoles(ctx.body.role)
    );
    if (!updatedMember) {
      throw new betterCall.APIError("BAD_REQUEST", {
        message: ORGANIZATION_ERROR_CODES.MEMBER_NOT_FOUND
      });
    }
    return ctx.json(updatedMember);
  }
);
const getActiveMember = account.createAuthEndpoint(
  "/organization/get-active-member",
  {
    method: "GET",
    use: [orgMiddleware, orgSessionMiddleware],
    metadata: {
      openapi: {
        description: "Get the member details of the active organization",
        responses: {
          "200": {
            description: "Success",
            content: {
              "application/json": {
                schema: {
                  type: "object",
                  properties: {
                    id: {
                      type: "string"
                    },
                    userId: {
                      type: "string"
                    },
                    organizationId: {
                      type: "string"
                    },
                    role: {
                      type: "string"
                    }
                  },
                  required: ["id", "userId", "organizationId", "role"]
                }
              }
            }
          }
        }
      }
    }
  },
  async (ctx) => {
    const session = ctx.context.session;
    const organizationId = session.session.activeOrganizationId;
    if (!organizationId) {
      return ctx.json(null, {
        status: 400,
        body: {
          message: ORGANIZATION_ERROR_CODES.NO_ACTIVE_ORGANIZATION
        }
      });
    }
    const adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);
    const member = await adapter.findMemberByOrgId({
      userId: session.user.id,
      organizationId
    });
    if (!member) {
      return ctx.json(null, {
        status: 400,
        body: {
          message: ORGANIZATION_ERROR_CODES.MEMBER_NOT_FOUND
        }
      });
    }
    return ctx.json(member);
  }
);
const leaveOrganization = account.createAuthEndpoint(
  "/organization/leave",
  {
    method: "POST",
    body: zod.z.object({
      organizationId: zod.z.string()
    }),
    use: [account.sessionMiddleware, orgMiddleware]
  },
  async (ctx) => {
    const session = ctx.context.session;
    const adapter = getOrgAdapter(ctx.context);
    const member = await adapter.findMemberByOrgId({
      userId: session.user.id,
      organizationId: ctx.body.organizationId
    });
    if (!member) {
      throw new betterCall.APIError("BAD_REQUEST", {
        message: ORGANIZATION_ERROR_CODES.MEMBER_NOT_FOUND
      });
    }
    const isOwnerLeaving = member.role === (ctx.context.orgOptions?.creatorRole || "owner");
    if (isOwnerLeaving) {
      const members = await ctx.context.adapter.findMany({
        model: "member",
        where: [
          {
            field: "organizationId",
            value: ctx.body.organizationId
          }
        ]
      });
      const owners = members.filter(
        (member2) => member2.role === (ctx.context.orgOptions?.creatorRole || "owner")
      );
      if (owners.length <= 1) {
        throw new betterCall.APIError("BAD_REQUEST", {
          message: ORGANIZATION_ERROR_CODES.YOU_CANNOT_LEAVE_THE_ORGANIZATION_AS_THE_ONLY_OWNER
        });
      }
    }
    await adapter.deleteMember(member.id);
    if (session.session.activeOrganizationId === ctx.body.organizationId) {
      await adapter.setActiveOrganization(session.session.token, null);
    }
    return ctx.json(member);
  }
);

const createOrganization = account.createAuthEndpoint(
  "/organization/create",
  {
    method: "POST",
    body: zod.z.object({
      name: zod.z.string({
        description: "The name of the organization"
      }),
      slug: zod.z.string({
        description: "The slug of the organization"
      }),
      userId: zod.z.coerce.string({
        description: "The user id of the organization creator. If not provided, the current user will be used. Should only be used by admins or when called by the server."
      }).optional(),
      logo: zod.z.string({
        description: "The logo of the organization"
      }).optional(),
      metadata: zod.z.record(zod.z.string(), zod.z.any(), {
        description: "The metadata of the organization"
      }).optional(),
      keepCurrentActiveOrganization: zod.z.boolean({
        description: "Whether to keep the current active organization active after creating a new one"
      }).optional()
    }),
    use: [orgMiddleware],
    metadata: {
      openapi: {
        description: "Create an organization",
        responses: {
          "200": {
            description: "Success",
            content: {
              "application/json": {
                schema: {
                  type: "object",
                  description: "The organization that was created",
                  $ref: "#/components/schemas/Organization"
                }
              }
            }
          }
        }
      }
    }
  },
  async (ctx) => {
    const session = await account.getSessionFromCtx(ctx);
    if (!session && (ctx.request || ctx.headers)) {
      throw new betterCall.APIError("UNAUTHORIZED");
    }
    let user = session?.user || null;
    if (!user) {
      if (!ctx.body.userId) {
        throw new betterCall.APIError("UNAUTHORIZED");
      }
      user = await ctx.context.internalAdapter.findUserById(ctx.body.userId);
    }
    if (!user) {
      return ctx.json(null, {
        status: 401
      });
    }
    const options = ctx.context.orgOptions;
    const canCreateOrg = typeof options?.allowUserToCreateOrganization === "function" ? await options.allowUserToCreateOrganization(user) : options?.allowUserToCreateOrganization === void 0 ? true : options.allowUserToCreateOrganization;
    if (!canCreateOrg) {
      throw new betterCall.APIError("FORBIDDEN", {
        message: ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_ORGANIZATION
      });
    }
    const adapter = getOrgAdapter(ctx.context, options);
    const userOrganizations = await adapter.listOrganizations(user.id);
    const hasReachedOrgLimit = typeof options.organizationLimit === "number" ? userOrganizations.length >= options.organizationLimit : typeof options.organizationLimit === "function" ? await options.organizationLimit(user) : false;
    if (hasReachedOrgLimit) {
      throw new betterCall.APIError("FORBIDDEN", {
        message: ORGANIZATION_ERROR_CODES.YOU_HAVE_REACHED_THE_MAXIMUM_NUMBER_OF_ORGANIZATIONS
      });
    }
    const existingOrganization = await adapter.findOrganizationBySlug(
      ctx.body.slug
    );
    if (existingOrganization) {
      throw new betterCall.APIError("BAD_REQUEST", {
        message: ORGANIZATION_ERROR_CODES.ORGANIZATION_ALREADY_EXISTS
      });
    }
    let hookResponse = void 0;
    if (options.organizationCreation?.beforeCreate) {
      const response = await options.organizationCreation.beforeCreate(
        {
          organization: {
            slug: ctx.body.slug,
            name: ctx.body.name,
            logo: ctx.body.logo,
            createdAt: /* @__PURE__ */ new Date(),
            metadata: ctx.body.metadata
          },
          user
        },
        ctx.request
      );
      if (response && typeof response === "object" && "data" in response) {
        hookResponse = response;
      }
    }
    const organization = await adapter.createOrganization({
      organization: {
        slug: ctx.body.slug,
        name: ctx.body.name,
        logo: ctx.body.logo,
        createdAt: /* @__PURE__ */ new Date(),
        metadata: ctx.body.metadata,
        ...hookResponse?.data || {}
      }
    });
    let member;
    if (options?.teams?.enabled && options.teams.defaultTeam?.enabled !== false) {
      const defaultTeam = await options.teams.defaultTeam?.customCreateDefaultTeam?.(
        organization,
        ctx.request
      ) || await adapter.createTeam({
        organizationId: organization.id,
        name: `${organization.name}`,
        createdAt: /* @__PURE__ */ new Date()
      });
      member = await adapter.createMember({
        teamId: defaultTeam.id,
        userId: user.id,
        organizationId: organization.id,
        role: ctx.context.orgOptions.creatorRole || "owner"
      });
    } else {
      member = await adapter.createMember({
        userId: user.id,
        organizationId: organization.id,
        role: ctx.context.orgOptions.creatorRole || "owner"
      });
    }
    if (options.organizationCreation?.afterCreate) {
      await options.organizationCreation.afterCreate(
        {
          organization,
          user,
          member
        },
        ctx.request
      );
    }
    if (ctx.context.session && !ctx.body.keepCurrentActiveOrganization) {
      await adapter.setActiveOrganization(
        ctx.context.session.session.token,
        organization.id
      );
    }
    return ctx.json({
      ...organization,
      metadata: ctx.body.metadata,
      members: [member]
    });
  }
);
const checkOrganizationSlug = account.createAuthEndpoint(
  "/organization/check-slug",
  {
    method: "POST",
    body: zod.z.object({
      slug: zod.z.string()
    }),
    use: [account.requestOnlySessionMiddleware, orgMiddleware]
  },
  async (ctx) => {
    const orgAdapter = getOrgAdapter(ctx.context);
    const org = await orgAdapter.findOrganizationBySlug(ctx.body.slug);
    if (!org) {
      return ctx.json({
        status: true
      });
    }
    throw new betterCall.APIError("BAD_REQUEST", {
      message: "slug is taken"
    });
  }
);
const updateOrganization = account.createAuthEndpoint(
  "/organization/update",
  {
    method: "POST",
    body: zod.z.object({
      data: zod.z.object({
        name: zod.z.string({
          description: "The name of the organization"
        }).optional(),
        slug: zod.z.string({
          description: "The slug of the organization"
        }).optional(),
        logo: zod.z.string({
          description: "The logo of the organization"
        }).optional(),
        metadata: zod.z.record(zod.z.string(), zod.z.any(), {
          description: "The metadata of the organization"
        }).optional()
      }).partial(),
      organizationId: zod.z.string().optional()
    }),
    requireHeaders: true,
    use: [orgMiddleware],
    metadata: {
      openapi: {
        description: "Update an organization",
        responses: {
          "200": {
            description: "Success",
            content: {
              "application/json": {
                schema: {
                  type: "object",
                  description: "The updated organization",
                  $ref: "#/components/schemas/Organization"
                }
              }
            }
          }
        }
      }
    }
  },
  async (ctx) => {
    const session = await ctx.context.getSession(ctx);
    if (!session) {
      throw new betterCall.APIError("UNAUTHORIZED", {
        message: "User not found"
      });
    }
    const organizationId = ctx.body.organizationId || session.session.activeOrganizationId;
    if (!organizationId) {
      throw new betterCall.APIError("BAD_REQUEST", {
        message: ORGANIZATION_ERROR_CODES.ORGANIZATION_NOT_FOUND
      });
    }
    const adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);
    const member = await adapter.findMemberByOrgId({
      userId: session.user.id,
      organizationId
    });
    if (!member) {
      throw new betterCall.APIError("BAD_REQUEST", {
        message: ORGANIZATION_ERROR_CODES.USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION
      });
    }
    const canUpdateOrg = hasPermission.hasPermission({
      permissions: {
        organization: ["update"]
      },
      role: member.role,
      options: ctx.context.orgOptions
    });
    if (!canUpdateOrg) {
      throw new betterCall.APIError("FORBIDDEN", {
        message: ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_ORGANIZATION
      });
    }
    const updatedOrg = await adapter.updateOrganization(
      organizationId,
      ctx.body.data
    );
    return ctx.json(updatedOrg);
  }
);
const deleteOrganization = account.createAuthEndpoint(
  "/organization/delete",
  {
    method: "POST",
    body: zod.z.object({
      organizationId: zod.z.string({
        description: "The organization id to delete"
      })
    }),
    requireHeaders: true,
    use: [orgMiddleware],
    metadata: {
      openapi: {
        description: "Delete an organization",
        responses: {
          "200": {
            description: "Success",
            content: {
              "application/json": {
                schema: {
                  type: "string",
                  description: "The organization id that was deleted"
                }
              }
            }
          }
        }
      }
    }
  },
  async (ctx) => {
    const session = await ctx.context.getSession(ctx);
    if (!session) {
      return ctx.json(null, {
        status: 401
      });
    }
    const organizationId = ctx.body.organizationId;
    if (!organizationId) {
      return ctx.json(null, {
        status: 400,
        body: {
          message: ORGANIZATION_ERROR_CODES.ORGANIZATION_NOT_FOUND
        }
      });
    }
    const adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);
    const member = await adapter.findMemberByOrgId({
      userId: session.user.id,
      organizationId
    });
    if (!member) {
      return ctx.json(null, {
        status: 400,
        body: {
          message: ORGANIZATION_ERROR_CODES.USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION
        }
      });
    }
    const canDeleteOrg = hasPermission.hasPermission({
      role: member.role,
      permissions: {
        organization: ["delete"]
      },
      options: ctx.context.orgOptions
    });
    if (!canDeleteOrg) {
      throw new betterCall.APIError("FORBIDDEN", {
        message: ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_ORGANIZATION
      });
    }
    if (organizationId === session.session.activeOrganizationId) {
      await adapter.setActiveOrganization(session.session.token, null);
    }
    const option = ctx.context.orgOptions.organizationDeletion;
    if (option?.disabled) {
      throw new betterCall.APIError("FORBIDDEN");
    }
    const org = await adapter.findOrganizationById(organizationId);
    if (!org) {
      throw new betterCall.APIError("BAD_REQUEST");
    }
    if (option?.beforeDelete) {
      await option.beforeDelete({
        organization: org,
        user: session.user
      });
    }
    await adapter.deleteOrganization(organizationId);
    if (option?.afterDelete) {
      await option.afterDelete({
        organization: org,
        user: session.user
      });
    }
    return ctx.json(org);
  }
);
const getFullOrganization = () => account.createAuthEndpoint(
  "/organization/get-full-organization",
  {
    method: "GET",
    query: zod.z.optional(
      zod.z.object({
        organizationId: zod.z.string({
          description: "The organization id to get"
        }).optional(),
        organizationSlug: zod.z.string({
          description: "The organization slug to get"
        }).optional()
      })
    ),
    requireHeaders: true,
    use: [orgMiddleware, orgSessionMiddleware],
    metadata: {
      openapi: {
        description: "Get the full organization",
        responses: {
          "200": {
            description: "Success",
            content: {
              "application/json": {
                schema: {
                  type: "object",
                  description: "The organization",
                  $ref: "#/components/schemas/Organization"
                }
              }
            }
          }
        }
      }
    }
  },
  async (ctx) => {
    const session = ctx.context.session;
    const organizationId = ctx.query?.organizationSlug || ctx.query?.organizationId || session.session.activeOrganizationId;
    if (!organizationId) {
      return ctx.json(null, {
        status: 200
      });
    }
    const adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);
    const organization = await adapter.findFullOrganization({
      organizationId,
      isSlug: !!ctx.query?.organizationSlug,
      includeTeams: ctx.context.orgOptions.teams?.enabled
    });
    const isMember = organization?.members.find(
      (member) => member.userId === session.user.id
    );
    if (!isMember) {
      await adapter.setActiveOrganization(session.session.token, null);
      throw new betterCall.APIError("FORBIDDEN", {
        message: ORGANIZATION_ERROR_CODES.USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION
      });
    }
    if (!organization) {
      throw new betterCall.APIError("BAD_REQUEST", {
        message: ORGANIZATION_ERROR_CODES.ORGANIZATION_NOT_FOUND
      });
    }
    return ctx.json(organization);
  }
);
const setActiveOrganization = () => {
  return account.createAuthEndpoint(
    "/organization/set-active",
    {
      method: "POST",
      body: zod.z.object({
        organizationId: zod.z.string({
          description: "The organization id to set as active. It can be null to unset the active organization"
        }).nullable().optional(),
        organizationSlug: zod.z.string({
          description: "The organization slug to set as active. It can be null to unset the active organization if organizationId is not provided"
        }).optional()
      }),
      use: [orgSessionMiddleware, orgMiddleware],
      metadata: {
        openapi: {
          description: "Set the active organization",
          responses: {
            "200": {
              description: "Success",
              content: {
                "application/json": {
                  schema: {
                    type: "object",
                    description: "The organization",
                    $ref: "#/components/schemas/Organization"
                  }
                }
              }
            }
          }
        }
      }
    },
    async (ctx) => {
      const adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);
      const session = ctx.context.session;
      let organizationId = ctx.body.organizationSlug || ctx.body.organizationId;
      if (organizationId === null) {
        const sessionOrgId = session.session.activeOrganizationId;
        if (!sessionOrgId) {
          return ctx.json(null);
        }
        const updatedSession2 = await adapter.setActiveOrganization(
          session.session.token,
          null
        );
        await cookies_index.setSessionCookie(ctx, {
          session: updatedSession2,
          user: session.user
        });
        return ctx.json(null);
      }
      if (!organizationId) {
        const sessionOrgId = session.session.activeOrganizationId;
        if (!sessionOrgId) {
          return ctx.json(null);
        }
        organizationId = sessionOrgId;
      }
      const organization = await adapter.findFullOrganization({
        organizationId,
        isSlug: !!ctx.body.organizationSlug
      });
      const isMember = organization?.members.find(
        (member) => member.userId === session.user.id
      );
      if (!isMember) {
        await adapter.setActiveOrganization(session.session.token, null);
        throw new betterCall.APIError("FORBIDDEN", {
          message: ORGANIZATION_ERROR_CODES.USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION
        });
      }
      if (!organization) {
        throw new betterCall.APIError("BAD_REQUEST", {
          message: ORGANIZATION_ERROR_CODES.ORGANIZATION_NOT_FOUND
        });
      }
      const updatedSession = await adapter.setActiveOrganization(
        session.session.token,
        organization.id
      );
      await cookies_index.setSessionCookie(ctx, {
        session: updatedSession,
        user: session.user
      });
      return ctx.json(organization);
    }
  );
};
const listOrganizations = account.createAuthEndpoint(
  "/organization/list",
  {
    method: "GET",
    use: [orgMiddleware, orgSessionMiddleware],
    metadata: {
      openapi: {
        description: "List all organizations",
        responses: {
          "200": {
            description: "Success",
            content: {
              "application/json": {
                schema: {
                  type: "array",
                  items: {
                    $ref: "#/components/schemas/Organization"
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  async (ctx) => {
    const adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);
    const organizations = await adapter.listOrganizations(
      ctx.context.session.user.id
    );
    return ctx.json(organizations);
  }
);

const role = zod.z.string();
const invitationStatus = zod.z.enum(["pending", "accepted", "rejected", "canceled"]).default("pending");
zod.z.object({
  id: zod.z.string().default(id.generateId),
  name: zod.z.string(),
  slug: zod.z.string(),
  logo: zod.z.string().nullish().optional(),
  metadata: zod.z.record(zod.z.string()).or(zod.z.string().transform((v) => JSON.parse(v))).optional(),
  createdAt: zod.z.date()
});
zod.z.object({
  id: zod.z.string().default(id.generateId),
  organizationId: zod.z.string(),
  userId: zod.z.coerce.string(),
  role,
  createdAt: zod.z.date().default(() => /* @__PURE__ */ new Date()),
  teamId: zod.z.string().optional()
});
zod.z.object({
  id: zod.z.string().default(id.generateId),
  organizationId: zod.z.string(),
  email: zod.z.string(),
  role,
  status: invitationStatus,
  teamId: zod.z.string().optional(),
  inviterId: zod.z.string(),
  expiresAt: zod.z.date()
});
const teamSchema = zod.z.object({
  id: zod.z.string().default(id.generateId),
  name: zod.z.string().min(1),
  organizationId: zod.z.string(),
  createdAt: zod.z.date(),
  updatedAt: zod.z.date().optional()
});

const createTeam = (options) => account.createAuthEndpoint(
  "/organization/create-team",
  {
    method: "POST",
    body: zod.z.object({
      organizationId: zod.z.string().optional(),
      name: zod.z.string()
    }),
    use: [orgMiddleware],
    metadata: {
      openapi: {
        description: "Create a new team within an organization",
        responses: {
          "200": {
            description: "Team created successfully",
            content: {
              "application/json": {
                schema: {
                  type: "object",
                  properties: {
                    id: {
                      type: "string",
                      description: "Unique identifier of the created team"
                    },
                    name: {
                      type: "string",
                      description: "Name of the team"
                    },
                    organizationId: {
                      type: "string",
                      description: "ID of the organization the team belongs to"
                    },
                    createdAt: {
                      type: "string",
                      format: "date-time",
                      description: "Timestamp when the team was created"
                    },
                    updatedAt: {
                      type: "string",
                      format: "date-time",
                      description: "Timestamp when the team was last updated"
                    }
                  },
                  required: [
                    "id",
                    "name",
                    "organizationId",
                    "createdAt",
                    "updatedAt"
                  ]
                }
              }
            }
          }
        }
      }
    }
  },
  async (ctx) => {
    const session = await account.getSessionFromCtx(ctx);
    const organizationId = ctx.body.organizationId || session?.session.activeOrganizationId;
    if (!session && (ctx.request || ctx.headers)) {
      throw new betterCall.APIError("UNAUTHORIZED");
    }
    if (!organizationId) {
      throw new betterCall.APIError("BAD_REQUEST", {
        message: ORGANIZATION_ERROR_CODES.NO_ACTIVE_ORGANIZATION
      });
    }
    const adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);
    if (session) {
      const member = await adapter.findMemberByOrgId({
        userId: session.user.id,
        organizationId
      });
      if (!member) {
        throw new betterCall.APIError("FORBIDDEN", {
          message: ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_INVITE_USERS_TO_THIS_ORGANIZATION
        });
      }
      const canCreate = hasPermission.hasPermission({
        role: member.role,
        options: ctx.context.orgOptions,
        permissions: {
          team: ["create"]
        }
      });
      if (!canCreate) {
        throw new betterCall.APIError("FORBIDDEN", {
          message: ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_CREATE_TEAMS_IN_THIS_ORGANIZATION
        });
      }
    }
    const existingTeams = await adapter.listTeams(organizationId);
    const maximum = typeof ctx.context.orgOptions.teams?.maximumTeams === "function" ? await ctx.context.orgOptions.teams?.maximumTeams(
      {
        organizationId,
        session
      },
      ctx.request
    ) : ctx.context.orgOptions.teams?.maximumTeams;
    const maxTeamsReached = maximum ? existingTeams.length >= maximum : false;
    if (maxTeamsReached) {
      throw new betterCall.APIError("BAD_REQUEST", {
        message: ORGANIZATION_ERROR_CODES.YOU_HAVE_REACHED_THE_MAXIMUM_NUMBER_OF_TEAMS
      });
    }
    const createdTeam = await adapter.createTeam({
      name: ctx.body.name,
      organizationId,
      createdAt: /* @__PURE__ */ new Date(),
      updatedAt: /* @__PURE__ */ new Date()
    });
    return ctx.json(createdTeam);
  }
);
const removeTeam = account.createAuthEndpoint(
  "/organization/remove-team",
  {
    method: "POST",
    body: zod.z.object({
      teamId: zod.z.string(),
      organizationId: zod.z.string().optional()
    }),
    use: [orgMiddleware],
    metadata: {
      openapi: {
        description: "Remove a team from an organization",
        responses: {
          "200": {
            description: "Team removed successfully",
            content: {
              "application/json": {
                schema: {
                  type: "object",
                  properties: {
                    message: {
                      type: "string",
                      description: "Confirmation message indicating successful removal",
                      enum: ["Team removed successfully."]
                    }
                  },
                  required: ["message"]
                }
              }
            }
          }
        }
      }
    }
  },
  async (ctx) => {
    const session = await account.getSessionFromCtx(ctx);
    const organizationId = ctx.body.organizationId || session?.session.activeOrganizationId;
    if (!organizationId) {
      return ctx.json(null, {
        status: 400,
        body: {
          message: ORGANIZATION_ERROR_CODES.NO_ACTIVE_ORGANIZATION
        }
      });
    }
    if (!session && (ctx.request || ctx.headers)) {
      throw new betterCall.APIError("UNAUTHORIZED");
    }
    const adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);
    if (session) {
      const member = await adapter.findMemberByOrgId({
        userId: session.user.id,
        organizationId
      });
      if (!member || member.teamId === ctx.body.teamId) {
        throw new betterCall.APIError("FORBIDDEN", {
          message: ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_TEAM
        });
      }
      const canRemove = hasPermission.hasPermission({
        role: member.role,
        options: ctx.context.orgOptions,
        permissions: {
          team: ["delete"]
        }
      });
      if (!canRemove) {
        throw new betterCall.APIError("FORBIDDEN", {
          message: ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_DELETE_TEAMS_IN_THIS_ORGANIZATION
        });
      }
    }
    const team = await adapter.findTeamById({
      teamId: ctx.body.teamId,
      organizationId
    });
    if (!team || team.organizationId !== organizationId) {
      throw new betterCall.APIError("BAD_REQUEST", {
        message: ORGANIZATION_ERROR_CODES.TEAM_NOT_FOUND
      });
    }
    if (!ctx.context.orgOptions.teams?.allowRemovingAllTeams) {
      const teams = await adapter.listTeams(organizationId);
      if (teams.length <= 1) {
        throw new betterCall.APIError("BAD_REQUEST", {
          message: ORGANIZATION_ERROR_CODES.UNABLE_TO_REMOVE_LAST_TEAM
        });
      }
    }
    await adapter.deleteTeam(team.id);
    return ctx.json({ message: "Team removed successfully." });
  }
);
const updateTeam = account.createAuthEndpoint(
  "/organization/update-team",
  {
    method: "POST",
    body: zod.z.object({
      teamId: zod.z.string(),
      data: teamSchema.partial()
    }),
    use: [orgMiddleware, orgSessionMiddleware],
    metadata: {
      openapi: {
        description: "Update an existing team in an organization",
        responses: {
          "200": {
            description: "Team updated successfully",
            content: {
              "application/json": {
                schema: {
                  type: "object",
                  properties: {
                    id: {
                      type: "string",
                      description: "Unique identifier of the updated team"
                    },
                    name: {
                      type: "string",
                      description: "Updated name of the team"
                    },
                    organizationId: {
                      type: "string",
                      description: "ID of the organization the team belongs to"
                    },
                    createdAt: {
                      type: "string",
                      format: "date-time",
                      description: "Timestamp when the team was created"
                    },
                    updatedAt: {
                      type: "string",
                      format: "date-time",
                      description: "Timestamp when the team was last updated"
                    }
                  },
                  required: [
                    "id",
                    "name",
                    "organizationId",
                    "createdAt",
                    "updatedAt"
                  ]
                }
              }
            }
          }
        }
      }
    }
  },
  async (ctx) => {
    const session = ctx.context.session;
    const organizationId = ctx.body.data.organizationId || session.session.activeOrganizationId;
    if (!organizationId) {
      return ctx.json(null, {
        status: 400,
        body: {
          message: ORGANIZATION_ERROR_CODES.NO_ACTIVE_ORGANIZATION
        }
      });
    }
    const adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);
    const member = await adapter.findMemberByOrgId({
      userId: session.user.id,
      organizationId
    });
    if (!member) {
      throw new betterCall.APIError("FORBIDDEN", {
        message: ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_TEAM
      });
    }
    const canUpdate = hasPermission.hasPermission({
      role: member.role,
      options: ctx.context.orgOptions,
      permissions: {
        team: ["update"]
      }
    });
    if (!canUpdate) {
      throw new betterCall.APIError("FORBIDDEN", {
        message: ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_TEAM
      });
    }
    const team = await adapter.findTeamById({
      teamId: ctx.body.teamId,
      organizationId
    });
    if (!team || team.organizationId !== organizationId) {
      throw new betterCall.APIError("BAD_REQUEST", {
        message: ORGANIZATION_ERROR_CODES.TEAM_NOT_FOUND
      });
    }
    const updatedTeam = await adapter.updateTeam(team.id, {
      name: ctx.body.data.name
    });
    return ctx.json(updatedTeam);
  }
);
const listOrganizationTeams = account.createAuthEndpoint(
  "/organization/list-teams",
  {
    method: "GET",
    query: zod.z.optional(
      zod.z.object({
        organizationId: zod.z.string().optional()
      })
    ),
    metadata: {
      openapi: {
        description: "List all teams in an organization",
        responses: {
          "200": {
            description: "Teams retrieved successfully",
            content: {
              "application/json": {
                schema: {
                  type: "array",
                  items: {
                    type: "object",
                    properties: {
                      id: {
                        type: "string",
                        description: "Unique identifier of the team"
                      },
                      name: {
                        type: "string",
                        description: "Name of the team"
                      },
                      organizationId: {
                        type: "string",
                        description: "ID of the organization the team belongs to"
                      },
                      createdAt: {
                        type: "string",
                        format: "date-time",
                        description: "Timestamp when the team was created"
                      },
                      updatedAt: {
                        type: "string",
                        format: "date-time",
                        description: "Timestamp when the team was last updated"
                      }
                    },
                    required: [
                      "id",
                      "name",
                      "organizationId",
                      "createdAt",
                      "updatedAt"
                    ]
                  },
                  description: "Array of team objects within the organization"
                }
              }
            }
          }
        }
      }
    },
    use: [orgMiddleware, orgSessionMiddleware]
  },
  async (ctx) => {
    const session = ctx.context.session;
    const organizationId = session.session.activeOrganizationId || ctx.query?.organizationId;
    if (!organizationId) {
      return ctx.json(null, {
        status: 400,
        body: {
          message: ORGANIZATION_ERROR_CODES.NO_ACTIVE_ORGANIZATION
        }
      });
    }
    const adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);
    const member = await adapter.findMemberByOrgId({
      userId: session?.user.id,
      organizationId: organizationId || ""
    });
    if (!member) {
      throw new betterCall.APIError("FORBIDDEN");
    }
    const teams = await adapter.listTeams(organizationId);
    return ctx.json(teams);
  }
);

function parseRoles(roles) {
  return Array.isArray(roles) ? roles.join(",") : roles;
}
const organization = (options) => {
  let endpoints = {
    createOrganization,
    updateOrganization,
    deleteOrganization,
    setActiveOrganization: setActiveOrganization(),
    getFullOrganization: getFullOrganization(),
    listOrganizations,
    createInvitation: createInvitation(),
    cancelInvitation,
    acceptInvitation,
    getInvitation,
    rejectInvitation,
    checkOrganizationSlug,
    addMember: addMember(),
    removeMember,
    updateMemberRole: updateMemberRole(),
    getActiveMember,
    leaveOrganization,
    listInvitations
  };
  const teamSupport = options?.teams?.enabled;
  const teamEndpoints = {
    createTeam: createTeam(),
    listOrganizationTeams,
    removeTeam,
    updateTeam
  };
  if (teamSupport) {
    endpoints = {
      ...endpoints,
      ...teamEndpoints
    };
  }
  const roles = {
    ...plugins_organization_access_index.defaultRoles,
    ...options?.roles
  };
  const teamSchema = teamSupport ? {
    team: {
      modelName: options?.schema?.team?.modelName,
      fields: {
        name: {
          type: "string",
          required: true,
          fieldName: options?.schema?.team?.fields?.name
        },
        organizationId: {
          type: "string",
          required: true,
          references: {
            model: "organization",
            field: "id"
          },
          fieldName: options?.schema?.team?.fields?.organizationId
        },
        createdAt: {
          type: "date",
          required: true,
          fieldName: options?.schema?.team?.fields?.createdAt
        },
        updatedAt: {
          type: "date",
          required: false,
          fieldName: options?.schema?.team?.fields?.updatedAt
        }
      }
    }
  } : void 0;
  const api = shimContext(endpoints, {
    orgOptions: options || {},
    roles,
    getSession: async (context) => {
      return await account.getSessionFromCtx(context);
    }
  });
  return {
    id: "organization",
    endpoints: {
      ...api,
      hasPermission: account.createAuthEndpoint(
        "/organization/has-permission",
        {
          method: "POST",
          requireHeaders: true,
          body: zod.z.object({
            organizationId: zod.z.string().optional()
          }).and(
            zod.z.union([
              zod.z.object({
                permission: zod.z.record(zod.z.string(), zod.z.array(zod.z.string())),
                permissions: zod.z.undefined()
              }),
              zod.z.object({
                permission: zod.z.undefined(),
                permissions: zod.z.record(zod.z.string(), zod.z.array(zod.z.string()))
              })
            ])
          ),
          use: [orgSessionMiddleware],
          metadata: {
            $Infer: {
              body: {}
            },
            openapi: {
              description: "Check if the user has permission",
              requestBody: {
                content: {
                  "application/json": {
                    schema: {
                      type: "object",
                      properties: {
                        permission: {
                          type: "object",
                          description: "The permission to check",
                          deprecated: true
                        },
                        permissions: {
                          type: "object",
                          description: "The permission to check"
                        }
                      },
                      required: ["permissions"]
                    }
                  }
                }
              },
              responses: {
                "200": {
                  description: "Success",
                  content: {
                    "application/json": {
                      schema: {
                        type: "object",
                        properties: {
                          error: {
                            type: "string"
                          },
                          success: {
                            type: "boolean"
                          }
                        },
                        required: ["success"]
                      }
                    }
                  }
                }
              }
            }
          }
        },
        async (ctx) => {
          const activeOrganizationId = ctx.body.organizationId || ctx.context.session.session.activeOrganizationId;
          if (!activeOrganizationId) {
            throw new betterCall.APIError("BAD_REQUEST", {
              message: ORGANIZATION_ERROR_CODES.NO_ACTIVE_ORGANIZATION
            });
          }
          const adapter = getOrgAdapter(ctx.context);
          const member = await adapter.findMemberByOrgId({
            userId: ctx.context.session.user.id,
            organizationId: activeOrganizationId
          });
          if (!member) {
            throw new betterCall.APIError("UNAUTHORIZED", {
              message: ORGANIZATION_ERROR_CODES.USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION
            });
          }
          const result = hasPermission.hasPermission({
            role: member.role,
            options: options || {},
            permissions: ctx.body.permissions ?? ctx.body.permission
          });
          return ctx.json({
            error: null,
            success: result
          });
        }
      )
    },
    schema: {
      session: {
        fields: {
          activeOrganizationId: {
            type: "string",
            required: false,
            fieldName: options?.schema?.session?.fields?.activeOrganizationId
          }
        }
      },
      organization: {
        modelName: options?.schema?.organization?.modelName,
        fields: {
          name: {
            type: "string",
            required: true,
            sortable: true,
            fieldName: options?.schema?.organization?.fields?.name
          },
          slug: {
            type: "string",
            unique: true,
            sortable: true,
            fieldName: options?.schema?.organization?.fields?.slug
          },
          logo: {
            type: "string",
            required: false,
            fieldName: options?.schema?.organization?.fields?.logo
          },
          createdAt: {
            type: "date",
            required: true,
            fieldName: options?.schema?.organization?.fields?.createdAt
          },
          metadata: {
            type: "string",
            required: false,
            fieldName: options?.schema?.organization?.fields?.metadata
          }
        }
      },
      member: {
        modelName: options?.schema?.member?.modelName,
        fields: {
          organizationId: {
            type: "string",
            required: true,
            references: {
              model: "organization",
              field: "id"
            },
            fieldName: options?.schema?.member?.fields?.organizationId
          },
          userId: {
            type: "string",
            required: true,
            fieldName: options?.schema?.member?.fields?.userId,
            references: {
              model: "user",
              field: "id"
            }
          },
          role: {
            type: "string",
            required: true,
            sortable: true,
            defaultValue: "member",
            fieldName: options?.schema?.member?.fields?.role
          },
          ...teamSupport ? {
            teamId: {
              type: "string",
              required: false,
              sortable: true,
              fieldName: options?.schema?.member?.fields?.teamId
            }
          } : {},
          createdAt: {
            type: "date",
            required: true,
            fieldName: options?.schema?.member?.fields?.createdAt
          }
        }
      },
      invitation: {
        modelName: options?.schema?.invitation?.modelName,
        fields: {
          organizationId: {
            type: "string",
            required: true,
            references: {
              model: "organization",
              field: "id"
            },
            fieldName: options?.schema?.invitation?.fields?.organizationId
          },
          email: {
            type: "string",
            required: true,
            sortable: true,
            fieldName: options?.schema?.invitation?.fields?.email
          },
          role: {
            type: "string",
            required: false,
            sortable: true,
            fieldName: options?.schema?.invitation?.fields?.role
          },
          ...teamSupport ? {
            teamId: {
              type: "string",
              required: false,
              sortable: true,
              fieldName: options?.schema?.invitation?.fields?.teamId
            }
          } : {},
          status: {
            type: "string",
            required: true,
            sortable: true,
            defaultValue: "pending",
            fieldName: options?.schema?.invitation?.fields?.status
          },
          expiresAt: {
            type: "date",
            required: true,
            fieldName: options?.schema?.invitation?.fields?.expiresAt
          },
          inviterId: {
            type: "string",
            references: {
              model: "user",
              field: "id"
            },
            fieldName: options?.schema?.invitation?.fields?.inviterId,
            required: true
          }
        }
      },
      ...teamSupport ? teamSchema : {}
    },
    $Infer: {
      Organization: {},
      Invitation: {},
      Member: {},
      Team: teamSupport ? {} : {},
      ActiveOrganization: {}
    },
    $ERROR_CODES: ORGANIZATION_ERROR_CODES
  };
};

exports.organization = organization;
exports.parseRoles = parseRoles;