chore: caching optimisations in sync endpoints (#2111)

Co-authored-by: Johannes <72809645+jobenjada@users.noreply.github.com>
This commit is contained in:
Matti Nannt
2024-02-22 19:18:06 +01:00
committed by GitHub
parent 312858e278
commit 186feefdb5
66 changed files with 494 additions and 625 deletions

View File

@@ -29,7 +29,6 @@ export default function AppPage({}) {
environmentId: process.env.NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID,
apiHost: process.env.NEXT_PUBLIC_FORMBRICKS_API_HOST,
userId,
debug: true,
attributes,
});
window.formbricks = formbricks;
@@ -105,8 +104,8 @@ export default function AppPage({}) {
Reset person / pull data from Formbricks app
</h3>
<p className="text-slate-700 dark:text-slate-300">
On formbricks.reset() a few things happen: <strong>New person is created</strong> and{" "}
<strong>surveys & no-code actions are pulled from Formbricks:</strong>.
On formbricks.reset() the local state will <strong>be deleted</strong> and formbricks gets{" "}
<strong>reinitialized</strong>.
</p>
<button
className="my-4 rounded-lg bg-slate-500 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600"

View File

@@ -45,8 +45,7 @@ Adds an Actions for a given User by their User ID
curl --location --request POST 'https://app.formbricks.com/api/v1/client/<environment-id>/actions' \
--data-raw '{
"userId": "1",
"name": "new_action_v2",
"properties":{}
"name": "new_action_v2"
}'
```
@@ -54,8 +53,7 @@ Adds an Actions for a given User by their User ID
```json {{ title: 'Example Request Body' }}
{
"userId": "1",
"name": "new_action_v3",
"properties":{}
"name": "new_action_v3"
}
```

View File

@@ -1,6 +1,5 @@
import { responses } from "@/app/lib/api/response";
import { headers } from "next/headers";
import { NextResponse } from "next/server";
import { ProductFeatureKeys } from "@formbricks/ee/billing/lib/constants";
import { reportUsageToStripe } from "@formbricks/ee/billing/lib/reportUsage";
@@ -52,7 +51,7 @@ async function reportTeamUsage(team: TTeam) {
}
}
export async function POST(): Promise<NextResponse> {
export async function POST(): Promise<Response> {
const headersList = headers();
const apiKey = headersList.get("x-api-key");

View File

@@ -1,6 +1,5 @@
import { responses } from "@/app/lib/api/response";
import { headers } from "next/headers";
import { NextResponse } from "next/server";
import { prisma } from "@formbricks/database";
import { CRON_SECRET } from "@formbricks/lib/constants";
@@ -10,7 +9,7 @@ import { EnvironmentData, NotificationResponse, ProductData, Survey, SurveyRespo
const BATCH_SIZE = 500;
export async function POST(): Promise<NextResponse> {
export async function POST(): Promise<Response> {
// Check authentication
if (headers().get("x-api-key") !== CRON_SECRET) {
return responses.notAuthenticatedResponse();

View File

@@ -1,6 +1,5 @@
import { responses } from "@/app/lib/api/response";
import { google } from "googleapis";
import { NextRequest, NextResponse } from "next/server";
import {
GOOGLE_SHEETS_CLIENT_ID,
@@ -10,7 +9,7 @@ import {
} from "@formbricks/lib/constants";
import { createOrUpdateIntegration } from "@formbricks/lib/integration/service";
export async function GET(req: NextRequest) {
export async function GET(req: Request) {
const url = req.url;
const queryParams = new URLSearchParams(url.split("?")[1]); // Split the URL and get the query parameters
const environmentId = queryParams.get("state"); // Get the value of the 'state' parameter
@@ -66,6 +65,6 @@ export async function GET(req: NextRequest) {
const result = await createOrUpdateIntegration(environmentId, googleSheetIntegration);
if (result) {
return NextResponse.redirect(`${WEBAPP_URL}/environments/${environmentId}/integrations/google-sheets`);
return Response.redirect(`${WEBAPP_URL}/environments/${environmentId}/integrations/google-sheets`);
}
}

View File

@@ -1,7 +1,7 @@
import { responses } from "@/app/lib/api/response";
import { AsyncParser } from "@json2csv/node";
import { getServerSession } from "next-auth";
import { NextRequest, NextResponse } from "next/server";
import { NextRequest } from "next/server";
import { authOptions } from "@formbricks/lib/authOptions";
@@ -40,7 +40,7 @@ export async function POST(request: NextRequest) {
`attachment; filename="${fallbackFileName}"; filename*=UTF-8''${encodedFileName}`
);
return NextResponse.json(
return Response.json(
{
fileResponse: csv,
},

View File

@@ -1,6 +1,6 @@
import { responses } from "@/app/lib/api/response";
import { getServerSession } from "next-auth";
import { NextRequest, NextResponse } from "next/server";
import { NextRequest } from "next/server";
import * as xlsx from "xlsx";
import { authOptions } from "@formbricks/lib/authOptions";
@@ -36,7 +36,7 @@ export async function POST(request: NextRequest) {
`attachment; filename="${fallbackFileName}"; filename*=UTF-8''${encodedFileName}`
);
return NextResponse.json(
return Response.json(
{
fileResponse: base64String,
},

View File

@@ -1,7 +1,6 @@
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { headers } from "next/headers";
import { NextResponse } from "next/server";
import { prisma } from "@formbricks/database";
import { INTERNAL_SECRET } from "@formbricks/lib/constants";
@@ -185,5 +184,5 @@ export async function POST(request: Request) {
await updateSurveyStatus(surveyId);
}
return NextResponse.json({ data: {} });
return Response.json({ data: {} });
}

View File

@@ -1,6 +1,5 @@
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { NextResponse } from "next/server";
import { getActionClasses } from "@formbricks/lib/actionClass/service";
import { createAttributeClass, getAttributeClassByName } from "@formbricks/lib/attributeClass/service";
@@ -18,11 +17,11 @@ interface Context {
};
}
export async function OPTIONS(): Promise<NextResponse> {
export async function OPTIONS(): Promise<Response> {
return responses.successResponse({}, true);
}
export async function POST(req: Request, context: Context): Promise<NextResponse> {
export async function POST(req: Request, context: Context): Promise<Response> {
try {
const { userId, environmentId } = context.params;
const personId = userId; // legacy workaround for formbricks-js 1.2.0 & 1.2.1

View File

@@ -1,13 +1,12 @@
import { responses } from "@/app/lib/api/response";
import { NextResponse } from "next/server";
import { markDisplayRespondedLegacy } from "@formbricks/lib/display/service";
export async function OPTIONS(): Promise<NextResponse> {
export async function OPTIONS(): Promise<Response> {
return responses.successResponse({}, true);
}
export async function POST(_: Request, { params }: { params: { displayId: string } }): Promise<NextResponse> {
export async function POST(_: Request, { params }: { params: { displayId: string } }): Promise<Response> {
const { displayId } = params;
if (!displayId) {

View File

@@ -1,18 +1,17 @@
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { NextResponse } from "next/server";
import { updateDisplayLegacy } from "@formbricks/lib/display/service";
import { ZDisplayLegacyUpdateInput } from "@formbricks/types/displays";
export async function OPTIONS(): Promise<NextResponse> {
export async function OPTIONS(): Promise<Response> {
return responses.successResponse({}, true);
}
export async function PUT(
request: Request,
{ params }: { params: { displayId: string } }
): Promise<NextResponse> {
): Promise<Response> {
const { displayId } = params;
if (!displayId) {
return responses.badRequestResponse("Missing displayId", undefined, true);

View File

@@ -1,6 +1,5 @@
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { NextResponse } from "next/server";
import { createDisplayLegacy } from "@formbricks/lib/display/service";
import { capturePosthogEnvironmentEvent } from "@formbricks/lib/posthogServer";
@@ -8,11 +7,11 @@ import { getSurvey } from "@formbricks/lib/survey/service";
import { TDisplay, ZDisplayLegacyCreateInput } from "@formbricks/types/displays";
import { InvalidInputError } from "@formbricks/types/errors";
export async function OPTIONS(): Promise<NextResponse> {
export async function OPTIONS(): Promise<Response> {
return responses.successResponse({}, true);
}
export async function POST(request: Request): Promise<NextResponse> {
export async function POST(request: Request): Promise<Response> {
const jsonInput = await request.json();
if (jsonInput.personId === "legacy") {
delete jsonInput.personId;

View File

@@ -1,6 +1,5 @@
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { NextResponse } from "next/server";
import { getActionClasses } from "@formbricks/lib/actionClass/service";
import { createAttributeClass, getAttributeClassByName } from "@formbricks/lib/attributeClass/service";
@@ -18,11 +17,11 @@ interface Context {
};
}
export async function OPTIONS(): Promise<NextResponse> {
export async function OPTIONS(): Promise<Response> {
return responses.successResponse({}, true);
}
export async function POST(req: Request, context: Context): Promise<NextResponse> {
export async function POST(req: Request, context: Context): Promise<Response> {
try {
const { personId, environmentId } = context.params;
const jsonInput = await req.json();

View File

@@ -1,21 +1,20 @@
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { sendToPipeline } from "@/app/lib/pipelines";
import { NextResponse } from "next/server";
import { updateResponse } from "@formbricks/lib/response/service";
import { getSurvey } from "@formbricks/lib/survey/service";
import { DatabaseError, InvalidInputError, ResourceNotFoundError } from "@formbricks/types/errors";
import { ZResponseUpdateInput } from "@formbricks/types/responses";
export async function OPTIONS(): Promise<NextResponse> {
export async function OPTIONS(): Promise<Response> {
return responses.successResponse({}, true);
}
export async function PUT(
request: Request,
{ params }: { params: { responseId: string } }
): Promise<NextResponse> {
): Promise<Response> {
const { responseId } = params;
if (!responseId) {

View File

@@ -2,7 +2,6 @@ import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { sendToPipeline } from "@/app/lib/pipelines";
import { headers } from "next/headers";
import { NextResponse } from "next/server";
import { UAParser } from "ua-parser-js";
import { capturePosthogEnvironmentEvent } from "@formbricks/lib/posthogServer";
@@ -12,11 +11,11 @@ import { InvalidInputError } from "@formbricks/types/errors";
import { TResponse, ZResponseLegacyInput } from "@formbricks/types/responses";
import { TSurvey } from "@formbricks/types/surveys";
export async function OPTIONS(): Promise<NextResponse> {
export async function OPTIONS(): Promise<Response> {
return responses.successResponse({}, true);
}
export async function POST(request: Request): Promise<NextResponse> {
export async function POST(request: Request): Promise<Response> {
const responseInput = await request.json();
if (responseInput.personId === "legacy") {
responseInput.personId = null;

View File

@@ -1,10 +1,9 @@
import { responses } from "@/app/lib/api/response";
import { NextResponse } from "next/server";
export async function OPTIONS(): Promise<NextResponse> {
export async function OPTIONS(): Promise<Response> {
return responses.successResponse({}, true);
}
export async function POST(): Promise<NextResponse> {
export async function POST(): Promise<Response> {
return responses.successResponse({}, true);
}

View File

@@ -1,7 +1,6 @@
import { getUpdatedState } from "@/app/api/v1/(legacy)/js/sync/lib/sync";
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { NextResponse } from "next/server";
import { createAttributeClass, getAttributeClassByName } from "@formbricks/lib/attributeClass/service";
import { personCache } from "@formbricks/lib/person/cache";
@@ -10,11 +9,11 @@ import { surveyCache } from "@formbricks/lib/survey/cache";
import { ZJsPeopleLegacyAttributeInput } from "@formbricks/types/js";
import { TPersonClient } from "@formbricks/types/people";
export async function OPTIONS(): Promise<NextResponse> {
export async function OPTIONS(): Promise<Response> {
return responses.successResponse({}, true);
}
export async function POST(req: Request, { params }): Promise<NextResponse> {
export async function POST(req: Request, { params }): Promise<Response> {
try {
const { personId } = params;

View File

@@ -1,16 +1,15 @@
import { getUpdatedState } from "@/app/api/v1/(legacy)/js/sync/lib/sync";
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { NextResponse } from "next/server";
import { createPerson, getPersonByUserId } from "@formbricks/lib/person/service";
import { ZJsPeopleUserIdInput } from "@formbricks/types/js";
export async function OPTIONS(): Promise<NextResponse> {
export async function OPTIONS(): Promise<Response> {
return responses.successResponse({}, true);
}
export async function POST(req: Request): Promise<NextResponse> {
export async function POST(req: Request): Promise<Response> {
try {
const jsonInput = await req.json();

View File

@@ -1,16 +1,15 @@
import { getUpdatedState } from "@/app/api/v1/(legacy)/js/sync/lib/sync";
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { NextResponse } from "next/server";
import { ZJsSyncLegacyInput } from "@formbricks/types/js";
import { TPersonClient } from "@formbricks/types/people";
export async function OPTIONS(): Promise<NextResponse> {
export async function OPTIONS(): Promise<Response> {
return responses.successResponse({}, true);
}
export async function POST(req: Request): Promise<NextResponse> {
export async function POST(req: Request): Promise<Response> {
try {
const jsonInput = await req.json();

View File

@@ -1,5 +1,4 @@
import { responses } from "@/app/lib/api/response";
import { NextResponse } from "next/server";
import { getApiKeyFromKey } from "@formbricks/lib/apiKey/service";
import { TAuthenticationApiKey } from "@formbricks/types/auth";
@@ -21,7 +20,7 @@ export async function authenticateRequest(request: Request): Promise<TAuthentica
return null;
}
export function handleErrorResponse(error: any): NextResponse {
export function handleErrorResponse(error: any): Response {
switch (error.message) {
case "NotAuthenticated":
return responses.notAuthenticatedResponse();

View File

@@ -1,6 +1,5 @@
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { NextResponse } from "next/server";
import { createAction } from "@formbricks/lib/action/service";
import { ZActionInput } from "@formbricks/types/actions";
@@ -11,11 +10,11 @@ interface Context {
};
}
export async function OPTIONS(): Promise<NextResponse> {
export async function OPTIONS(): Promise<Response> {
return responses.successResponse({}, true);
}
export async function POST(req: Request, context: Context): Promise<NextResponse> {
export async function POST(req: Request, context: Context): Promise<Response> {
try {
const jsonInput = await req.json();

View File

@@ -1,6 +1,5 @@
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { NextResponse } from "next/server";
import { updateDisplay } from "@formbricks/lib/display/service";
import { ZDisplayUpdateInput } from "@formbricks/types/displays";
@@ -12,11 +11,11 @@ interface Context {
};
}
export async function OPTIONS(): Promise<NextResponse> {
export async function OPTIONS(): Promise<Response> {
return responses.successResponse({}, true);
}
export async function PUT(request: Request, context: Context): Promise<NextResponse> {
export async function PUT(request: Request, context: Context): Promise<Response> {
const { displayId, environmentId } = context.params;
const jsonInput = await request.json();
const inputValidation = ZDisplayUpdateInput.safeParse({

View File

@@ -1,6 +1,5 @@
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { NextResponse } from "next/server";
import { createDisplay } from "@formbricks/lib/display/service";
import { capturePosthogEnvironmentEvent } from "@formbricks/lib/posthogServer";
@@ -13,11 +12,11 @@ interface Context {
};
}
export async function OPTIONS(): Promise<NextResponse> {
export async function OPTIONS(): Promise<Response> {
return responses.successResponse({}, true);
}
export async function POST(request: Request, context: Context): Promise<NextResponse> {
export async function POST(request: Request, context: Context): Promise<Response> {
const jsonInput = await request.json();
const inputValidation = ZDisplayCreateInput.safeParse({
...jsonInput,

View File

@@ -1,7 +1,7 @@
import { sendFreeLimitReachedEventToPosthogBiWeekly } from "@/app/api/v1/client/[environmentId]/in-app/sync/lib/posthog";
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { NextRequest, NextResponse, userAgent } from "next/server";
import { NextRequest, userAgent } from "next/server";
import { getLatestActionByPersonId } from "@formbricks/lib/action/service";
import { getActionClasses } from "@formbricks/lib/actionClass/service";
@@ -22,7 +22,7 @@ import {
import { TEnvironment } from "@formbricks/types/environment";
import { TJsStateSync, ZJsPeopleUserIdInput } from "@formbricks/types/js";
export async function OPTIONS(): Promise<NextResponse> {
export async function OPTIONS(): Promise<Response> {
return responses.successResponse({}, true);
}
@@ -36,7 +36,7 @@ export async function GET(
userId: string;
};
}
): Promise<NextResponse> {
): Promise<Response> {
try {
const { device } = userAgent(request);
const apiVersion = request.nextUrl.searchParams.get("version");
@@ -103,12 +103,20 @@ export async function GET(
const errorMessage = `Monthly Active Users limit in the current plan is reached in ${environmentId}`;
if (!person) {
// if it's a new person and MAU limit is reached, throw an error
throw new Error(errorMessage);
return responses.tooManyRequestsResponse(
errorMessage,
true,
"public, s-maxage=600, max-age=840, stale-while-revalidate=600, stale-if-error=600"
);
} else {
// check if person has been active this month
const latestAction = await getLatestActionByPersonId(person.id);
if (!latestAction || new Date(latestAction.createdAt).getMonth() !== new Date().getMonth()) {
throw new Error(errorMessage);
return responses.tooManyRequestsResponse(
errorMessage,
true,
"public, s-maxage=600, max-age=840, stale-while-revalidate=600, stale-if-error=600"
);
}
}
}
@@ -136,7 +144,11 @@ export async function GET(
product,
};
return responses.successResponse({ ...state }, true);
return responses.successResponse(
{ ...state },
true,
"public, s-maxage=100, max-age=110, stale-while-revalidate=100, stale-if-error=100"
);
} catch (error) {
console.error(error);
return responses.internalServerErrorResponse("Unable to handle the request: " + error.message, true);

View File

@@ -1,7 +1,7 @@
import { sendFreeLimitReachedEventToPosthogBiWeekly } from "@/app/api/v1/client/[environmentId]/in-app/sync/lib/posthog";
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { NextRequest, NextResponse } from "next/server";
import { NextRequest } from "next/server";
import { getActionClasses } from "@formbricks/lib/actionClass/service";
import { IS_FORMBRICKS_CLOUD, PRICING_APPSURVEYS_FREE_RESPONSES } from "@formbricks/lib/constants";
@@ -11,14 +11,14 @@ import { getSurveys } from "@formbricks/lib/survey/service";
import { getMonthlyTeamResponseCount, getTeamByEnvironmentId } from "@formbricks/lib/team/service";
import { TJsStateSync, ZJsPublicSyncInput } from "@formbricks/types/js";
export async function OPTIONS(): Promise<NextResponse> {
export async function OPTIONS(): Promise<Response> {
return responses.successResponse({}, true);
}
export async function GET(
_: NextRequest,
{ params }: { params: { environmentId: string } }
): Promise<NextResponse> {
): Promise<Response> {
try {
// validate using zod
const environmentIdValidation = ZJsPublicSyncInput.safeParse({

View File

@@ -1,6 +1,5 @@
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { NextResponse } from "next/server";
import { createPerson, getPersonByUserId, updatePerson } from "@formbricks/lib/person/service";
import { ZPersonUpdateInput } from "@formbricks/types/people";
@@ -12,11 +11,11 @@ interface Context {
};
}
export async function OPTIONS(): Promise<NextResponse> {
export async function OPTIONS(): Promise<Response> {
return responses.successResponse({}, true);
}
export async function POST(req: Request, context: Context): Promise<NextResponse> {
export async function POST(req: Request, context: Context): Promise<Response> {
try {
const { userId, environmentId } = context.params;
const jsonInput = await req.json();
@@ -40,9 +39,35 @@ export async function POST(req: Request, context: Context): Promise<NextResponse
person = await createPerson(environmentId, userId);
}
// Check if the person is already up to date
const updatedAtttributes = inputValidation.data.attributes;
const oldAttributes = person.attributes;
let isUpToDate = true;
for (const key in updatedAtttributes) {
if (updatedAtttributes[key] !== oldAttributes[key]) {
isUpToDate = false;
break;
}
}
if (isUpToDate) {
return responses.successResponse(
{
changed: false,
message: "No updates were necessary; the person is already up to date.",
},
true
);
}
await updatePerson(person.id, inputValidation.data);
return responses.successResponse({}, true);
return responses.successResponse(
{
changed: true,
message: "The person was successfully updated.",
},
true
);
} catch (error) {
console.error(error);
return responses.internalServerErrorResponse(`Unable to complete request: ${error.message}`, true);

View File

@@ -1,7 +1,6 @@
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { sendToPipeline } from "@/app/lib/pipelines";
import { NextResponse } from "next/server";
import { getPerson } from "@formbricks/lib/person/service";
import { updateResponse } from "@formbricks/lib/response/service";
@@ -9,14 +8,14 @@ import { getSurvey } from "@formbricks/lib/survey/service";
import { DatabaseError, InvalidInputError, ResourceNotFoundError } from "@formbricks/types/errors";
import { ZResponseUpdateInput } from "@formbricks/types/responses";
export async function OPTIONS(): Promise<NextResponse> {
export async function OPTIONS(): Promise<Response> {
return responses.successResponse({}, true);
}
export async function PUT(
request: Request,
{ params }: { params: { responseId: string } }
): Promise<NextResponse> {
): Promise<Response> {
const { responseId } = params;
if (!responseId) {

View File

@@ -2,7 +2,6 @@ import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { sendToPipeline } from "@/app/lib/pipelines";
import { headers } from "next/headers";
import { NextResponse } from "next/server";
import { UAParser } from "ua-parser-js";
import { getPerson } from "@formbricks/lib/person/service";
@@ -19,11 +18,11 @@ interface Context {
};
}
export async function OPTIONS(): Promise<NextResponse> {
export async function OPTIONS(): Promise<Response> {
return responses.successResponse({}, true);
}
export async function POST(request: Request, context: Context): Promise<NextResponse> {
export async function POST(request: Request, context: Context): Promise<Response> {
const { environmentId } = context.params;
const environmentIdValidation = ZId.safeParse(environmentId);

View File

@@ -3,7 +3,7 @@
// method -> PUT (to be the same as the signedUrl method)
import { responses } from "@/app/lib/api/response";
import { headers } from "next/headers";
import { NextRequest, NextResponse } from "next/server";
import { NextRequest } from "next/server";
import { UPLOADS_DIR } from "@formbricks/lib/constants";
import { validateLocalSignedUrl } from "@formbricks/lib/crypto";
@@ -18,8 +18,8 @@ interface Context {
};
}
export async function OPTIONS(): Promise<NextResponse> {
return NextResponse.json(
export async function OPTIONS(): Promise<Response> {
return Response.json(
{},
{
headers: {
@@ -32,7 +32,7 @@ export async function OPTIONS(): Promise<NextResponse> {
);
}
export async function POST(req: NextRequest, context: Context): Promise<NextResponse> {
export async function POST(req: NextRequest, context: Context): Promise<Response> {
const environmentId = context.params.environmentId;
const accessType = "private"; // private files are accessible only by authorized users

View File

@@ -1,5 +1,5 @@
import { responses } from "@/app/lib/api/response";
import { NextRequest, NextResponse } from "next/server";
import { NextRequest } from "next/server";
import { getSurvey } from "@formbricks/lib/survey/service";
import { getTeamByEnvironmentId } from "@formbricks/lib/team/service";
@@ -12,7 +12,7 @@ interface Context {
};
}
export async function OPTIONS(): Promise<NextResponse> {
export async function OPTIONS(): Promise<Response> {
return responses.successResponse({}, true);
}
@@ -22,7 +22,7 @@ export async function OPTIONS(): Promise<NextResponse> {
// use this to let users upload files to a survey for example
// this api endpoint will return a signed url for uploading the file to s3 and another url for uploading file to the local storage
export async function POST(req: NextRequest, context: Context): Promise<NextResponse> {
export async function POST(req: NextRequest, context: Context): Promise<Response> {
const environmentId = context.params.environmentId;
const { fileName, fileType, surveyId } = await req.json();

View File

@@ -1,6 +1,6 @@
import { responses } from "@/app/lib/api/response";
import { getServerSession } from "next-auth";
import { NextRequest, NextResponse } from "next/server";
import { NextRequest } from "next/server";
import * as z from "zod";
import { connectAirtable, fetchAirtableAuthToken } from "@formbricks/lib/airtable/service";
@@ -70,7 +70,7 @@ export async function GET(req: NextRequest) {
email,
key,
});
return NextResponse.redirect(`${WEBAPP_URL}/environments/${environmentId}/integrations/airtable`);
return Response.redirect(`${WEBAPP_URL}/environments/${environmentId}/integrations/airtable`);
} catch (error) {
console.error(error);
responses.internalServerErrorResponse(error);

View File

@@ -1,5 +1,5 @@
import { responses } from "@/app/lib/api/response";
import { NextRequest, NextResponse } from "next/server";
import { NextRequest } from "next/server";
import {
ENCRYPTION_KEY,
@@ -66,10 +66,10 @@ export async function GET(req: NextRequest) {
const result = await createOrUpdateIntegration(environmentId, notionIntegration);
if (result) {
return NextResponse.redirect(`${WEBAPP_URL}/environments/${environmentId}/integrations/notion`);
return Response.redirect(`${WEBAPP_URL}/environments/${environmentId}/integrations/notion`);
}
} else if (error) {
return NextResponse.redirect(
return Response.redirect(
`${WEBAPP_URL}/environments/${environmentId}/integrations/notion?error=${error}`
);
}

View File

@@ -1,8 +1,6 @@
import { authenticateRequest } from "@/app/api/v1/auth";
import { handleErrorResponse } from "@/app/api/v1/auth";
import { authenticateRequest, handleErrorResponse } from "@/app/api/v1/auth";
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { NextResponse } from "next/server";
import { deleteActionClass, getActionClass, updateActionClass } from "@formbricks/lib/actionClass/service";
import { TActionClass, ZActionClassInput } from "@formbricks/types/actionClasses";
@@ -25,7 +23,7 @@ async function fetchAndAuthorizeActionClass(
export async function GET(
request: Request,
{ params }: { params: { actionClassId: string } }
): Promise<NextResponse> {
): Promise<Response> {
try {
const authentication = await authenticateRequest(request);
if (!authentication) return responses.notAuthenticatedResponse();
@@ -42,7 +40,7 @@ export async function GET(
export async function PUT(
request: Request,
{ params }: { params: { actionClassId: string } }
): Promise<NextResponse> {
): Promise<Response> {
try {
const authentication = await authenticateRequest(request);
if (!authentication) return responses.notAuthenticatedResponse();
@@ -75,7 +73,7 @@ export async function PUT(
export async function DELETE(
request: Request,
{ params }: { params: { actionClassId: string } }
): Promise<NextResponse> {
): Promise<Response> {
try {
const authentication = await authenticateRequest(request);
if (!authentication) return responses.notAuthenticatedResponse();

View File

@@ -1,7 +1,6 @@
import { authenticateRequest } from "@/app/api/v1/auth";
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { NextResponse } from "next/server";
import { createActionClass, getActionClasses } from "@formbricks/lib/actionClass/service";
import { TActionClass, ZActionClassInput } from "@formbricks/types/actionClasses";
@@ -21,7 +20,7 @@ export async function GET(request: Request) {
}
}
export async function POST(request: Request): Promise<NextResponse> {
export async function POST(request: Request): Promise<Response> {
try {
const authentication = await authenticateRequest(request);
if (!authentication) return responses.notAuthenticatedResponse();

View File

@@ -1,8 +1,6 @@
import { handleErrorResponse } from "@/app/api/v1/auth";
import { authenticateRequest } from "@/app/api/v1/auth";
import { authenticateRequest, handleErrorResponse } from "@/app/api/v1/auth";
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { NextResponse } from "next/server";
import {
deleteAttributeClass,
@@ -29,7 +27,7 @@ async function fetchAndAuthorizeAttributeClass(
export async function GET(
request: Request,
{ params }: { params: { attributeClassId: string } }
): Promise<NextResponse> {
): Promise<Response> {
try {
const authentication = await authenticateRequest(request);
if (!authentication) return responses.notAuthenticatedResponse();
@@ -46,7 +44,7 @@ export async function GET(
export async function DELETE(
request: Request,
{ params }: { params: { attributeClassId: string } }
): Promise<NextResponse> {
): Promise<Response> {
try {
const authentication = await authenticateRequest(request);
if (!authentication) return responses.notAuthenticatedResponse();
@@ -67,7 +65,7 @@ export async function DELETE(
export async function PUT(
request: Request,
{ params }: { params: { attributeClassId: string } }
): Promise<NextResponse> {
): Promise<Response> {
try {
const authentication = await authenticateRequest(request);
if (!authentication) return responses.notAuthenticatedResponse();

View File

@@ -1,7 +1,6 @@
import { authenticateRequest } from "@/app/api/v1/auth";
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { NextResponse } from "next/server";
import { createAttributeClass, getAttributeClasses } from "@formbricks/lib/attributeClass/service";
import { TAttributeClass, ZAttributeClassInput } from "@formbricks/types/attributeClasses";
@@ -21,7 +20,7 @@ export async function GET(request: Request) {
}
}
export async function POST(request: Request): Promise<NextResponse> {
export async function POST(request: Request): Promise<Response> {
try {
const authentication = await authenticateRequest(request);
if (!authentication) return responses.notAuthenticatedResponse();

View File

@@ -1,6 +1,5 @@
import { getSessionUser, hashApiKey } from "@/app/lib/api/apiHelper";
import { headers } from "next/headers";
import { NextResponse } from "next/server";
import { prisma } from "@formbricks/database";
@@ -35,7 +34,7 @@ export async function GET() {
status: 401,
});
}
return NextResponse.json(apiKeyData.environment);
return Response.json(apiKeyData.environment);
} else {
const sessionUser = await getSessionUser();
if (!sessionUser) {
@@ -50,6 +49,6 @@ export async function GET() {
},
});
return NextResponse.json(user);
return Response.json(user);
}
}

View File

@@ -1,11 +1,12 @@
import { authenticateRequest, handleErrorResponse } from "@/app/api/v1/auth";
import { responses } from "@/app/lib/api/response";
import { NextResponse } from "next/server";
import { deletePerson, getPerson } from "@formbricks/lib/person/service";
import { TAuthenticationApiKey } from "@formbricks/types/auth";
import { TPerson } from "@formbricks/types/people";
// Please use the methods provided by the client API to update a person
async function fetchAndAuthorizePerson(
authentication: TAuthenticationApiKey,
personId: string
@@ -20,10 +21,7 @@ async function fetchAndAuthorizePerson(
return person;
}
export async function GET(
request: Request,
{ params }: { params: { personId: string } }
): Promise<NextResponse> {
export async function GET(request: Request, { params }: { params: { personId: string } }): Promise<Response> {
try {
const authentication = await authenticateRequest(request);
if (!authentication) return responses.notAuthenticatedResponse();
@@ -37,31 +35,6 @@ export async function GET(
}
}
// Please use the methods provided by the client API to update a person
/* export async function PUT(
request: Request,
{ params }: { params: { personId: string } }
): Promise<NextResponse> {
try {
const authentication = await authenticateRequest(request);
if (!authentication) return responses.notAuthenticatedResponse();
await fetchAndAuthorizePerson(authentication, params.personId);
const personUpdate = await request.json();
const inputValidation = ZPersonUpdateInput.safeParse(personUpdate);
if (!inputValidation.success) {
return responses.badRequestResponse(
"Fields are missing or incorrectly formatted",
transformErrorToDetails(inputValidation.error)
);
}
return responses.successResponse(await updatePerson(params.personId, inputValidation.data));
} catch (error) {
return handleErrorResponse(error);
}
} */
export async function DELETE(request: Request, { params }: { params: { personId: string } }) {
try {
const authentication = await authenticateRequest(request);

View File

@@ -20,17 +20,3 @@ export async function GET(request: Request) {
}
// Please use the client API to create a new person
/* export async function POST(request: Request): Promise<NextResponse> {
try {
const authentication = await authenticateRequest(request);
if (!authentication) return responses.notAuthenticatedResponse();
const person: TPerson = await createPerson(authentication.environmentId);
return responses.successResponse(person);
} catch (error) {
if (error instanceof DatabaseError) {
return responses.badRequestResponse(error.message);
}
throw error;
}
} */

View File

@@ -1,8 +1,6 @@
import { authenticateRequest } from "@/app/api/v1/auth";
import { handleErrorResponse } from "@/app/api/v1/auth";
import { authenticateRequest, handleErrorResponse } from "@/app/api/v1/auth";
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { NextResponse } from "next/server";
import { hasUserEnvironmentAccess } from "@formbricks/lib/environment/auth";
import { deleteResponse, getResponse, updateResponse } from "@formbricks/lib/response/service";
@@ -33,7 +31,7 @@ const canUserAccessResponse = async (authentication: any, response: TResponse):
export async function GET(
request: Request,
{ params }: { params: { responseId: string } }
): Promise<NextResponse> {
): Promise<Response> {
try {
const authentication = await authenticateRequest(request);
if (!authentication) return responses.notAuthenticatedResponse();
@@ -51,7 +49,7 @@ export async function GET(
export async function DELETE(
request: Request,
{ params }: { params: { responseId: string } }
): Promise<NextResponse> {
): Promise<Response> {
try {
const authentication = await authenticateRequest(request);
if (!authentication) return responses.notAuthenticatedResponse();
@@ -69,7 +67,7 @@ export async function DELETE(
export async function PUT(
request: Request,
{ params }: { params: { responseId: string } }
): Promise<NextResponse> {
): Promise<Response> {
try {
const authentication = await authenticateRequest(request);
if (!authentication) return responses.notAuthenticatedResponse();

View File

@@ -4,7 +4,7 @@
import { responses } from "@/app/lib/api/response";
import { getServerSession } from "next-auth";
import { headers } from "next/headers";
import { NextRequest, NextResponse } from "next/server";
import { NextRequest } from "next/server";
import { authOptions } from "@formbricks/lib/authOptions";
import { UPLOADS_DIR } from "@formbricks/lib/constants";
@@ -13,7 +13,7 @@ import { env } from "@formbricks/lib/env.mjs";
import { hasUserEnvironmentAccess } from "@formbricks/lib/environment/auth";
import { putFileToLocalStorage } from "@formbricks/lib/storage/service";
export async function POST(req: NextRequest): Promise<NextResponse> {
export async function POST(req: NextRequest): Promise<Response> {
const accessType = "public"; // public files are accessible by anyone
const headersList = headers();

View File

@@ -1,6 +1,6 @@
import { responses } from "@/app/lib/api/response";
import { getServerSession } from "next-auth";
import { NextRequest, NextResponse } from "next/server";
import { NextRequest } from "next/server";
import { authOptions } from "@formbricks/lib/authOptions";
import { hasUserEnvironmentAccess } from "@formbricks/lib/environment/auth";
@@ -13,7 +13,7 @@ import getSignedUrlForPublicFile from "./lib/getSignedUrl";
// use this to upload files for a specific resource, e.g. a user profile picture or a survey
// this api endpoint will return a signed url for uploading the file to s3 and another url for uploading file to the local storage
export async function POST(req: NextRequest): Promise<NextResponse> {
export async function POST(req: NextRequest): Promise<Response> {
const { fileName, fileType, environmentId, allowedFileExtensions } = await req.json();
if (!fileName) {

View File

@@ -1,8 +1,6 @@
import { authenticateRequest } from "@/app/api/v1/auth";
import { handleErrorResponse } from "@/app/api/v1/auth";
import { authenticateRequest, handleErrorResponse } from "@/app/api/v1/auth";
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { NextResponse } from "next/server";
import { deleteSurvey, getSurvey, updateSurvey } from "@formbricks/lib/survey/service";
import { TSurvey, ZSurvey } from "@formbricks/types/surveys";
@@ -18,10 +16,7 @@ async function fetchAndAuthorizeSurvey(authentication: any, surveyId: string): P
return survey;
}
export async function GET(
request: Request,
{ params }: { params: { surveyId: string } }
): Promise<NextResponse> {
export async function GET(request: Request, { params }: { params: { surveyId: string } }): Promise<Response> {
try {
const authentication = await authenticateRequest(request);
if (!authentication) return responses.notAuthenticatedResponse();
@@ -38,7 +33,7 @@ export async function GET(
export async function DELETE(
request: Request,
{ params }: { params: { surveyId: string } }
): Promise<NextResponse> {
): Promise<Response> {
try {
const authentication = await authenticateRequest(request);
if (!authentication) return responses.notAuthenticatedResponse();
@@ -53,10 +48,7 @@ export async function DELETE(
}
}
export async function PUT(
request: Request,
{ params }: { params: { surveyId: string } }
): Promise<NextResponse> {
export async function PUT(request: Request, { params }: { params: { surveyId: string } }): Promise<Response> {
try {
const authentication = await authenticateRequest(request);
if (!authentication) return responses.notAuthenticatedResponse();

View File

@@ -1,7 +1,6 @@
import { authenticateRequest } from "@/app/api/v1/auth";
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { NextResponse } from "next/server";
import { createSurvey, getSurveys } from "@formbricks/lib/survey/service";
import { DatabaseError } from "@formbricks/types/errors";
@@ -21,7 +20,7 @@ export async function GET(request: Request) {
}
}
export async function POST(request: Request): Promise<NextResponse> {
export async function POST(request: Request): Promise<Response> {
try {
const authentication = await authenticateRequest(request);
if (!authentication) return responses.notAuthenticatedResponse();

View File

@@ -1,42 +0,0 @@
import { getSessionUser } from "@/app/lib/api/apiHelper";
import { NextResponse } from "next/server";
import { prisma } from "@formbricks/database";
export async function GET() {
const sessionUser = await getSessionUser();
if (!sessionUser) {
return new Response("Not authenticated", {
status: 401,
});
}
// get memberships
const memberships = await prisma.membership.findMany({
where: {
userId: sessionUser.id,
},
include: {
team: {
select: {
id: true,
name: true,
products: {
select: {
id: true,
name: true,
environments: {
select: {
id: true,
type: true,
},
},
},
},
},
},
},
});
return NextResponse.json(memberships);
}

View File

@@ -1,5 +1,3 @@
import { NextResponse } from "next/server";
import { prisma } from "@formbricks/database";
import { sendForgotPasswordEmail } from "@formbricks/lib/emails/emails";
@@ -14,13 +12,13 @@ export async function POST(request: Request) {
});
if (!foundUser) {
return NextResponse.json({ error: "No user with this email found" }, { status: 409 });
return Response.json({ error: "No user with this email found" }, { status: 409 });
}
await sendForgotPasswordEmail(foundUser);
return NextResponse.json({});
return Response.json({});
} catch (e) {
return NextResponse.json(
return Response.json(
{
error: e.message,
errorCode: e.code,

View File

@@ -1,6 +1,6 @@
import { getSessionUser } from "@/app/lib/api/apiHelper";
import { MembershipRole } from "@prisma/client";
import { NextRequest, NextResponse } from "next/server";
import { NextRequest } from "next/server";
import { prisma } from "@formbricks/database";
@@ -23,7 +23,7 @@ export async function GET() {
},
});
return NextResponse.json(user);
return Response.json(user);
}
export async function PUT(request: NextRequest) {
@@ -42,7 +42,7 @@ export async function PUT(request: NextRequest) {
data: body,
});
return NextResponse.json(user);
return Response.json(user);
}
const deleteUser = async (userId: string) => {
@@ -130,8 +130,8 @@ export async function DELETE() {
await deleteUser(currentUser.id);
return NextResponse.json({ deletedUser: currentUser }, { status: 200 });
return Response.json({ deletedUser: currentUser }, { status: 200 });
} catch (error) {
return NextResponse.json({ message: error.message }, { status: 500 });
return Response.json({ message: error.message }, { status: 500 });
}
}

View File

@@ -1,5 +1,3 @@
import { NextResponse } from "next/server";
import { prisma } from "@formbricks/database";
import { sendPasswordResetNotifyEmail } from "@formbricks/lib/emails/emails";
import { verifyToken } from "@formbricks/lib/jwt";
@@ -15,16 +13,16 @@ export async function POST(request: Request) {
},
});
if (!user) {
return NextResponse.json({ error: "Invalid token provided or no longer valid" }, { status: 409 });
return Response.json({ error: "Invalid token provided or no longer valid" }, { status: 409 });
}
await prisma.user.update({
where: { id: user.id },
data: { password: hashedPassword },
});
await sendPasswordResetNotifyEmail(user);
return NextResponse.json({});
return Response.json({});
} catch (e) {
return NextResponse.json(
return Response.json(
{
error: e.message,
errorCode: e.code,

View File

@@ -1,5 +1,3 @@
import { NextResponse } from "next/server";
import { prisma } from "@formbricks/database";
import {
EMAIL_AUTH_ENABLED,
@@ -19,7 +17,7 @@ import { createUser, updateUser } from "@formbricks/lib/user/service";
export async function POST(request: Request) {
let { inviteToken, ...user } = await request.json();
if (!EMAIL_AUTH_ENABLED || inviteToken ? INVITE_DISABLED : !SIGNUP_ENABLED) {
return NextResponse.json({ error: "Signup disabled" }, { status: 403 });
return Response.json({ error: "Signup disabled" }, { status: 403 });
}
user = { ...user, ...{ email: user.email.toLowerCase() } };
@@ -44,7 +42,7 @@ export async function POST(request: Request) {
});
if (!invite) {
return NextResponse.json({ error: "Invalid invite ID" }, { status: 400 });
return Response.json({ error: "Invalid invite ID" }, { status: 400 });
}
// assign user to existing team
@@ -60,7 +58,7 @@ export async function POST(request: Request) {
await sendInviteAcceptedEmail(invite.creator.name, user.name, invite.creator.email);
await deleteInvite(inviteId);
return NextResponse.json(user);
return Response.json(user);
}
// User signs up without invite
@@ -103,10 +101,10 @@ export async function POST(request: Request) {
await sendVerificationEmail(user);
}
return NextResponse.json(user);
return Response.json(user);
} catch (e) {
if (e.code === "P2002") {
return NextResponse.json(
return Response.json(
{
error: "user with this email address already exists",
errorCode: e.code,
@@ -114,7 +112,7 @@ export async function POST(request: Request) {
{ status: 409 }
);
} else {
return NextResponse.json(
return Response.json(
{
error: e.message,
errorCode: e.code,

View File

@@ -1,5 +1,3 @@
import { NextResponse } from "next/server";
import { prisma } from "@formbricks/database";
import { sendVerificationEmail } from "@formbricks/lib/emails/emails";
@@ -11,15 +9,15 @@ export async function POST(request: Request) {
where: { email },
});
if (!user) {
return NextResponse.json({ error: "No user with this email address found" }, { status: 404 });
return Response.json({ error: "No user with this email address found" }, { status: 404 });
}
if (user.emailVerified) {
return NextResponse.json({ error: "Email address has already been verified" }, { status: 400 });
return Response.json({ error: "Email address has already been verified" }, { status: 400 });
}
await sendVerificationEmail(user);
return NextResponse.json(user);
return Response.json(user);
} catch (e) {
return NextResponse.json(
return Response.json(
{
error: e.message,
errorCode: e.code,

View File

@@ -1,7 +1,6 @@
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { headers } from "next/headers";
import { NextResponse } from "next/server";
import { getApiKeyFromKey } from "@formbricks/lib/apiKey/service";
import { createWebhook, getWebhooks } from "@formbricks/lib/webhook/service";
@@ -21,7 +20,7 @@ export async function GET() {
// get webhooks from database
try {
const webhooks = await getWebhooks(apiKeyData.environmentId);
return NextResponse.json({ data: webhooks });
return Response.json({ data: webhooks });
} catch (error) {
if (error instanceof DatabaseError) {
return responses.badRequestResponse(error.message);

View File

@@ -1,5 +1,4 @@
import { NextApiResponse } from "next";
import { NextResponse } from "next/server";
export type ApiResponse = ApiSuccessResponse | ApiErrorResponse;
@@ -30,7 +29,7 @@ const corsHeaders = {
};
const badRequestResponse = (message: string, details?: { [key: string]: string }, cors: boolean = false) =>
NextResponse.json(
Response.json(
{
code: "bad_request",
message,
@@ -56,7 +55,7 @@ const methodNotAllowedResponse = (
allowedMethods: string[],
cors: boolean = false
) =>
NextResponse.json(
Response.json(
{
code: "method_not_allowed",
message: `The HTTP ${res.req?.method} method is not supported by this route.`,
@@ -71,7 +70,7 @@ const methodNotAllowedResponse = (
);
const notFoundResponse = (resourceType: string, resourceId: string, cors: boolean = false) =>
NextResponse.json(
Response.json(
{
code: "not_found",
message: `${resourceType} not found`,
@@ -87,7 +86,7 @@ const notFoundResponse = (resourceType: string, resourceId: string, cors: boolea
);
const notAuthenticatedResponse = (cors: boolean = false) =>
NextResponse.json(
Response.json(
{
code: "not_authenticated",
message: "Not authenticated",
@@ -102,7 +101,7 @@ const notAuthenticatedResponse = (cors: boolean = false) =>
);
const unauthorizedResponse = (cors: boolean = false) =>
NextResponse.json(
Response.json(
{
code: "unauthorized",
message: "You are not authorized to access this resource",
@@ -115,24 +114,33 @@ const unauthorizedResponse = (cors: boolean = false) =>
);
const successResponse = (data: Object, cors: boolean = false, cache: string = "private, no-store") => {
const responseHeaders = {
const headers = {
...(cors && corsHeaders),
"Cache-Control": cache,
};
return NextResponse.json(
return Response.json(
{
data,
} as ApiSuccessResponse<typeof data>,
{
status: 200,
headers: responseHeaders,
headers,
}
);
};
const internalServerErrorResponse = (message: string, cors: boolean = false) =>
NextResponse.json(
const internalServerErrorResponse = (
message: string,
cors: boolean = false,
cache: string = "private, no-store"
) => {
const headers = {
...(cors && corsHeaders),
"Cache-Control": cache,
};
return Response.json(
{
code: "internal_server_error",
message,
@@ -140,9 +148,33 @@ const internalServerErrorResponse = (message: string, cors: boolean = false) =>
} as ApiErrorResponse,
{
status: 500,
...(cors && { headers: corsHeaders }),
headers,
}
);
};
const tooManyRequestsResponse = (
message: string,
cors: boolean = false,
cache: string = "private, no-store"
) => {
const headers = {
...(cors && corsHeaders),
"Cache-Control": cache,
};
return Response.json(
{
code: "internal_server_error",
message,
details: {},
} as ApiErrorResponse,
{
status: 429,
headers,
}
);
};
export const responses = {
badRequestResponse,
@@ -153,4 +185,5 @@ export const responses = {
unauthorizedResponse,
notFoundResponse,
successResponse,
tooManyRequestsResponse,
};

View File

@@ -22,40 +22,40 @@
"@formbricks/ui": "workspace:*",
"@headlessui/react": "^1.7.18",
"@heroicons/react": "^2.1.1",
"@json2csv/node": "^7.0.5",
"@json2csv/node": "^7.0.6",
"@paralleldrive/cuid2": "^2.2.2",
"@radix-ui/react-collapsible": "^1.0.3",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@react-email/components": "^0.0.14",
"@sentry/nextjs": "^7.99.0",
"@react-email/components": "^0.0.15",
"@sentry/nextjs": "^7.102.1",
"@vercel/og": "^0.6.2",
"@vercel/speed-insights": "^1.0.9",
"@vercel/speed-insights": "^1.0.10",
"bcryptjs": "^2.4.3",
"dotenv": "^16.4.1",
"dotenv": "^16.4.5",
"encoding": "^0.1.13",
"framer-motion": "11.0.3",
"googleapis": "^131.0.0",
"framer-motion": "11.0.5",
"googleapis": "^133.0.0",
"jsonwebtoken": "^9.0.2",
"lodash": "^4.17.21",
"lru-cache": "^10.2.0",
"lucide-react": "^0.321.0",
"lucide-react": "^0.336.0",
"mime": "^4.0.1",
"next": "14.1.0",
"nodemailer": "^6.9.9",
"nodemailer": "^6.9.10",
"otplib": "^12.0.1",
"posthog-js": "^1.104.4",
"posthog-js": "^1.108.2",
"prismjs": "^1.29.0",
"qrcode": "^1.5.3",
"react": "18.2.0",
"react-beautiful-dnd": "^13.1.1",
"react-dom": "18.2.0",
"react-email": "^2.0.0",
"react-hook-form": "^7.50.0",
"react-email": "^2.1.0",
"react-hook-form": "^7.50.1",
"react-hot-toast": "^2.4.1",
"react-icons": "^5.0.1",
"sharp": "^0.33.2",
"ua-parser-js": "^1.0.37",
"webpack": "^5.90.1",
"webpack": "^5.90.3",
"xlsx": "^0.18.5"
},
"devDependencies": {

View File

@@ -5,7 +5,7 @@
},
"app/api/v1/client/**/*.ts": {
"maxDuration": 10,
"memory": 512
"memory": 300
},
"app/**/*.ts": {
"maxDuration": 10,

View File

@@ -20,7 +20,10 @@ export class PeopleAPI {
});
}
async update(userId: string, personInput: TPersonUpdateInput): Promise<Result<{}, NetworkError | Error>> {
async update(
userId: string,
personInput: TPersonUpdateInput
): Promise<Result<{ changed: boolean; message: string }, NetworkError | Error>> {
return makeRequest(
this.apiHost,
`/api/v1/client/${this.environmentId}/people/${userId}`,

View File

@@ -6,6 +6,7 @@ import { Config } from "./config";
import { NetworkError, Result, err, okVoid } from "./errors";
import { Logger } from "./logger";
import { sync } from "./sync";
import { getIsDebug } from "./utils";
import { renderWidget } from "./widget";
const logger = Logger.getInstance();
@@ -18,16 +19,12 @@ const shouldDisplayBasedOnPercentage = (displayPercentage: number) => {
return randomNum <= displayPercentage;
};
export const trackAction = async (
name: string,
properties: TJsActionInput["properties"] = {}
): Promise<Result<void, NetworkError>> => {
export const trackAction = async (name: string): Promise<Result<void, NetworkError>> => {
const { userId } = config.get();
const input: TJsActionInput = {
environmentId: config.get().environmentId,
userId,
name,
properties: properties || {},
};
// don't send actions to the backend if the person is not identified
@@ -53,12 +50,19 @@ export const trackAction = async (
});
}
// sync again
await sync({
environmentId: config.get().environmentId,
apiHost: config.get().apiHost,
userId,
});
// we skip the resync on a new action since this leads to too many requests if the user has a lot of actions
// also this always leads to a second sync call on the `New Session` action
// when debug: sync after every action for testing purposes
if (getIsDebug()) {
await sync(
{
environmentId: config.get().environmentId,
apiHost: config.get().apiHost,
userId,
},
true
);
}
}
logger.debug(`Formbricks: Action "${name}" tracked`);

View File

@@ -25,7 +25,7 @@ export class Config {
public update(newConfig: TJsConfigUpdateInput): void {
if (newConfig) {
const expiresAt = new Date(new Date().getTime() + 15 * 60000); // 15 minutes in the future
const expiresAt = new Date(new Date().getTime() + 2 * 60000); // 2 minutes from now
this.config = {
...this.config,

View File

@@ -18,6 +18,7 @@ import { Logger } from "./logger";
import { checkPageUrl } from "./noCodeActions";
import { updatePersonAttributes } from "./person";
import { sync } from "./sync";
import { getIsDebug } from "./utils";
import { addWidgetContainer, closeSurvey } from "./widget";
const config = Config.getInstance();
@@ -25,13 +26,6 @@ const logger = Logger.getInstance();
let isInitialized = false;
const setDebugLevel = (c: TJsConfigInput): void => {
if (c.debug || window.location.search.includes("formbricksDebug=true")) {
logger.configure({ logLevel: "debug" });
logger.debug(`Setting log level to debug`);
}
};
export const initialize = async (
c: TJsConfigInput
): Promise<Result<void, MissingFieldError | NetworkError | MissingPersonError>> => {
@@ -40,7 +34,9 @@ export const initialize = async (
return okVoid();
}
setDebugLevel(c);
if (getIsDebug()) {
logger.configure({ logLevel: "debug" });
}
ErrorHandler.getInstance().printStatus();

View File

@@ -38,9 +38,10 @@ export const updatePersonAttribute = async (
};
const api = new FormbricksAPI({
apiHost: config.get().apiHost,
environmentId: config.get().environmentId,
apiHost,
environmentId,
});
const res = await api.client.people.update(userId, input);
if (!res.ok) {
@@ -53,13 +54,17 @@ export const updatePersonAttribute = async (
});
}
logger.debug("Attribute updated. Syncing...");
await sync({
environmentId: environmentId,
apiHost: apiHost,
userId: userId,
});
if (res.data.changed) {
logger.debug("Attribute updated. Syncing...");
await sync(
{
environmentId: environmentId,
apiHost: apiHost,
userId: userId,
},
true
);
}
return okVoid();
};

View File

@@ -4,25 +4,33 @@ import { TJsState, TJsStateSync, TJsSyncParams } from "@formbricks/types/js";
import { Config } from "./config";
import { NetworkError, Result, err, ok } from "./errors";
import { Logger } from "./logger";
import { getIsDebug } from "./utils";
const config = Config.getInstance();
const logger = Logger.getInstance();
let syncIntervalId: number | null = null;
const syncWithBackend = async ({
apiHost,
environmentId,
userId,
}: TJsSyncParams): Promise<Result<TJsStateSync, NetworkError>> => {
const url = `${apiHost}/api/v1/client/${environmentId}/in-app/sync/${userId}?version=${import.meta.env.VERSION}`;
const publicUrl = `${apiHost}/api/v1/client/${environmentId}/in-app/sync`;
const syncWithBackend = async (
{ apiHost, environmentId, userId }: TJsSyncParams,
noCache: boolean
): Promise<Result<TJsStateSync, NetworkError>> => {
const baseUrl = `${apiHost}/api/v1/client/${environmentId}/in-app/sync`;
const urlSuffix = `?version=${import.meta.env.VERSION}`;
let fetchOptions: RequestInit = {};
if (noCache || getIsDebug()) {
fetchOptions.cache = "no-cache";
logger.debug("No cache option set for sync");
}
// if user id is available
if (!userId) {
const url = baseUrl + urlSuffix;
// public survey
const response = await fetch(publicUrl);
const response = await fetch(url, fetchOptions);
if (!response.ok) {
const jsonRes = await response.json();
@@ -41,7 +49,9 @@ const syncWithBackend = async ({
// userId is available, call the api with the `userId` param
const response = await fetch(url);
const url = `${baseUrl}/${userId}${urlSuffix}`;
const response = await fetch(url, fetchOptions);
if (!response.ok) {
const jsonRes = await response.json();
@@ -61,9 +71,9 @@ const syncWithBackend = async ({
return ok(state as TJsStateSync);
};
export const sync = async (params: TJsSyncParams): Promise<void> => {
export const sync = async (params: TJsSyncParams, noCache = false): Promise<void> => {
try {
const syncResult = await syncWithBackend(params);
const syncResult = await syncWithBackend(params, noCache);
if (syncResult?.ok !== true) {
logger.error(`Sync failed: ${JSON.stringify(syncResult.error)}`);
throw syncResult.error;
@@ -161,7 +171,7 @@ export const filterPublicSurveys = (state: TJsState): TJsState => {
};
export const addExpiryCheckListener = (): void => {
const updateInterval = 1000 * 60; // every minute
const updateInterval = 1000 * 30; // every 30 seconds
// add event listener to check sync with backend on regular interval
if (typeof window !== "undefined" && syncIntervalId === null) {
syncIntervalId = window.setInterval(async () => {

View File

@@ -0,0 +1 @@
export const getIsDebug = () => window.location.search.includes("formbricksDebug=true");

View File

@@ -174,11 +174,14 @@ export const closeSurvey = async (): Promise<void> => {
// for identified users we sync to get the latest surveys
try {
await sync({
apiHost: config.get().apiHost,
environmentId: config.get().environmentId,
userId: config.get().userId,
});
await sync(
{
apiHost: config.get().apiHost,
environmentId: config.get().environmentId,
userId: config.get().userId,
},
true
);
surveyRunning = false;
} catch (e) {
errorHandler.handle(e);

View File

@@ -218,7 +218,7 @@ export const getActionsByEnvironmentId = async (environmentId: string, page?: nu
export const createAction = async (data: TActionInput): Promise<TAction> => {
validateInputs([data, ZActionInput]);
const { environmentId, name, properties, userId } = data;
const { environmentId, name, userId } = data;
let actionType: TActionClassType = "code";
if (name === "Exit Intent (Desktop)" || name === "50% Scroll") {
@@ -244,7 +244,6 @@ export const createAction = async (data: TActionInput): Promise<TAction> => {
const action = await prisma.action.create({
data: {
properties,
person: {
connect: {
id: person.id,

View File

@@ -16,7 +16,6 @@ export const ZActionInput = z.object({
environmentId: z.string().cuid(),
userId: z.string(),
name: z.string(),
properties: z.record(z.string()),
});
export type TActionInput = z.infer<typeof ZActionInput>;

View File

@@ -94,7 +94,6 @@ export type TJsConfigUpdateInput = z.infer<typeof ZJsConfigUpdateInput>;
export const ZJsConfigInput = z.object({
environmentId: z.string().cuid(),
apiHost: z.string(),
debug: z.boolean().optional(),
errorHandler: z.function().args(z.any()).returns(z.void()).optional(),
userId: z.string().optional(),
attributes: ZPersonAttributes.optional(),
@@ -128,7 +127,6 @@ export const ZJsActionInput = z.object({
environmentId: z.string().cuid(),
userId: z.string().optional(),
name: z.string(),
properties: z.record(z.string()),
});
export type TJsActionInput = z.infer<typeof ZJsActionInput>;

499
pnpm-lock.yaml generated
View File

@@ -93,7 +93,7 @@ importers:
version: 0.9.0
'@mdx-js/loader':
specifier: ^3.0.0
version: 3.0.0(webpack@5.90.1)
version: 3.0.0(webpack@5.90.3)
'@mdx-js/react':
specifier: ^3.0.0
version: 3.0.0(@types/react@18.2.48)(react@18.2.0)
@@ -319,8 +319,8 @@ importers:
specifier: ^2.1.1
version: 2.1.1(react@18.2.0)
'@json2csv/node':
specifier: ^7.0.5
version: 7.0.5
specifier: ^7.0.6
version: 7.0.6
'@paralleldrive/cuid2':
specifier: ^2.2.2
version: 2.2.2
@@ -331,32 +331,32 @@ importers:
specifier: ^2.0.6
version: 2.0.6(react-dom@18.2.0)(react@18.2.0)
'@react-email/components':
specifier: ^0.0.14
version: 0.0.14(@types/react@18.2.48)(react@18.2.0)
specifier: ^0.0.15
version: 0.0.15(@types/react@18.2.48)(react-email@2.1.0)(react@18.2.0)
'@sentry/nextjs':
specifier: ^7.99.0
version: 7.99.0(encoding@0.1.13)(next@14.1.0)(react@18.2.0)(webpack@5.90.1)
specifier: ^7.102.1
version: 7.102.1(encoding@0.1.13)(next@14.1.0)(react@18.2.0)(webpack@5.90.3)
'@vercel/og':
specifier: ^0.6.2
version: 0.6.2
'@vercel/speed-insights':
specifier: ^1.0.9
version: 1.0.9(next@14.1.0)(react@18.2.0)
specifier: ^1.0.10
version: 1.0.10(next@14.1.0)(react@18.2.0)
bcryptjs:
specifier: ^2.4.3
version: 2.4.3
dotenv:
specifier: ^16.4.1
version: 16.4.1
specifier: ^16.4.5
version: 16.4.5
encoding:
specifier: ^0.1.13
version: 0.1.13
framer-motion:
specifier: 11.0.3
version: 11.0.3(react-dom@18.2.0)(react@18.2.0)
specifier: 11.0.5
version: 11.0.5(react-dom@18.2.0)(react@18.2.0)
googleapis:
specifier: ^131.0.0
version: 131.0.0(encoding@0.1.13)
specifier: ^133.0.0
version: 133.0.0(encoding@0.1.13)
jsonwebtoken:
specifier: ^9.0.2
version: 9.0.2
@@ -367,8 +367,8 @@ importers:
specifier: ^10.2.0
version: 10.2.0
lucide-react:
specifier: ^0.321.0
version: 0.321.0(react@18.2.0)
specifier: ^0.336.0
version: 0.336.0(react@18.2.0)
mime:
specifier: ^4.0.1
version: 4.0.1
@@ -376,14 +376,14 @@ importers:
specifier: 14.1.0
version: 14.1.0(@babel/core@7.23.7)(react-dom@18.2.0)(react@18.2.0)
nodemailer:
specifier: ^6.9.9
version: 6.9.9
specifier: ^6.9.10
version: 6.9.10
otplib:
specifier: ^12.0.1
version: 12.0.1
posthog-js:
specifier: ^1.104.4
version: 1.104.4
specifier: ^1.108.2
version: 1.108.2
prismjs:
specifier: ^1.29.0
version: 1.29.0
@@ -400,11 +400,11 @@ importers:
specifier: 18.2.0
version: 18.2.0(react@18.2.0)
react-email:
specifier: ^2.0.0
version: 2.0.0(eslint@8.56.0)
specifier: ^2.1.0
version: 2.1.0(eslint@8.56.0)
react-hook-form:
specifier: ^7.50.0
version: 7.50.0(react@18.2.0)
specifier: ^7.50.1
version: 7.50.1(react@18.2.0)
react-hot-toast:
specifier: ^2.4.1
version: 2.4.1(csstype@3.1.3)(react-dom@18.2.0)(react@18.2.0)
@@ -418,8 +418,8 @@ importers:
specifier: ^1.0.37
version: 1.0.37
webpack:
specifier: ^5.90.1
version: 5.90.1
specifier: ^5.90.3
version: 5.90.3
xlsx:
specifier: ^0.18.5
version: 0.18.5
@@ -3052,7 +3052,6 @@ packages:
engines: {node: '>=6.9.0'}
dependencies:
regenerator-runtime: 0.14.1
dev: false
/@babel/template@7.22.15:
resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==}
@@ -4621,21 +4620,21 @@ packages:
'@jridgewell/sourcemap-codec': 1.4.15
dev: true
/@json2csv/formatters@7.0.5:
resolution: {integrity: sha512-ihONIB8S1ZG0ji5nDFpfCUPphVLnEKlLxDmDqRMjmPhzw8WCvyLX2trIrNHCf1t2IV8rx+1tNYaDb1dh87jHgA==}
/@json2csv/formatters@7.0.6:
resolution: {integrity: sha512-hjIk1H1TR4ydU5ntIENEPgoMGW+Q7mJ+537sDFDbsk+Y3EPl2i4NfFVjw0NJRgT+ihm8X30M67mA8AS6jPidSA==}
dev: false
/@json2csv/node@7.0.5:
resolution: {integrity: sha512-dOp2yHW8gDmIaGYUMEjhxZsMXld1hdvMLpgNdAOGJYqOrEMkGXbxZD3LF6hBJQnHsIZWsiWzLk4itFmvp/ZF7w==}
/@json2csv/node@7.0.6:
resolution: {integrity: sha512-J3AX8cDBeQyriJj0oFxJot52hScUN4hhUBRnUGIPt+yI1YpwUuftriJi1RJS60Uz6Stce1sewHeG56dBc9/XGg==}
dependencies:
'@json2csv/plainjs': 7.0.5
'@json2csv/plainjs': 7.0.6
dev: false
/@json2csv/plainjs@7.0.5:
resolution: {integrity: sha512-iJsYEzgRZUhRb8LltCeuBQyexQVUbovRZFF73Lo0iIvi5SUWsfXGTtbriIROsOKgfZPtYxmkZV+FYlsnBu9ybQ==}
/@json2csv/plainjs@7.0.6:
resolution: {integrity: sha512-4Md7RPDCSYpmW1HWIpWBOqCd4vWfIqm53S3e/uzQ62iGi7L3r34fK/8nhOMEe+/eVfCx8+gdSCt1d74SlacQHw==}
dependencies:
'@json2csv/formatters': 7.0.5
'@streamparser/json': 0.0.19
'@json2csv/formatters': 7.0.6
'@streamparser/json': 0.0.20
dev: false
/@juggle/resize-observer@3.4.0:
@@ -4896,14 +4895,14 @@ packages:
unist-util-visit: 2.0.3
dev: false
/@mdx-js/loader@3.0.0(webpack@5.90.1):
/@mdx-js/loader@3.0.0(webpack@5.90.3):
resolution: {integrity: sha512-9kLv83YtgxpoXVYHaf0ygx1dmhCffo0MQCv6KtNG67jy/JlBK/2Q0dSWfuuyStP3jnZKABHfbjv8zsiT1buu6A==}
peerDependencies:
webpack: '>=5'
dependencies:
'@mdx-js/mdx': 3.0.0
source-map: 0.7.4
webpack: 5.90.1
webpack: 5.90.3
transitivePeerDependencies:
- supports-color
dev: false
@@ -5010,10 +5009,6 @@ packages:
resolution: {integrity: sha512-Yac/bV5sBGkkEXmAX5FWPS9Mmo2rthrOPRQQNfycJPkjUAUclomCPH7QFVCDQ4Mp2k2K1SSM6m0zrxYrOwtFQw==}
dev: false
/@next/env@14.0.5-canary.46:
resolution: {integrity: sha512-dvNzrArTfe3VY1VIscpb3E2e7SZ1qwFe82WGzpOVbxilT3JcsnVGYF/uq8Jj1qKWPI5C/aePNXwA97JRNAXpRQ==}
dev: false
/@next/env@14.1.0:
resolution: {integrity: sha512-Py8zIo+02ht82brwwhTg36iogzFqGLPXlRGKQw5s+qP/kMNc4MAyDeEwBKDijk6zTIbegEgu8Qy7C1LboslQAw==}
dev: false
@@ -5035,7 +5030,7 @@ packages:
'@mdx-js/react':
optional: true
dependencies:
'@mdx-js/loader': 3.0.0(webpack@5.90.1)
'@mdx-js/loader': 3.0.0(webpack@5.90.3)
'@mdx-js/react': 3.0.0(@types/react@18.2.48)(react@18.2.0)
source-map: 0.7.4
dev: false
@@ -5049,15 +5044,6 @@ packages:
dev: false
optional: true
/@next/swc-darwin-arm64@14.0.5-canary.46:
resolution: {integrity: sha512-7Bq9rjWl4sq70Zkn6h6mn8/tgYTH2SQ8lIm8b/j1MAnTiJYyVBLapu//gT/cgtqx6y8SwSc2JNviBue35zeCNw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: false
optional: true
/@next/swc-darwin-arm64@14.1.0:
resolution: {integrity: sha512-nUDn7TOGcIeyQni6lZHfzNoo9S0euXnu0jhsbMOmMJUBfgsnESdjN97kM7cBqQxZa8L/bM9om/S5/1dzCrW6wQ==}
engines: {node: '>= 10'}
@@ -5076,15 +5062,6 @@ packages:
dev: false
optional: true
/@next/swc-darwin-x64@14.0.5-canary.46:
resolution: {integrity: sha512-3oI8rDVBZsfkTdqXwtRjxA85o0RIjZv9uuOLohfaIuFP3oZnCM0dRZREP2umYcFQRxdavW+TDJzYcqzKxYTujA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: false
optional: true
/@next/swc-darwin-x64@14.1.0:
resolution: {integrity: sha512-1jgudN5haWxiAl3O1ljUS2GfupPmcftu2RYJqZiMJmmbBT5M1XDffjUtRUzP4W3cBHsrvkfOFdQ71hAreNQP6g==}
engines: {node: '>= 10'}
@@ -5103,15 +5080,6 @@ packages:
dev: false
optional: true
/@next/swc-linux-arm64-gnu@14.0.5-canary.46:
resolution: {integrity: sha512-gXSS328bUWxBwQfeDFROOzFSzzoyX1075JxOeArLl63sV59cbnRrwHHhD4CWG1bYYzcHxHfVugZgvyCucaHCIw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@next/swc-linux-arm64-gnu@14.1.0:
resolution: {integrity: sha512-RHo7Tcj+jllXUbK7xk2NyIDod3YcCPDZxj1WLIYxd709BQ7WuRYl3OWUNG+WUfqeQBds6kvZYlc42NJJTNi4tQ==}
engines: {node: '>= 10'}
@@ -5130,15 +5098,6 @@ packages:
dev: false
optional: true
/@next/swc-linux-arm64-musl@14.0.5-canary.46:
resolution: {integrity: sha512-7QkBRKlDsjaWGbfIKh6qJK0HiHJISNGoKpwFTcnZvlhAEaydS5Hmu0zh64kbLRlzwXtkpj6/iCwjrWnHes59aA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@next/swc-linux-arm64-musl@14.1.0:
resolution: {integrity: sha512-v6kP8sHYxjO8RwHmWMJSq7VZP2nYCkRVQ0qolh2l6xroe9QjbgV8siTbduED4u0hlk0+tjS6/Tuy4n5XCp+l6g==}
engines: {node: '>= 10'}
@@ -5157,15 +5116,6 @@ packages:
dev: false
optional: true
/@next/swc-linux-x64-gnu@14.0.5-canary.46:
resolution: {integrity: sha512-DS5wTjw3FtcLFVzRxLMJgmDNMoeaXp5qBdKUSBrKTq4zQnqUi99CGz2461DlUSxJCWPUgAVo23MdoQD6Siuk7A==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@next/swc-linux-x64-gnu@14.1.0:
resolution: {integrity: sha512-zJ2pnoFYB1F4vmEVlb/eSe+VH679zT1VdXlZKX+pE66grOgjmKJHKacf82g/sWE4MQ4Rk2FMBCRnX+l6/TVYzQ==}
engines: {node: '>= 10'}
@@ -5184,15 +5134,6 @@ packages:
dev: false
optional: true
/@next/swc-linux-x64-musl@14.0.5-canary.46:
resolution: {integrity: sha512-d409ur5JGj6HFp8DBu5M2oTh5EddDcrT+vjewQkAq/A7MZoAMAOH74xOFouEnJs0/dQ71XvH9Lw+1gJSnElcyQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@next/swc-linux-x64-musl@14.1.0:
resolution: {integrity: sha512-rbaIYFt2X9YZBSbH/CwGAjbBG2/MrACCVu2X0+kSykHzHnYH5FjHxwXLkcoJ10cX0aWCEynpu+rP76x0914atg==}
engines: {node: '>= 10'}
@@ -5211,15 +5152,6 @@ packages:
dev: false
optional: true
/@next/swc-win32-arm64-msvc@14.0.5-canary.46:
resolution: {integrity: sha512-goyh/RCFtivflIOvbwircMxTSObETufm3pcxtI8rIz9+pg/M2MmK8/z48EZybkEcPKl41xu4s1iqXThy/jDPng==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: false
optional: true
/@next/swc-win32-arm64-msvc@14.1.0:
resolution: {integrity: sha512-o1N5TsYc8f/HpGt39OUQpQ9AKIGApd3QLueu7hXk//2xq5Z9OxmV6sQfNp8C7qYmiOlHYODOGqNNa0e9jvchGQ==}
engines: {node: '>= 10'}
@@ -5238,15 +5170,6 @@ packages:
dev: false
optional: true
/@next/swc-win32-ia32-msvc@14.0.5-canary.46:
resolution: {integrity: sha512-SEnrOZ7ASXdd/GBq2x0IfpSbfamv1rZfcDeZZLF7kzu0pY7jDQwcW8zTKwwC8JH5CLGLfI3wD6wUYrA+PgJSCw==}
engines: {node: '>= 10'}
cpu: [ia32]
os: [win32]
requiresBuild: true
dev: false
optional: true
/@next/swc-win32-ia32-msvc@14.1.0:
resolution: {integrity: sha512-XXIuB1DBRCFwNO6EEzCTMHT5pauwaSj4SWs7CYnME57eaReAKBXCnkUE80p/pAZcewm7hs+vGvNqDPacEXHVkw==}
engines: {node: '>= 10'}
@@ -5265,15 +5188,6 @@ packages:
dev: false
optional: true
/@next/swc-win32-x64-msvc@14.0.5-canary.46:
resolution: {integrity: sha512-NK1EJLyeUxgX9IHSxO0kN1Nk8VsaDfjHVYL4p9fM24e/9rG8jPcxquIQJ4Wy+ZdqxaVivqQ2eHrJYUpXpfOXmw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: false
optional: true
/@next/swc-win32-x64-msvc@14.1.0:
resolution: {integrity: sha512-9WEbVRRAqJ3YFVqEZIxUqkiO8l1nool1LmNxygr5HWF8AcSYsEpneUDhmjUVJEzO2A04+oPtZdombzzPPkTtgg==}
engines: {node: '>= 10'}
@@ -6345,7 +6259,7 @@ packages:
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.23.6
'@babel/runtime': 7.23.8
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
@@ -6984,7 +6898,7 @@ packages:
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.23.6
'@babel/runtime': 7.23.8
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-direction': 1.0.1(@types/react@18.2.48)(react@18.2.0)
@@ -7011,7 +6925,7 @@ packages:
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.23.6
'@babel/runtime': 7.23.8
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-direction': 1.0.1(@types/react@18.2.48)(react@18.2.0)
@@ -7105,7 +7019,7 @@ packages:
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.23.6
'@babel/runtime': 7.23.8
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
@@ -7370,8 +7284,8 @@ packages:
react: 18.2.0
dev: false
/@react-email/button@0.0.13(react@18.2.0):
resolution: {integrity: sha512-e/y8u2odJ8fF83B+wvL2FXzVcbQSUh2Cn2JH2Ez4L6AuPELsh8s2JYo081IDsXc16IyFiYpObn0blOt7s/qp8g==}
/@react-email/button@0.0.14(react@18.2.0):
resolution: {integrity: sha512-SMk40moGcAvkHIALX4XercQlK0PNeeEIam6OXHw68ea9WtzzqVwiK4pzLY0iiMI9B4xWHcaS2lCPf3cKbQBf1Q==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: 18.2.0
@@ -7379,8 +7293,8 @@ packages:
react: 18.2.0
dev: false
/@react-email/code-block@0.0.2(react@18.2.0):
resolution: {integrity: sha512-bQApEmpsvIcVYXdPCXhJB9CGCyShhn/c1JdctE/6R1uIosLbWt40evvVfp2X9STdi02Dhsjxw/AcGuQE6zGZqw==}
/@react-email/code-block@0.0.3(react@18.2.0):
resolution: {integrity: sha512-nxhl7WjjM2cOYtl0boBZfSObTrUCz2LbarcMyHkTVAsA9rbjbtWAQF7jmlefXJusk3Uol5l2c8hTh2lHLlHTRQ==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: 18.2.0
@@ -7407,15 +7321,15 @@ packages:
react: 18.2.0
dev: false
/@react-email/components@0.0.14(@types/react@18.2.48)(react@18.2.0):
resolution: {integrity: sha512-t/sNj0R9Mx9Sx5degPQcSBeWotNs7eUwiv72KN8v6fxaf87XlnMo0CPcKI/1by2DHZr5S0258ZQOO7vEFrbcLw==}
/@react-email/components@0.0.15(@types/react@18.2.48)(react-email@2.1.0)(react@18.2.0):
resolution: {integrity: sha512-jXfKiuyi94JBYfPVptEUwF57nRCvhEZIfyl2LqbL53fKsMrGlcjlN921iNnx1z41GAJOqZ8LPogeix3Iid23zw==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: 18.2.0
dependencies:
'@react-email/body': 0.0.7(react@18.2.0)
'@react-email/button': 0.0.13(react@18.2.0)
'@react-email/code-block': 0.0.2(react@18.2.0)
'@react-email/button': 0.0.14(react@18.2.0)
'@react-email/code-block': 0.0.3(react@18.2.0)
'@react-email/code-inline': 0.0.1(react@18.2.0)
'@react-email/column': 0.0.9(react@18.2.0)
'@react-email/container': 0.0.11(react@18.2.0)
@@ -7426,6 +7340,7 @@ packages:
'@react-email/html': 0.0.7(react@18.2.0)
'@react-email/img': 0.0.7(react@18.2.0)
'@react-email/link': 0.0.7(react@18.2.0)
'@react-email/markdown': 0.0.8(react-email@2.1.0)(react@18.2.0)
'@react-email/preview': 0.0.8(react@18.2.0)
'@react-email/render': 0.0.12
'@react-email/row': 0.0.7(react@18.2.0)
@@ -7435,6 +7350,7 @@ packages:
react: 18.2.0
transitivePeerDependencies:
- '@types/react'
- react-email
dev: false
/@react-email/container@0.0.11(react@18.2.0):
@@ -7511,6 +7427,18 @@ packages:
react: 18.2.0
dev: false
/@react-email/markdown@0.0.8(react-email@2.1.0)(react@18.2.0):
resolution: {integrity: sha512-x/2iTWskE0XoM13Rx80ckwbWaWdS6koYvxW6PHkOJ/k88NPnDIm+TaYvvg2DYSFJAUI0gK/LmIwenbebgNDS+w==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: 18.2.0
dependencies:
md-to-react-email: 4.1.0(react-email@2.1.0)(react@18.2.0)
react: 18.2.0
transitivePeerDependencies:
- react-email
dev: false
/@react-email/preview@0.0.8(react@18.2.0):
resolution: {integrity: sha512-Jm0KUYBZQd2w0s2QRMQy0zfHdo3Ns+9bYSE1OybjknlvhANirjuZw9E5KfWgdzO7PyrRtB1OBOQD8//Obc4uIQ==}
engines: {node: '>=18.0.0'}
@@ -7759,45 +7687,45 @@ packages:
selderee: 0.11.0
dev: false
/@sentry-internal/feedback@7.99.0:
resolution: {integrity: sha512-exIO1o+bE0MW4z30FxC0cYzJ4ZHSMlDPMHCBDPzU+MWGQc/fb8s58QUrx5Dnm6HTh9G3H+YlroCxIo9u0GSwGQ==}
/@sentry-internal/feedback@7.102.1:
resolution: {integrity: sha512-vY4hpLLMNLjICtWiizc7KeGbWOTUMGrF7C+9dPCztZww3CLgzWy9A7DvPj5hodRiYzpdRnAMl8yQnMFbYXh7bA==}
engines: {node: '>=12'}
dependencies:
'@sentry/core': 7.99.0
'@sentry/types': 7.99.0
'@sentry/utils': 7.99.0
'@sentry/core': 7.102.1
'@sentry/types': 7.102.1
'@sentry/utils': 7.102.1
dev: false
/@sentry-internal/replay-canvas@7.99.0:
resolution: {integrity: sha512-PoIkfusToDq0snfl2M6HJx/1KJYtXxYhQplrn11kYadO04SdG0XGXf4h7wBTMEQ7LDEAtQyvsOu4nEQtTO3YjQ==}
/@sentry-internal/replay-canvas@7.102.1:
resolution: {integrity: sha512-GUX4RWI10uRjdjeyvCLtAAhWRVqnAnG6+yNxWfqUQ3qMA7B7XxG43KT2UhSnulmErNzODQ6hA68rGPwwYeRIww==}
engines: {node: '>=12'}
dependencies:
'@sentry/core': 7.99.0
'@sentry/replay': 7.99.0
'@sentry/types': 7.99.0
'@sentry/utils': 7.99.0
'@sentry/core': 7.102.1
'@sentry/replay': 7.102.1
'@sentry/types': 7.102.1
'@sentry/utils': 7.102.1
dev: false
/@sentry-internal/tracing@7.99.0:
resolution: {integrity: sha512-z3JQhHjoM1KdM20qrHwRClKJrNLr2CcKtCluq7xevLtXHJWNAQQbafnWD+Aoj85EWXBzKt9yJMv2ltcXJ+at+w==}
/@sentry-internal/tracing@7.102.1:
resolution: {integrity: sha512-RkFlFyAC0fQOvBbBqnq0CLmFW5m3JJz9pKbZd5vXPraWAlniKSb1bC/4DF9SlNx0FN1LWG+IU3ISdpzwwTeAGg==}
engines: {node: '>=8'}
dependencies:
'@sentry/core': 7.99.0
'@sentry/types': 7.99.0
'@sentry/utils': 7.99.0
'@sentry/core': 7.102.1
'@sentry/types': 7.102.1
'@sentry/utils': 7.102.1
dev: false
/@sentry/browser@7.99.0:
resolution: {integrity: sha512-bgfoUv3wkwwLgN5YUOe0ibB3y268ZCnamZh6nLFqnY/UBKC1+FXWFdvzVON/XKUm62LF8wlpCybOf08ebNj2yg==}
/@sentry/browser@7.102.1:
resolution: {integrity: sha512-7BOfPBiM7Kp6q/iy0JIbsBTxIASV+zWXByqqjuEMWGj3X2u4oRIfm3gv4erPU/l+CORQUVQZLSPGoIoM1gbB/A==}
engines: {node: '>=8'}
dependencies:
'@sentry-internal/feedback': 7.99.0
'@sentry-internal/replay-canvas': 7.99.0
'@sentry-internal/tracing': 7.99.0
'@sentry/core': 7.99.0
'@sentry/replay': 7.99.0
'@sentry/types': 7.99.0
'@sentry/utils': 7.99.0
'@sentry-internal/feedback': 7.102.1
'@sentry-internal/replay-canvas': 7.102.1
'@sentry-internal/tracing': 7.102.1
'@sentry/core': 7.102.1
'@sentry/replay': 7.102.1
'@sentry/types': 7.102.1
'@sentry/utils': 7.102.1
dev: false
/@sentry/cli@1.77.1(encoding@0.1.13):
@@ -7817,26 +7745,26 @@ packages:
- supports-color
dev: false
/@sentry/core@7.99.0:
resolution: {integrity: sha512-vOAtzcAXEUtS/oW7wi3wMkZ3hsb5Ch96gKyrrj/mXdOp2zrcwdNV6N9/pawq2E9P/7Pw8AXw4CeDZztZrjQLuA==}
/@sentry/core@7.102.1:
resolution: {integrity: sha512-QjY+LSP3du3J/C8x/FfEbRxgZgsWd0jfTJ4P7s9f219I1csK4OeBMC3UA1HwEa0pY/9OF6H/egW2CjOcMM5Pdg==}
engines: {node: '>=8'}
dependencies:
'@sentry/types': 7.99.0
'@sentry/utils': 7.99.0
'@sentry/types': 7.102.1
'@sentry/utils': 7.102.1
dev: false
/@sentry/integrations@7.99.0:
resolution: {integrity: sha512-q4Nwpc27DTWlR7nDerd1o6KHlT/0usK+3xfBTZ1feVIAHCxt6ohCyZdoQ97+4kQiJJdX47MEmJYsXUlj62yZNg==}
/@sentry/integrations@7.102.1:
resolution: {integrity: sha512-Its3Ru6xCAqpaLE3cTxW/b91js2SIFoXa8LWtQDJ7tmTdwPAbT8Pij1F4bOhhaqLYbjLtCXGl/NR2cffsiRLww==}
engines: {node: '>=8'}
dependencies:
'@sentry/core': 7.99.0
'@sentry/types': 7.99.0
'@sentry/utils': 7.99.0
'@sentry/core': 7.102.1
'@sentry/types': 7.102.1
'@sentry/utils': 7.102.1
localforage: 1.10.0
dev: false
/@sentry/nextjs@7.99.0(encoding@0.1.13)(next@14.1.0)(react@18.2.0)(webpack@5.90.1):
resolution: {integrity: sha512-8eeEPFJjRBiCp2sFUhDLQFdWFagQ2yBvmALZIOIuoMei69N+clYVSxz84beeztbLal0zvRadJO5LAkBCb6d66Q==}
/@sentry/nextjs@7.102.1(encoding@0.1.13)(next@14.1.0)(react@18.2.0)(webpack@5.90.3):
resolution: {integrity: sha512-gOI/GD7DWhc3WucyYnepl8Nu5jmpa1YfR6jWDzTkPE2CV9zKK9zulTdqk+Aig9ch62SAmJOGkgejm5k9PE2XzQ==}
engines: {node: '>=8'}
peerDependencies:
next: ^10.0.8 || ^11.0 || ^12.0 || ^13.0 || ^14.0
@@ -7847,13 +7775,13 @@ packages:
optional: true
dependencies:
'@rollup/plugin-commonjs': 24.0.0(rollup@2.78.0)
'@sentry/core': 7.99.0
'@sentry/integrations': 7.99.0
'@sentry/node': 7.99.0
'@sentry/react': 7.99.0(react@18.2.0)
'@sentry/types': 7.99.0
'@sentry/utils': 7.99.0
'@sentry/vercel-edge': 7.99.0
'@sentry/core': 7.102.1
'@sentry/integrations': 7.102.1
'@sentry/node': 7.102.1
'@sentry/react': 7.102.1(react@18.2.0)
'@sentry/types': 7.102.1
'@sentry/utils': 7.102.1
'@sentry/vercel-edge': 7.102.1
'@sentry/webpack-plugin': 1.21.0(encoding@0.1.13)
chalk: 3.0.0
next: 14.1.0(@babel/core@7.23.7)(react-dom@18.2.0)(react@18.2.0)
@@ -7861,66 +7789,66 @@ packages:
resolve: 1.22.8
rollup: 2.78.0
stacktrace-parser: 0.1.10
webpack: 5.90.1
webpack: 5.90.3
transitivePeerDependencies:
- encoding
- supports-color
dev: false
/@sentry/node@7.99.0:
resolution: {integrity: sha512-34wYtLddnPcQ8qvKq62AfxowaMFw+GMUZGv7fIs9FxeBqqqn6Ckl0gFCTADudIIBQ3rSbmN7sHJIXdyiQv+pcw==}
/@sentry/node@7.102.1:
resolution: {integrity: sha512-mb3vmM3SGuCruckPiv/Vafeh89UQavTfpPFoU6Jwe6dSpQ39BO8fO8k8Zev+/nP6r/FKLtX17mJobErHECXsYw==}
engines: {node: '>=8'}
dependencies:
'@sentry-internal/tracing': 7.99.0
'@sentry/core': 7.99.0
'@sentry/types': 7.99.0
'@sentry/utils': 7.99.0
'@sentry-internal/tracing': 7.102.1
'@sentry/core': 7.102.1
'@sentry/types': 7.102.1
'@sentry/utils': 7.102.1
dev: false
/@sentry/react@7.99.0(react@18.2.0):
resolution: {integrity: sha512-RtHwgzMHJhzJfSQpVG0SDPQYMTGDX3Q37/YWI59S4ALMbSW4/F6n/eQAvGVYZKbh2UCSqgFuRWaXOYkSZT17wA==}
/@sentry/react@7.102.1(react@18.2.0):
resolution: {integrity: sha512-X4j2DgbktlEifnd21YJKCayAmff5hnaS+9MNz9OonEwD0ARi0ks7bo0wtWHMjPK20992MO+JwczVg/1BXJYDdQ==}
engines: {node: '>=8'}
peerDependencies:
react: 15.x || 16.x || 17.x || 18.x
dependencies:
'@sentry/browser': 7.99.0
'@sentry/core': 7.99.0
'@sentry/types': 7.99.0
'@sentry/utils': 7.99.0
'@sentry/browser': 7.102.1
'@sentry/core': 7.102.1
'@sentry/types': 7.102.1
'@sentry/utils': 7.102.1
hoist-non-react-statics: 3.3.2
react: 18.2.0
dev: false
/@sentry/replay@7.99.0:
resolution: {integrity: sha512-gyN/I2WpQrLAZDT+rScB/0jnFL2knEVBo8U8/OVt8gNP20Pq8T/rDZKO/TG0cBfvULDUbJj2P4CJryn2p/O2rA==}
/@sentry/replay@7.102.1:
resolution: {integrity: sha512-HR/j9dGIvbrId8fh8mQlODx7JrhRmawEd9e9P3laPtogWCg/5TI+XPb2VGSaXOX9VWtb/6Z2UjHsaGjgg6YcuA==}
engines: {node: '>=12'}
dependencies:
'@sentry-internal/tracing': 7.99.0
'@sentry/core': 7.99.0
'@sentry/types': 7.99.0
'@sentry/utils': 7.99.0
'@sentry-internal/tracing': 7.102.1
'@sentry/core': 7.102.1
'@sentry/types': 7.102.1
'@sentry/utils': 7.102.1
dev: false
/@sentry/types@7.99.0:
resolution: {integrity: sha512-94qwOw4w40sAs5mCmzcGyj8ZUu/KhnWnuMZARRq96k+SjRW/tHFAOlIdnFSrt3BLPvSOK7R3bVAskZQ0N4FTmA==}
/@sentry/types@7.102.1:
resolution: {integrity: sha512-htKorf3t/D0XYtM7foTcmG+rM47rDP6XdbvCcX5gBCuCYlzpM1vqCt2rl3FLktZC6TaIpFRJw1TLfx6m+x5jdA==}
engines: {node: '>=8'}
dev: false
/@sentry/utils@7.99.0:
resolution: {integrity: sha512-cYZy5WNTkWs5GgggGnjfGqC44CWir0pAv4GVVSx0fsup4D4pMKBJPrtub15f9uC+QkUf3vVkqwpBqeFxtmJQTQ==}
/@sentry/utils@7.102.1:
resolution: {integrity: sha512-+8WcFjHVV/HROXSAwMuUzveElBFC43EiTG7SNEBNgOUeQzQVTmbUZXyTVgLrUmtoWqvnIxCacoLxtZo1o67kdg==}
engines: {node: '>=8'}
dependencies:
'@sentry/types': 7.99.0
'@sentry/types': 7.102.1
dev: false
/@sentry/vercel-edge@7.99.0:
resolution: {integrity: sha512-9Uw3Iuy8KMlcv71ifnaguwndb1NkHeOAbYcBEeq9W+n0f5ocFZvMlnwszSlVNAL3cK+hlpcBhelXNAO/mBWCfg==}
/@sentry/vercel-edge@7.102.1:
resolution: {integrity: sha512-iB6KCSxrvO172VjfQHGiYpyXPKNbx6Cz01GA1YDByRiUSgSpAq0qAFRY4lvd736w5KX4s9hen+XmZ7Gmqj8Pag==}
engines: {node: '>=8'}
dependencies:
'@sentry-internal/tracing': 7.99.0
'@sentry/core': 7.99.0
'@sentry/types': 7.99.0
'@sentry/utils': 7.99.0
'@sentry-internal/tracing': 7.102.1
'@sentry/core': 7.102.1
'@sentry/types': 7.102.1
'@sentry/utils': 7.102.1
dev: false
/@sentry/webpack-plugin@1.21.0(encoding@0.1.13):
@@ -9087,8 +9015,8 @@ packages:
file-system-cache: 2.3.0
dev: true
/@streamparser/json@0.0.19:
resolution: {integrity: sha512-VTxtZGME6ZSWNCSjZ0Zd2LNNodx62XvmARb/liHIwGlInuuvoDzioLuRP8ajV8iLBknsTrsqpRDGlvzKSzpNEw==}
/@streamparser/json@0.0.20:
resolution: {integrity: sha512-VqAAkydywPpkw63WQhPVKCD3SdwXuihCUVZbbiY3SfSTGQyHmwRoq27y4dmJdZuJwd5JIlQoMPyGvMbUPY0RKQ==}
dev: false
/@swc/core-darwin-arm64@1.3.101:
@@ -9828,7 +9756,7 @@ packages:
dependencies:
'@types/node': 20.11.6
tapable: 2.2.1
webpack: 5.90.1(@swc/core@1.3.101)(esbuild@0.19.11)
webpack: 5.90.3(@swc/core@1.3.101)(esbuild@0.19.11)
transitivePeerDependencies:
- '@swc/core'
- esbuild
@@ -10196,8 +10124,8 @@ packages:
yoga-wasm-web: 0.3.3
dev: false
/@vercel/speed-insights@1.0.9(next@14.1.0)(react@18.2.0):
resolution: {integrity: sha512-f+XFP0O+aZ4Olj9h+BitkB1b4NJQaxtyCb69wWuDxytJHY6Pa4QtZPdBUftHOcajUCHRVeq062fk3MKXKtjNVQ==}
/@vercel/speed-insights@1.0.10(next@14.1.0)(react@18.2.0):
resolution: {integrity: sha512-4uzdKB0RW6Ff2FkzshzjZ+RlJfLPxgm/00i0XXgxfMPhwnnsk92YgtqsxT9OcPLdJUyVU1DqFlSWWjIQMPkh0g==}
requiresBuild: true
peerDependencies:
'@sveltejs/kit': ^1 || ^2
@@ -10872,7 +10800,7 @@ packages:
/asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
/autoprefixer@10.4.14(postcss@8.4.32):
/autoprefixer@10.4.14(postcss@8.4.35):
resolution: {integrity: sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==}
engines: {node: ^10 || ^12 || >=14}
hasBin: true
@@ -10880,11 +10808,11 @@ packages:
postcss: ^8.1.0
dependencies:
browserslist: 4.22.2
caniuse-lite: 1.0.30001570
caniuse-lite: 1.0.30001580
fraction.js: 4.3.7
normalize-range: 0.1.2
picocolors: 1.0.0
postcss: 8.4.32
postcss: 8.4.35
postcss-value-parser: 4.2.0
dev: false
@@ -12338,7 +12266,7 @@ packages:
hasBin: true
dependencies:
cross-spawn: 7.0.3
dotenv: 16.4.1
dotenv: 16.4.5
dotenv-expand: 10.0.0
minimist: 1.2.8
dev: false
@@ -12351,8 +12279,8 @@ packages:
resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==}
engines: {node: '>=12'}
/dotenv@16.4.1:
resolution: {integrity: sha512-CjA3y+Dr3FyFDOAMnxZEGtnW9KBR2M0JvvUtXNW+dYJL5ROWxP9DUHCwgFqpMk0OXCc0ljhaNTr2w/kutYIcHQ==}
/dotenv@16.4.5:
resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==}
engines: {node: '>=12'}
/duplexify@3.7.1:
@@ -13480,8 +13408,8 @@ packages:
'@emotion/is-prop-valid': 0.8.8
dev: false
/framer-motion@11.0.3(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-6x2poQpIWBdbZwLd73w6cKZ1I9IEPIU94C6/Swp1Zt3LJ+sB5bPe1E2wC6EH5hSISXNkMJ4afH7AdwS7MrtkWw==}
/framer-motion@11.0.5(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-Lb0EYbQcSK/pgyQUJm+KzsQrKrJRX9sFRyzl9hSr9gFG4Mk8yP7BjhuxvRXzblOM/+JxycrJdCDVmOQBsjpYlw==}
peerDependencies:
react: ^18.0.0
react-dom: ^18.0.0
@@ -13833,8 +13761,8 @@ packages:
- supports-color
dev: false
/googleapis@131.0.0(encoding@0.1.13):
resolution: {integrity: sha512-fa4kdkY0VwHDw/04ItpQv2tlvlPIwbh6NjHDoWAVrV52GuaZbYCMOC5Y+hRmprp5HHIMRODmyb2YujlbZSRUbQ==}
/googleapis@133.0.0(encoding@0.1.13):
resolution: {integrity: sha512-6xyc49j+x7N4smawJs/q1i7mbSkt6SYUWWd9RbsmmDW7gRv+mhwZ4xT+XkPihZcNyo/diF//543WZq4szdS74w==}
engines: {node: '>=14.0.0'}
dependencies:
google-auth-library: 9.4.1(encoding@0.1.13)
@@ -15463,7 +15391,7 @@ packages:
engines: {node: '>=14.0.0'}
dependencies:
app-root-dir: 1.0.2
dotenv: 16.4.1
dotenv: 16.4.5
dotenv-expand: 10.0.0
dev: true
@@ -15753,8 +15681,8 @@ packages:
react: 18.2.0
dev: false
/lucide-react@0.321.0(react@18.2.0):
resolution: {integrity: sha512-Fi9VahIna6642U+2nAGSjnXwUBV3WyfFFPQq4yi3w30jtqxDLfSyiYCtCYCYQZ2KWNZc1MDI+rcsa0t+ChdYpw==}
/lucide-react@0.336.0(react@18.2.0):
resolution: {integrity: sha512-e06J4Qrdrk3BP/hf3MnkW3LnymdthfyEtVbdEg0xPQ/olTEKfOfTv03DYmdRm1+XpaNQdCpiHT7Vi6regbxDCQ==}
peerDependencies:
react: ^16.5.1 || ^17.0.0 || ^18.0.0
dependencies:
@@ -15851,6 +15779,23 @@ packages:
react: 18.2.0
dev: true
/marked@7.0.4:
resolution: {integrity: sha512-t8eP0dXRJMtMvBojtkcsA7n48BkauktUKzfkPSCq85ZMTJ0v76Rke4DYz01omYpPTUh4p/f7HePgRo3ebG8+QQ==}
engines: {node: '>= 16'}
hasBin: true
dev: false
/md-to-react-email@4.1.0(react-email@2.1.0)(react@18.2.0):
resolution: {integrity: sha512-aQvj4dCuy0wmBVvSB377qTErlpjN5Pl61+5v+B8Z76KoxOgKhbzvK3qnO94eOsuGSWwI+9n4zb3xD3/MypxM4w==}
peerDependencies:
react: 18.x
react-email: '>1.9.3'
dependencies:
marked: 7.0.4
react: 18.2.0
react-email: 2.1.0(eslint@8.56.0)
dev: false
/mdast-util-definitions@4.0.0:
resolution: {integrity: sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==}
dependencies:
@@ -16810,45 +16755,6 @@ packages:
- babel-plugin-macros
dev: false
/next@14.0.5-canary.46(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-u8yiAK7L+fl/U9yFmq3VOpkHlImx5wg3OoDz3qxTXhPmmMzNcPbblWgxBf5d6Z+aik8BEn27L31k/tXCRzwFxA==}
engines: {node: '>=18.17.0'}
hasBin: true
peerDependencies:
'@opentelemetry/api': ^1.1.0
react: ^18.2.0
react-dom: ^18.2.0
sass: ^1.3.0
peerDependenciesMeta:
'@opentelemetry/api':
optional: true
sass:
optional: true
dependencies:
'@next/env': 14.0.5-canary.46
'@swc/helpers': 0.5.2
busboy: 1.6.0
caniuse-lite: 1.0.30001570
graceful-fs: 4.2.11
postcss: 8.4.31
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
styled-jsx: 5.1.1(@babel/core@7.23.7)(react@18.2.0)
optionalDependencies:
'@next/swc-darwin-arm64': 14.0.5-canary.46
'@next/swc-darwin-x64': 14.0.5-canary.46
'@next/swc-linux-arm64-gnu': 14.0.5-canary.46
'@next/swc-linux-arm64-musl': 14.0.5-canary.46
'@next/swc-linux-x64-gnu': 14.0.5-canary.46
'@next/swc-linux-x64-musl': 14.0.5-canary.46
'@next/swc-win32-arm64-msvc': 14.0.5-canary.46
'@next/swc-win32-ia32-msvc': 14.0.5-canary.46
'@next/swc-win32-x64-msvc': 14.0.5-canary.46
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros
dev: false
/next@14.1.0(@babel/core@7.23.7)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-wlzrsbfeSU48YQBjZhDzOwhWhGsy+uQycR8bHAOt1LY1bn3zZEcDyHQOEoN3aWzQ8LHCAJ1nqrWCc9XF2+O45Q==}
engines: {node: '>=18.17.0'}
@@ -16940,13 +16846,13 @@ packages:
/node-releases@2.0.14:
resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
/nodemailer@6.9.8:
resolution: {integrity: sha512-cfrYUk16e67Ks051i4CntM9kshRYei1/o/Gi8K1d+R34OIs21xdFnW7Pt7EucmVKA0LKtqUGNcjMZ7ehjl49mQ==}
/nodemailer@6.9.10:
resolution: {integrity: sha512-qtoKfGFhvIFW5kLfrkw2R6Nm6Ur4LNUMykyqu6n9BRKJuyQrqEGwdXXUAbwWEKt33dlWUGXb7rzmJP/p4+O+CA==}
engines: {node: '>=6.0.0'}
dev: false
/nodemailer@6.9.9:
resolution: {integrity: sha512-dexTll8zqQoVJEZPwQAKzxxtFn0qTnjdQTchoU6Re9BUUGBJiOy3YMn/0ShTW6J5M0dfQ1NeDeRTTl4oIWgQMA==}
/nodemailer@6.9.8:
resolution: {integrity: sha512-cfrYUk16e67Ks051i4CntM9kshRYei1/o/Gi8K1d+R34OIs21xdFnW7Pt7EucmVKA0LKtqUGNcjMZ7ehjl49mQ==}
engines: {node: '>=6.0.0'}
dev: false
@@ -17584,8 +17490,17 @@ packages:
picocolors: 1.0.0
source-map-js: 1.0.2
/posthog-js@1.104.4:
resolution: {integrity: sha512-eZyNh0mhyfC129udFh5ln1QnUy67cPnRITVFvcOK4hdniM1v+T+cPxAkQK+4CjdHvvLM8hjh6OhiMWfppYqUzA==}
/postcss@8.4.35:
resolution: {integrity: sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==}
engines: {node: ^10 || ^12 || >=14}
dependencies:
nanoid: 3.3.7
picocolors: 1.0.0
source-map-js: 1.0.2
dev: false
/posthog-js@1.108.2:
resolution: {integrity: sha512-31gAeJuql5TXoxSE/69w9htHN35B1f1NN5XBEIrS9xX7LoqzIgUIuhAPFOpX4yUtsXqqWD57xEtR/2hfdEsWRA==}
dependencies:
fflate: 0.4.8
preact: 10.19.3
@@ -18091,8 +18006,8 @@ packages:
react-is: 18.1.0
dev: true
/react-email@2.0.0(eslint@8.56.0):
resolution: {integrity: sha512-XzxyWkrfZC3zF9HnAjWwB823u9eTMpAQCy+SjLMtNSh4i8WuV8Fr5LriTTz/p1RRt6aXoiV3c/ZthaDt0nvBEA==}
/react-email@2.1.0(eslint@8.56.0):
resolution: {integrity: sha512-fTt85ca1phsBu57iy32wn4LTR37rOzDZoY2AOWVq3JQYVwk6GlBdUuQWif2cudkwWINL9COf9kRMS4/QWtKtAQ==}
engines: {node: '>=18.0.0'}
hasBin: true
dependencies:
@@ -18102,13 +18017,13 @@ packages:
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-toggle-group': 1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-tooltip': 1.0.6(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@react-email/components': 0.0.14(@types/react@18.2.48)(react@18.2.0)
'@react-email/components': 0.0.15(@types/react@18.2.48)(react-email@2.1.0)(react@18.2.0)
'@react-email/render': 0.0.12
'@swc/core': 1.3.101
'@types/react': 18.2.48
'@types/react-dom': 18.2.18
'@types/webpack': 5.28.5(@swc/core@1.3.101)(esbuild@0.19.11)
autoprefixer: 10.4.14(postcss@8.4.32)
autoprefixer: 10.4.14(postcss@8.4.35)
chalk: 4.1.2
chokidar: 3.5.3
clsx: 2.1.0
@@ -18121,10 +18036,10 @@ packages:
glob: 10.3.4
log-symbols: 4.1.0
mime-types: 2.1.35
next: 14.0.5-canary.46(react-dom@18.2.0)(react@18.2.0)
next: 14.1.0(@babel/core@7.23.7)(react-dom@18.2.0)(react@18.2.0)
normalize-path: 3.0.0
ora: 5.4.1
postcss: 8.4.32
postcss: 8.4.35
prism-react-renderer: 2.1.0(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
@@ -18194,8 +18109,8 @@ packages:
react: 18.2.0
dev: false
/react-hook-form@7.50.0(react@18.2.0):
resolution: {integrity: sha512-AOhuzM3RdP09ZCnq+Z0yvKGHK25yiOX5phwxjV9L7U6HMla10ezkBnvQ+Pk4GTuDfsC5P2zza3k8mawFwFLVuQ==}
/react-hook-form@7.50.1(react@18.2.0):
resolution: {integrity: sha512-3PCY82oE0WgeOgUtIr3nYNNtNvqtJ7BZjsbxh6TnYNbXButaD5WpjOmTjdxZfheuHKR68qfeFnEDVYoSSFPMTQ==}
engines: {node: '>=12.22.0'}
peerDependencies:
react: ^16.8.0 || ^17 || ^18
@@ -19734,7 +19649,7 @@ packages:
/tailwind-merge@2.2.0:
resolution: {integrity: sha512-SqqhhaL0T06SW59+JVNfAqKdqLs0497esifRrZ7jOaefP3o64fdFNDMrAQWZFMxTLJPiHVjRLUywT8uFz1xNWQ==}
dependencies:
'@babel/runtime': 7.23.6
'@babel/runtime': 7.23.8
dev: false
/tailwind-merge@2.2.1:
@@ -19871,7 +19786,7 @@ packages:
supports-hyperlinks: 2.3.0
dev: true
/terser-webpack-plugin@5.3.10(@swc/core@1.3.101)(esbuild@0.19.11)(webpack@5.90.1):
/terser-webpack-plugin@5.3.10(@swc/core@1.3.101)(esbuild@0.19.11)(webpack@5.90.3):
resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==}
engines: {node: '>= 10.13.0'}
peerDependencies:
@@ -19894,10 +19809,10 @@ packages:
schema-utils: 3.3.0
serialize-javascript: 6.0.1
terser: 5.27.0
webpack: 5.90.1(@swc/core@1.3.101)(esbuild@0.19.11)
webpack: 5.90.3(@swc/core@1.3.101)(esbuild@0.19.11)
dev: false
/terser-webpack-plugin@5.3.10(webpack@5.90.1):
/terser-webpack-plugin@5.3.10(webpack@5.90.3):
resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==}
engines: {node: '>= 10.13.0'}
peerDependencies:
@@ -19918,7 +19833,7 @@ packages:
schema-utils: 3.3.0
serialize-javascript: 6.0.1
terser: 5.27.0
webpack: 5.90.1
webpack: 5.90.3
dev: false
/terser@5.27.0:
@@ -20993,8 +20908,8 @@ packages:
resolution: {integrity: sha512-poXpCylU7ExuvZK8z+On3kX+S8o/2dQ/SVYueKA0D4WEMXROXgY8Ez50/bQEUmvoSMMrWcrJqCHuhAbsiwg7Dg==}
dev: true
/webpack@5.90.1:
resolution: {integrity: sha512-SstPdlAC5IvgFnhiRok8hqJo/+ArAbNv7rhU4fnWGHNVfN59HSQFaxZDSAL3IFG2YmqxuRs+IU33milSxbPlog==}
/webpack@5.90.3:
resolution: {integrity: sha512-h6uDYlWCctQRuXBs1oYpVe6sFcWedl0dpcVaTf/YF67J9bKvwJajFulMVSYKHrksMB3I/pIagRzDxwxkebuzKA==}
engines: {node: '>=10.13.0'}
hasBin: true
peerDependencies:
@@ -21024,7 +20939,7 @@ packages:
neo-async: 2.6.2
schema-utils: 3.3.0
tapable: 2.2.1
terser-webpack-plugin: 5.3.10(webpack@5.90.1)
terser-webpack-plugin: 5.3.10(webpack@5.90.3)
watchpack: 2.4.0
webpack-sources: 3.2.3
transitivePeerDependencies:
@@ -21033,8 +20948,8 @@ packages:
- uglify-js
dev: false
/webpack@5.90.1(@swc/core@1.3.101)(esbuild@0.19.11):
resolution: {integrity: sha512-SstPdlAC5IvgFnhiRok8hqJo/+ArAbNv7rhU4fnWGHNVfN59HSQFaxZDSAL3IFG2YmqxuRs+IU33milSxbPlog==}
/webpack@5.90.3(@swc/core@1.3.101)(esbuild@0.19.11):
resolution: {integrity: sha512-h6uDYlWCctQRuXBs1oYpVe6sFcWedl0dpcVaTf/YF67J9bKvwJajFulMVSYKHrksMB3I/pIagRzDxwxkebuzKA==}
engines: {node: '>=10.13.0'}
hasBin: true
peerDependencies:
@@ -21064,7 +20979,7 @@ packages:
neo-async: 2.6.2
schema-utils: 3.3.0
tapable: 2.2.1
terser-webpack-plugin: 5.3.10(@swc/core@1.3.101)(esbuild@0.19.11)(webpack@5.90.1)
terser-webpack-plugin: 5.3.10(@swc/core@1.3.101)(esbuild@0.19.11)(webpack@5.90.3)
watchpack: 2.4.0
webpack-sources: 3.2.3
transitivePeerDependencies: