import { NextResponse } from "next/server";
import { z } from "zod";
import { logCrudAccess, makeCrudRoute } from "@open-mercato/shared/lib/crud/factory";
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
import { User, Role, UserRole } from "@open-mercato/core/modules/auth/data/entities";
import { Organization, Tenant } from "@open-mercato/core/modules/directory/data/entities";
import { E } from "../../../../generated/entities.ids.generated.js";
import { loadCustomFieldValues } from "@open-mercato/shared/lib/crud/custom-fields";
import { userCrudEvents, userCrudIndexer } from "@open-mercato/core/modules/auth/commands/users";
import {
  assertActorCanAccessUserTarget,
  assertActorCanGrantRoleTokens,
  assertActorCanModifySuperAdminUserTarget,
  listSuperAdminUserIds
} from "@open-mercato/core/modules/auth/lib/grantChecks";
import { findOneWithDecryption, findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
import { buildPasswordSchema } from "@open-mercato/shared/lib/auth/passwordPolicy";
import { escapeLikePattern } from "@open-mercato/shared/lib/db/escapeLikePattern";
import { resolveSearchConfig } from "@open-mercato/shared/lib/search/config";
import { tokenizeText } from "@open-mercato/shared/lib/search/tokenize";
import { sql } from "kysely";
import { normalizeDisplayNameInput } from "@open-mercato/core/modules/auth/lib/displayName";
import {
  getSelectedTenantFromRequest,
  resolveOrganizationScopeForRequest
} from "@open-mercato/core/modules/directory/utils/organizationScope";
import { createLogger } from "@open-mercato/shared/lib/logger";
const logger = createLogger("auth").child({ component: "users" });
const querySchema = z.object({
  id: z.string().uuid().optional(),
  page: z.coerce.number().min(1).default(1),
  pageSize: z.coerce.number().min(1).max(100).default(50),
  search: z.string().optional(),
  name: z.string().optional(),
  organizationId: z.string().uuid().optional(),
  roleIds: z.array(z.string().uuid()).optional()
}).passthrough();
const rawBodySchema = z.object({}).passthrough();
const passwordSchema = buildPasswordSchema();
const displayNameSchema = z.preprocess(
  normalizeDisplayNameInput,
  z.string().trim().min(1).max(120).nullable().optional()
);
const userCreateSchema = z.object({
  email: z.string().email(),
  name: displayNameSchema,
  password: passwordSchema.optional(),
  sendInviteEmail: z.boolean().optional(),
  organizationId: z.string().uuid(),
  roles: z.array(z.string()).optional()
}).refine(
  (data) => data.password || data.sendInviteEmail,
  { message: "Either password or sendInviteEmail is required", path: ["password"] }
);
const userUpdateSchema = z.object({
  id: z.string().uuid(),
  email: z.string().email().optional(),
  name: displayNameSchema,
  password: passwordSchema.optional(),
  organizationId: z.string().uuid().optional(),
  roles: z.array(z.string()).optional()
});
const userListItemSchema = z.object({
  id: z.string().uuid(),
  email: z.string().email(),
  name: z.string().nullable(),
  organizationId: z.string().uuid().nullable(),
  organizationName: z.string().nullable(),
  tenantId: z.string().uuid().nullable(),
  tenantName: z.string().nullable(),
  roles: z.array(z.string()),
  roleIds: z.array(z.string().uuid()).optional(),
  hasPassword: z.boolean().optional(),
  updatedAt: z.string().nullable().optional()
});
const userListResponseSchema = z.object({
  items: z.array(userListItemSchema),
  total: z.number().int().nonnegative(),
  totalPages: z.number().int().positive(),
  isSuperAdmin: z.boolean().optional()
});
const okResponseSchema = z.object({ ok: z.literal(true) });
const errorResponseSchema = z.object({ error: z.string() });
const routeMetadata = {
  GET: { requireAuth: true, requireFeatures: ["auth.users.list"] },
  POST: { requireAuth: true, requireFeatures: ["auth.users.create"] },
  PUT: { requireAuth: true, requireFeatures: ["auth.users.edit"] },
  DELETE: { requireAuth: true, requireFeatures: ["auth.users.delete"] }
};
const metadata = routeMetadata;
const crud = makeCrudRoute({
  metadata: routeMetadata,
  orm: {
    entity: User,
    idField: "id",
    orgField: null,
    tenantField: null,
    softDeleteField: "deletedAt"
  },
  events: userCrudEvents,
  indexer: userCrudIndexer,
  actions: {
    create: {
      commandId: "auth.users.create",
      schema: rawBodySchema,
      mapInput: async ({ parsed, ctx }) => {
        if (ctx.request) {
          await assertCanAssignRoles(ctx.request, parsed.roles, parsed);
        }
        return parsed;
      },
      response: ({ result }) => ({
        id: String(result.user.id),
        ...result.warning ? { _warning: result.warning } : {}
      }),
      status: 201
    },
    update: {
      commandId: "auth.users.update",
      schema: rawBodySchema,
      mapInput: async ({ parsed, ctx }) => {
        if (ctx.request) {
          if (typeof parsed.id === "string" && parsed.id.length) {
            await assertCanModifySuperAdminTarget(ctx.request, parsed.id);
            await assertCanAccessUserTarget(ctx.request, parsed.id);
          }
          await assertCanAssignRoles(ctx.request, parsed.roles, parsed);
        }
        return parsed;
      },
      response: () => ({ ok: true })
    },
    delete: {
      commandId: "auth.users.delete",
      mapInput: async ({ parsed, raw, ctx }) => {
        const targetId = resolveDeleteTargetId(parsed, raw);
        if (ctx.request && targetId) {
          await assertCanModifySuperAdminTarget(ctx.request, targetId);
          await assertCanAccessUserTarget(ctx.request, targetId);
        }
        return parsed;
      },
      response: () => ({ ok: true })
    }
  }
});
async function GET(req) {
  const auth = await getAuthFromRequest(req);
  if (!auth) return NextResponse.json({ items: [], total: 0, totalPages: 1 });
  const url = new URL(req.url);
  const rawRoleIds = url.searchParams.getAll("roleId").filter((id2) => typeof id2 === "string" && id2.trim().length > 0);
  const parsed = querySchema.safeParse({
    id: url.searchParams.get("id") || void 0,
    page: url.searchParams.get("page") || void 0,
    pageSize: url.searchParams.get("pageSize") || void 0,
    search: url.searchParams.get("search") || void 0,
    name: url.searchParams.get("name") || void 0,
    organizationId: url.searchParams.get("organizationId") || void 0,
    roleIds: rawRoleIds.length ? rawRoleIds : void 0
  });
  if (!parsed.success) return NextResponse.json({ items: [], total: 0, totalPages: 1 });
  const container = await createRequestContainer();
  const em = container.resolve("em");
  let isSuperAdmin = auth.isSuperAdmin === true;
  try {
    if (auth.sub) {
      const rbacService = container.resolve("rbacService");
      const acl = await rbacService.loadAcl(auth.sub, { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null });
      isSuperAdmin = isSuperAdmin || !!acl?.isSuperAdmin;
    }
  } catch (err) {
    logger.error("Failed to resolve rbac", { err });
  }
  const { id, page, pageSize, search, name, organizationId, roleIds } = parsed.data;
  const filters = [{ deletedAt: null }];
  const actorTenantId = auth.tenantId ? String(auth.tenantId) : null;
  let effectiveTenantId = null;
  let effectiveOrganizationIds = null;
  let effectiveSelectedOrganizationId = null;
  let usesSelectedTenantScope = false;
  if (!isSuperAdmin) {
    if (!actorTenantId) {
      return NextResponse.json({ items: [], total: 0, totalPages: 1, isSuperAdmin });
    }
    effectiveTenantId = actorTenantId;
    const superAdminUserIds = await listSuperAdminUserIds(em, actorTenantId);
    if (superAdminUserIds.size) {
      filters.push({ id: { $nin: Array.from(superAdminUserIds) } });
    }
  } else {
    const selectedTenantId = getSelectedTenantFromRequest(req);
    if (typeof selectedTenantId === "string" && selectedTenantId.trim().length > 0) {
      const scope = await resolveOrganizationScopeForRequest({
        container,
        auth,
        request: req,
        tenantId: selectedTenantId.trim()
      });
      if (!scope.tenantId) {
        return NextResponse.json({ items: [], total: 0, totalPages: 1, isSuperAdmin });
      }
      effectiveTenantId = scope.tenantId;
      effectiveSelectedOrganizationId = scope.selectedId;
      usesSelectedTenantScope = true;
      if (Array.isArray(scope.filterIds)) {
        if (scope.filterIds.length === 0) {
          return NextResponse.json({ items: [], total: 0, totalPages: 1, isSuperAdmin });
        }
        effectiveOrganizationIds = scope.filterIds;
      }
    }
  }
  if (effectiveTenantId) {
    filters.push({ tenantId: effectiveTenantId });
  }
  if (effectiveOrganizationIds) {
    filters.push({ organizationId: { $in: effectiveOrganizationIds } });
  }
  const scopeOrganizationId = usesSelectedTenantScope ? effectiveSelectedOrganizationId : auth.orgId ?? null;
  if (organizationId) filters.push({ organizationId });
  const trimmedName = typeof name === "string" ? name.trim() : "";
  if (trimmedName) {
    const searchPattern = `%${escapeLikePattern(trimmedName)}%`;
    const displayNameFilters = [{ name: { $ilike: searchPattern } }];
    const nameTokenScope = isSuperAdmin ? effectiveTenantId ?? void 0 : auth.tenantId ?? null;
    const matchedDisplayNameIds = await findUserIdsBySearchTokens(em, E.auth.user, trimmedName, nameTokenScope, "name");
    if (matchedDisplayNameIds && matchedDisplayNameIds.length) {
      displayNameFilters.push({ id: { $in: matchedDisplayNameIds } });
    }
    filters.push(displayNameFilters.length > 1 ? { $or: displayNameFilters } : displayNameFilters[0]);
  }
  let idFilter = id ? /* @__PURE__ */ new Set([id]) : null;
  if (Array.isArray(roleIds) && roleIds.length > 0) {
    const uniqueRoleIds = Array.from(new Set(roleIds));
    const linksForRoles = await em.find(UserRole, { role: { $in: uniqueRoleIds } });
    const roleUserIds = /* @__PURE__ */ new Set();
    for (const link of linksForRoles) {
      const uid = String(link.user?.id || link.user || "");
      if (uid) roleUserIds.add(uid);
    }
    if (roleUserIds.size === 0) return NextResponse.json({ items: [], total: 0, totalPages: 1 });
    if (idFilter) {
      for (const uid of Array.from(idFilter)) {
        if (!roleUserIds.has(uid)) idFilter.delete(uid);
      }
    } else {
      idFilter = roleUserIds;
    }
    if (!idFilter || idFilter.size === 0) return NextResponse.json({ items: [], total: 0, totalPages: 1 });
  }
  const trimmedSearch = typeof search === "string" ? search.trim() : "";
  if (trimmedSearch) {
    const tenantScope = isSuperAdmin ? effectiveTenantId ?? void 0 : auth.tenantId ?? null;
    const searchFilters = [];
    const matchedIds = await findUserIdsBySearchTokens(em, E.auth.user, trimmedSearch, tenantScope);
    if (matchedIds && matchedIds.length) {
      searchFilters.push({ id: { $in: matchedIds } });
    }
    const searchPattern = `%${escapeLikePattern(trimmedSearch)}%`;
    const organizationSearchFilters = [
      { deletedAt: null },
      { name: { $ilike: searchPattern } }
    ];
    if (tenantScope) {
      organizationSearchFilters.push({ tenant: tenantScope });
    }
    const matchingOrganizations = await em.find(
      Organization,
      organizationSearchFilters.length > 1 ? { $and: organizationSearchFilters } : organizationSearchFilters[0]
    );
    const matchingOrganizationIds = matchingOrganizations.map((org) => org?.id ? String(org.id) : null).filter((orgId) => typeof orgId === "string" && orgId.length > 0);
    if (matchingOrganizationIds.length) {
      searchFilters.push({ organizationId: { $in: matchingOrganizationIds } });
    }
    const roleSearchFilters = [
      { deletedAt: null },
      { name: { $ilike: searchPattern } }
    ];
    if (tenantScope) {
      roleSearchFilters.push({ $or: [{ tenantId: tenantScope }, { tenantId: null }] });
    }
    const matchingRoles = await em.find(
      Role,
      roleSearchFilters.length > 1 ? { $and: roleSearchFilters } : roleSearchFilters[0]
    );
    const matchingRoleIds = matchingRoles.map((role) => role?.id ? String(role.id) : null).filter((roleId) => typeof roleId === "string" && roleId.length > 0);
    if (matchingRoleIds.length) {
      const roleSearchLinks = await em.find(
        UserRole,
        { role: { $in: matchingRoleIds } }
      );
      const matchingRoleUserIds = Array.from(new Set(
        roleSearchLinks.map((link) => {
          const userRef = link.user;
          const userId = userRef?.id ?? userRef;
          return userId ? String(userId) : null;
        }).filter((userId) => typeof userId === "string" && userId.length > 0)
      ));
      if (matchingRoleUserIds.length) {
        searchFilters.push({ id: { $in: matchingRoleUserIds } });
      }
    }
    if (!searchFilters.length) {
      return NextResponse.json({ items: [], total: 0, totalPages: 1, isSuperAdmin });
    }
    filters.push(searchFilters.length > 1 ? { $or: searchFilters } : searchFilters[0]);
  }
  if (idFilter && idFilter.size) {
    filters.push({ id: { $in: Array.from(idFilter) } });
  } else if (id) {
    filters.push({ id });
  }
  const where = filters.length > 1 ? { $and: filters } : filters[0];
  const [rows, count] = await em.findAndCount(User, where, { limit: pageSize, offset: (page - 1) * pageSize });
  const userIds = rows.map((u) => u.id);
  const links = userIds.length ? await findWithDecryption(
    em,
    UserRole,
    { user: { $in: userIds } },
    { populate: ["role"] },
    {
      tenantId: effectiveTenantId ?? auth.tenantId ?? null,
      organizationId: scopeOrganizationId
    }
  ) : [];
  const roleMap = {};
  const roleIdMap = {};
  for (const l of links) {
    const uid = String(l.user?.id || l.user);
    const rname = String(l.role?.name || "");
    const rid = String(l.role?.id ?? "");
    if (!roleMap[uid]) roleMap[uid] = [];
    if (!roleIdMap[uid]) roleIdMap[uid] = [];
    if (rname) roleMap[uid].push(rname);
    if (rid) roleIdMap[uid].push(rid);
  }
  const orgIds = rows.map((u) => u.organizationId ? String(u.organizationId) : null).filter((id2) => !!id2);
  const uniqueOrgIds = Array.from(new Set(orgIds));
  let orgMap = {};
  if (uniqueOrgIds.length) {
    const organizations = await em.find(
      Organization,
      { id: { $in: uniqueOrgIds }, deletedAt: null }
    );
    orgMap = organizations.reduce((acc, org) => {
      const orgId = org?.id ? String(org.id) : null;
      if (!orgId) return acc;
      const rawName = org?.name;
      const orgName = typeof rawName === "string" && rawName.length > 0 ? rawName : orgId;
      acc[orgId] = orgName;
      return acc;
    }, {});
  }
  const tenantIds = rows.map((u) => u.tenantId ? String(u.tenantId) : null).filter((id2) => !!id2);
  const uniqueTenantIds = Array.from(new Set(tenantIds));
  let tenantMap = {};
  if (uniqueTenantIds.length) {
    const tenants = await em.find(
      Tenant,
      { id: { $in: uniqueTenantIds }, deletedAt: null }
    );
    tenantMap = tenants.reduce((acc, tenant) => {
      const tenantId = tenant?.id ? String(tenant.id) : null;
      if (!tenantId) return acc;
      const rawName = tenant?.name;
      const tenantName = typeof rawName === "string" && rawName.length > 0 ? rawName : tenantId;
      acc[tenantId] = tenantName;
      return acc;
    }, {});
  }
  const tenantByUser = {};
  const organizationByUser = {};
  for (const u of rows) {
    const uid = String(u.id);
    tenantByUser[uid] = u.tenantId ? String(u.tenantId) : null;
    organizationByUser[uid] = u.organizationId ? String(u.organizationId) : null;
  }
  const cfByUser = userIds.length ? await loadCustomFieldValues({
    em,
    entityId: E.auth.user,
    recordIds: userIds.map(String),
    tenantIdByRecord: tenantByUser,
    organizationIdByRecord: organizationByUser,
    tenantFallbacks: effectiveTenantId ? [effectiveTenantId] : auth.tenantId ? [auth.tenantId] : []
  }) : {};
  const items = rows.map((u) => {
    const uid = String(u.id);
    const orgId = u.organizationId ? String(u.organizationId) : null;
    return {
      id: uid,
      email: String(u.email),
      name: u.name ? String(u.name) : null,
      organizationId: orgId,
      organizationName: orgId ? orgMap[orgId] ?? orgId : null,
      tenantId: u.tenantId ? String(u.tenantId) : null,
      tenantName: u.tenantId ? tenantMap[String(u.tenantId)] ?? String(u.tenantId) : null,
      roles: roleMap[uid] || [],
      roleIds: roleIdMap[uid] || [],
      ...id ? { hasPassword: !!u.passwordHash } : {},
      updatedAt: u.updatedAt instanceof Date ? u.updatedAt.toISOString() : null,
      ...cfByUser[uid] || {}
    };
  });
  const totalPages = Math.max(1, Math.ceil(count / pageSize));
  await logCrudAccess({
    container,
    auth,
    request: req,
    items,
    idField: "id",
    resourceKind: "auth.user",
    organizationId: effectiveSelectedOrganizationId,
    tenantId: effectiveTenantId ?? auth.tenantId ?? null,
    query: parsed.data,
    accessType: id ? "read:item" : void 0
  });
  return NextResponse.json({ items, total: count, totalPages, isSuperAdmin });
}
const POST = async (req) => {
  return crud.POST(req);
};
const PUT = async (req) => {
  return crud.PUT(req);
};
const DELETE = async (req) => {
  const targetId = new URL(req.url).searchParams.get("id");
  if (targetId) {
    try {
      await assertCanModifySuperAdminTarget(req, targetId);
      await assertCanAccessUserTarget(req, targetId);
    } catch (err) {
      if (err instanceof CrudHttpError) {
        return NextResponse.json(err.body, { status: err.status });
      }
      throw err;
    }
  }
  return crud.DELETE(req);
};
async function findUserIdsBySearchTokens(em, entityType, search, tenantScope, field) {
  const trimmed = search.trim();
  if (!trimmed) return null;
  const searchConfig = resolveSearchConfig();
  if (!searchConfig.enabled) return [];
  const { hashes } = tokenizeText(trimmed, searchConfig);
  if (!hashes.length) return [];
  const db = em.getKysely();
  let query = db.selectFrom("search_tokens").select("entity_id").where("entity_type", "=", entityType).where("token_hash", "in", hashes).groupBy("entity_id").having(sql`count(distinct token_hash) >= ${hashes.length}`);
  if (field) {
    query = query.where("field", "=", field);
  }
  if (tenantScope !== void 0) {
    query = query.where(sql`tenant_id is not distinct from ${tenantScope}`);
  }
  const rows = await query.execute();
  return rows.map((row) => typeof row.entity_id === "string" ? row.entity_id : null).filter((id) => typeof id === "string" && id.length > 0);
}
async function assertCanModifySuperAdminTarget(req, targetUserId) {
  const auth = await getAuthFromRequest(req);
  if (!auth?.sub) throw new CrudHttpError(401, { error: "Unauthorized" });
  const container = await createRequestContainer();
  const em = container.resolve("em");
  await assertActorCanModifySuperAdminUserTarget({
    em,
    rbacService: container.resolve("rbacService"),
    actorUserId: auth.sub,
    tenantId: auth.tenantId ?? null,
    organizationId: auth.orgId ?? null,
    targetUserId
  });
}
async function assertCanAccessUserTarget(req, targetUserId) {
  const auth = await getAuthFromRequest(req);
  if (!auth?.sub) throw new CrudHttpError(401, { error: "Unauthorized" });
  const container = await createRequestContainer();
  const em = container.resolve("em");
  await assertActorCanAccessUserTarget({
    em,
    rbacService: container.resolve("rbacService"),
    actorUserId: auth.sub,
    tenantId: auth.tenantId ?? null,
    organizationId: auth.orgId ?? null,
    targetUserId
  });
}
function resolveDeleteTargetId(parsed, raw) {
  const fromParsed = readId(parsed);
  if (fromParsed) return fromParsed;
  const rawRecord = raw;
  return readId(rawRecord?.query) ?? readId(rawRecord?.body);
}
function readId(record) {
  const value = record?.id;
  return typeof value === "string" && value.length > 0 ? value : null;
}
async function assertCanAssignRoles(req, roles, payload) {
  if (!Array.isArray(roles)) return;
  const auth = await getAuthFromRequest(req);
  if (!auth?.sub) throw new CrudHttpError(401, { error: "Unauthorized" });
  const container = await createRequestContainer();
  const em = container.resolve("em");
  const tenantId = await resolveTargetTenantIdForRoleGrant(em, payload, auth.tenantId ?? null);
  await assertActorCanGrantRoleTokens({
    em,
    rbacService: container.resolve("rbacService"),
    actorUserId: auth.sub,
    tenantId,
    organizationId: auth.orgId ?? null,
    roleTokens: roles
  });
}
async function resolveTargetTenantIdForRoleGrant(em, payload, fallbackTenantId) {
  const organizationId = typeof payload.organizationId === "string" ? payload.organizationId : null;
  if (organizationId) {
    const organization = await findOneWithDecryption(
      em,
      Organization,
      { id: organizationId },
      { populate: ["tenant"] },
      { tenantId: null, organizationId }
    );
    return organization?.tenant?.id ? String(organization.tenant.id) : fallbackTenantId;
  }
  const userId = typeof payload.id === "string" ? payload.id : null;
  if (userId) {
    const user = await findOneWithDecryption(
      em,
      User,
      { id: userId, deletedAt: null },
      {},
      { tenantId: null, organizationId: null }
    );
    return user?.tenantId ? String(user.tenantId) : fallbackTenantId;
  }
  return fallbackTenantId;
}
const openApi = {
  tag: "Authentication & Accounts",
  summary: "User management",
  methods: {
    GET: {
      summary: "List users",
      description: "Returns users for the effective selected tenant and organization scope. Search matches email, organization name, and role name. Super administrators may scope the response via the topbar context, organization filters, or role filters.",
      query: querySchema,
      responses: [
        { status: 200, description: "User collection", schema: userListResponseSchema }
      ]
    },
    POST: {
      summary: "Create user",
      description: "Creates a new confirmed user within the specified organization, optional display name, and optional roles.",
      requestBody: {
        contentType: "application/json",
        schema: userCreateSchema
      },
      responses: [
        {
          status: 201,
          description: "User created",
          schema: z.object({ id: z.string().uuid() })
        }
      ],
      errors: [
        { status: 400, description: "Invalid payload or duplicate email", schema: errorResponseSchema },
        { status: 401, description: "Unauthorized", schema: errorResponseSchema },
        { status: 403, description: "Attempted to assign privileged roles", schema: errorResponseSchema }
      ]
    },
    PUT: {
      summary: "Update user",
      description: "Updates profile fields including display name, organization assignment, credentials, or role memberships.",
      requestBody: {
        contentType: "application/json",
        schema: userUpdateSchema
      },
      responses: [
        { status: 200, description: "User updated", schema: okResponseSchema }
      ],
      errors: [
        { status: 400, description: "Invalid payload", schema: errorResponseSchema },
        { status: 401, description: "Unauthorized", schema: errorResponseSchema },
        { status: 403, description: "Attempted to assign privileged roles", schema: errorResponseSchema },
        { status: 404, description: "User not found", schema: errorResponseSchema }
      ]
    },
    DELETE: {
      summary: "Delete user",
      description: "Deletes a user by identifier. Undo support is provided via the command bus.",
      query: z.object({ id: z.string().uuid().describe("User identifier") }),
      responses: [
        { status: 200, description: "User deleted", schema: okResponseSchema }
      ],
      errors: [
        { status: 400, description: "User cannot be deleted", schema: errorResponseSchema },
        { status: 401, description: "Unauthorized", schema: errorResponseSchema },
        { status: 404, description: "User not found", schema: errorResponseSchema }
      ]
    }
  }
};
export {
  DELETE,
  GET,
  POST,
  PUT,
  metadata,
  openApi
};
//# sourceMappingURL=route.js.map
