From a5ee4fafe57bf2ff12e2baba72c5bf05b59a128b Mon Sep 17 00:00:00 2001 From: Luke Vella Date: Sun, 21 Jul 2024 20:42:53 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=92=B8=20Create=20abstraction=20for=20han?= =?UTF-8?q?dling=20pricing=20(#1215)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 1 + apps/landing/next-env.d.ts | 1 + apps/landing/package.json | 1 + apps/landing/public/locales/en/pricing.json | 5 +- apps/landing/src/pages/pricing.tsx | 52 +++++++++--- apps/landing/tsconfig.json | 20 ++++- apps/web/package.json | 1 + apps/web/public/locales/en/app.json | 6 +- .../(admin)/settings/billing/billing-page.tsx | 18 ++--- .../settings}/billing/billing-plans.tsx | 46 ++++++++--- .../(admin)/settings/billing/page.tsx | 13 ++- apps/web/src/env.ts | 2 + apps/web/src/pages/api/stripe/checkout.ts | 8 +- apps/web/src/utils/constants.ts | 3 - packages/billing/package.json | 16 ++++ packages/billing/src/index.ts | 1 + packages/billing/src/lib/get-pricing.ts | 20 +++++ packages/billing/src/lib/stripe.ts | 35 ++++++++ packages/billing/src/next/index.ts | 23 ++++++ packages/billing/tsconfig.json | 5 ++ packages/ui/src/billing-plan.tsx | 14 +--- yarn.lock | 79 ++++++++++++++++++- 22 files changed, 302 insertions(+), 68 deletions(-) rename apps/web/src/{components => app/[locale]/(admin)/settings}/billing/billing-plans.tsx (81%) create mode 100644 packages/billing/package.json create mode 100644 packages/billing/src/index.ts create mode 100644 packages/billing/src/lib/get-pricing.ts create mode 100644 packages/billing/src/lib/stripe.ts create mode 100644 packages/billing/src/next/index.ts create mode 100644 packages/billing/tsconfig.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7a5ad9570..7e75bcf93 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,6 +64,7 @@ jobs: - name: Set environment variables run: | echo "DATABASE_URL=postgresql://postgres:postgres@localhost:5450/rallly" >> $GITHUB_ENV + echo "STRIPE_SECRET_KEY=${{ secrets.STRIPE_SECRET_KEY }}" >> $GITHUB_ENV - name: Create production build run: yarn turbo build:test --filter=@rallly/web diff --git a/apps/landing/next-env.d.ts b/apps/landing/next-env.d.ts index 4f11a03dc..fd36f9494 100644 --- a/apps/landing/next-env.d.ts +++ b/apps/landing/next-env.d.ts @@ -1,5 +1,6 @@ /// /// +/// // NOTE: This file should not be edited // see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/apps/landing/package.json b/apps/landing/package.json index 1c080c9f0..a6a74e7af 100644 --- a/apps/landing/package.json +++ b/apps/landing/package.json @@ -17,6 +17,7 @@ "@rallly/languages": "*", "@rallly/tailwind-config": "*", "@rallly/ui": "*", + "@rallly/billing": "*", "@svgr/webpack": "^6.5.1", "@vercel/analytics": "^0.1.8", "class-variance-authority": "^0.7.0", diff --git a/apps/landing/public/locales/en/pricing.json b/apps/landing/public/locales/en/pricing.json index 5589d6dda..49b5026a8 100644 --- a/apps/landing/public/locales/en/pricing.json +++ b/apps/landing/public/locales/en/pricing.json @@ -3,7 +3,6 @@ "pricingDescription": "Get started for free. No login required.", "freeForever": "free forever", "planPro": "Pro", - "annualBillingDescription": "per month, billed annually", "monthlyBillingDescription": "per month", "upgrade": "Upgrade", "faq": "Frequently Asked Questions", @@ -28,5 +27,7 @@ "planFree": "Free", "keepPollsIndefinitely": "Keep polls indefinitely", "whenPollInactive": "When does a poll become inactive?", - "whenPollInactiveAnswer": "Polls become inactive when all date options are in the past AND the poll has not been accessed for over 30 days. Inactive polls are automatically deleted if you do not have a paid subscription." + "whenPollInactiveAnswer": "Polls become inactive when all date options are in the past AND the poll has not been accessed for over 30 days. Inactive polls are automatically deleted if you do not have a paid subscription.", + "discount": "Save {amount}", + "yearlyBillingDescription": "per year" } diff --git a/apps/landing/src/pages/pricing.tsx b/apps/landing/src/pages/pricing.tsx index a8b8beff6..a4792a872 100644 --- a/apps/landing/src/pages/pricing.tsx +++ b/apps/landing/src/pages/pricing.tsx @@ -1,3 +1,6 @@ +import type { PricingData } from "@rallly/billing"; +import { getProPricing } from "@rallly/billing"; +import { Badge } from "@rallly/ui/badge"; import { BillingPlan, BillingPlanDescription, @@ -11,6 +14,7 @@ import { import { Button } from "@rallly/ui/button"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@rallly/ui/tabs"; import { TrendingUpIcon } from "lucide-react"; +import { GetStaticProps } from "next"; import Link from "next/link"; import { useTranslation } from "next-i18next"; import { NextSeo } from "next-seo"; @@ -22,9 +26,6 @@ import { linkToApp } from "@/lib/linkToApp"; import { NextPageWithLayout } from "@/types"; import { getStaticTranslations } from "@/utils/page-translations"; -const monthlyPriceUsd = 7; -const annualPriceUsd = 42; - export const UpgradeButton = ({ children, annual, @@ -48,7 +49,7 @@ export const UpgradeButton = ({ ); }; -const PriceTables = () => { +const PriceTables = ({ pricingData }: { pricingData: PricingData }) => { const [tab, setTab] = React.useState("yearly"); return ( @@ -57,8 +58,17 @@ const PriceTables = () => { - + + + + @@ -115,18 +125,20 @@ const PriceTables = () => { - - ${monthlyPriceUsd} + + ${pricingData.yearly.amount / 100} - ${monthlyPriceUsd} + + ${pricingData.monthly.amount / 100} + { ); }; -const Page: NextPageWithLayout = () => { +const Page: NextPageWithLayout<{ pricingData: PricingData }> = ({ + pricingData, +}) => { const { t } = useTranslation(["pricing"]); return (
@@ -286,7 +300,7 @@ const Page: NextPageWithLayout = () => {

- +
@@ -317,4 +331,16 @@ Page.getLayout = getPageLayout; export default Page; -export const getStaticProps = getStaticTranslations(["pricing"]); +export const getStaticProps: GetStaticProps = async (ctx) => { + const pricingData = await getProPricing(); + const res = await getStaticTranslations(["pricing"])(ctx); + if ("props" in res) { + return { + props: { + ...res.props, + pricingData, + }, + }; + } + return res; +}; diff --git a/apps/landing/tsconfig.json b/apps/landing/tsconfig.json index 00fe2e1a7..2cc56f4ed 100644 --- a/apps/landing/tsconfig.json +++ b/apps/landing/tsconfig.json @@ -3,11 +3,23 @@ "compilerOptions": { "baseUrl": ".", "paths": { - "@/*": ["src/*"], - "~/*": ["public/*"], + "@/*": [ + "src/*" + ], + "~/*": [ + "public/*" + ] }, "checkJs": false, + "strictNullChecks": true }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"], + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts" + ], + "exclude": [ + "node_modules" + ] } diff --git a/apps/web/package.json b/apps/web/package.json index ecd5aee1e..2889a51eb 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -27,6 +27,7 @@ "@rallly/backend": "*", "@rallly/database": "*", "@rallly/icons": "*", + "@rallly/billing": "*", "@rallly/languages": "*", "@rallly/tailwind-config": "*", "@rallly/ui": "*", diff --git a/apps/web/public/locales/en/app.json b/apps/web/public/locales/en/app.json index 1ef1f4117..889373a7b 100644 --- a/apps/web/public/locales/en/app.json +++ b/apps/web/public/locales/en/app.json @@ -139,7 +139,6 @@ "billingStatus": "Billing Status", "billingStatusDescription": "Manage your subscription and billing details", "freeForever": "free forever", - "annualBillingDescription": "per month, billed annually", "billingStatusState": "Status", "billingStatusActive": "Active", "billingStatusPaused": "Paused", @@ -228,7 +227,6 @@ "autoTimeZoneHelp": "Enable this setting to automatically adjust event times to each participant's local time zone.", "commentsDisabled": "Comments have been disabled", "allParticipants": "All Participants", - "pollsListAll": "All", "noParticipantsDescription": "Click Share to invite participants", "timeShownIn": "Times shown in {timeZone}", "pollStatusPausedDescription": "Votes cannot be submitted or edited at this time", @@ -263,5 +261,7 @@ "pastEventsEmptyStateTitle": "No Past Events", "pastEventsEmptyStateDescription": "When you schedule events, they will appear here.", "activePollCount": "{{activePollCount}} Live", - "createPoll": "Create poll" + "createPoll": "Create poll", + "yearlyDiscount": "Save {amount}", + "yearlyBillingDescription": "per year" } diff --git a/apps/web/src/app/[locale]/(admin)/settings/billing/billing-page.tsx b/apps/web/src/app/[locale]/(admin)/settings/billing/billing-page.tsx index 296da19eb..ce7f01c41 100644 --- a/apps/web/src/app/[locale]/(admin)/settings/billing/billing-page.tsx +++ b/apps/web/src/app/[locale]/(admin)/settings/billing/billing-page.tsx @@ -4,12 +4,9 @@ import { Card } from "@rallly/ui/card"; import { Label } from "@rallly/ui/label"; import dayjs from "dayjs"; import { ArrowUpRight, CreditCardIcon, SendIcon } from "lucide-react"; -import Head from "next/head"; import Link from "next/link"; import Script from "next/script"; -import { useTranslation } from "next-i18next"; -import { BillingPlans } from "@/components/billing/billing-plans"; import { Settings, SettingsContent, @@ -19,6 +16,8 @@ import { Trans } from "@/components/trans"; import { useSubscription } from "@/contexts/plan"; import { trpc } from "@/utils/trpc/client"; +import { BillingPlans, PricingData } from "./billing-plans"; + declare global { interface Window { // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -57,7 +56,7 @@ const BillingPortal = () => { const proPlanIdMonthly = process.env.NEXT_PUBLIC_PRO_PLAN_ID_MONTHLY as string; -const SubscriptionStatus = () => { +const SubscriptionStatus = ({ pricingData }: { pricingData: PricingData }) => { const data = useSubscription(); if (!data) { @@ -67,7 +66,7 @@ const SubscriptionStatus = () => { return (
{!data.active ? ( - + ) : data.legacy ? ( ) : ( @@ -238,16 +237,11 @@ const LegacyBilling = () => {
; -export function BillingPage() { - const { t } = useTranslation(); - +export function BillingPage({ pricingData }: { pricingData: PricingData }) { return ( - - {t("billing")} - - +
} diff --git a/apps/web/src/components/billing/billing-plans.tsx b/apps/web/src/app/[locale]/(admin)/settings/billing/billing-plans.tsx similarity index 81% rename from apps/web/src/components/billing/billing-plans.tsx rename to apps/web/src/app/[locale]/(admin)/settings/billing/billing-plans.tsx index c75f7260f..b4bf4821b 100644 --- a/apps/web/src/components/billing/billing-plans.tsx +++ b/apps/web/src/app/[locale]/(admin)/settings/billing/billing-plans.tsx @@ -1,3 +1,4 @@ +import { Badge } from "@rallly/ui/badge"; import { BillingPlan, BillingPlanDescription, @@ -15,9 +16,21 @@ import React from "react"; import { Trans } from "@/components/trans"; import { UpgradeButton } from "@/components/upgrade-button"; -import { annualPriceUsd, monthlyPriceUsd } from "@/utils/constants"; -export const BillingPlans = () => { +export type PricingData = { + monthly: { + id: string; + amount: number; + currency: string; + }; + yearly: { + id: string; + amount: number; + currency: string; + }; +}; + +export const BillingPlans = ({ pricingData }: { pricingData: PricingData }) => { const [tab, setTab] = React.useState("yearly"); return ( @@ -28,8 +41,21 @@ export const BillingPlans = () => { - + + + +
@@ -85,20 +111,20 @@ export const BillingPlans = () => {
- - ${monthlyPriceUsd} + + ${pricingData.yearly.amount / 100} - ${monthlyPriceUsd} + + ${pricingData.monthly.amount / 100} + ; + if (env.NEXT_PUBLIC_SELF_HOSTED === "true") { + notFound(); + } + const prices = await getProPricing(); + return ; } export async function generateMetadata({ params }: { params: Params }) { diff --git a/apps/web/src/env.ts b/apps/web/src/env.ts index 81087bf8f..f621462eb 100644 --- a/apps/web/src/env.ts +++ b/apps/web/src/env.ts @@ -58,6 +58,7 @@ export const env = createEnv({ client: { NEXT_PUBLIC_POSTHOG_API_KEY: z.string().optional(), NEXT_PUBLIC_POSTHOG_API_HOST: z.string().url().optional(), + NEXT_PUBLIC_SELF_HOSTED: z.enum(["true", "false"]).optional(), }, /* * Due to how Next.js bundles environment variables on Edge and Client, @@ -88,6 +89,7 @@ export const env = createEnv({ ALLOWED_EMAILS: process.env.ALLOWED_EMAILS, NEXT_PUBLIC_POSTHOG_API_KEY: process.env.NEXT_PUBLIC_POSTHOG_API_KEY, NEXT_PUBLIC_POSTHOG_API_HOST: process.env.NEXT_PUBLIC_POSTHOG_API_HOST, + NEXT_PUBLIC_SELF_HOSTED: process.env.NEXT_PUBLIC_SELF_HOSTED, }, skipValidation: !!process.env.SKIP_ENV_VALIDATION, }); diff --git a/apps/web/src/pages/api/stripe/checkout.ts b/apps/web/src/pages/api/stripe/checkout.ts index add865a59..b4d1d08be 100644 --- a/apps/web/src/pages/api/stripe/checkout.ts +++ b/apps/web/src/pages/api/stripe/checkout.ts @@ -1,4 +1,4 @@ -import { stripe } from "@rallly/backend/stripe"; +import { getProPricing, stripe } from "@rallly/billing"; import { prisma } from "@rallly/database"; import { NextApiRequest, NextApiResponse } from "next"; import { z } from "zod"; @@ -61,6 +61,8 @@ export default async function handler( return; } + const proPricingData = await getProPricing(); + const session = await stripe.checkout.sessions.create({ success_url: absoluteUrl( return_path ?? "/api/stripe/portal?session_id={CHECKOUT_SESSION_ID}", @@ -97,8 +99,8 @@ export default async function handler( { price: period === "yearly" - ? (process.env.STRIPE_YEARLY_PRICE as string) - : (process.env.STRIPE_MONTHLY_PRICE as string), + ? proPricingData.yearly.id + : proPricingData.monthly.id, quantity: 1, }, ], diff --git a/apps/web/src/utils/constants.ts b/apps/web/src/utils/constants.ts index ad748850a..28a4aa3d3 100644 --- a/apps/web/src/utils/constants.ts +++ b/apps/web/src/utils/constants.ts @@ -8,7 +8,4 @@ export const isSelfHosted = process.env.NEXT_PUBLIC_SELF_HOSTED === "true"; export const isFeedbackEnabled = false; -export const monthlyPriceUsd = 7; - -export const annualPriceUsd = 42; export const appVersion = process.env.NEXT_PUBLIC_APP_VERSION; diff --git a/packages/billing/package.json b/packages/billing/package.json new file mode 100644 index 000000000..1fdcda1d3 --- /dev/null +++ b/packages/billing/package.json @@ -0,0 +1,16 @@ +{ + "name": "@rallly/billing", + "version": "0.0.0", + "private": true, + "exports": { + "./server/*": "./src/server/*.tsx", + "./next": "./src/next/index.ts", + ".": "./src/index.ts" + }, + "dependencies": { + "@rallly/ui": "*", + "stripe": "^13.2.0", + "@radix-ui/react-radio-group": "^1.2.0", + "next": "*" + } +} diff --git a/packages/billing/src/index.ts b/packages/billing/src/index.ts new file mode 100644 index 000000000..63fc923c0 --- /dev/null +++ b/packages/billing/src/index.ts @@ -0,0 +1 @@ +export * from "./lib/stripe"; diff --git a/packages/billing/src/lib/get-pricing.ts b/packages/billing/src/lib/get-pricing.ts new file mode 100644 index 000000000..27b31af05 --- /dev/null +++ b/packages/billing/src/lib/get-pricing.ts @@ -0,0 +1,20 @@ +import { stripe } from ".."; + +export async function getPricing() { + const prices = await stripe.prices.list({ + lookup_keys: ["pro-monthly", "pro-yearly"], + }); + + const [monthly, yearly] = prices.data; + + return { + monthly: { + currency: monthly.currency, + price: monthly.unit_amount_decimal, + }, + yearly: { + currency: yearly.currency, + price: yearly.unit_amount, + }, + }; +} diff --git a/packages/billing/src/lib/stripe.ts b/packages/billing/src/lib/stripe.ts new file mode 100644 index 000000000..b8fe2cb60 --- /dev/null +++ b/packages/billing/src/lib/stripe.ts @@ -0,0 +1,35 @@ +import Stripe from "stripe"; + +export type { Stripe } from "stripe"; + +export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY as string, { + apiVersion: "2023-08-16", + typescript: true, +}); + +export async function getProPricing() { + const prices = await stripe.prices.list({ + lookup_keys: ["pro-monthly", "pro-yearly"], + }); + + const [monthly, yearly] = prices.data; + + if (monthly.unit_amount === null || yearly.unit_amount === null) { + throw new Error("Price not found"); + } + + return { + monthly: { + id: monthly.id, + amount: monthly.unit_amount, + currency: monthly.currency, + }, + yearly: { + id: yearly.id, + amount: yearly.unit_amount, + currency: yearly.currency, + }, + }; +} + +export type PricingData = Awaited>; diff --git a/packages/billing/src/next/index.ts b/packages/billing/src/next/index.ts new file mode 100644 index 000000000..9b9be3d70 --- /dev/null +++ b/packages/billing/src/next/index.ts @@ -0,0 +1,23 @@ +import { NextRequest, NextResponse } from "next/server"; +import { getPricing } from "../lib/get-pricing"; + +export async function GET( + request: NextRequest, + { + params, + }: { + params: { + method: string; + }; + }, +) { + switch (params.method) { + case "pricing": + const data = await getPricing(); + return NextResponse.json(data); + default: + return NextResponse.json({ message: "Method not found" }); + } +} + +export const handlers = { GET }; diff --git a/packages/billing/tsconfig.json b/packages/billing/tsconfig.json new file mode 100644 index 000000000..beac8603e --- /dev/null +++ b/packages/billing/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "@rallly/tsconfig/next.json", + "include": ["**/*.ts", "**/*.tsx", "**/*.js"], + "exclude": ["node_modules"], +} diff --git a/packages/ui/src/billing-plan.tsx b/packages/ui/src/billing-plan.tsx index 980d2acb7..2c8dd9e48 100644 --- a/packages/ui/src/billing-plan.tsx +++ b/packages/ui/src/billing-plan.tsx @@ -37,20 +37,10 @@ export const BillingPlanDescription = ({ export const BillingPlanPrice = ({ children, - discount, }: React.PropsWithChildren<{ discount?: React.ReactNode }>) => { return ( -
- {discount ? ( - <> - - {children} - - {discount} - - ) : ( - {children} - )} +
+ {children}
); }; diff --git a/yarn.lock b/yarn.lock index d59a729f5..a246f2592 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2293,6 +2293,11 @@ resolved "https://registry.yarnpkg.com/@next/env/-/env-14.0.5-canary.46.tgz#b9b597baaba77a2836eaf836712a6e0afed1ca2d" integrity sha512-dvNzrArTfe3VY1VIscpb3E2e7SZ1qwFe82WGzpOVbxilT3JcsnVGYF/uq8Jj1qKWPI5C/aePNXwA97JRNAXpRQ== +"@next/env@14.1.0": + version "14.1.0" + resolved "https://registry.yarnpkg.com/@next/env/-/env-14.1.0.tgz#43d92ebb53bc0ae43dcc64fb4d418f8f17d7a341" + integrity sha512-Py8zIo+02ht82brwwhTg36iogzFqGLPXlRGKQw5s+qP/kMNc4MAyDeEwBKDijk6zTIbegEgu8Qy7C1LboslQAw== + "@next/env@14.2.4": version "14.2.4" resolved "https://registry.yarnpkg.com/@next/env/-/env-14.2.4.tgz#5546813dc4f809884a37d257b254a5ce1b0248d7" @@ -2310,6 +2315,11 @@ resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.0.5-canary.46.tgz#94c67fa212614892f94db120c92a9f4207da13b8" integrity sha512-7Bq9rjWl4sq70Zkn6h6mn8/tgYTH2SQ8lIm8b/j1MAnTiJYyVBLapu//gT/cgtqx6y8SwSc2JNviBue35zeCNw== +"@next/swc-darwin-arm64@14.1.0": + version "14.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.1.0.tgz#70a57c87ab1ae5aa963a3ba0f4e59e18f4ecea39" + integrity sha512-nUDn7TOGcIeyQni6lZHfzNoo9S0euXnu0jhsbMOmMJUBfgsnESdjN97kM7cBqQxZa8L/bM9om/S5/1dzCrW6wQ== + "@next/swc-darwin-arm64@14.2.4": version "14.2.4" resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.4.tgz#da9f04c34a3d5f0b8401ed745768420e4a604036" @@ -2320,6 +2330,11 @@ resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-14.0.5-canary.46.tgz#25e2a5acfc5b20d3a25ad6adcfbfc91aaa44d79f" integrity sha512-3oI8rDVBZsfkTdqXwtRjxA85o0RIjZv9uuOLohfaIuFP3oZnCM0dRZREP2umYcFQRxdavW+TDJzYcqzKxYTujA== +"@next/swc-darwin-x64@14.1.0": + version "14.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-14.1.0.tgz#0863a22feae1540e83c249384b539069fef054e9" + integrity sha512-1jgudN5haWxiAl3O1ljUS2GfupPmcftu2RYJqZiMJmmbBT5M1XDffjUtRUzP4W3cBHsrvkfOFdQ71hAreNQP6g== + "@next/swc-darwin-x64@14.2.4": version "14.2.4" resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.4.tgz#46dedb29ec5503bf171a72a3ecb8aac6e738e9d6" @@ -2330,6 +2345,11 @@ resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.0.5-canary.46.tgz#00fad5be6cada895e513d81427c462c92abdae3f" integrity sha512-gXSS328bUWxBwQfeDFROOzFSzzoyX1075JxOeArLl63sV59cbnRrwHHhD4CWG1bYYzcHxHfVugZgvyCucaHCIw== +"@next/swc-linux-arm64-gnu@14.1.0": + version "14.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.1.0.tgz#893da533d3fce4aec7116fe772d4f9b95232423c" + integrity sha512-RHo7Tcj+jllXUbK7xk2NyIDod3YcCPDZxj1WLIYxd709BQ7WuRYl3OWUNG+WUfqeQBds6kvZYlc42NJJTNi4tQ== + "@next/swc-linux-arm64-gnu@14.2.4": version "14.2.4" resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.4.tgz#c9697ab9eb422bd1d7ffd0eb0779cc2aefa9d4a1" @@ -2340,6 +2360,11 @@ resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.0.5-canary.46.tgz#a931a1312d3f5e66ea59c4b23e0ae90721f8e252" integrity sha512-7QkBRKlDsjaWGbfIKh6qJK0HiHJISNGoKpwFTcnZvlhAEaydS5Hmu0zh64kbLRlzwXtkpj6/iCwjrWnHes59aA== +"@next/swc-linux-arm64-musl@14.1.0": + version "14.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.1.0.tgz#d81ddcf95916310b8b0e4ad32b637406564244c0" + integrity sha512-v6kP8sHYxjO8RwHmWMJSq7VZP2nYCkRVQ0qolh2l6xroe9QjbgV8siTbduED4u0hlk0+tjS6/Tuy4n5XCp+l6g== + "@next/swc-linux-arm64-musl@14.2.4": version "14.2.4" resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.4.tgz#cbbceb2008571c743b5a310a488d2e166d200a75" @@ -2350,6 +2375,11 @@ resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.0.5-canary.46.tgz#32bf69fa93975ca3fef141121eaa8a1a67086694" integrity sha512-DS5wTjw3FtcLFVzRxLMJgmDNMoeaXp5qBdKUSBrKTq4zQnqUi99CGz2461DlUSxJCWPUgAVo23MdoQD6Siuk7A== +"@next/swc-linux-x64-gnu@14.1.0": + version "14.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.1.0.tgz#18967f100ec19938354332dcb0268393cbacf581" + integrity sha512-zJ2pnoFYB1F4vmEVlb/eSe+VH679zT1VdXlZKX+pE66grOgjmKJHKacf82g/sWE4MQ4Rk2FMBCRnX+l6/TVYzQ== + "@next/swc-linux-x64-gnu@14.2.4": version "14.2.4" resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.4.tgz#d79184223f857bacffb92f643cb2943a43632568" @@ -2360,6 +2390,11 @@ resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.0.5-canary.46.tgz#59f221d83096b0362849fabbcda1fdc1671cf6b1" integrity sha512-d409ur5JGj6HFp8DBu5M2oTh5EddDcrT+vjewQkAq/A7MZoAMAOH74xOFouEnJs0/dQ71XvH9Lw+1gJSnElcyQ== +"@next/swc-linux-x64-musl@14.1.0": + version "14.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.1.0.tgz#77077cd4ba8dda8f349dc7ceb6230e68ee3293cf" + integrity sha512-rbaIYFt2X9YZBSbH/CwGAjbBG2/MrACCVu2X0+kSykHzHnYH5FjHxwXLkcoJ10cX0aWCEynpu+rP76x0914atg== + "@next/swc-linux-x64-musl@14.2.4": version "14.2.4" resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.4.tgz#6b6c3e5ac02ca5e63394d280ec8ee607491902df" @@ -2370,6 +2405,11 @@ resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.0.5-canary.46.tgz#465d24227cd1b8840b85ee488327725e478da221" integrity sha512-goyh/RCFtivflIOvbwircMxTSObETufm3pcxtI8rIz9+pg/M2MmK8/z48EZybkEcPKl41xu4s1iqXThy/jDPng== +"@next/swc-win32-arm64-msvc@14.1.0": + version "14.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.1.0.tgz#5f0b8cf955644104621e6d7cc923cad3a4c5365a" + integrity sha512-o1N5TsYc8f/HpGt39OUQpQ9AKIGApd3QLueu7hXk//2xq5Z9OxmV6sQfNp8C7qYmiOlHYODOGqNNa0e9jvchGQ== + "@next/swc-win32-arm64-msvc@14.2.4": version "14.2.4" resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.4.tgz#dbad3906e870dba84c5883d9d4c4838472e0697f" @@ -2380,6 +2420,11 @@ resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.0.5-canary.46.tgz#0a65de42dcb8a8293ee0f8e3082d4d8c326f3d12" integrity sha512-SEnrOZ7ASXdd/GBq2x0IfpSbfamv1rZfcDeZZLF7kzu0pY7jDQwcW8zTKwwC8JH5CLGLfI3wD6wUYrA+PgJSCw== +"@next/swc-win32-ia32-msvc@14.1.0": + version "14.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.1.0.tgz#21f4de1293ac5e5a168a412b139db5d3420a89d0" + integrity sha512-XXIuB1DBRCFwNO6EEzCTMHT5pauwaSj4SWs7CYnME57eaReAKBXCnkUE80p/pAZcewm7hs+vGvNqDPacEXHVkw== + "@next/swc-win32-ia32-msvc@14.2.4": version "14.2.4" resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.4.tgz#6074529b91ba49132922ce89a2e16d25d2ec235d" @@ -2390,6 +2435,11 @@ resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.0.5-canary.46.tgz#e19326097b306c58eb47984acf7f7eca4485b604" integrity sha512-NK1EJLyeUxgX9IHSxO0kN1Nk8VsaDfjHVYL4p9fM24e/9rG8jPcxquIQJ4Wy+ZdqxaVivqQ2eHrJYUpXpfOXmw== +"@next/swc-win32-x64-msvc@14.1.0": + version "14.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.1.0.tgz#e561fb330466d41807123d932b365cf3d33ceba2" + integrity sha512-9WEbVRRAqJ3YFVqEZIxUqkiO8l1nool1LmNxygr5HWF8AcSYsEpneUDhmjUVJEzO2A04+oPtZdombzzPPkTtgg== + "@next/swc-win32-x64-msvc@14.2.4": version "14.2.4" resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.4.tgz#e65a1c6539a671f97bb86d5183d6e3a1733c29c7" @@ -6344,9 +6394,9 @@ caniuse-lite@^1.0.30001406, caniuse-lite@^1.0.30001426, caniuse-lite@^1.0.300014 integrity sha512-c3dl911slnQhmxUIT4HhYzT7wnBK/XYpGnYLOj4nJBaRiw52Ibe7YxlDaAeRECvA786zCuExhxIUJ2K7nHMrBw== caniuse-lite@^1.0.30001579: - version "1.0.30001633" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001633.tgz#45a4ade9fb9ec80a06537a6271ac1e0afadcb324" - integrity sha512-6sT0yf/z5jqf8tISAgpJDrmwOpLsrpnyCdD/lOZKvKkkJK4Dn0X5i7KF7THEZhOq+30bmhwBlNEaqPUiHiKtZg== + version "1.0.30001583" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001583.tgz#abb2970cc370801dc7e27bf290509dc132cfa390" + integrity sha512-acWTYaha8xfhA/Du/z4sNZjHUWjkiuoAi2LM+T/aL+kemKQgPT1xBb/YKjlQ0Qo8gvbHsGNplrEJ+9G3gL7i4Q== caniuse-lite@^1.0.30001629: version "1.0.30001636" @@ -10367,6 +10417,29 @@ next-seo@^6.1.0: resolved "https://registry.npmjs.org/next-seo/-/next-seo-6.1.0.tgz" integrity sha512-iMBpFoJsR5zWhguHJvsoBDxDSmdYTHtnVPB1ij+CD0NReQCP78ZxxbdL9qkKIf4oEuZEqZkrjAQLB0bkII7RYA== +next@*: + version "14.1.0" + resolved "https://registry.yarnpkg.com/next/-/next-14.1.0.tgz#b31c0261ff9caa6b4a17c5af019ed77387174b69" + integrity sha512-wlzrsbfeSU48YQBjZhDzOwhWhGsy+uQycR8bHAOt1LY1bn3zZEcDyHQOEoN3aWzQ8LHCAJ1nqrWCc9XF2+O45Q== + dependencies: + "@next/env" "14.1.0" + "@swc/helpers" "0.5.2" + busboy "1.6.0" + caniuse-lite "^1.0.30001579" + graceful-fs "^4.2.11" + postcss "8.4.31" + styled-jsx "5.1.1" + optionalDependencies: + "@next/swc-darwin-arm64" "14.1.0" + "@next/swc-darwin-x64" "14.1.0" + "@next/swc-linux-arm64-gnu" "14.1.0" + "@next/swc-linux-arm64-musl" "14.1.0" + "@next/swc-linux-x64-gnu" "14.1.0" + "@next/swc-linux-x64-musl" "14.1.0" + "@next/swc-win32-arm64-msvc" "14.1.0" + "@next/swc-win32-ia32-msvc" "14.1.0" + "@next/swc-win32-x64-msvc" "14.1.0" + next@14.0.5-canary.46: version "14.0.5-canary.46" resolved "https://registry.yarnpkg.com/next/-/next-14.0.5-canary.46.tgz#003c8588488fd72bec70bcd4ea2f9ac65fd873f3"