diff --git a/.github/template-1-after.pdf b/.github/template-1-after.pdf new file mode 100644 index 000000000..acb29f691 Binary files /dev/null and b/.github/template-1-after.pdf differ diff --git a/.github/template-1-after.png b/.github/template-1-after.png new file mode 100644 index 000000000..f8de24ea0 Binary files /dev/null and b/.github/template-1-after.png differ diff --git a/.github/template-1-before.pdf b/.github/template-1-before.pdf new file mode 100644 index 000000000..5312da551 Binary files /dev/null and b/.github/template-1-before.pdf differ diff --git a/.github/template-1-before.png b/.github/template-1-before.png new file mode 100644 index 000000000..6e15f4746 Binary files /dev/null and b/.github/template-1-before.png differ diff --git a/.github/template-1-comparison.png b/.github/template-1-comparison.png new file mode 100644 index 000000000..6d2114fed Binary files /dev/null and b/.github/template-1-comparison.png differ diff --git a/.github/template-1-diff.png b/.github/template-1-diff.png new file mode 100644 index 000000000..e0d76c2d4 Binary files /dev/null and b/.github/template-1-diff.png differ diff --git a/.github/template-2-after.pdf b/.github/template-2-after.pdf new file mode 100644 index 000000000..b83d145d1 Binary files /dev/null and b/.github/template-2-after.pdf differ diff --git a/.github/template-2-after.png b/.github/template-2-after.png new file mode 100644 index 000000000..dec4949f9 Binary files /dev/null and b/.github/template-2-after.png differ diff --git a/.github/template-2-before.pdf b/.github/template-2-before.pdf new file mode 100644 index 000000000..2a77c93dd Binary files /dev/null and b/.github/template-2-before.pdf differ diff --git a/.github/template-2-before.png b/.github/template-2-before.png new file mode 100644 index 000000000..ce26eb7d2 Binary files /dev/null and b/.github/template-2-before.png differ diff --git a/.github/template-2-comparison.png b/.github/template-2-comparison.png new file mode 100644 index 000000000..e2bb38f2f Binary files /dev/null and b/.github/template-2-comparison.png differ diff --git a/.github/template-2-diff.png b/.github/template-2-diff.png new file mode 100644 index 000000000..64d3c3138 Binary files /dev/null and b/.github/template-2-diff.png differ diff --git a/app/api/invoice/generate/route.ts b/app/api/invoice/generate/route.ts index bcb94419a..dc5504493 100644 --- a/app/api/invoice/generate/route.ts +++ b/app/api/invoice/generate/route.ts @@ -1,5 +1,5 @@ export const runtime = "nodejs"; -export const maxDuration = 60; +export const maxDuration = 10; import { NextRequest } from "next/server"; diff --git a/app/components/templates/invoice-pdf/DynamicInvoiceTemplate.tsx b/app/components/templates/invoice-pdf/DynamicInvoiceTemplate.tsx index e0f91d831..c4dab0bac 100644 --- a/app/components/templates/invoice-pdf/DynamicInvoiceTemplate.tsx +++ b/app/components/templates/invoice-pdf/DynamicInvoiceTemplate.tsx @@ -12,7 +12,6 @@ const DynamicInvoiceTemplateSkeleton = () => { }; const DynamicInvoiceTemplate = (props: InvoiceType) => { - // Dynamic template component name const templateName = `InvoiceTemplate${props.details.pdfTemplate}`; const DynamicInvoice = useMemo( diff --git a/app/components/templates/invoice-pdf/InvoiceTemplate1.data.json b/app/components/templates/invoice-pdf/InvoiceTemplate1.data.json new file mode 100644 index 000000000..4975a6ef2 --- /dev/null +++ b/app/components/templates/invoice-pdf/InvoiceTemplate1.data.json @@ -0,0 +1,75 @@ +{ + "sender": { + "name": "John Doe", + "address": "123 Main St", + "zipCode": "12345", + "city": "Anytown", + "country": "USA", + "email": "johndoe@example.com", + "phone": "123-456-7890" + }, + "receiver": { + "name": "Jane Smith", + "address": "456 Elm St", + "zipCode": "54321", + "city": "Other Town", + "country": "Canada", + "email": "jane@example.com", + "phone": "555-987-6543" + }, + "details": { + "invoiceLogo": "", + "invoiceNumber": "INV0001", + "invoiceDate": "March 24, 2026", + "dueDate": "March 24, 2026", + "currency": "USD", + "language": "English", + "items": [ + { + "name": "Product 1", + "description": "Description of product 1", + "quantity": 4, + "unitPrice": 50, + "total": 200 + }, + { + "name": "Product 2", + "description": "Description of product 2", + "quantity": 5, + "unitPrice": 50, + "total": 250 + }, + { + "name": "Product 3", + "description": "Description of product 3", + "quantity": 5, + "unitPrice": 80, + "total": 400 + } + ], + "paymentInformation": { + "bankName": "Bank Inc.", + "accountName": "John Doe", + "accountNumber": "445566998877" + }, + "taxDetails": { + "amount": 15, + "taxID": "TAX-123", + "amountType": "percentage" + }, + "discountDetails": { + "amount": 5, + "amountType": "percentage" + }, + "shippingDetails": { + "cost": 5, + "costType": "amount" + }, + "subTotal": 850, + "totalAmount": 975.06, + "totalAmountInWords": "Nine hundred seventy-five Dollar and six Cents", + "additionalNotes": "Thank you for your business", + "paymentTerms": "Net 30", + "pdfTemplate": 1 + } +} diff --git a/app/components/templates/invoice-pdf/InvoiceTemplate1.tsx b/app/components/templates/invoice-pdf/InvoiceTemplate1.tsx index 01999e3ef..17807c847 100644 --- a/app/components/templates/invoice-pdf/InvoiceTemplate1.tsx +++ b/app/components/templates/invoice-pdf/InvoiceTemplate1.tsx @@ -1,230 +1,253 @@ import React from "react"; -// Components -import { InvoiceLayout } from "@/app/components"; +import { Document, Page, View, Text, Image } from "@formepdf/react"; +import { tw } from "@formepdf/tailwind"; +import { readFileSync } from "fs"; +import { join } from "path"; // Helpers -import { formatNumberWithCommas, isDataUrl } from "@/lib/helpers"; - -// Variables -import { DATE_OPTIONS } from "@/lib/variables"; +import { formatNumberWithCommas, isDataUrl } from "@/lib/pdf-helpers"; // Types import { InvoiceType } from "@/types"; +// Load Outfit font family. +// In Next.js, process.cwd() is the project root so readFileSync works. +// In VS Code preview, cwd differs so readFileSync throws ENOENT — fall back +// to a relative path string which the renderer resolves from this file's dir. +const fontsDir = join(process.cwd(), "fonts"); +const fontFiles = ["Outfit-Regular.ttf", "Outfit-Medium.ttf", "Outfit-SemiBold.ttf", "Outfit-Bold.ttf"] as const; +const fontWeights = [400, 500, 600, 700] as const; + +function loadFont(file: string): string | Buffer { + try { + return readFileSync(join(fontsDir, file)); + } catch { + // VS Code preview: fs unavailable or wrong cwd — use relative path + return join("../../../../fonts", file); + } +} + +const outfitFonts = fontFiles.map((file, i) => ({ + family: "Outfit" as const, + src: loadFont(file), + fontWeight: fontWeights[i], +})); + const InvoiceTemplate = (data: InvoiceType) => { const { sender, receiver, details } = data; return ( - -
-
- {details.invoiceLogo && ( - {`Logo - )} -

{sender.name}

-
-
-

Invoice #

- {details.invoiceNumber} -
- {sender.address} -
- {sender.zipCode}, {sender.city} -
- {sender.country} -
-
-
-
- -
-
-

Bill to:

-

{receiver.name}

- {} -
- {receiver.address && receiver.address.length > 0 ? receiver.address : null} - {receiver.zipCode && receiver.zipCode.length > 0 ? `, ${receiver.zipCode}` : null} -
- {receiver.city}, {receiver.country} -
-
-
-
-
-
-
Invoice date:
-
- {new Date(details.invoiceDate).toLocaleDateString("en-US", DATE_OPTIONS)} -
-
-
-
Due date:
-
- {new Date(details.dueDate).toLocaleDateString("en-US", DATE_OPTIONS)} -
-
-
-
-
- -
-
-
-
Item
-
Qty
-
Rate
-
Amount
-
-
-
- {details.items.map((item, index) => ( - -
-

{item.name}

-

{item.description}

-
-
-

{item.quantity}

-
-
-

- {item.unitPrice} {details.currency} -

-
-
-

- {item.total} {details.currency} -

-
-
- ))} -
-
-
-
- -
-
-
-
-
Subtotal:
-
- {formatNumberWithCommas(Number(details.subTotal))} {details.currency} -
-
- {details.discountDetails?.amount != undefined && - details.discountDetails?.amount > 0 && ( -
-
Discount:
-
+ + + {/* Header */} + + + {details.invoiceLogo && details.invoiceLogo !== "" && isDataUrl(details.invoiceLogo) ? ( + + ) : null} + {sender.name} + + + Invoice # + {details.invoiceNumber} + + {sender.address} + {sender.zipCode}, {sender.city} + {sender.country} + + + + + {/* Bill To + Dates */} + + + Bill to: + {receiver.name} + + + {[receiver.address, receiver.zipCode].filter(Boolean).join(", ")} + + {receiver.city}, {receiver.country} + + + + + Invoice date: + + {details.invoiceDate} + + + + Due date: + + {details.dueDate} + + + + + + {/* Items Table */} + + {/* Header Row */} + + + Item + + + Qty + + + Rate + + + Amount + + + + + {/* Item Rows */} + {details.items.map((item, index) => ( + + + {item.name} + {item.description} + + + {item.quantity} + + + {item.unitPrice} {details.currency} + + + {item.total} {details.currency} + + + ))} + + + {/* Totals */} + + + + + Subtotal: + + + {formatNumberWithCommas(Number(details.subTotal))} {details.currency} + + + {details.discountDetails?.amount != undefined && details.discountDetails?.amount > 0 && ( + + + Discount: + + + {details.discountDetails.amountType === "amount" ? `- ${details.discountDetails.amount} ${details.currency}` : `- ${details.discountDetails.amount}%`} -
-
- )} + + + + )} {details.taxDetails?.amount != undefined && details.taxDetails?.amount > 0 && ( -
-
Tax:
-
- {details.taxDetails.amountType === "amount" - ? `+ ${details.taxDetails.amount} ${details.currency}` - : `+ ${details.taxDetails.amount}%`} -
-
+ + + Tax: + + + + {details.taxDetails.amountType === "amount" + ? `+ ${details.taxDetails.amount} ${details.currency}` + : `+ ${details.taxDetails.amount}%`} + + + )} {details.shippingDetails?.cost != undefined && details.shippingDetails?.cost > 0 && ( -
-
Shipping:
-
- {details.shippingDetails.costType === "amount" - ? `+ ${details.shippingDetails.cost} ${details.currency}` - : `+ ${details.shippingDetails.cost}%`} -
-
+ + + Shipping: + + + + {details.shippingDetails.costType === "amount" + ? `+ ${details.shippingDetails.cost} ${details.currency}` + : `+ ${details.shippingDetails.cost}%`} + + + )} -
-
Total:
-
- {formatNumberWithCommas(Number(details.totalAmount))} {details.currency} -
-
+ + + Total: + + + {formatNumberWithCommas(Number(details.totalAmount))} {details.currency} + + {details.totalAmountInWords && ( -
-
Total in words:
-
- - {details.totalAmountInWords} {details.currency} - -
-
+ + + Total in words: + + + {details.totalAmountInWords} {details.currency} + + )} -
-
-
- -
-
-
-

Additional notes:

-

{details.additionalNotes}

-
-
-

Payment terms:

-

{details.paymentTerms}

-
-
- - Please send the payment to this address -

Bank: {details.paymentInformation?.bankName}

-

Account name: {details.paymentInformation?.accountName}

-

Account no: {details.paymentInformation?.accountNumber}

-
-
-
-

- If you have any questions concerning this invoice, use the following contact information: -

-
-

{sender.email}

-

{sender.phone}

-
-
- - {/* Signature */} - {details?.signature?.data && isDataUrl(details?.signature?.data) ? ( -
-

Signature:

- {`Signature -
- ) : details.signature?.data ? ( -
-

Signature:

-

- {details.signature.data} -

-
- ) : null} -
+ + + + {/* Notes & Payment */} + + + Additional notes: + {details.additionalNotes} + + + Payment terms: + {details.paymentTerms} + + + Please send the payment to this address + Bank: {details.paymentInformation?.bankName} + Account name: {details.paymentInformation?.accountName} + Account no: {details.paymentInformation?.accountNumber} + + + + + + If you have any questions concerning this invoice, use the following contact information: + + {sender.email} + {sender.phone} + + + {/* Signature */} + {details?.signature?.data && isDataUrl(details?.signature?.data) ? ( + + Signature: + + + ) : details?.signature?.data ? ( + + Signature: + + {details.signature.data} + + + ) : null} + + ); }; diff --git a/app/components/templates/invoice-pdf/InvoiceTemplate2.data.json b/app/components/templates/invoice-pdf/InvoiceTemplate2.data.json new file mode 100644 index 000000000..ff307f575 --- /dev/null +++ b/app/components/templates/invoice-pdf/InvoiceTemplate2.data.json @@ -0,0 +1,75 @@ +{ + "sender": { + "name": "John Doe", + "address": "123 Main St", + "zipCode": "12345", + "city": "Anytown", + "country": "USA", + "email": "johndoe@example.com", + "phone": "123-456-7890" + }, + "receiver": { + "name": "Jane Smith", + "address": "456 Elm St", + "zipCode": "54321", + "city": "Other Town", + "country": "Canada", + "email": "jane@example.com", + "phone": "555-987-6543" + }, + "details": { + "invoiceLogo": "", + "invoiceNumber": "INV0001", + "invoiceDate": "March 24, 2026", + "dueDate": "March 24, 2026", + "currency": "USD", + "language": "English", + "items": [ + { + "name": "Product 1", + "description": "Description of product 1", + "quantity": 4, + "unitPrice": 50, + "total": 200 + }, + { + "name": "Product 2", + "description": "Description of product 2", + "quantity": 5, + "unitPrice": 50, + "total": 250 + }, + { + "name": "Product 3", + "description": "Description of product 3", + "quantity": 5, + "unitPrice": 80, + "total": 400 + } + ], + "paymentInformation": { + "bankName": "Bank Inc.", + "accountName": "John Doe", + "accountNumber": "445566998877" + }, + "taxDetails": { + "amount": 15, + "taxID": "TAX-123", + "amountType": "percentage" + }, + "discountDetails": { + "amount": 5, + "amountType": "percentage" + }, + "shippingDetails": { + "cost": 5, + "costType": "amount" + }, + "subTotal": 850, + "totalAmount": 975.06, + "totalAmountInWords": "Nine hundred seventy-five Dollar and six Cents", + "additionalNotes": "Thank you for your business", + "paymentTerms": "Net 30", + "pdfTemplate": 2 + } +} diff --git a/app/components/templates/invoice-pdf/InvoiceTemplate2.tsx b/app/components/templates/invoice-pdf/InvoiceTemplate2.tsx index 86406424f..1bea1f2f0 100644 --- a/app/components/templates/invoice-pdf/InvoiceTemplate2.tsx +++ b/app/components/templates/invoice-pdf/InvoiceTemplate2.tsx @@ -1,307 +1,254 @@ import React from "react"; -// Components -import { InvoiceLayout } from "@/app/components"; +import { Document, Page, View, Text, Image } from "@formepdf/react"; +import { tw } from "@formepdf/tailwind"; +import { readFileSync } from "fs"; +import { join } from "path"; // Helpers -import { formatNumberWithCommas, isDataUrl } from "@/lib/helpers"; - -// Variables -import { DATE_OPTIONS } from "@/lib/variables"; +import { formatNumberWithCommas, isDataUrl } from "@/lib/pdf-helpers"; // Types import { InvoiceType } from "@/types"; +// Load Outfit font family. +// In Next.js, process.cwd() is the project root so readFileSync works. +// In VS Code preview, cwd differs so readFileSync throws ENOENT — fall back +// to a relative path string which the renderer resolves from this file's dir. +const fontsDir = join(process.cwd(), "fonts"); +const fontFiles = ["Outfit-Regular.ttf", "Outfit-Medium.ttf", "Outfit-SemiBold.ttf", "Outfit-Bold.ttf"] as const; +const fontWeights = [400, 500, 600, 700] as const; + +function loadFont(file: string): string | Buffer { + try { + return readFileSync(join(fontsDir, file)); + } catch { + // VS Code preview: fs unavailable or wrong cwd — use relative path + return join("../../../../fonts", file); + } +} + +const outfitFonts = fontFiles.map((file, i) => ({ + family: "Outfit" as const, + src: loadFont(file), + fontWeight: fontWeights[i], +})); + const InvoiceTemplate2 = (data: InvoiceType) => { - const { sender, receiver, details } = data; - return ( - -
-
-

- Invoice # -

- - {details.invoiceNumber} - - {details.invoiceLogo && ( - {`Logo - )} + const { sender, receiver, details } = data; + + return ( + + + {/* Header */} + + + Invoice # + {details.invoiceNumber} + {details.invoiceLogo && details.invoiceLogo !== "" && isDataUrl(details.invoiceLogo) ? ( + + ) : null} + {sender.name} + + + + {sender.address} + {sender.zipCode}, {sender.city} + {sender.country} + + + + + {/* Bill To + Dates */} + + + Bill to: + {receiver.name} + + + {[receiver.address, receiver.zipCode].filter(Boolean).join(", ")} + + {receiver.city}, {receiver.country} + + + + + Invoice date: + + {details.invoiceDate} + + + + Due date: + + {details.dueDate} + + + + -

- {sender.name} -

-
-
-
- {sender.address} -
- {sender.zipCode}, {sender.city} -
- {sender.country} -
-
-
-
+ {/* Items Table */} + + {/* Header Row */} + + + Item + + + Qty + + + Rate + + + Amount + + + -
-
-

- Bill to: -

-

- {receiver.name} -

-
- {receiver.address}, {receiver.zipCode} -
- {receiver.city}, {receiver.country} -
-
-
-
-
-
-
- Invoice date: -
-
- {new Date( - details.invoiceDate - ).toLocaleDateString("en-US", DATE_OPTIONS)} -
-
-
-
- Due date: -
-
- {new Date(details.dueDate).toLocaleDateString( - "en-US", - DATE_OPTIONS - )} -
-
-
-
-
+ {/* Item Rows */} + {details.items.map((item, index) => ( + + + {item.name} + {item.description} + + + {item.quantity} + + + {item.unitPrice} {details.currency} + + + {item.total} {details.currency} + + + ))} +
-
-
-
-
- Item -
-
- Qty -
-
- Rate -
-
- Amount -
-
-
-
- {details.items.map((item, index) => ( - -
-

- {item.name} -

-

- {item.description} -

-
-
-

- {item.quantity} -

-
-
-

- {item.unitPrice} {details.currency} -

-
-
-

- {item.total} {details.currency} -

-
-
- ))} -
-
-
-
+ {/* Totals */} + + + + + Subtotal: + + + {formatNumberWithCommas(Number(details.subTotal))} {details.currency} + + + {details.discountDetails?.amount != undefined && details.discountDetails?.amount > 0 && ( + + + Discount: + + + + {details.discountDetails.amountType === "amount" + ? `- ${details.discountDetails.amount} ${details.currency}` + : `- ${details.discountDetails.amount}%`} + + + + )} + {details.taxDetails?.amount != undefined && details.taxDetails?.amount > 0 && ( + + + Tax: + + + + {details.taxDetails.amountType === "amount" + ? `+ ${details.taxDetails.amount} ${details.currency}` + : `+ ${details.taxDetails.amount}%`} + + + + )} + {details.shippingDetails?.cost != undefined && details.shippingDetails?.cost > 0 && ( + + + Shipping: + + + + {details.shippingDetails.costType === "amount" + ? `+ ${details.shippingDetails.cost} ${details.currency}` + : `+ ${details.shippingDetails.cost}%`} + + + + )} + + + Total: + + + {formatNumberWithCommas(Number(details.totalAmount))} {details.currency} + + + {details.totalAmountInWords && ( + + + Total in words: + + + {details.totalAmountInWords} {details.currency} + + + )} + + -
-
-
-
-
- Subtotal: -
-
- {formatNumberWithCommas( - Number(details.subTotal) - )}{" "} - {details.currency} -
-
- {details.discountDetails?.amount != undefined && - details.discountDetails?.amount > 0 && ( -
-
- Discount: -
-
- {details.discountDetails.amountType === - "amount" - ? `- ${details.discountDetails.amount} ${details.currency}` - : `- ${details.discountDetails.amount}%`} -
-
- )} - {details.taxDetails?.amount != undefined && - details.taxDetails?.amount > 0 && ( -
-
- Tax: -
-
- {details.taxDetails.amountType === - "amount" - ? `+ ${details.taxDetails.amount} ${details.currency}` - : `+ ${details.taxDetails.amount}%`} -
-
- )} - {details.shippingDetails?.cost != undefined && - details.shippingDetails?.cost > 0 && ( -
-
- Shipping: -
-
- {details.shippingDetails.costType === - "amount" - ? `+ ${details.shippingDetails.cost} ${details.currency}` - : `+ ${details.shippingDetails.cost}%`} -
-
- )} -
-
- Total: -
-
- {formatNumberWithCommas( - Number(details.totalAmount) - )}{" "} - {details.currency} -
-
- {details.totalAmountInWords && ( -
-
- Total in words: -
-
- - {details.totalAmountInWords}{" "} - {details.currency} - -
-
- )} -
-
-
+ {/* Notes & Payment */} + + + Additional notes: + {details.additionalNotes} + + + Payment terms: + {details.paymentTerms} + + + Please send the payment to this address + Bank: {details.paymentInformation?.bankName} + Account name: {details.paymentInformation?.accountName} + Account no: {details.paymentInformation?.accountNumber} + + -
-
-
-

- Additional notes: -

-

- {details.additionalNotes} -

-
-
-

- Payment terms: -

-

- {details.paymentTerms} -

-
-
- - Please send the payment to this address -

- Bank: {details.paymentInformation?.bankName} -

-

- Account name:{" "} - {details.paymentInformation?.accountName} -

-

- Account no:{" "} - {details.paymentInformation?.accountNumber} -

-
-
-
-

- If you have any questions concerning this invoice, use the - following contact information: -

-
-

- {sender.email} -

-

- {sender.phone} -

-
-
+ + + If you have any questions concerning this invoice, use the following contact information: + + {sender.email} + {sender.phone} + - {/* Signature */} - {details?.signature?.data && isDataUrl(details?.signature?.data) ? ( -
-

Signature:

- {`Signature -
- ) : details.signature?.data ? ( -
-

Signature:

-

- {details.signature.data} -

-
- ) : null} -
- ); + {/* Signature */} + {details?.signature?.data && isDataUrl(details?.signature?.data) ? ( + + Signature: + + + ) : details?.signature?.data ? ( + + Signature: + + {details.signature.data} + + + ) : null} + + + ); }; export default InvoiceTemplate2; diff --git a/fonts/Outfit-Bold.ttf b/fonts/Outfit-Bold.ttf new file mode 100644 index 000000000..0389ff271 Binary files /dev/null and b/fonts/Outfit-Bold.ttf differ diff --git a/fonts/Outfit-Medium.ttf b/fonts/Outfit-Medium.ttf new file mode 100644 index 000000000..0ba786a6e Binary files /dev/null and b/fonts/Outfit-Medium.ttf differ diff --git a/fonts/Outfit-Regular.ttf b/fonts/Outfit-Regular.ttf new file mode 100644 index 000000000..0b0a0d7d6 Binary files /dev/null and b/fonts/Outfit-Regular.ttf differ diff --git a/fonts/Outfit-SemiBold.ttf b/fonts/Outfit-SemiBold.ttf new file mode 100644 index 000000000..52e25bc92 Binary files /dev/null and b/fonts/Outfit-SemiBold.ttf differ diff --git a/lib/pdf-helpers.ts b/lib/pdf-helpers.ts new file mode 100644 index 000000000..364bb27aa --- /dev/null +++ b/lib/pdf-helpers.ts @@ -0,0 +1,21 @@ +/** + * Formats a number with commas and decimal places + * + * @param {number} number - Number to format + * @returns {string} A styled number to be displayed on the invoice + */ +export const formatNumberWithCommas = (number: number) => { + return number.toLocaleString("en-US", { + style: "decimal", + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }); +}; + +/** + * A method to check if a string is a data URL + * + * @param {string} str - String to check + * @returns {boolean} Boolean indicating if the string is a data URL + */ +export const isDataUrl = (str: string) => str.startsWith("data:"); diff --git a/lib/variables.ts b/lib/variables.ts index 97a1774b3..c645797a7 100644 --- a/lib/variables.ts +++ b/lib/variables.ts @@ -31,12 +31,6 @@ export const CURRENCIES_API = */ export const LOCAL_STORAGE_INVOICE_DRAFT_KEY = "invoify:invoiceDraft"; -/** - * Tailwind - */ -export const TAILWIND_CDN = - "https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css"; - /** * Google */ diff --git a/next.config.js b/next.config.js index 892c0e5ec..eddca75ea 100644 --- a/next.config.js +++ b/next.config.js @@ -2,12 +2,15 @@ const withNextIntl = require("next-intl/plugin")("./i18n/request.ts"); /** @type {import('next').NextConfig} */ const nextConfig = { - serverExternalPackages: ["@sparticuz/chromium", "puppeteer-core"], webpack: (config) => { config.module.rules.push({ test: /\.map$/, use: "ignore-loader", }); + config.experiments = { + ...config.experiments, + asyncWebAssembly: true, + }; return config; }, }; diff --git a/package-lock.json b/package-lock.json index 81e788d1b..7cb576cd7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,10 @@ "@dnd-kit/core": "^6.1.0", "@dnd-kit/sortable": "^8.0.0", "@dnd-kit/utilities": "^3.2.2", + "@fontsource/outfit": "^5.2.8", + "@formepdf/core": "^0.8.3", + "@formepdf/react": "^0.8.3", + "@formepdf/tailwind": "^0.8.3", "@hookform/resolvers": "^3.3.2", "@json2csv/node": "^7.0.3", "@radix-ui/react-alert-dialog": "^1.1.15", @@ -41,7 +45,6 @@ "nodemailer": "^6.9.7", "number-to-words": "^1.2.4", "postcss": "8.4.31", - "puppeteer-core": "^24.9.0", "react": "18.2.0", "react-day-picker": "^8.8.2", "react-dom": "18.2.0", @@ -61,7 +64,6 @@ }, "devDependencies": { "@next/bundle-analyzer": "^14.0.4", - "@sparticuz/chromium": "138.0.2", "@types/nodemailer": "^6.4.14", "@types/number-to-words": "^1.2.1", "@types/react": "^18.2.22", @@ -69,8 +71,7 @@ "@types/react-signature-canvas": "^1.0.4", "@types/xml2js": "^0.4.13", "cross-env": "^7.0.3", - "ignore-loader": "^0.1.2", - "puppeteer": "^24.10.0" + "ignore-loader": "^0.1.2" } }, "node_modules/@alloc/quick-lru": { @@ -1403,6 +1404,15 @@ "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", "license": "MIT" }, + "node_modules/@fontsource/outfit": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/@fontsource/outfit/-/outfit-5.2.8.tgz", + "integrity": "sha512-rXC6g0MqD7cOBjht0bMqc43qM6lRqDLML9KXsmg9uykz0wLQhy8Z/ajrMG6iyoT3NJR+MYgU+OEHp7uHoTb+Yw==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, "node_modules/@formatjs/ecma402-abstract": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.4.tgz", @@ -1463,6 +1473,33 @@ "tslib": "2" } }, + "node_modules/@formepdf/core": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@formepdf/core/-/core-0.8.3.tgz", + "integrity": "sha512-cLv74kAzS3jhjgTc3jLKDgV/HkyY6IkelzL113nBo1joVyUQUsQArFDmXIpvfmK6lOU7kh7mrxueC9kxkSikzA==", + "license": "MIT", + "dependencies": { + "@formepdf/react": "0.8.3" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "node_modules/@formepdf/react": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@formepdf/react/-/react-0.8.3.tgz", + "integrity": "sha512-GxQv7/Z3mXd9C1T9UcuwHau0gnunHFrpdTSwn/3co5UTee7KuItlZdEaprEv/qdQhzcfEJyHSvNWr6RlQzJcmg==", + "license": "MIT", + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "node_modules/@formepdf/tailwind": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@formepdf/tailwind/-/tailwind-0.8.3.tgz", + "integrity": "sha512-cP9Ck0EeSx4MgkrPlJwAkZ+HSju2L+92y7kNC6ibGUJwJwb0DWobp4XXeSTFOq+cQQyUYsjnl4L6UpK7Su5pbw==", + "license": "MIT" + }, "node_modules/@hookform/resolvers": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.10.0.tgz", @@ -2234,27 +2271,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@puppeteer/browsers": { - "version": "2.10.8", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.10.8.tgz", - "integrity": "sha512-f02QYEnBDE0p8cteNoPYHHjbDuwyfbe4cCIVlNi8/MRicIxFW4w4CfgU0LNgWEID6s06P+hRJ1qjpBLMhPRCiQ==", - "license": "Apache-2.0", - "dependencies": { - "debug": "^4.4.1", - "extract-zip": "^2.0.1", - "progress": "^2.0.3", - "proxy-agent": "^6.5.0", - "semver": "^7.7.2", - "tar-fs": "^3.1.0", - "yargs": "^17.7.2" - }, - "bin": { - "browsers": "lib/cjs/main-cli.js" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/@radix-ui/number": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", @@ -4286,20 +4302,6 @@ "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", "license": "MIT" }, - "node_modules/@sparticuz/chromium": { - "version": "138.0.2", - "resolved": "https://registry.npmjs.org/@sparticuz/chromium/-/chromium-138.0.2.tgz", - "integrity": "sha512-vs5qUiK6kFCzLCxZ2buWONcB6jdF3VWdYp6kH1tt56tZ78p51dMAxfWsfk9P62z/jAeqbVg4V6Rb3Ic4aAeOKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.9", - "tar-fs": "^3.1.0" - }, - "engines": { - "node": ">=20.11.0" - } - }, "node_modules/@streamparser/json": { "version": "0.0.20", "resolved": "https://registry.npmjs.org/@streamparser/json/-/json-0.0.20.tgz", @@ -4320,12 +4322,6 @@ "tslib": "^2.8.0" } }, - "node_modules/@tootallnate/quickjs-emscripten": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", - "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", - "license": "MIT" - }, "node_modules/@types/cors": { "version": "2.8.19", "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", @@ -4416,16 +4412,6 @@ "@types/node": "*" } }, - "node_modules/@types/yauzl": { - "version": "2.10.3", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", - "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", - "license": "MIT", - "optional": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@vercel/analytics": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@vercel/analytics/-/analytics-1.5.0.tgz", @@ -4549,15 +4535,6 @@ "node": ">=0.8" } }, - "node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -4603,12 +4580,6 @@ "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, "node_modules/aria-hidden": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", @@ -4621,18 +4592,6 @@ "node": ">=10" } }, - "node_modules/ast-types": { - "version": "0.13.4", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", - "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.1" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/autoprefixer": { "version": "10.4.15", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.15.tgz", @@ -4669,100 +4628,11 @@ "postcss": "^8.1.0" } }, - "node_modules/b4a": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", - "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==" - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, - "node_modules/bare-events": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.7.0.tgz", - "integrity": "sha512-b3N5eTW1g7vXkw+0CXh/HazGTcO5KYuu/RCNaJbDMPI6LHDi+7qe8EmxKUVe1sUbY2KZOVZFyj62x0OEz9qyAA==", - "license": "Apache-2.0", - "optional": true - }, - "node_modules/bare-fs": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.4.4.tgz", - "integrity": "sha512-Q8yxM1eLhJfuM7KXVP3zjhBvtMJCYRByoTT+wHXjpdMELv0xICFJX+1w4c7csa+WZEOsq4ItJ4RGwvzid6m/dw==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "bare-events": "^2.5.4", - "bare-path": "^3.0.0", - "bare-stream": "^2.6.4", - "bare-url": "^2.2.2", - "fast-fifo": "^1.3.2" - }, - "engines": { - "bare": ">=1.16.0" - }, - "peerDependencies": { - "bare-buffer": "*" - }, - "peerDependenciesMeta": { - "bare-buffer": { - "optional": true - } - } - }, - "node_modules/bare-os": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.2.tgz", - "integrity": "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A==", - "license": "Apache-2.0", - "optional": true, - "engines": { - "bare": ">=1.14.0" - } - }, - "node_modules/bare-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", - "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "bare-os": "^3.0.1" - } - }, - "node_modules/bare-stream": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.7.0.tgz", - "integrity": "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "streamx": "^2.21.0" - }, - "peerDependencies": { - "bare-buffer": "*", - "bare-events": "*" - }, - "peerDependenciesMeta": { - "bare-buffer": { - "optional": true - }, - "bare-events": { - "optional": true - } - } - }, - "node_modules/bare-url": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.2.2.tgz", - "integrity": "sha512-g+ueNGKkrjMazDG3elZO1pNs3HY5+mMmOet1jtKyhOaCnkLzitxf26z7hoAEkDNgdNmnc1KIlt/dw6Po6xZMpA==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "bare-path": "^3.0.0" - } - }, "node_modules/base64id": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", @@ -4781,15 +4651,6 @@ "baseline-browser-mapping": "dist/cli.js" } }, - "node_modules/basic-ftp": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", - "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -4852,15 +4713,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "license": "MIT", - "engines": { - "node": "*" - } - }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -4872,16 +4724,6 @@ "node": ">=10.16.0" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/camelcase-css": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", @@ -4969,19 +4811,6 @@ "node": ">= 6" } }, - "node_modules/chromium-bidi": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-8.0.0.tgz", - "integrity": "sha512-d1VmE0FD7lxZQHzcDUCKZSNRtRwISXDsdg4HjdTR5+Ll5nQ/vzU12JeNmupD6VWffrPSlrnGhEWlLESKH3VO+g==", - "license": "Apache-2.0", - "dependencies": { - "mitt": "^3.0.1", - "zod": "^3.24.1" - }, - "peerDependencies": { - "devtools-protocol": "*" - } - }, "node_modules/citty": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", @@ -5035,20 +4864,6 @@ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "license": "MIT" }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -5170,33 +4985,6 @@ "node": ">= 0.10" } }, - "node_modules/cosmiconfig": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", - "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", - "dev": true, - "license": "MIT", - "dependencies": { - "env-paths": "^2.2.1", - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, "node_modules/crc-32": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", @@ -5256,15 +5044,6 @@ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "devOptional": true }, - "node_modules/data-uri-to-buffer": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", - "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, "node_modules/date-fns": { "version": "2.30.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", @@ -5288,9 +5067,9 @@ "dev": true }, "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -5318,20 +5097,6 @@ "node": ">=0.10.0" } }, - "node_modules/degenerator": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", - "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", - "license": "MIT", - "dependencies": { - "ast-types": "^0.13.4", - "escodegen": "^2.1.0", - "esprima": "^4.0.1" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/detect-libc": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.1.tgz", @@ -5346,12 +5111,6 @@ "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" }, - "node_modules/devtools-protocol": { - "version": "0.0.1495869", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1495869.tgz", - "integrity": "sha512-i+bkd9UYFis40RcnkW7XrOprCujXRAHg62IVh/Ah3G8MmNXpCGt1m0dTFhSdx/AVs8XEMbdOGRwdkR1Bcta8AA==", - "license": "BSD-3-Clause" - }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -5484,15 +5243,6 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, - "node_modules/end-of-stream": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", - "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, "node_modules/engine.io": { "version": "6.6.4", "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", @@ -5571,26 +5321,6 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/error-ex": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", - "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, "node_modules/esbuild": { "version": "0.25.10", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz", @@ -5648,58 +5378,6 @@ "node": ">=0.8.0" } }, - "node_modules/escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "license": "BSD-2-Clause", - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/exsolve": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", @@ -5722,31 +5400,6 @@ "node": ">=0.10.0" } }, - "node_modules/extract-zip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", - "license": "BSD-2-Clause", - "dependencies": { - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" - }, - "engines": { - "node": ">= 10.17.0" - }, - "optionalDependencies": { - "@types/yauzl": "^2.9.1" - } - }, - "node_modules/fast-fifo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", - "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==" - }, "node_modules/fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", @@ -5801,15 +5454,6 @@ "reusify": "^1.0.4" } }, - "node_modules/fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", - "license": "MIT", - "dependencies": { - "pend": "~1.2.0" - } - }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -5821,27 +5465,6 @@ "node": ">=8" } }, - "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, "node_modules/foreground-child": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", @@ -5900,15 +5523,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, "node_modules/get-east-asian-width": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", @@ -5929,40 +5543,11 @@ "node": ">=6" } }, - "node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "license": "MIT", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-uri": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", - "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==", - "license": "MIT", - "dependencies": { - "basic-ftp": "^5.0.2", - "data-uri-to-buffer": "^6.0.2", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/glob": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", - "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", - "license": "ISC", + "node_modules/glob": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", + "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", + "license": "ISC", "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", @@ -6083,55 +5668,12 @@ "entities": "^4.4.0" } }, - "node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/ignore-loader": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ignore-loader/-/ignore-loader-0.1.2.tgz", "integrity": "sha512-yOJQEKrNwoYqrWLS4DcnzM7SEQhRKis5mB+LdKKh4cPmGYlLPR0ozRzHV5jmEk2IxptqJNQA5Cc0gw8Fj12bXA==", "dev": true }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/ini": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", @@ -6149,21 +5691,6 @@ "tslib": "^2.8.0" } }, - "node_modules/ip-address": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", - "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -6399,19 +5926,6 @@ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, - "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -6424,12 +5938,6 @@ "node": ">=6" } }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -6510,15 +6018,6 @@ "loose-envify": "cli.js" } }, - "node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, "node_modules/lucide-react": { "version": "0.279.0", "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.279.0.tgz", @@ -6612,12 +6111,6 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/mitt": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", - "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", - "license": "MIT" - }, "node_modules/mrmime": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", @@ -6671,15 +6164,6 @@ "node": ">= 0.6" } }, - "node_modules/netmask": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", - "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/next": { "version": "15.3.8", "resolved": "https://registry.npmjs.org/next/-/next-15.3.8.tgz", @@ -7261,14 +6745,6 @@ "node": ">= 6" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, "node_modules/onetime": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", @@ -7394,74 +6870,11 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/pac-proxy-agent": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", - "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", - "license": "MIT", - "dependencies": { - "@tootallnate/quickjs-emscripten": "^0.23.0", - "agent-base": "^7.1.2", - "debug": "^4.3.4", - "get-uri": "^6.0.1", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.6", - "pac-resolver": "^7.0.1", - "socks-proxy-agent": "^8.0.5" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/pac-resolver": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", - "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", - "license": "MIT", - "dependencies": { - "degenerator": "^5.0.0", - "netmask": "^2.0.2" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/parseley": { "version": "0.12.1", "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.12.1.tgz", @@ -7524,12 +6937,6 @@ "url": "https://ko-fi.com/killymxi" } }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "license": "MIT" - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -7752,15 +7159,6 @@ "node": ">=0.10.0" } }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -7790,80 +7188,6 @@ "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==" }, - "node_modules/proxy-agent": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", - "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "^4.3.4", - "http-proxy-agent": "^7.0.1", - "https-proxy-agent": "^7.0.6", - "lru-cache": "^7.14.1", - "pac-proxy-agent": "^7.1.0", - "proxy-from-env": "^1.1.0", - "socks-proxy-agent": "^8.0.5" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" - }, - "node_modules/pump": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", - "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/puppeteer": { - "version": "24.19.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.19.0.tgz", - "integrity": "sha512-gUWgHX36m9K6yUbvNBEA7CXElIL92yXMoAVFrO8OpZkItqrruLVqYA8ikmfgwcw/cNfYgkt0n2+yP9jd9RSETA==", - "dev": true, - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "@puppeteer/browsers": "2.10.8", - "chromium-bidi": "8.0.0", - "cosmiconfig": "^9.0.0", - "devtools-protocol": "0.0.1495869", - "puppeteer-core": "24.19.0", - "typed-query-selector": "^2.12.0" - }, - "bin": { - "puppeteer": "lib/cjs/puppeteer/node/cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/puppeteer-core": { - "version": "24.19.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.19.0.tgz", - "integrity": "sha512-qsEys4OIb2VGC2tNWKAs4U0mnjkIAxueMOOzk2nEFM9g4Y8QuvYkEMtmwsEdvzNGsUFd7DprOQfABmlN7WBOlg==", - "license": "Apache-2.0", - "dependencies": { - "@puppeteer/browsers": "2.10.8", - "chromium-bidi": "8.0.0", - "debug": "^4.4.1", - "devtools-protocol": "0.0.1495869", - "typed-query-selector": "^2.12.0", - "ws": "^8.18.3" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -8144,15 +7468,6 @@ "node": ">=8.10.0" } }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", @@ -8173,16 +7488,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/restore-cursor": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", @@ -8256,9 +7561,9 @@ } }, "node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -8375,16 +7680,6 @@ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", "license": "MIT" }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "license": "MIT", - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, "node_modules/socket.io": { "version": "4.8.1", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", @@ -8498,44 +7793,6 @@ } } }, - "node_modules/socks": { - "version": "2.8.7", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", - "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", - "license": "MIT", - "dependencies": { - "ip-address": "^10.0.1", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks-proxy-agent": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", - "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "^4.3.4", - "socks": "^2.8.3" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -8575,19 +7832,6 @@ "node": ">=10.0.0" } }, - "node_modules/streamx": { - "version": "2.22.1", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.1.tgz", - "integrity": "sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA==", - "license": "MIT", - "dependencies": { - "fast-fifo": "^1.3.2", - "text-decoder": "^1.1.0" - }, - "optionalDependencies": { - "bare-events": "^2.2.0" - } - }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -8826,39 +8070,6 @@ "tailwindcss": ">=3.0.0 || insiders" } }, - "node_modules/tar-fs": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.1.tgz", - "integrity": "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==", - "license": "MIT", - "dependencies": { - "pump": "^3.0.0", - "tar-stream": "^3.1.5" - }, - "optionalDependencies": { - "bare-fs": "^4.0.1", - "bare-path": "^3.0.0" - } - }, - "node_modules/tar-stream": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", - "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", - "dependencies": { - "b4a": "^1.6.4", - "fast-fifo": "^1.2.0", - "streamx": "^2.15.0" - } - }, - "node_modules/text-decoder": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", - "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", - "license": "Apache-2.0", - "dependencies": { - "b4a": "^1.6.4" - } - }, "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", @@ -9011,12 +8222,6 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/typed-query-selector": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz", - "integrity": "sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==", - "license": "MIT" - }, "node_modules/typescript": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", @@ -9241,23 +8446,6 @@ "node": ">=0.8" } }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/wrap-ansi-cjs": { "name": "wrap-ansi", "version": "7.0.0", @@ -9275,32 +8463,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "node_modules/ws": { - "version": "8.18.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", - "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/xlsx": { "version": "0.18.5", "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz", @@ -9341,15 +8503,6 @@ "node": ">=4.0" } }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, "node_modules/yaml": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", @@ -9362,43 +8515,6 @@ "node": ">= 14.6" } }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", - "license": "MIT", - "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - }, "node_modules/yoctocolors": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", diff --git a/package.json b/package.json index d8366c713..75635b90f 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,10 @@ "@dnd-kit/core": "^6.1.0", "@dnd-kit/sortable": "^8.0.0", "@dnd-kit/utilities": "^3.2.2", + "@fontsource/outfit": "^5.2.8", + "@formepdf/core": "^0.8.3", + "@formepdf/react": "^0.8.3", + "@formepdf/tailwind": "^0.8.3", "@hookform/resolvers": "^3.3.2", "@json2csv/node": "^7.0.3", "@radix-ui/react-alert-dialog": "^1.1.15", @@ -43,7 +47,6 @@ "nodemailer": "^6.9.7", "number-to-words": "^1.2.4", "postcss": "8.4.31", - "puppeteer-core": "^24.9.0", "react": "18.2.0", "react-day-picker": "^8.8.2", "react-dom": "18.2.0", @@ -63,7 +66,6 @@ }, "devDependencies": { "@next/bundle-analyzer": "^14.0.4", - "@sparticuz/chromium": "138.0.2", "@types/nodemailer": "^6.4.14", "@types/number-to-words": "^1.2.1", "@types/react": "^18.2.22", @@ -71,7 +73,6 @@ "@types/react-signature-canvas": "^1.0.4", "@types/xml2js": "^0.4.13", "cross-env": "^7.0.3", - "ignore-loader": "^0.1.2", - "puppeteer": "^24.10.0" + "ignore-loader": "^0.1.2" } } diff --git a/puppeteer.config.cjs b/puppeteer.config.cjs deleted file mode 100644 index 0d729971b..000000000 --- a/puppeteer.config.cjs +++ /dev/null @@ -1,9 +0,0 @@ -const { join } = require("path"); - -/** - * @type {import("puppeteer").Configuration} - */ -module.exports = { - // Changes the cache location for Puppeteer. - cacheDirectory: join(__dirname, ".cache", "puppeteer"), -}; diff --git a/services/invoice/server/generatePdfService.ts b/services/invoice/server/generatePdfService.ts index c5241a1af..1f192e4e7 100644 --- a/services/invoice/server/generatePdfService.ts +++ b/services/invoice/server/generatePdfService.ts @@ -1,14 +1,8 @@ import { NextRequest, NextResponse } from "next/server"; -// Chromium -import chromium from "@sparticuz/chromium"; - // Helpers import { getInvoiceTemplate } from "@/lib/helpers"; -// Variables -import { ENV, TAILWIND_CDN } from "@/lib/variables"; - // Types import { InvoiceType } from "@/types"; @@ -22,88 +16,37 @@ import { InvoiceType } from "@/types"; */ export async function generatePdfService(req: NextRequest) { const body: InvoiceType = await req.json(); - let browser; - let page; try { - const ReactDOMServer = (await import("react-dom/server")).default; + const { renderDocument } = await import("@formepdf/core"); const templateId = body.details.pdfTemplate; const InvoiceTemplate = await getInvoiceTemplate(templateId); - const htmlTemplate = ReactDOMServer.renderToStaticMarkup( - InvoiceTemplate(body) - ); - - if (ENV === "production") { - const puppeteer = (await import("puppeteer-core")).default; - browser = await puppeteer.launch({ - args: [...chromium.args, "--disable-dev-shm-usage", "--ignore-certificate-errors"], - executablePath: await chromium.executablePath(), - headless: true, - }); - } else { - const puppeteer = (await import("puppeteer")).default; - browser = await puppeteer.launch({ - args: ["--no-sandbox", "--disable-setuid-sandbox"], - headless: true, - }); - } - if (!browser) { - throw new Error("Failed to launch browser"); + if (!InvoiceTemplate) { + throw new Error(`Template ${templateId} not found`); } - page = await browser.newPage(); - await page.setContent(await htmlTemplate, { - waitUntil: ["networkidle0", "load", "domcontentloaded"], - timeout: 30000, - }); + const pdf = await renderDocument(InvoiceTemplate(body)); - await page.addStyleTag({ - url: TAILWIND_CDN, + return new NextResponse(new Blob([pdf], { type: "application/pdf" }), { + headers: { + "Content-Type": "application/pdf", + "Content-Disposition": "attachment; filename=invoice.pdf", + "Cache-Control": "no-cache", + Pragma: "no-cache", + }, + status: 200, }); - - const pdf: Uint8Array = await page.pdf({ - format: "a4", - printBackground: true, - preferCSSPageSize: true, - }); - - return new NextResponse(new Blob([pdf], { type: "application/pdf" }), { - headers: { - "Content-Type": "application/pdf", - "Content-Disposition": "attachment; filename=invoice.pdf", - "Cache-Control": "no-cache", - Pragma: "no-cache", - }, - status: 200, - }); - } catch (error: any) { - console.error("PDF Generation Error:", error); - return new NextResponse( - JSON.stringify({ error: "Failed to generate PDF" }), - { - status: 500, - headers: { - "Content-Type": "application/json", - }, - } - ); - } finally { - if (page) { - try { - await page.close(); - } catch (e) { - console.error("Error closing page:", e); - } - } - if (browser) { - try { - const pages = await browser.pages(); - await Promise.all(pages.map((p) => p.close())); - await browser.close(); - } catch (e) { - console.error("Error closing browser:", e); - } - } - } + } catch (error: any) { + console.error("PDF Generation Error:", error); + return new NextResponse( + JSON.stringify({ error: "Failed to generate PDF" }), + { + status: 500, + headers: { + "Content-Type": "application/json", + }, + } + ); + } }