Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
7518cd9
Upgrade `next-axiom` + add logRequestDetails: "body"
steven-tey Nov 6, 2025
d7d50dc
Refactor to `@axiomhq/nextjs`
steven-tey Nov 6, 2025
528b2a9
missed a few spots
steven-tey Nov 6, 2025
496fa6f
Pass body and searchParams
devkiran Nov 7, 2025
0454044
Merge branch 'main' into upgrade-axiom
devkiran Nov 8, 2025
7a3f902
Update route.ts
devkiran Nov 8, 2025
f811cd9
Update workspace.ts
devkiran Nov 8, 2025
02868c9
rename the files
devkiran Nov 8, 2025
b681222
Send error logs as well
devkiran Nov 8, 2025
e6a5ae5
Update errors.ts
devkiran Nov 8, 2025
11a5370
remove @axiomhq/react"
devkiran Nov 8, 2025
f60a521
Update vitest.config.ts
devkiran Nov 8, 2025
4b6493f
Update vitest.config.ts
devkiran Nov 9, 2025
109a0b7
bump vite
devkiran Nov 9, 2025
ed0abda
Update pnpm-lock.yaml
devkiran Nov 9, 2025
126b6ef
Update server.ts
devkiran Nov 9, 2025
6b4d4e1
Update setupTests.ts
devkiran Nov 9, 2025
24ccb90
fix the tests
devkiran Nov 9, 2025
e575512
Refactor clientAccessCheck import paths across multiple components to…
devkiran Nov 9, 2025
64f76e9
Refactor exceededLimitError import paths to a new centralized locatio…
devkiran Nov 9, 2025
a6a0434
Refactor error handling and permission checks by centralizing error c…
devkiran Nov 9, 2025
58c9012
Replace validDateRangeForPlan with assertValidDateRangeForPlan in mul…
devkiran Nov 9, 2025
0973731
Refactor permission handling by updating import paths to use the new …
devkiran Nov 9, 2025
0f1fb5b
fix imports
devkiran Nov 9, 2025
bf70e20
fix a few more imports
devkiran Nov 9, 2025
39dbe94
Update safe-action.ts
devkiran Nov 9, 2025
eb32792
Merge branch 'main' into upgrade-axiom
devkiran Nov 9, 2025
d60dc15
await the promise
devkiran Nov 9, 2025
9ea8043
Update safe-action.ts
devkiran Nov 9, 2025
8e6f0e5
Update server.ts
devkiran Nov 9, 2025
5d30843
Merge branch 'main' into upgrade-axiom
steven-tey Nov 10, 2025
5078f49
Merge branch 'main' into upgrade-axiom
steven-tey Nov 11, 2025
7f114db
handleAndReturnErrorResponse
steven-tey Nov 11, 2025
afe9c31
Merge branch 'main' into upgrade-axiom
steven-tey Nov 11, 2025
f34bab2
Merge branch 'main' into upgrade-axiom
steven-tey Nov 11, 2025
8ea307b
rename env vars
steven-tey Nov 11, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { handleApiError } from "@/lib/api/errors";
import { handleAndReturnErrorResponse } from "@/lib/api/errors";
import { PROGRAM_SIMILARITY_SCORE_THRESHOLD } from "@/lib/constants/program";
import { qstash } from "@/lib/cron";
import { verifyQstashSignature } from "@/lib/cron/verify-qstash";
Expand Down Expand Up @@ -32,8 +32,7 @@ export async function GET(req: Request) {

return await calculateProgramSimilarity();
} catch (err) {
const { error, status } = handleApiError(err);
return logAndRespond(error.message, { status });
return handleAndReturnErrorResponse(err);
}
}

Expand All @@ -56,8 +55,7 @@ export async function POST(req: Request) {
comparisonBatchCursor,
});
} catch (err) {
const { error, status } = handleApiError(err);
return logAndRespond(error.message, { status });
return handleAndReturnErrorResponse(err);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { recordAuditLog } from "@/lib/api/audit-logs/record-audit-log";
import { exceededLimitError } from "@/lib/api/errors";
import { getEffectivePayoutMode } from "@/lib/api/payouts/get-effective-payout-mode";
import { getPayoutEligibilityFilter } from "@/lib/api/payouts/payout-eligibility-filter";
import {
Expand All @@ -8,6 +7,7 @@ import {
FOREX_MARKUP_RATE,
} from "@/lib/constants/payouts";
import { queueBatchEmail } from "@/lib/email/queue-batch-email";
import { exceededLimitError } from "@/lib/exceeded-limit-error";
import {
CUTOFF_PERIOD,
CUTOFF_PERIOD_TYPES,
Expand Down
6 changes: 3 additions & 3 deletions apps/web/app/(ee)/api/events/export/route.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { getEvents } from "@/lib/analytics/get-events";
import { getFolderIdsToFilter } from "@/lib/analytics/get-folder-ids-to-filter";
import { convertToCSV, validDateRangeForPlan } from "@/lib/analytics/utils";
import { convertToCSV } from "@/lib/analytics/utils";
import { getDomainOrThrow } from "@/lib/api/domains/get-domain-or-throw";
import { getLinkOrThrow } from "@/lib/api/links/get-link-or-throw";
import { throwIfClicksUsageExceeded } from "@/lib/api/links/usage-checks";
import { assertValidDateRangeForPlan } from "@/lib/api/utils/assert-valid-date-range-for-plan";
import { withWorkspace } from "@/lib/auth";
import { verifyFolderAccess } from "@/lib/folder/permissions";
import { ClickEvent, LeadEvent, SaleEvent } from "@/lib/types";
Expand Down Expand Up @@ -79,13 +80,12 @@ export const GET = withWorkspace(
});
}

validDateRangeForPlan({
assertValidDateRangeForPlan({
plan: workspace.plan,
dataAvailableFrom: workspace.createdAt,
interval,
start,
end,
throwError: true,
});

const folderIds = folderIdToVerify
Expand Down
5 changes: 2 additions & 3 deletions apps/web/app/(ee)/api/events/route.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { getEvents } from "@/lib/analytics/get-events";
import { getFolderIdsToFilter } from "@/lib/analytics/get-folder-ids-to-filter";
import { validDateRangeForPlan } from "@/lib/analytics/utils";
import { getDomainOrThrow } from "@/lib/api/domains/get-domain-or-throw";
import { getLinkOrThrow } from "@/lib/api/links/get-link-or-throw";
import { throwIfClicksUsageExceeded } from "@/lib/api/links/usage-checks";
import { assertValidDateRangeForPlan } from "@/lib/api/utils/assert-valid-date-range-for-plan";
import { withWorkspace } from "@/lib/auth";
import { verifyFolderAccess } from "@/lib/folder/permissions";
import { eventsQuerySchema } from "@/lib/zod/schemas/analytics";
Expand Down Expand Up @@ -57,13 +57,12 @@ export const GET = withWorkspace(
});
}

validDateRangeForPlan({
assertValidDateRangeForPlan({
plan: workspace.plan,
dataAvailableFrom: workspace.createdAt,
interval,
start,
end,
throwError: true,
});

const folderIds = folderIdToVerify
Expand Down
3 changes: 2 additions & 1 deletion apps/web/app/(ee)/api/groups/route.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { recordAuditLog } from "@/lib/api/audit-logs/record-audit-log";
import { createId } from "@/lib/api/create-id";
import { DubApiError, exceededLimitError } from "@/lib/api/errors";
import { DubApiError } from "@/lib/api/errors";
import { getGroups } from "@/lib/api/groups/get-groups";
import { getDefaultProgramIdOrThrow } from "@/lib/api/programs/get-default-program-id-or-throw";
import { parseRequestBody } from "@/lib/api/utils";
import { withWorkspace } from "@/lib/auth";
import { exceededLimitError } from "@/lib/exceeded-limit-error";
import {
createGroupSchema,
DEFAULT_PARTNER_GROUP,
Expand Down
4 changes: 2 additions & 2 deletions apps/web/app/(ee)/api/hubspot/webhook/route.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { DubApiError, handleAndReturnErrorResponse } from "@/lib/api/errors";
import { withAxiom } from "@/lib/axiom/server";
import { hubSpotOAuthProvider } from "@/lib/integrations/hubspot/oauth";
import {
hubSpotSettingsSchema,
Expand All @@ -8,13 +9,12 @@ import { trackHubSpotLeadEvent } from "@/lib/integrations/hubspot/track-lead";
import { trackHubSpotSaleEvent } from "@/lib/integrations/hubspot/track-sale";
import { prisma } from "@dub/prisma";
import crypto from "crypto";
import { AxiomRequest, withAxiom } from "next-axiom";
import { NextResponse } from "next/server";

const HUBSPOT_CLIENT_SECRET = process.env.HUBSPOT_CLIENT_SECRET || "";

// POST /api/hubspot/webhook – listen to webhook events from Hubspot
export const POST = withAxiom(async (req: AxiomRequest) => {
export const POST = withAxiom(async (req) => {
try {
const rawPayload = await req.text();
const signature = req.headers.get("X-HubSpot-Signature");
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/(ee)/api/partner-profile/users/route.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { DubApiError } from "@/lib/api/errors";
import { parseRequestBody } from "@/lib/api/utils";
import { withPartnerProfile } from "@/lib/auth/partner";
import { throwIfNoPermission } from "@/lib/auth/partner-user-permissions";
import { throwIfNoPermission } from "@/lib/auth/partner-users/throw-if-no-permission";
import {
getPartnerUsersQuerySchema,
partnerUserSchema,
Expand Down
4 changes: 2 additions & 2 deletions apps/web/app/(ee)/api/singular/webhook/route.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { DubApiError, handleAndReturnErrorResponse } from "@/lib/api/errors";
import { normalizeWorkspaceId } from "@/lib/api/workspaces/workspace-id";
import { withAxiom } from "@/lib/axiom/server";
import { trackSingularLeadEvent } from "@/lib/integrations/singular/track-lead";
import { trackSingularSaleEvent } from "@/lib/integrations/singular/track-sale";
import { prisma } from "@dub/prisma";
import { getSearchParams } from "@dub/utils";
import { AxiomRequest, withAxiom } from "next-axiom";
import { NextResponse } from "next/server";
import { z } from "zod";

Expand Down Expand Up @@ -37,7 +37,7 @@ const authSchema = z.object({
const singularWebhookToken = process.env.SINGULAR_WEBHOOK_TOKEN;

// GET /api/singular/webhook – listen to Postback events from Singular
export const GET = withAxiom(async (req: AxiomRequest) => {
export const GET = withAxiom(async (req) => {
try {
if (!singularWebhookToken) {
throw new DubApiError({
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/(ee)/api/stripe/integration/webhook/route.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { withAxiom } from "@/lib/axiom/server";
import { stripe } from "@/lib/stripe";
import { StripeMode } from "@/lib/types";
import { logAndRespond } from "app/(ee)/api/cron/utils";
import { withAxiom } from "next-axiom";
import Stripe from "stripe";
import { accountApplicationDeauthorized } from "./account-application-deauthorized";
import { chargeRefunded } from "./charge-refunded";
Expand Down
4 changes: 2 additions & 2 deletions apps/web/app/(ee)/api/track/click/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { DubApiError, handleAndReturnErrorResponse } from "@/lib/api/errors";
import { linkCache } from "@/lib/api/links/cache";
import { recordClickCache } from "@/lib/api/links/record-click-cache";
import { parseRequestBody } from "@/lib/api/utils";
import { withAxiom } from "@/lib/axiom/server";
import { getIdentityHash } from "@/lib/middleware/utils/get-identity-hash";
import { getWorkspaceViaEdge } from "@/lib/planetscale";
import { getLinkWithPartner } from "@/lib/planetscale/get-link-with-partner";
Expand All @@ -18,7 +19,6 @@ import { DiscountSchema } from "@/lib/zod/schemas/discount";
import { PartnerSchema } from "@/lib/zod/schemas/partners";
import { isValidUrl, nanoid } from "@dub/utils";
import { waitUntil } from "@vercel/functions";
import { AxiomRequest, withAxiom } from "next-axiom";
import { NextResponse } from "next/server";
import { z } from "zod";

Expand Down Expand Up @@ -47,7 +47,7 @@ const trackClickResponseSchema = z.object({
});

// POST /api/track/click – Track a click event for a link
export const POST = withAxiom(async (req: AxiomRequest) => {
export const POST = withAxiom(async (req) => {
try {
const { domain, key, url, referrer } = trackClickSchema.parse(
await parseRequestBody(req),
Expand Down
4 changes: 2 additions & 2 deletions apps/web/app/(ee)/api/track/open/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { DubApiError, handleAndReturnErrorResponse } from "@/lib/api/errors";
import { linkCache } from "@/lib/api/links/cache";
import { recordClickCache } from "@/lib/api/links/record-click-cache";
import { parseRequestBody } from "@/lib/api/utils";
import { withAxiom } from "@/lib/axiom/server";
import { getIdentityHash } from "@/lib/middleware/utils";
import { DeepLinkClickData } from "@/lib/middleware/utils/cache-deeplink-click-data";
import { getLinkViaEdge } from "@/lib/planetscale";
Expand All @@ -15,11 +16,10 @@ import {
} from "@/lib/zod/schemas/opens";
import { LOCALHOST_IP, nanoid } from "@dub/utils";
import { ipAddress, waitUntil } from "@vercel/functions";
import { AxiomRequest, withAxiom } from "next-axiom";
import { NextResponse } from "next/server";

// POST /api/track/open – Track an open event for deep link
export const POST = withAxiom(async (req: AxiomRequest) => {
export const POST = withAxiom(async (req) => {
try {
const { deepLink: deepLinkUrl, dubDomain } = trackOpenRequestSchema.parse(
await parseRequestBody(req),
Expand Down
5 changes: 3 additions & 2 deletions apps/web/app/(ee)/api/track/visit/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,20 @@ import { DubApiError, handleAndReturnErrorResponse } from "@/lib/api/errors";
import { linkCache } from "@/lib/api/links/cache";
import { recordClickCache } from "@/lib/api/links/record-click-cache";
import { parseRequestBody } from "@/lib/api/utils";
import { withAxiom } from "@/lib/axiom/server";
import { getIdentityHash } from "@/lib/middleware/utils";
import { getLinkViaEdge, getWorkspaceViaEdge } from "@/lib/planetscale";
import { recordClick } from "@/lib/tinybird";
import { RedisLinkProps } from "@/lib/types";
import { formatRedisLink, redis } from "@/lib/upstash";
import { isValidUrl, nanoid } from "@dub/utils";
import { waitUntil } from "@vercel/functions";
import { AxiomRequest, withAxiom } from "next-axiom";
import { NextResponse } from "next/server";

export const runtime = "edge";

// POST /api/track/visit – Track a visit event from the client-side
export const POST = withAxiom(async (req: AxiomRequest) => {
export const POST = withAxiom(async (req) => {
try {
const { domain, url, referrer } = await parseRequestBody(req);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { hasPermission } from "@/lib/auth/partner-user-permissions";
import { hasPermission } from "@/lib/auth/partner-users/partner-user-permissions";
import usePartnerProfile from "@/lib/swr/use-partner-profile";
import { ConnectPayoutButton } from "@/ui/partners/connect-payout-button";
import { Button } from "@dub/ui";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { updatePartnerProfileAction } from "@/lib/actions/partners/update-partner-profile";
import { hasPermission } from "@/lib/auth/partner-user-permissions";
import { hasPermission } from "@/lib/auth/partner-users/partner-user-permissions";
import {
industryInterests,
monthlyTrafficAmounts,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { updatePartnerProfileAction } from "@/lib/actions/partners/update-partner-profile";
import { hasPermission } from "@/lib/auth/partner-user-permissions";
import { hasPermission } from "@/lib/auth/partner-users/partner-user-permissions";
import { PartnerProps } from "@/lib/types";
import { Button, Check2 } from "@dub/ui";
import { useAction } from "next-safe-action/hooks";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";

import { updatePartnerProfileAction } from "@/lib/actions/partners/update-partner-profile";
import { hasPermission } from "@/lib/auth/partner-user-permissions";
import { hasPermission } from "@/lib/auth/partner-users/partner-user-permissions";
import usePartnerProfile from "@/lib/swr/use-partner-profile";
import { PartnerProps } from "@/lib/types";
import { PageContent } from "@/ui/layout/page-content";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { updatePartnerProfileAction } from "@/lib/actions/partners/update-partner-profile";
import { hasPermission } from "@/lib/auth/partner-user-permissions";
import { hasPermission } from "@/lib/auth/partner-users/partner-user-permissions";
import { mutatePrefix } from "@/lib/swr/mutate";
import { PartnerProps } from "@/lib/types";
import { useConfirmModal } from "@/ui/modals/confirm-modal";
Expand Down
12 changes: 4 additions & 8 deletions apps/web/app/api/analytics/dashboard/route.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { getAnalytics } from "@/lib/analytics/get-analytics";
import { validDateRangeForPlan } from "@/lib/analytics/utils";
import {
DubApiError,
exceededLimitError,
handleAndReturnErrorResponse,
} from "@/lib/api/errors";
import { DubApiError, handleAndReturnErrorResponse } from "@/lib/api/errors";
import { assertValidDateRangeForPlan } from "@/lib/api/utils/assert-valid-date-range-for-plan";
import { exceededLimitError } from "@/lib/exceeded-limit-error";
import { PlanProps } from "@/lib/types";
import { ratelimit } from "@/lib/upstash";
import { analyticsQuerySchema } from "@/lib/zod/schemas/analytics";
Expand Down Expand Up @@ -71,13 +68,12 @@ export const GET = async (req: Request) => {

const workspace = link.project;

validDateRangeForPlan({
assertValidDateRangeForPlan({
plan: workspace?.plan || "free",
dataAvailableFrom: workspace?.createdAt,
interval,
start,
end,
throwError: true,
});

if (workspace && workspace.usage > workspace.usageLimit) {
Expand Down
6 changes: 3 additions & 3 deletions apps/web/app/api/analytics/export/route.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { VALID_ANALYTICS_ENDPOINTS } from "@/lib/analytics/constants";
import { getAnalytics } from "@/lib/analytics/get-analytics";
import { getFolderIdsToFilter } from "@/lib/analytics/get-folder-ids-to-filter";
import { convertToCSV, validDateRangeForPlan } from "@/lib/analytics/utils";
import { convertToCSV } from "@/lib/analytics/utils";
import { getDomainOrThrow } from "@/lib/api/domains/get-domain-or-throw";
import { getLinkOrThrow } from "@/lib/api/links/get-link-or-throw";
import { throwIfClicksUsageExceeded } from "@/lib/api/links/usage-checks";
import { assertValidDateRangeForPlan } from "@/lib/api/utils/assert-valid-date-range-for-plan";
import { withWorkspace } from "@/lib/auth";
import { verifyFolderAccess } from "@/lib/folder/permissions";
import { analyticsQuerySchema } from "@/lib/zod/schemas/analytics";
Expand Down Expand Up @@ -48,13 +49,12 @@ export const GET = withWorkspace(
});
}

validDateRangeForPlan({
assertValidDateRangeForPlan({
plan: workspace.plan,
dataAvailableFrom: workspace.createdAt,
interval,
start,
end,
throwError: true,
});

const folderIds = folderIdToVerify
Expand Down
5 changes: 2 additions & 3 deletions apps/web/app/api/analytics/route.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { VALID_ANALYTICS_ENDPOINTS } from "@/lib/analytics/constants";
import { getAnalytics } from "@/lib/analytics/get-analytics";
import { getFolderIdsToFilter } from "@/lib/analytics/get-folder-ids-to-filter";
import { validDateRangeForPlan } from "@/lib/analytics/utils";
import { getDomainOrThrow } from "@/lib/api/domains/get-domain-or-throw";
import { DubApiError } from "@/lib/api/errors";
import { getLinkOrThrow } from "@/lib/api/links/get-link-or-throw";
import { throwIfClicksUsageExceeded } from "@/lib/api/links/usage-checks";
import { getDefaultProgramIdOrThrow } from "@/lib/api/programs/get-default-program-id-or-throw";
import { assertValidDateRangeForPlan } from "@/lib/api/utils/assert-valid-date-range-for-plan";
import { prefixWorkspaceId } from "@/lib/api/workspaces/workspace-id";
import { withWorkspace } from "@/lib/auth";
import { verifyFolderAccess } from "@/lib/folder/permissions";
Expand Down Expand Up @@ -88,13 +88,12 @@ export const GET = withWorkspace(
});
}

validDateRangeForPlan({
assertValidDateRangeForPlan({
plan: workspace.plan,
dataAvailableFrom: workspace.createdAt,
interval,
start,
end,
throwError: true,
});

// no need to get folder ids if we are filtering by a folder or program
Expand Down
3 changes: 2 additions & 1 deletion apps/web/app/api/domains/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import { createId } from "@/lib/api/create-id";
import { addDomainToVercel } from "@/lib/api/domains/add-domain-vercel";
import { transformDomain } from "@/lib/api/domains/transform-domain";
import { validateDomain } from "@/lib/api/domains/utils";
import { DubApiError, exceededLimitError } from "@/lib/api/errors";
import { DubApiError } from "@/lib/api/errors";
import { createLink, transformLink } from "@/lib/api/links";
import { parseRequestBody } from "@/lib/api/utils";
import { withWorkspace } from "@/lib/auth";
import { exceededLimitError } from "@/lib/exceeded-limit-error";
import { storage } from "@/lib/storage";
import {
createDomainBodySchemaExtended,
Expand Down
3 changes: 2 additions & 1 deletion apps/web/app/api/folders/route.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { createId } from "@/lib/api/create-id";
import { DubApiError, exceededLimitError } from "@/lib/api/errors";
import { DubApiError } from "@/lib/api/errors";
import { parseRequestBody } from "@/lib/api/utils";
import { withWorkspace } from "@/lib/auth";
import { exceededLimitError } from "@/lib/exceeded-limit-error";
import { getFolders } from "@/lib/folder/get-folders";
import { getPlanCapabilities } from "@/lib/plan-capabilities";
import {
Expand Down
3 changes: 2 additions & 1 deletion apps/web/app/api/links/bulk/route.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DubApiError, exceededLimitError } from "@/lib/api/errors";
import { DubApiError } from "@/lib/api/errors";
import {
bulkCreateLinks,
checkIfLinksHaveTags,
Expand All @@ -14,6 +14,7 @@ import { checkIfLinksHaveFolders } from "@/lib/api/links/utils/check-if-links-ha
import { combineTagIds } from "@/lib/api/tags/combine-tag-ids";
import { parseRequestBody } from "@/lib/api/utils";
import { withWorkspace } from "@/lib/auth";
import { exceededLimitError } from "@/lib/exceeded-limit-error";
import {
checkFolderPermissions,
verifyFolderAccess,
Expand Down
Loading