From 2b5e1252607b40c2ed2dca580c7fa5a63ea3fe61 Mon Sep 17 00:00:00 2001 From: Hardik Date: Sun, 10 May 2026 00:28:34 +0530 Subject: [PATCH] feat(export): match Sample_PO.xlsx formatting in XLSX and PDF exports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit XLSX (ExcelJS replaces bare SheetJS): - Full cell-level styling: bold fonts, gray fills, thin/medium borders, cell alignment, wrap-text — all matching the sample PO layout - Correct merge map across all sections: header block (A1:I4), PO meta row (A5:B5, C5:G5), PI/Quotation row (A6:B6, C6:F6, G6:H6), Vessel/Budget/Requested row (A7:B7, D7:E7, H7:I7), Requisition row (A8:B8, D8:E8, H8:I8), Place of Delivery spanning 2 rows (A9:B10, C9:I10), Invoice Details spanning 2 rows (A11:B12, C11:I11, C12:I12), Vendor block (A13:B13, D13:I13, A14:B14, C14:I14), line item Description column (B:C per row), totals labels and values (F:G and H:I), instructions header (A:I), T&C text (B:I per row), dual signature blocks - Description column spans B:C in header and every item row - Minimum 7 body rows reserved; alternating row fills - Totals section: gray fill, right-aligned, grand total darker gray - Instructions header with distinct fill; numbered T&C with B:I merge - Paired signature blocks (submitter left, vendor right) with borders - Column widths and row heights tuned to the sample dimensions - Page setup: A4 portrait, fit-to-width PDF (HTML print page): - Typography matches sample: Arial 8.5pt body, 13pt bold company name, 11pt underlined PURCHASE ORDER heading - All meta tables use gray (f2f2f2) label cells with borders - Place of Delivery and Invoice Details use rowspan for correct layout - Line items table: dark gray (d8d8d8) header, 1px borders, alternating row fills, minimum 7 blank rows reserved - Totals table (55% width, right-aligned) with gray rows and darker grand total - Instructions: distinct header fill, clean numbered layout - Signature blocks: flex-spaced bordered boxes with submitter/vendor - Print CSS: A4 page size, no-print class for the print button Co-Authored-By: Claude Sonnet 4.6 --- .../app/api/po/[id]/export/route.ts | 748 ++++++++++++------ App/pelagia-portal/package.json | 1 + App/pelagia-portal/pnpm-lock.yaml | 502 ++++++++++++ 3 files changed, 1010 insertions(+), 241 deletions(-) diff --git a/App/pelagia-portal/app/api/po/[id]/export/route.ts b/App/pelagia-portal/app/api/po/[id]/export/route.ts index 63f2d23..c1e8aa8 100644 --- a/App/pelagia-portal/app/api/po/[id]/export/route.ts +++ b/App/pelagia-portal/app/api/po/[id]/export/route.ts @@ -1,22 +1,28 @@ import { auth } from "@/auth"; import { db } from "@/lib/db"; import { NextRequest, NextResponse } from "next/server"; -import * as XLSX from "xlsx"; +import ExcelJS from "exceljs"; import { TC_FIXED_LINE, TC_DEFAULTS } from "@/lib/validations/po"; -const COMPANY_NAME = "PELAGIA MARINE SERVICES PVT. LTD"; -const COMPANY_ADDR = "409-410, ZION, Plot 273, Sector-10, Kharghar, Navi Mumbai- 410210"; -const COMPANY_TEL = "Tel: +91-22-6909 9028 / Email: technical@pelagiamarine.com / Mob: +91 74000 60772"; -const INVOICE_ADDR = "Pelagia Marine Services Pvt Ltd, 409-410, ZION, Plot 273, Sector-10, Kharghar, Navi Mumbai- 410210 (MH)"; -const INVOICE_GST = "Email: accounts@pelagiamarine.com GST NO: 27AAHCP5787B1Z6"; +// ── Company constants ───────────────────────────────────────────────────────── -function fmt(n: number, dec = 2) { - return n.toLocaleString("en-IN", { minimumFractionDigits: dec, maximumFractionDigits: dec }); -} -function fmtDate(d: Date | null | undefined) { +const CO_NAME = "PELAGIA MARINE SERVICES PVT. LTD"; +const CO_ADDR = "Office address: 409-410, ZION, Plot 273, Sector-10, Kharghar, Navi Mumbai- 410210"; +const CO_TEL = "Tel: +91-22-6909 9028 / Email: technical@pelagiamarine.com / Mob: +91 74000 60772"; +const INV_ADDR = "Pelagia Marine Services Pvt Ltd, 409-410, ZION, Plot 273, Sector-10, Kharghar, Navi Mumbai- 410210 (MH)"; +const INV_GST = "Email: accounts@pelagiamarine.com GST NO: 27AAHCP5787B1Z6"; + +// ── Helpers ─────────────────────────────────────────────────────────────────── + +function fmtDate(d: Date | null | undefined): string { if (!d) return ""; return new Date(d).toLocaleDateString("en-IN", { day: "2-digit", month: "short", year: "numeric" }); } +function fmtNum(n: number, dec = 2): string { + return n.toLocaleString("en-IN", { minimumFractionDigits: dec, maximumFractionDigits: dec }); +} + +// ── Route ───────────────────────────────────────────────────────────────────── interface Props { params: Promise<{ id: string }> } @@ -28,17 +34,13 @@ export async function GET(request: NextRequest, { params }: Props) { const po = await db.purchaseOrder.findUnique({ where: { id }, include: { - submitter: true, - vessel: true, - account: true, - vendor: true, + submitter: true, vessel: true, account: true, vendor: true, lineItems: { orderBy: { sortOrder: "asc" } }, actions: { include: { actor: true }, orderBy: { createdAt: "asc" } }, }, }); if (!po) return NextResponse.json({ error: "Not found" }, { status: 404 }); - // Permission: submitter can always export their own PO const canViewAll = ["ACCOUNTS", "MANAGER", "SUPERUSER", "AUDITOR", "ADMIN"].includes(session.user.role); if (!canViewAll && po.submitterId !== session.user.id) { return NextResponse.json({ error: "Forbidden" }, { status: 403 }); @@ -46,38 +48,39 @@ export async function GET(request: NextRequest, { params }: Props) { const format = request.nextUrl.searchParams.get("format") ?? "pdf"; - // Compute totals - const items = po.lineItems.map((li) => ({ - sn: li.sortOrder + 1, - desc: li.description, - unit: li.unit, - qty: Number(li.quantity), - unitPrice: Number(li.unitPrice), - gstRate: Number((li as { gstRate?: unknown }).gstRate ?? 0.18), - taxable: Number(li.totalPrice), - get gstAmt() { return this.taxable * this.gstRate; }, - get total() { return this.taxable + this.gstAmt; }, - })); + // ── Computed data ───────────────────────────────────────────────────────── + + const items = po.lineItems.map((li, i) => { + const qty = Number(li.quantity); + const unitPrice = Number(li.unitPrice); + const gstRate = Number((li as { gstRate?: unknown }).gstRate ?? 0.18); + const taxable = Number(li.totalPrice); + const gstAmt = taxable * gstRate; + return { sn: i + 1, desc: li.description, unit: li.unit, qty, unitPrice, gstRate, taxable, gstAmt, total: taxable + gstAmt }; + }); const totalTaxable = items.reduce((s, i) => s + i.taxable, 0); - const totalGst = items.reduce((s, i) => s + i.gstAmt, 0); - const grandTotal = totalTaxable + totalGst; + const totalGst = items.reduce((s, i) => s + i.gstAmt, 0); + const grandTotal = totalTaxable + totalGst; const approvalAction = [...po.actions].reverse() - .find((a) => a.actionType === "APPROVED" || a.actionType === "APPROVED_WITH_NOTE"); + .find(a => a.actionType === "APPROVED" || a.actionType === "APPROVED_WITH_NOTE"); const approvedBy = approvalAction?.actor.name ?? ""; - const piNo = (po as { piQuotationNo?: string | null }).piQuotationNo ?? ""; - const piDate = fmtDate((po as { piQuotationDate?: Date | null }).piQuotationDate); - const reqNo = (po as { requisitionNo?: string | null }).requisitionNo ?? ""; - const reqDate = fmtDate((po as { requisitionDate?: Date | null }).requisitionDate); - const placeOfDelivery = (po as { placeOfDelivery?: string | null }).placeOfDelivery ?? ""; const ext = po as { + piQuotationNo?: string | null; piQuotationDate?: Date | null; + requisitionNo?: string | null; requisitionDate?: Date | null; + placeOfDelivery?: string | null; tcDelivery?: string | null; tcDispatch?: string | null; tcInspection?: string | null; tcTransitInsurance?: string | null; tcPaymentTerms?: string | null; tcOthers?: string | null; }; - // Build ordered TC lines: [number, label, value] + const piNo = ext.piQuotationNo ?? ""; + const piDate = fmtDate(ext.piQuotationDate); + const reqNo = ext.requisitionNo ?? ""; + const reqDate = fmtDate(ext.requisitionDate); + const delivery = ext.placeOfDelivery ?? ""; + const tcLines: [number, string, string][] = [ [1, "", TC_FIXED_LINE], [2, "DELIVERY", ext.tcDelivery ?? TC_DEFAULTS.tcDelivery], @@ -88,296 +91,561 @@ export async function GET(request: NextRequest, { params }: Props) { ...(ext.tcOthers ? [[7, "OTHERS", ext.tcOthers] as [number, string, string]] : []), ]; - // ── XLSX ──────────────────────────────────────────────────────────────────── + const vendorAddr = [ + po.vendor?.address, + po.vendor?.gstin ? `GSTIN: ${po.vendor.gstin}` : null, + ].filter(Boolean).join(" "); + const vendorContact = [po.vendor?.contactName, po.vendor?.contactMobile, po.vendor?.contactEmail] + .filter(Boolean).join(" "); + + // ── GST summary row label ───────────────────────────────────────────────── + // If all items share the same GST rate, show it; otherwise show "GST" + const gstRates = [...new Set(items.map(i => Math.round(i.gstRate * 100)))]; + const gstLabel = gstRates.length === 1 ? `GST (${gstRates[0]}%)` : "GST"; + + // ═══════════════════════════════════════════════════════════════════════════ + // XLSX — ExcelJS with full formatting + // ═══════════════════════════════════════════════════════════════════════════ + if (format === "xlsx") { - const wb = XLSX.utils.book_new(); - const ws: XLSX.WorkSheet = {}; - - function s(r: number, c: number, v: string | number | Date, opts?: XLSX.CellObject) { - const addr = XLSX.utils.encode_cell({ r, c }); - ws[addr] = { v, t: typeof v === "number" ? "n" : v instanceof Date ? "d" : "s", ...opts }; - } - function n(v: number): XLSX.CellObject { return { v, t: "n" }; } - - // Header - s(0, 0, COMPANY_NAME); - s(1, 0, COMPANY_ADDR); - s(2, 0, COMPANY_TEL); - s(3, 0, "PURCHASE ORDER"); - // PO meta - s(4, 0, "Purchase Order No:"); - s(4, 2, po.poNumber); - s(4, 7, "Date:"); - s(4, 8, new Date(po.createdAt).toLocaleDateString("en-IN")); - s(5, 0, "Performa Invoice / Quotation No:"); - s(5, 2, piNo); - s(5, 6, "P I / Quotation Date:"); - s(5, 8, piDate); - // Vessel / Requisition - s(6, 0, "Vessel Owner Name"); - s(6, 2, "Pelagia Marine Services Pvt. Ltd."); - s(6, 3, "Budget head"); - s(6, 5, po.account.code); - s(6, 6, "Requested By"); - s(6, 7, po.submitter.name); - s(7, 0, "Vessel/Office Requisition Number"); - s(7, 2, reqNo); - s(7, 3, "Reqn. Date"); - s(7, 5, reqDate); - s(7, 6, "Approved By"); - s(7, 7, approvedBy); - // Delivery - s(8, 0, "Place of Delivery"); - s(8, 2, placeOfDelivery); - // Invoice details - s(10, 0, "Invoice Details"); - s(10, 2, INVOICE_ADDR); - s(11, 2, INVOICE_GST); - // Vendor - s(12, 0, "Vendor Name & Address"); - s(12, 2, po.vendor?.name ?? ""); - s(12, 3, [po.vendor?.address ?? "", po.vendor?.gstin ? `GSTIN: ${po.vendor.gstin}` : ""].filter(Boolean).join(" ")); - s(13, 0, "Contact Person name / mobile no."); - s(13, 2, [po.vendor?.contactName, po.vendor?.contactMobile, po.vendor?.contactEmail].filter(Boolean).join(" ")); - // Line item header - const LH = 14; - s(LH, 0, "S.N."); - s(LH, 1, "Description"); - s(LH, 3, "Unit"); - s(LH, 4, "Qnty"); - s(LH, 5, "Unit price"); - s(LH, 6, "Taxable cost"); - s(LH, 7, "GST%"); - s(LH, 8, "Total cost"); - // Line items - items.forEach((item, idx) => { - const r = LH + 1 + idx; - ws[XLSX.utils.encode_cell({ r, c: 0 })] = n(item.sn); - s(r, 1, item.desc); - s(r, 3, item.unit); - ws[XLSX.utils.encode_cell({ r, c: 4 })] = n(item.qty); - ws[XLSX.utils.encode_cell({ r, c: 5 })] = n(item.unitPrice); - ws[XLSX.utils.encode_cell({ r, c: 6 })] = n(item.taxable); - ws[XLSX.utils.encode_cell({ r, c: 7 })] = n(item.gstRate); - ws[XLSX.utils.encode_cell({ r, c: 8 })] = n(item.total); - }); - // Totals - const TR = LH + items.length + 1 + 6; // leave blank rows up to row 23 (0-indexed) - s(TR, 5, "Total taxable value"); - ws[XLSX.utils.encode_cell({ r: TR, c: 7 })] = n(totalTaxable); - s(TR + 1, 5, `GST (${Math.round(items[0]?.gstRate * 100 || 18)}%)`); - ws[XLSX.utils.encode_cell({ r: TR + 1, c: 7 })] = n(totalGst); - s(TR + 2, 5, "GRAND TOTAL"); - ws[XLSX.utils.encode_cell({ r: TR + 2, c: 7 })] = n(grandTotal); - - // Instructions - const IR = TR + 4; - s(IR, 0, "INSTRUCTIONS TO VENDORS"); - tcLines.forEach(([num, label, value], idx) => { - ws[XLSX.utils.encode_cell({ r: IR + 1 + idx, c: 0 })] = { v: num, t: "n" }; - s(IR + 1 + idx, 1, label ? `${label}: ${value}` : value); + const wb = new ExcelJS.Workbook(); + wb.creator = "Pelagia Portal"; + const ws = wb.addWorksheet(po.poNumber.replace(/\//g, "-"), { + pageSetup: { paperSize: 9, orientation: "portrait", fitToPage: true, fitToWidth: 1, fitToHeight: 0 }, }); - // Signature - const SR = IR + tcLines.length + 3; - s(SR, 0, po.submitter.name); - s(SR + 1, 0, "Authorized Signatory & Stamp"); - s(SR + 1, 6, "Authorized Signatory & Stamp"); - s(SR + 2, 0, "For, Pelagia Marine Services Pvt. Ltd."); - s(SR + 2, 5, "For"); - s(SR + 2, 6, po.vendor?.name ?? ""); - - ws["!ref"] = XLSX.utils.encode_range({ r: 0, c: 0 }, { r: SR + 3, c: 8 }); - ws["!cols"] = [ - { wch: 22 }, { wch: 30 }, { wch: 22 }, { wch: 8 }, { wch: 8 }, - { wch: 14 }, { wch: 14 }, { wch: 8 }, { wch: 14 }, + // ── Column widths (A-I) ───────────────────────────────────────────────── + ws.columns = [ + { width: 20 }, // A + { width: 4 }, // B + { width: 22 }, // C + { width: 10 }, // D + { width: 7 }, // E + { width: 16 }, // F + { width: 7 }, // G + { width: 7 }, // H + { width: 16 }, // I ]; - XLSX.utils.book_append_sheet(wb, ws, po.poNumber.replace(/\//g, "-")); - const buf = XLSX.write(wb, { type: "buffer", bookType: "xlsx" }); + // ── Style constants ───────────────────────────────────────────────────── + const thin = (argb = "FF999999") => ({ style: "thin" as const, color: { argb } }); + const med = (argb = "FF555555") => ({ style: "medium" as const, color: { argb } }); + const bordAll = { top: thin(), left: thin(), bottom: thin(), right: thin() }; + const bordMed = { top: med(), left: med(), bottom: med(), right: med() }; - return new NextResponse(buf, { + const ARIAL = "Arial"; + const fBase = { name: ARIAL, size: 9 }; + const fBold = { name: ARIAL, size: 9, bold: true }; + const fTitle = { name: ARIAL, size: 13, bold: true }; + const fH2 = { name: ARIAL, size: 11, bold: true }; + const fSmall = { name: ARIAL, size: 8 }; + + const fillHdr = { type: "pattern" as const, pattern: "solid" as const, fgColor: { argb: "FFD8D8D8" } }; + const fillLbl = { type: "pattern" as const, pattern: "solid" as const, fgColor: { argb: "FFF2F2F2" } }; + const fillTot = { type: "pattern" as const, pattern: "solid" as const, fgColor: { argb: "FFF0F0F0" } }; + const fillGT = { type: "pattern" as const, pattern: "solid" as const, fgColor: { argb: "FFD8D8D8" } }; + const fillInst = { type: "pattern" as const, pattern: "solid" as const, fgColor: { argb: "FFEAEAEA" } }; + + const alignC: Partial = { horizontal: "center", vertical: "middle", wrapText: true }; + const alignL: Partial = { horizontal: "left", vertical: "middle", wrapText: true }; + const alignR: Partial = { horizontal: "right", vertical: "middle" }; + + // Helper: set cell value + style + function sc(row: number, col: number, value: ExcelJS.CellValue, opts: { + font?: Partial; + fill?: ExcelJS.Fill; + border?: Partial; + align?: Partial; + numFmt?: string; + } = {}) { + const cell = ws.getCell(row, col); + cell.value = value; + if (opts.font) cell.font = opts.font as ExcelJS.Font; + if (opts.fill) cell.fill = opts.fill; + if (opts.border) cell.border = opts.border; + if (opts.align) cell.alignment = opts.align; + if (opts.numFmt) cell.numFmt = opts.numFmt; + } + + // Helper: apply border to a whole row range + function rowBorder(row: number, c1: number, c2: number, border: Partial) { + for (let c = c1; c <= c2; c++) ws.getCell(row, c).border = border; + } + + // ── Row heights ───────────────────────────────────────────────────────── + ws.getRow(1).height = 22; + ws.getRow(2).height = 14; + ws.getRow(3).height = 14; + ws.getRow(4).height = 18; + + // ══ ROW 1: Company name ═════════════════════════════════════════════════ + sc(1, 1, CO_NAME, { font: fTitle, align: alignC }); + ws.mergeCells("A1:I1"); + ws.getRow(1).getCell(1).border = { bottom: thin() }; + + // ══ ROW 2: Address ══════════════════════════════════════════════════════ + sc(2, 1, CO_ADDR, { font: fBase, align: alignC }); + ws.mergeCells("A2:I2"); + + // ══ ROW 3: Tel / Email ══════════════════════════════════════════════════ + sc(3, 1, CO_TEL, { font: fSmall, align: alignC }); + ws.mergeCells("A3:I3"); + + // ══ ROW 4: PURCHASE ORDER ════════════════════════════════════════════════ + sc(4, 1, "PURCHASE ORDER", { font: { ...fH2, underline: true }, align: alignC }); + ws.mergeCells("A4:I4"); + ws.getRow(4).border = { top: thin(), bottom: thin() }; + + // ══ ROW 5: PO Number & Date ══════════════════════════════════════════════ + ws.getRow(5).height = 16; + sc(5, 1, "Purchase Order No:", { font: fBold, fill: fillLbl, border: bordAll, align: alignL }); + ws.mergeCells("A5:B5"); + sc(5, 3, po.poNumber, { font: { ...fBold, color: { argb: "FF1A1A1A" } }, border: bordAll, align: alignL }); + ws.mergeCells("C5:G5"); + sc(5, 8, "Date:", { font: fBold, fill: fillLbl, border: bordAll, align: alignR }); + sc(5, 9, fmtDate(po.createdAt), { font: fBase, border: bordAll, align: alignL }); + + // ══ ROW 6: PI / Quotation ════════════════════════════════════════════════ + ws.getRow(6).height = 14; + sc(6, 1, "Performa Invoice / Quotation No:", { font: fBold, fill: fillLbl, border: bordAll, align: alignL }); + ws.mergeCells("A6:B6"); + sc(6, 3, piNo, { font: fBase, border: bordAll, align: alignL }); + ws.mergeCells("C6:F6"); + sc(6, 7, "P I / Quotation Date:", { font: fBold, fill: fillLbl, border: bordAll, align: alignR }); + ws.mergeCells("G6:H6"); + sc(6, 9, piDate, { font: fBase, border: bordAll, align: alignL }); + + // ══ ROW 7: Vessel Owner / Budget / Requested By ═══════════════════════ + ws.getRow(7).height = 14; + sc(7, 1, "Vessel Owner Name", { font: fBold, fill: fillLbl, border: bordAll, align: alignL }); + ws.mergeCells("A7:B7"); + sc(7, 3, "Pelagia Marine Services Pvt. Ltd.", { font: fBase, border: bordAll, align: alignL }); + sc(7, 4, "Budget head", { font: fBold, fill: fillLbl, border: bordAll, align: alignC }); + ws.mergeCells("D7:E7"); + sc(7, 6, po.account.code, { font: fBase, border: bordAll, align: alignC }); + sc(7, 7, "Requested By", { font: fBold, fill: fillLbl, border: bordAll, align: alignC }); + sc(7, 8, po.submitter.name, { font: fBase, border: bordAll, align: alignL }); + ws.mergeCells("H7:I7"); + + // ══ ROW 8: Requisition / Approved By ═════════════════════════════════ + ws.getRow(8).height = 14; + sc(8, 1, "Vessel/Office Requisition Number", { font: fBold, fill: fillLbl, border: bordAll, align: alignL }); + ws.mergeCells("A8:B8"); + sc(8, 3, reqNo, { font: fBase, border: bordAll, align: alignL }); + sc(8, 4, "Reqn. Date", { font: fBold, fill: fillLbl, border: bordAll, align: alignC }); + ws.mergeCells("D8:E8"); + sc(8, 6, reqDate, { font: fBase, border: bordAll, align: alignC }); + sc(8, 7, "Approved By", { font: fBold, fill: fillLbl, border: bordAll, align: alignC }); + sc(8, 8, approvedBy, { font: fBase, border: bordAll, align: alignL }); + ws.mergeCells("H8:I8"); + + // ══ ROWS 9-10: Place of Delivery (2-row span) ═══════════════════════ + ws.getRow(9).height = 14; + ws.getRow(10).height = 14; + sc(9, 1, "Place of Delivery", { font: fBold, fill: fillLbl, border: bordAll, align: alignC }); + ws.mergeCells("A9:B10"); + sc(9, 3, delivery, { font: fBase, border: bordAll, align: alignL }); + ws.mergeCells("C9:I10"); + + // ══ ROWS 11-12: Invoice Details (2-row span) ════════════════════════ + ws.getRow(11).height = 14; + ws.getRow(12).height = 13; + sc(11, 1, "Invoice Details", { font: fBold, fill: fillLbl, border: bordAll, align: alignC }); + ws.mergeCells("A11:B12"); + sc(11, 3, INV_ADDR, { font: fSmall, border: { top: thin(), left: thin(), right: thin() }, align: alignL }); + ws.mergeCells("C11:I11"); + sc(12, 3, INV_GST, { font: fSmall, border: { bottom: thin(), left: thin(), right: thin() }, align: alignL }); + ws.mergeCells("C12:I12"); + + // ══ ROW 13: Vendor Name & Address ═══════════════════════════════════ + ws.getRow(13).height = 28; + sc(13, 1, "Vendor Name & Address", { font: fBold, fill: fillLbl, border: bordAll, align: alignC }); + ws.mergeCells("A13:B13"); + sc(13, 3, po.vendor?.name ?? "—", { font: fBold, border: bordAll, align: alignL }); + sc(13, 4, vendorAddr, { font: fSmall, border: bordAll, align: alignL }); + ws.mergeCells("D13:I13"); + + // ══ ROW 14: Contact ═════════════════════════════════════════════════ + ws.getRow(14).height = 14; + sc(14, 1, "Contact Person name / mobile no.", { font: fBold, fill: fillLbl, border: bordAll, align: alignC }); + ws.mergeCells("A14:B14"); + sc(14, 3, vendorContact, { font: fSmall, border: bordAll, align: alignL }); + ws.mergeCells("C14:I14"); + + // ══ ROW 15: Line item header ═════════════════════════════════════════ + const HDR_ROW = 15; + ws.getRow(HDR_ROW).height = 16; + const hdrStyle = { font: fBold, fill: fillHdr, border: bordAll }; + sc(HDR_ROW, 1, "S.N.", { ...hdrStyle, align: alignC }); + sc(HDR_ROW, 2, "Description", { ...hdrStyle, align: alignC }); + ws.mergeCells(`B${HDR_ROW}:C${HDR_ROW}`); + sc(HDR_ROW, 4, "Unit", { ...hdrStyle, align: alignC }); + sc(HDR_ROW, 5, "Qnty", { ...hdrStyle, align: alignC }); + sc(HDR_ROW, 6, "Unit price", { ...hdrStyle, align: alignC }); + sc(HDR_ROW, 7, "Taxable cost",{ ...hdrStyle, align: alignC }); + sc(HDR_ROW, 8, "GST%", { ...hdrStyle, align: alignC }); + sc(HDR_ROW, 9, "Total cost", { ...hdrStyle, align: alignC }); + + // ══ Line items ═══════════════════════════════════════════════════════ + const NUM_FMT = '#,##0.00'; + const BODY_ROWS = Math.max(items.length, 7); // reserve at least 7 rows + for (let idx = 0; idx < BODY_ROWS; idx++) { + const r = HDR_ROW + 1 + idx; + ws.getRow(r).height = 15; + const item = items[idx]; + const fillAlt = idx % 2 === 1 + ? { type: "pattern" as const, pattern: "solid" as const, fgColor: { argb: "FFFAFAFA" } } + : undefined; + + sc(r, 1, item?.sn ?? null, { font: fBase, fill: fillAlt, border: bordAll, align: alignC }); + sc(r, 2, item?.desc ?? "", { font: fBase, fill: fillAlt, border: bordAll, align: alignL }); + ws.mergeCells(`B${r}:C${r}`); + sc(r, 4, item?.unit ?? "", { font: fBase, fill: fillAlt, border: bordAll, align: alignC }); + sc(r, 5, item ? item.qty : null, { font: fBase, fill: fillAlt, border: bordAll, align: alignR, numFmt: NUM_FMT }); + sc(r, 6, item ? item.unitPrice : null, { font: fBase, fill: fillAlt, border: bordAll, align: alignR, numFmt: NUM_FMT }); + sc(r, 7, item ? item.taxable : null, { font: fBase, fill: fillAlt, border: bordAll, align: alignR, numFmt: NUM_FMT }); + sc(r, 8, item ? item.gstRate : null, { font: fBase, fill: fillAlt, border: bordAll, align: alignC, numFmt: '0%' }); + sc(r, 9, item ? item.total : null, { font: { ...fBase, bold: !!item }, fill: fillAlt, border: bordAll, align: alignR, numFmt: NUM_FMT }); + } + + // ══ Totals ════════════════════════════════════════════════════════════ + const TOT_ROW = HDR_ROW + 1 + BODY_ROWS + 1; + ws.getRow(TOT_ROW).height = 14; + ws.getRow(TOT_ROW + 1).height = 14; + ws.getRow(TOT_ROW + 2).height = 16; + + // "Total taxable value" + sc(TOT_ROW, 6, "Total taxable value", { font: fBold, fill: fillTot, border: bordAll, align: alignR }); + ws.mergeCells(`F${TOT_ROW}:G${TOT_ROW}`); + sc(TOT_ROW, 8, totalTaxable, { font: fBold, fill: fillTot, border: bordAll, align: alignR, numFmt: NUM_FMT }); + ws.mergeCells(`H${TOT_ROW}:I${TOT_ROW}`); + + // "GST" + sc(TOT_ROW + 1, 6, gstLabel, { font: fBold, fill: fillTot, border: bordAll, align: alignR }); + ws.mergeCells(`F${TOT_ROW + 1}:G${TOT_ROW + 1}`); + sc(TOT_ROW + 1, 8, totalGst, { font: fBold, fill: fillTot, border: bordAll, align: alignR, numFmt: NUM_FMT }); + ws.mergeCells(`H${TOT_ROW + 1}:I${TOT_ROW + 1}`); + + // "GRAND TOTAL" + sc(TOT_ROW + 2, 6, "GRAND TOTAL", { font: { ...fBold, size: 10 }, fill: fillGT, border: bordAll, align: alignR }); + ws.mergeCells(`F${TOT_ROW + 2}:G${TOT_ROW + 2}`); + sc(TOT_ROW + 2, 8, grandTotal, { font: { ...fBold, size: 10 }, fill: fillGT, border: bordAll, align: alignR, numFmt: NUM_FMT }); + ws.mergeCells(`H${TOT_ROW + 2}:I${TOT_ROW + 2}`); + + // ══ Instructions ═════════════════════════════════════════════════════ + const INST_ROW = TOT_ROW + 4; + ws.getRow(INST_ROW).height = 16; + sc(INST_ROW, 1, "INSTRUCTIONS TO VENDORS", { font: { ...fBold, size: 10 }, fill: fillInst, border: bordAll, align: alignC }); + ws.mergeCells(`A${INST_ROW}:I${INST_ROW}`); + + tcLines.forEach(([num, label, value], i) => { + const r = INST_ROW + 1 + i; + ws.getRow(r).height = 14; + sc(r, 1, num, { font: fBase, border: bordAll, align: alignC }); + const text = label ? `${label}: ${value}` : value; + sc(r, 2, text, { font: fBase, border: bordAll, align: alignL }); + ws.mergeCells(`B${r}:I${r}`); + }); + + // ══ Signatures ═══════════════════════════════════════════════════════ + const SIG_ROW = INST_ROW + tcLines.length + 3; + ws.getRow(SIG_ROW).height = 40; + ws.getRow(SIG_ROW + 1).height = 14; + ws.getRow(SIG_ROW + 2).height = 14; + + // Left sig block (submitter) + sc(SIG_ROW, 1, po.submitter.name, { font: fBold, border: { top: thin(), left: thin(), right: thin() }, align: alignC }); + ws.mergeCells(`A${SIG_ROW}:D${SIG_ROW}`); + sc(SIG_ROW + 1, 1, "Authorized Signatory & Stamp", { font: fSmall, border: { left: thin(), right: thin() }, align: alignC }); + ws.mergeCells(`A${SIG_ROW + 1}:D${SIG_ROW + 1}`); + sc(SIG_ROW + 2, 1, "For, Pelagia Marine Services Pvt. Ltd.", { font: fSmall, border: { left: thin(), bottom: thin(), right: thin() }, align: alignC }); + ws.mergeCells(`A${SIG_ROW + 2}:D${SIG_ROW + 2}`); + + // Right sig block (vendor) + const vName = po.vendor?.name ?? ""; + sc(SIG_ROW, 6, vName, { font: fBold, border: { top: thin(), left: thin(), right: thin() }, align: alignC }); + ws.mergeCells(`F${SIG_ROW}:I${SIG_ROW}`); + sc(SIG_ROW + 1, 6, "Authorized Signatory & Stamp", { font: fSmall, border: { left: thin(), right: thin() }, align: alignC }); + ws.mergeCells(`F${SIG_ROW + 1}:I${SIG_ROW + 1}`); + sc(SIG_ROW + 2, 6, `For, ${vName}`, { font: fSmall, border: { left: thin(), bottom: thin(), right: thin() }, align: alignC }); + ws.mergeCells(`F${SIG_ROW + 2}:I${SIG_ROW + 2}`); + + // ── Serialise ───────────────────────────────────────────────────────── + const buf = await wb.xlsx.writeBuffer(); + const slug = po.poNumber.replace(/\//g, "-"); + return new NextResponse(Buffer.from(buf), { headers: { "Content-Type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - "Content-Disposition": `attachment; filename="${po.poNumber.replace(/\//g, "-")}.xlsx"`, + "Content-Disposition": `attachment; filename="${slug}.xlsx"`, }, }); } - // ── PDF (HTML print page) ─────────────────────────────────────────────────── + // ═══════════════════════════════════════════════════════════════════════════ + // PDF — HTML print page matching sample layout + // ═══════════════════════════════════════════════════════════════════════════ + const itemRows = items.map((item, i) => ` ${item.sn} ${item.desc} ${item.unit} - ${fmt(item.qty, 3)} - ${fmt(item.unitPrice)} - ${fmt(item.taxable)} + ${fmtNum(item.qty, item.qty % 1 === 0 ? 0 : 3)} + ${fmtNum(item.unitPrice)} + ${fmtNum(item.taxable)} ${Math.round(item.gstRate * 100)}% - ${fmt(item.total)} + ${fmtNum(item.total)} `).join(""); - const termLines = tcLines.map(([num, label, value]) => - `
  • ${num}. ${label ? `${label}: ` : ""}${value}
  • ` + // Pad to at least 7 rows so the table doesn't collapse + const blankRows = Math.max(0, 7 - items.length); + const padRows = Array(blankRows).fill( + '' ).join(""); + const termRows = tcLines.map(([num, label, value]) => ` + + ${num}. + ${label ? `${label}: ` : ""}${value} + `).join(""); + const html = ` ${po.poNumber} — Purchase Order -
    - + +
    +
    - -

    ${COMPANY_NAME}

    -

    ${COMPANY_ADDR}

    -

    ${COMPANY_TEL}

    -
    -

    PURCHASE ORDER

    -
    + +
    ${CO_NAME}
    +
    ${CO_ADDR}
    +
    ${CO_TEL}
    +
    PURCHASE ORDER
    - - + +
    - - - + + + - + - +
    Purchase Order No:${po.poNumber}Date:Purchase Order No:${po.poNumber}Date: ${fmtDate(po.createdAt)}
    PI / Quotation No:Performa Invoice / Quotation No: ${piNo}PI / Quotation Date:P I / Quotation Date: ${piDate}
    - - + +
    - - - - - - + + + + + + - + - + - +
    Vessel Owner NamePelagia Marine Services Pvt. Ltd.Budget Head${po.account.code}Requested By${po.submitter.name}Vessel Owner NamePelagia Marine Services Pvt. Ltd.Budget head${po.account.code}Requested By${po.submitter.name}
    Vessel/Office Req. No.Vessel/Office Requisition Number ${reqNo}Reqn. DateReqn. Date ${reqDate}Approved ByApproved By ${approvedBy}
    - - + +
    - - + +
    Place of Delivery${placeOfDelivery}Place of Delivery${delivery}
    - - + +
    - - + + + + +
    Invoice Details${INVOICE_ADDR}
    ${INVOICE_GST}
    Invoice Details${INV_ADDR}
    ${INV_GST}
    - - + +
    - - - + + + - - + +
    Vendor Name & Address${po.vendor?.name ?? "—"}${[po.vendor?.address, po.vendor?.gstin ? `GSTIN: ${po.vendor.gstin}` : ""].filter(Boolean).join(" ")}Vendor Name & Address${po.vendor?.name ?? "—"}${vendorAddr}
    Contact Person / Mobile${[po.vendor?.contactName, po.vendor?.contactMobile, po.vendor?.contactEmail].filter(Boolean).join(" ")}Contact Person name / mobile no.${vendorContact}
    - - + +
    - - - - + + + + - + ${itemRows} - ${Array(Math.max(0, 6 - items.length)).fill('').join("")} + ${padRows}
    S.N. DescriptionUnitQtyUnit PriceTaxable CostUnitQntyUnit priceTaxable cost GST%Total CostTotal cost
     
    - - - - - + +
    Total Taxable Value${fmt(totalTaxable)}
    + + + - - - + + + - - - + + +
    Total taxable value${fmtNum(totalTaxable)}
    GST (${Math.round((items[0]?.gstRate ?? 0.18) * 100)}%)${fmt(totalGst)}
    ${gstLabel}${fmtNum(totalGst)}
    GRAND TOTAL (INR)${fmt(grandTotal)}
    GRAND TOTAL${fmtNum(grandTotal)}
    - - - - +
    + + +
    INSTRUCTIONS TO VENDORS
      ${termLines}
    + + ${termRows}
    INSTRUCTIONS TO VENDORS
    - -
    + +
    -

    ${po.submitter.name}

    -

    Authorized Signatory & Stamp

    -

    For, Pelagia Marine Services Pvt. Ltd.

    +
    ${po.submitter.name}
    +
    +
    Authorized Signatory & Stamp
    +
    For, Pelagia Marine Services Pvt. Ltd.
    +
    -

    ${po.vendor?.name ?? ""}

    -

    Authorized Signatory & Stamp

    -

    For, ${po.vendor?.name ?? ""}

    +
    ${po.vendor?.name ?? ""}
    +
    +
    Authorized Signatory & Stamp
    +
    For, ${po.vendor?.name ?? ""}
    +
    @@ -385,7 +653,5 @@ export async function GET(request: NextRequest, { params }: Props) { `; - return new NextResponse(html, { - headers: { "Content-Type": "text/html; charset=utf-8" }, - }); + return new NextResponse(html, { headers: { "Content-Type": "text/html; charset=utf-8" } }); } diff --git a/App/pelagia-portal/package.json b/App/pelagia-portal/package.json index 04ec12e..4feacc7 100644 --- a/App/pelagia-portal/package.json +++ b/App/pelagia-portal/package.json @@ -43,6 +43,7 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "date-fns": "^4.1.0", + "exceljs": "^4.4.0", "lucide-react": "^0.468.0", "next": "^15.1.0", "next-auth": "^5.0.0-beta.25", diff --git a/App/pelagia-portal/pnpm-lock.yaml b/App/pelagia-portal/pnpm-lock.yaml index 350aa16..41a35c1 100644 --- a/App/pelagia-portal/pnpm-lock.yaml +++ b/App/pelagia-portal/pnpm-lock.yaml @@ -62,6 +62,9 @@ importers: date-fns: specifier: ^4.1.0 version: 4.1.0 + exceljs: + specifier: ^4.4.0 + version: 4.4.0 lucide-react: specifier: ^0.468.0 version: 0.468.0(react@19.2.5) @@ -998,6 +1001,12 @@ packages: '@noble/hashes': optional: true + '@fast-csv/format@4.3.5': + resolution: {integrity: sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A==} + + '@fast-csv/parse@4.3.6': + resolution: {integrity: sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==} + '@floating-ui/core@1.7.5': resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} @@ -2620,6 +2629,9 @@ packages: '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + '@types/node@14.18.63': + resolution: {integrity: sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==} + '@types/node@22.19.17': resolution: {integrity: sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==} @@ -2879,6 +2891,18 @@ packages: resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} + archiver-utils@2.1.0: + resolution: {integrity: sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==} + engines: {node: '>= 6'} + + archiver-utils@3.0.4: + resolution: {integrity: sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==} + engines: {node: '>= 10'} + + archiver@5.3.2: + resolution: {integrity: sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==} + engines: {node: '>= 10'} + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -2936,6 +2960,9 @@ packages: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} engines: {node: '>= 0.4'} + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} @@ -2973,9 +3000,19 @@ packages: bidi-js@1.0.3: resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} + big-integer@1.6.52: + resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} + engines: {node: '>=0.6'} + + binary@0.3.0: + resolution: {integrity: sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==} + bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + bluebird@3.4.7: + resolution: {integrity: sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==} + bowser@2.14.1: resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} @@ -2998,9 +3035,20 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + buffer-crc32@0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + + buffer-indexof-polyfill@1.0.2: + resolution: {integrity: sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==} + engines: {node: '>=0.10'} + buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + buffers@0.1.1: + resolution: {integrity: sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==} + engines: {node: '>=0.2.0'} + busboy@1.6.0: resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} engines: {node: '>=10.16.0'} @@ -3036,6 +3084,9 @@ packages: resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} engines: {node: '>=18'} + chainsaw@0.1.0: + resolution: {integrity: sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==} + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -3096,6 +3147,10 @@ packages: resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} engines: {node: '>=16'} + compress-commons@4.1.2: + resolution: {integrity: sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==} + engines: {node: '>= 10'} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -3109,6 +3164,9 @@ packages: resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} engines: {node: '>= 0.6'} + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + cors@2.8.6: resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} engines: {node: '>= 0.10'} @@ -3118,6 +3176,10 @@ packages: engines: {node: '>=0.8'} hasBin: true + crc32-stream@4.0.3: + resolution: {integrity: sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==} + engines: {node: '>= 10'} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -3198,6 +3260,9 @@ packages: date-fns@4.1.0: resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} + dayjs@1.11.20: + resolution: {integrity: sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==} + debounce@2.0.0: resolution: {integrity: sha512-xRetU6gL1VJbs85Mc4FoEGSjQxzpdxRyFhe3lmWFyy2EzydIcD4xzUvRJMD+NPDfMwKNhxa3PvsIOU32luIWeA==} engines: {node: '>=18'} @@ -3297,6 +3362,9 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} + duplexer2@0.1.4: + resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==} + eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} @@ -3314,6 +3382,9 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + engine.io-parser@5.2.3: resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} engines: {node: '>=10.0.0'} @@ -3522,10 +3593,18 @@ packages: eventemitter3@4.0.7: resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + exceljs@4.4.0: + resolution: {integrity: sha512-XctvKaEMaj1Ii9oDOqbW/6e1gXknSY4g/aLCDicOXqBE4M0nRWkUu0PTp++UPNzoFY12BNHMfs/VadKIS6llvg==} + engines: {node: '>=8.3.0'} + expect-type@1.3.0: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} + fast-csv@4.3.6: + resolution: {integrity: sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==} + engines: {node: '>=10.0.0'} + fast-deep-equal@2.0.1: resolution: {integrity: sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==} @@ -3596,6 +3675,12 @@ packages: resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==} engines: {node: '>=0.8'} + fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + fsevents@2.3.2: resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -3606,6 +3691,11 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + fstream@1.0.12: + resolution: {integrity: sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==} + engines: {node: '>=0.6'} + deprecated: This package is no longer supported. + function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} @@ -3662,6 +3752,10 @@ packages: deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} @@ -3726,6 +3820,9 @@ packages: resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} + immediate@3.0.6: + resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} @@ -3738,6 +3835,10 @@ packages: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -3873,6 +3974,9 @@ packages: resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} engines: {node: '>= 0.4'} + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} @@ -3949,6 +4053,9 @@ packages: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} + jszip@3.10.1: + resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -3959,6 +4066,10 @@ packages: resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} engines: {node: '>=0.10'} + lazystream@1.0.1: + resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} + engines: {node: '>= 0.6.3'} + leac@0.6.0: resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==} @@ -3966,6 +4077,9 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lie@3.3.0: + resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} + lightningcss-android-arm64@1.32.0: resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} engines: {node: '>= 12.0.0'} @@ -4040,13 +4154,56 @@ packages: resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} engines: {node: '>= 12.0.0'} + listenercount@1.0.1: + resolution: {integrity: sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + lodash.defaults@4.2.0: + resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} + + lodash.difference@4.5.0: + resolution: {integrity: sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==} + + lodash.escaperegexp@4.1.2: + resolution: {integrity: sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==} + + lodash.flatten@4.4.0: + resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==} + + lodash.groupby@4.6.0: + resolution: {integrity: sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==} + + lodash.isboolean@3.0.3: + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + + lodash.isequal@4.5.0: + resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. + + lodash.isfunction@3.0.9: + resolution: {integrity: sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==} + + lodash.isnil@4.0.0: + resolution: {integrity: sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + + lodash.isundefined@3.0.1: + resolution: {integrity: sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==} + lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lodash.union@4.6.0: + resolution: {integrity: sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==} + + lodash.uniq@4.5.0: + resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} + lodash@4.18.1: resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} @@ -4131,6 +4288,10 @@ packages: minimatch@3.1.5: resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + minimatch@5.1.9: + resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==} + engines: {node: '>=10'} + minimatch@9.0.9: resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} engines: {node: '>=16 || 14 >=14.17'} @@ -4142,6 +4303,10 @@ packages: resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} engines: {node: '>=16 || 14 >=14.17'} + mkdirp@0.5.6: + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -4272,6 +4437,9 @@ packages: resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + onetime@5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} @@ -4299,6 +4467,9 @@ packages: package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -4320,6 +4491,10 @@ packages: resolution: {integrity: sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==} engines: {node: '>=14.0.0'} + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -4404,6 +4579,9 @@ packages: resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==} engines: {node: '>=6'} + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} @@ -4489,10 +4667,16 @@ packages: resolution: {integrity: sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==} engines: {node: '>=0.10.0'} + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} + readdir-glob@1.1.3: + resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} + readdirp@4.1.2: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} @@ -4547,6 +4731,11 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rimraf@2.7.1: + resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + rollup@4.60.2: resolution: {integrity: sha512-J9qZyW++QK/09NyN/zeO0dG/1GdGfyp9lV8ajHnRVLfo/uFsbji5mHnDgn/qYdUHyCkM2N+8VyspgZclfAh0eQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -4559,6 +4748,9 @@ packages: resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==} engines: {node: '>=0.4'} + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -4570,6 +4762,10 @@ packages: resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} engines: {node: '>= 0.4'} + saxes@5.0.1: + resolution: {integrity: sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==} + engines: {node: '>=10'} + saxes@6.0.0: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} @@ -4601,6 +4797,9 @@ packages: resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} engines: {node: '>= 0.4'} + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + sharp@0.33.5: resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -4713,6 +4912,9 @@ packages: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} engines: {node: '>= 0.4'} + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} @@ -4773,6 +4975,10 @@ packages: resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} engines: {node: '>=6'} + tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} @@ -4805,6 +5011,10 @@ packages: resolution: {integrity: sha512-ELrFxuqsDdHUwoh0XxDbxuLD3Wnz49Z57IFvTtvWy1hJdcMZjXLIuonjilCiWHlT2GbE4Wlv1wKVTzDFnXH1aw==} hasBin: true + tmp@0.2.5: + resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} + engines: {node: '>=14.14'} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -4817,6 +5027,9 @@ packages: resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} engines: {node: '>=20'} + traverse@0.3.9: + resolution: {integrity: sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==} + ts-api-utils@2.5.0: resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} engines: {node: '>=18.12'} @@ -4876,6 +5089,9 @@ packages: unrs-resolver@1.11.1: resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} + unzipper@0.10.14: + resolution: {integrity: sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==} + update-browserslist-db@1.2.3: resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true @@ -4913,6 +5129,11 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). + hasBin: true + vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} @@ -5046,6 +5267,9 @@ packages: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} engines: {node: '>=12'} + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + ws@8.18.3: resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} engines: {node: '>=10.0.0'} @@ -5077,6 +5301,10 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + zip-stream@4.1.1: + resolution: {integrity: sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==} + engines: {node: '>= 10'} + zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -6051,6 +6279,25 @@ snapshots: '@exodus/bytes@1.15.0': {} + '@fast-csv/format@4.3.5': + dependencies: + '@types/node': 14.18.63 + lodash.escaperegexp: 4.1.2 + lodash.isboolean: 3.0.3 + lodash.isequal: 4.5.0 + lodash.isfunction: 3.0.9 + lodash.isnil: 4.0.0 + + '@fast-csv/parse@4.3.6': + dependencies: + '@types/node': 14.18.63 + lodash.escaperegexp: 4.1.2 + lodash.groupby: 4.6.0 + lodash.isfunction: 3.0.9 + lodash.isnil: 4.0.0 + lodash.isundefined: 3.0.1 + lodash.uniq: 4.5.0 + '@floating-ui/core@1.7.5': dependencies: '@floating-ui/utils': 0.2.11 @@ -7516,6 +7763,8 @@ snapshots: '@types/json5@0.0.29': {} + '@types/node@14.18.63': {} + '@types/node@22.19.17': dependencies: undici-types: 6.21.0 @@ -7770,6 +8019,42 @@ snapshots: ansi-styles@6.2.3: {} + archiver-utils@2.1.0: + dependencies: + glob: 7.2.3 + graceful-fs: 4.2.11 + lazystream: 1.0.1 + lodash.defaults: 4.2.0 + lodash.difference: 4.5.0 + lodash.flatten: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.union: 4.6.0 + normalize-path: 3.0.0 + readable-stream: 2.3.8 + + archiver-utils@3.0.4: + dependencies: + glob: 7.2.3 + graceful-fs: 4.2.11 + lazystream: 1.0.1 + lodash.defaults: 4.2.0 + lodash.difference: 4.5.0 + lodash.flatten: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.union: 4.6.0 + normalize-path: 3.0.0 + readable-stream: 3.6.2 + + archiver@5.3.2: + dependencies: + archiver-utils: 2.1.0 + async: 3.2.6 + buffer-crc32: 0.2.13 + readable-stream: 3.6.2 + readdir-glob: 1.1.3 + tar-stream: 2.2.0 + zip-stream: 4.1.1 + argparse@2.0.1: {} aria-hidden@1.2.6: @@ -7855,6 +8140,8 @@ snapshots: async-function@1.0.0: {} + async@3.2.6: {} + available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.1.0 @@ -7879,12 +8166,21 @@ snapshots: dependencies: require-from-string: 2.0.2 + big-integer@1.6.52: {} + + binary@0.3.0: + dependencies: + buffers: 0.1.1 + chainsaw: 0.1.0 + bl@4.1.0: dependencies: buffer: 5.7.1 inherits: 2.0.4 readable-stream: 3.6.2 + bluebird@3.4.7: {} + bowser@2.14.1: {} brace-expansion@1.1.14: @@ -7912,11 +8208,17 @@ snapshots: node-releases: 2.0.38 update-browserslist-db: 1.2.3(browserslist@4.28.2) + buffer-crc32@0.2.13: {} + + buffer-indexof-polyfill@1.0.2: {} + buffer@5.7.1: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 + buffers@0.1.1: {} + busboy@1.6.0: dependencies: streamsearch: 1.1.0 @@ -7957,6 +8259,10 @@ snapshots: loupe: 3.2.1 pathval: 2.0.1 + chainsaw@0.1.0: + dependencies: + traverse: 0.3.9 + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 @@ -8008,6 +8314,13 @@ snapshots: commander@11.1.0: {} + compress-commons@4.1.2: + dependencies: + buffer-crc32: 0.2.13 + crc32-stream: 4.0.3 + normalize-path: 3.0.0 + readable-stream: 3.6.2 + concat-map@0.0.1: {} config-chain@1.1.13: @@ -8019,6 +8332,8 @@ snapshots: cookie@0.7.2: {} + core-util-is@1.0.3: {} + cors@2.8.6: dependencies: object-assign: 4.1.1 @@ -8026,6 +8341,11 @@ snapshots: crc-32@1.2.2: {} + crc32-stream@4.0.3: + dependencies: + crc-32: 1.2.2 + readable-stream: 3.6.2 + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -8108,6 +8428,8 @@ snapshots: date-fns@4.1.0: {} + dayjs@1.11.20: {} + debounce@2.0.0: {} debug@3.2.7: @@ -8191,6 +8513,10 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 + duplexer2@0.1.4: + dependencies: + readable-stream: 2.3.8 + eastasianwidth@0.2.0: {} editorconfig@1.0.7: @@ -8206,6 +8532,10 @@ snapshots: emoji-regex@9.2.2: {} + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + engine.io-parser@5.2.3: {} engine.io@6.6.7: @@ -8629,8 +8959,25 @@ snapshots: eventemitter3@4.0.7: {} + exceljs@4.4.0: + dependencies: + archiver: 5.3.2 + dayjs: 1.11.20 + fast-csv: 4.3.6 + jszip: 3.10.1 + readable-stream: 3.6.2 + saxes: 5.0.1 + tmp: 0.2.5 + unzipper: 0.10.14 + uuid: 8.3.2 + expect-type@1.3.0: {} + fast-csv@4.3.6: + dependencies: + '@fast-csv/format': 4.3.5 + '@fast-csv/parse': 4.3.6 + fast-deep-equal@2.0.1: {} fast-deep-equal@3.1.3: {} @@ -8699,12 +9046,23 @@ snapshots: frac@1.1.2: {} + fs-constants@1.0.0: {} + + fs.realpath@1.0.0: {} + fsevents@2.3.2: optional: true fsevents@2.3.3: optional: true + fstream@1.0.12: + dependencies: + graceful-fs: 4.2.11 + inherits: 2.0.4 + mkdirp: 0.5.6 + rimraf: 2.7.1 + function-bind@1.1.2: {} function.prototype.name@1.1.8: @@ -8777,6 +9135,15 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 1.11.1 + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.5 + once: 1.4.0 + path-is-absolute: 1.0.1 + globals@14.0.0: {} globalthis@1.0.4: @@ -8837,6 +9204,8 @@ snapshots: ignore@7.0.5: {} + immediate@3.0.6: {} + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 @@ -8846,6 +9215,11 @@ snapshots: indent-string@4.0.0: {} + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + inherits@2.0.4: {} ini@1.3.8: {} @@ -8981,6 +9355,8 @@ snapshots: call-bound: 1.0.4 get-intrinsic: 1.3.0 + isarray@1.0.0: {} + isarray@2.0.5: {} isexe@2.0.0: {} @@ -9073,6 +9449,13 @@ snapshots: object.assign: 4.1.7 object.values: 1.2.1 + jszip@3.10.1: + dependencies: + lie: 3.3.0 + pako: 1.0.11 + readable-stream: 2.3.8 + setimmediate: 1.0.5 + keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -9083,6 +9466,10 @@ snapshots: dependencies: language-subtag-registry: 0.3.23 + lazystream@1.0.1: + dependencies: + readable-stream: 2.3.8 + leac@0.6.0: {} levn@0.4.1: @@ -9090,6 +9477,10 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + lie@3.3.0: + dependencies: + immediate: 3.0.6 + lightningcss-android-arm64@1.32.0: optional: true @@ -9139,12 +9530,40 @@ snapshots: lightningcss-win32-arm64-msvc: 1.32.0 lightningcss-win32-x64-msvc: 1.32.0 + listenercount@1.0.1: {} + locate-path@6.0.0: dependencies: p-locate: 5.0.0 + lodash.defaults@4.2.0: {} + + lodash.difference@4.5.0: {} + + lodash.escaperegexp@4.1.2: {} + + lodash.flatten@4.4.0: {} + + lodash.groupby@4.6.0: {} + + lodash.isboolean@3.0.3: {} + + lodash.isequal@4.5.0: {} + + lodash.isfunction@3.0.9: {} + + lodash.isnil@4.0.0: {} + + lodash.isplainobject@4.0.6: {} + + lodash.isundefined@3.0.1: {} + lodash.merge@4.6.2: {} + lodash.union@4.6.0: {} + + lodash.uniq@4.5.0: {} + lodash@4.18.1: {} log-symbols@4.1.0: @@ -9212,6 +9631,10 @@ snapshots: dependencies: brace-expansion: 1.1.14 + minimatch@5.1.9: + dependencies: + brace-expansion: 2.1.0 + minimatch@9.0.9: dependencies: brace-expansion: 2.1.0 @@ -9220,6 +9643,10 @@ snapshots: minipass@7.1.3: {} + mkdirp@0.5.6: + dependencies: + minimist: 1.2.8 + ms@2.1.3: {} nanoid@3.3.11: {} @@ -9345,6 +9772,10 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + once@1.4.0: + dependencies: + wrappy: 1.0.2 + onetime@5.1.2: dependencies: mimic-fn: 2.1.0 @@ -9386,6 +9817,8 @@ snapshots: package-json-from-dist@1.0.1: {} + pako@1.0.11: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -9407,6 +9840,8 @@ snapshots: path-expression-matcher@1.5.0: {} + path-is-absolute@1.0.1: {} + path-key@3.1.1: {} path-parse@1.0.7: {} @@ -9474,6 +9909,8 @@ snapshots: prismjs@1.29.0: {} + process-nextick-args@2.0.1: {} + prop-types@15.8.1: dependencies: loose-envify: 1.4.0 @@ -9577,12 +10014,26 @@ snapshots: react@19.2.5: {} + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + readable-stream@3.6.2: dependencies: inherits: 2.0.4 string_decoder: 1.3.0 util-deprecate: 1.0.2 + readdir-glob@1.1.3: + dependencies: + minimatch: 5.1.9 + readdirp@4.1.2: {} recharts-scale@0.4.5: @@ -9656,6 +10107,10 @@ snapshots: reusify@1.1.0: {} + rimraf@2.7.1: + dependencies: + glob: 7.2.3 + rollup@4.60.2: dependencies: '@types/estree': 1.0.8 @@ -9699,6 +10154,8 @@ snapshots: has-symbols: 1.1.0 isarray: 2.0.5 + safe-buffer@5.1.2: {} + safe-buffer@5.2.1: {} safe-push-apply@1.0.0: @@ -9712,6 +10169,10 @@ snapshots: es-errors: 1.3.0 is-regex: 1.2.1 + saxes@5.0.1: + dependencies: + xmlchars: 2.2.0 + saxes@6.0.0: dependencies: xmlchars: 2.2.0 @@ -9748,6 +10209,8 @@ snapshots: es-errors: 1.3.0 es-object-atoms: 1.1.1 + setimmediate@1.0.5: {} + sharp@0.33.5: dependencies: color: 4.2.3 @@ -9963,6 +10426,10 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + string_decoder@1.3.0: dependencies: safe-buffer: 5.2.1 @@ -10006,6 +10473,14 @@ snapshots: tapable@2.3.3: {} + tar-stream@2.2.0: + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.5 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + tiny-invariant@1.3.3: {} tinybench@2.9.0: {} @@ -10029,6 +10504,8 @@ snapshots: dependencies: tldts-core: 7.0.30 + tmp@0.2.5: {} + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -10041,6 +10518,8 @@ snapshots: dependencies: punycode: 2.3.1 + traverse@0.3.9: {} + ts-api-utils@2.5.0(typescript@5.9.3): dependencies: typescript: 5.9.3 @@ -10137,6 +10616,19 @@ snapshots: '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 + unzipper@0.10.14: + dependencies: + big-integer: 1.6.52 + binary: 0.3.0 + bluebird: 3.4.7 + buffer-indexof-polyfill: 1.0.2 + duplexer2: 0.1.4 + fstream: 1.0.12 + graceful-fs: 4.2.11 + listenercount: 1.0.1 + readable-stream: 2.3.8 + setimmediate: 1.0.5 + update-browserslist-db@1.2.3(browserslist@4.28.2): dependencies: browserslist: 4.28.2 @@ -10168,6 +10660,8 @@ snapshots: util-deprecate@1.0.2: {} + uuid@8.3.2: {} + vary@1.1.2: {} victory-vendor@36.9.2: @@ -10339,6 +10833,8 @@ snapshots: string-width: 5.1.2 strip-ansi: 7.2.0 + wrappy@1.0.2: {} + ws@8.18.3: {} xlsx@0.18.5: @@ -10359,4 +10855,10 @@ snapshots: yocto-queue@0.1.0: {} + zip-stream@4.1.1: + dependencies: + archiver-utils: 3.0.4 + compress-commons: 4.1.2 + readable-stream: 3.6.2 + zod@3.25.76: {}