From 92107b441f00806695f21b5e94ceb00babedc9fc Mon Sep 17 00:00:00 2001 From: markalmax Date: Wed, 3 Jun 2026 07:16:12 +0300 Subject: [PATCH 1/2] feat: added items that counts towards the event in the event shop --- .gitignore | 3 +- admin/src/lib/api/schema.d.ts | 214 ++- admin/src/routes/(app)/shop/+page.svelte | 43 +- backend/BACKEND.md | 4 +- .../migration.sql | 5 + .../migration.sql | 1 + backend/prisma/schema.prisma | 4 + backend/src/balance/balance.service.ts | 3 + backend/src/events/dto/events-response.dto.ts | 6 + backend/src/events/events.service.ts | 42 +- backend/src/shop/dto/create-item.dto.ts | 5 + backend/src/shop/dto/shop-response.dto.ts | 8 + backend/src/shop/dto/update-item.dto.ts | 5 + backend/src/shop/shop.service.ts | 15 +- frontend/src/lib/api/schema.d.ts | 289 ++- .../components/cards/EventColumnCard.svelte | 6 +- frontend/src/routes/app/+page.svelte | 12 +- frontend/src/routes/app/events/+page.svelte | 15 +- .../app/events/shop/[itemId]/+page.svelte | 6 + package-lock.json | 1559 ++--------------- package.json | 2 +- pnpm-lock.yaml | 16 +- 22 files changed, 716 insertions(+), 1547 deletions(-) create mode 100644 backend/prisma/migrations/20260603010909_add_event_hours_reduction/migration.sql create mode 100644 backend/prisma/migrations/20260603010954_add_event_hours_reduction/migration.sql diff --git a/.gitignore b/.gitignore index 9579e2c3..b8e19feb 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ backend/mail-service/certs/ *.p12 *.pfx *.pem -.DS_Store \ No newline at end of file +.DS_Store +/generated/prisma diff --git a/admin/src/lib/api/schema.d.ts b/admin/src/lib/api/schema.d.ts index bbdbebc2..69eec510 100644 --- a/admin/src/lib/api/schema.d.ts +++ b/admin/src/lib/api/schema.d.ts @@ -1141,6 +1141,22 @@ export interface paths { patch: operations["AdminController_updateUser"]; trace?: never; }; + "/api/admin/users/{id}/hours-adjustment": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["AdminController_adjustUserHours"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/api/admin/import/csv": { parameters: { query?: never; @@ -1980,7 +1996,7 @@ export interface paths { path?: never; cookie?: never; }; - /** Check if a URL is reachable */ + /** Check if a URL is reachable (forwarded to isolated worker) */ get: operations["UrlCheckController_checkUrl"]; put?: never; post?: never; @@ -2139,7 +2155,7 @@ export interface components { repoUrl?: string; /** @description README URL */ readmeUrl?: string; - /** @description Screenshot URL */ + /** @description Screenshot URL (Hack Club CDN only) */ screenshotUrl?: string; /** @description Linked Hackatime project names */ nowHackatimeProjects?: string[]; @@ -2289,7 +2305,7 @@ export interface components { readmeUrl?: string; /** * Format: uri - * @description Screenshot URL + * @description Screenshot URL (Hack Club CDN only) */ screenshotUrl?: string; /** @description Journal URL (for hardware projects) */ @@ -2321,6 +2337,8 @@ export interface components { }; /** @description Hours tallied at the time of the last submission; null if never submitted */ lastSubmittedHours?: number | null; + /** @description Hours tallied at the time of the most recent approved submission; null if never approved. Used by the ship gate to require an additional 3 hours per reship. */ + lastApprovedHours?: number | null; /** @description Consecutive days the user has logged >=1 hour on linked Hackatime projects, with timezone-aware lazy decay applied */ currentStreak: number; /** @description Longest streak ever achieved by the user */ @@ -2574,6 +2592,18 @@ export interface components { user: components["schemas"]["TimelineActorResponse"]; timeline: components["schemas"]["TimelineEventResponse"][]; }; + ProjectManifestSummaryEntry: { + projectId: number; + /** @description Sum of hoursShipped across non-Horizons YSWS submissions registered for this project codeUrl on Manifest. */ + priorYswsHoursShipped: number; + /** @description Names of the non-Horizons YSWS programs this codeUrl has been submitted to. */ + priorYswsNames: string[]; + }; + ProjectManifestSummaryResponse: { + entries: components["schemas"]["ProjectManifestSummaryEntry"][]; + /** @description True when Manifest is configured and reachable. False means the entries array is empty because the lookup was skipped. */ + enabled: boolean; + }; UpdateAdminProjectDto: { projectTitle?: string; /** @enum {string} */ @@ -2587,7 +2617,7 @@ export interface components { readmeUrl?: string | null; /** Format: uri */ journalUrl?: string | null; - /** Format: uri */ + /** @description Hack Club CDN URL only */ screenshotUrl?: string | null; nowHackatimeProjects?: string[]; adminComment?: string | null; @@ -2884,16 +2914,6 @@ export interface components { /** Format: date-time */ endDate: string | null; }; - StatsSignupQualificationModeCounts: { - engaged: number; - rsvped: number; - qualified: number; - }; - StatsSignupQualificationModes: { - approved: components["schemas"]["StatsSignupQualificationModeCounts"]; - shipped: components["schemas"]["StatsSignupQualificationModeCounts"]; - unshipped: components["schemas"]["StatsSignupQualificationModeCounts"]; - }; StatsSignupQualificationEntry: { eventId: number; title: string; @@ -2993,13 +3013,6 @@ export interface components { totalDaysWritten: number; results: components["schemas"]["StreakBackfillEntry"][]; }; - TriggerReviewerLeaderboardResponse: { - posted: boolean; - dateLabel: string; - totalReviews: number; - reviewerCount: number; - message: string; - }; EventStatsEventDetail: { eventId: number; slug: string; @@ -3022,6 +3035,16 @@ export interface components { date: string; value: number; }; + StatsSignupQualificationModeCounts: { + engaged: number; + rsvped: number; + qualified: number; + }; + StatsSignupQualificationModes: { + approved: components["schemas"]["StatsSignupQualificationModeCounts"]; + shipped: components["schemas"]["StatsSignupQualificationModeCounts"]; + unshipped: components["schemas"]["StatsSignupQualificationModeCounts"]; + }; EventStatsQualification: { signedUp: number; engaged: number; @@ -3064,13 +3087,15 @@ export interface components { LedgerEntryResponse: { transactionId: number; /** @enum {string} */ - kind: "ShopItem" | "EventTicket"; + kind: "ShopItem" | "EventTicket" | "AdminAdjustment"; itemDescription: string; cost: number; isFulfilled: boolean; /** Format: date-time */ fulfilledAt: string | null; /** Format: date-time */ + refundedAt: string | null; + /** Format: date-time */ createdAt: string; user: components["schemas"]["LedgerEntryUserSummary"]; item: components["schemas"]["LedgerEntryItemSummary"] | null; @@ -3081,6 +3106,7 @@ export interface components { totalSpent: number; shopCount: number; ticketCount: number; + adminAdjustmentCount: number; }; LedgerResponse: { entries: components["schemas"]["LedgerEntryResponse"][]; @@ -3097,6 +3123,13 @@ export interface components { /** Format: date-time */ lastReviewedAt: string | null; }; + TriggerReviewerLeaderboardResponse: { + posted: boolean; + dateLabel: string; + totalReviews: number; + reviewerCount: number; + message: string; + }; ToggleFraudFlagDto: { isFraud: boolean; }; @@ -3134,15 +3167,6 @@ export interface components { displayName?: string; realName?: string; }; - ProjectManifestSummaryEntry: { - projectId: number; - priorYswsHoursShipped: number; - priorYswsNames: string[]; - }; - ProjectManifestSummaryResponse: { - entries: components["schemas"]["ProjectManifestSummaryEntry"][]; - enabled: boolean; - }; PriorityUserResponse: { userId: number; email: string; @@ -3191,6 +3215,23 @@ export interface components { hackatimeStartDate: string | null; recalculatedProjects: number; }; + AdjustUserHoursDto: { + /** @description Hours to credit (positive) or deduct (negative) from the user. Cannot be zero. */ + hours: number; + /** @description Reason for the adjustment. Stored as the transaction description and visible to the user in their transaction history. */ + reason: string; + }; + AdjustUserHoursResponse: { + transactionId: number; + userId: number; + /** @description Signed adjustment in hours: positive when hours were credited to the user, negative when deducted. */ + hours: number; + reason: string; + /** @description New balance after the adjustment. */ + newBalance: number; + /** Format: date-time */ + createdAt: string; + }; ImportCsvSkipped: { row: number; email: string; @@ -3448,6 +3489,11 @@ export interface components { submissionId: number; status: string; }; + PreviewSlackMessageDto: { + userFeedback?: string; + approvedHours?: number; + approved?: boolean; + }; QuickApproveDto: { userFeedback?: string; hoursJustification?: string; @@ -3571,6 +3617,11 @@ export interface components { itemId: number; variantId: number | null; cost: number; + isFulfilled: boolean; + /** Format: date-time */ + fulfilledAt: string | null; + /** Format: date-time */ + refundedAt: string | null; /** Format: date-time */ createdAt: string; item: components["schemas"]["TransactionItemSummary"]; @@ -3666,15 +3717,15 @@ export interface components { itemId: number; variantId: number | null; cost: number; - /** Format: date-time */ - createdAt: string; - item: components["schemas"]["TransactionItemSummary"]; - variant: components["schemas"]["TransactionVariantSummary"] | null; isFulfilled: boolean; /** Format: date-time */ fulfilledAt: string | null; /** Format: date-time */ refundedAt: string | null; + /** Format: date-time */ + createdAt: string; + item: components["schemas"]["TransactionItemSummary"]; + variant: components["schemas"]["TransactionVariantSummary"] | null; user: components["schemas"]["TransactionUserSummary"]; }; RefundResponse: { @@ -3920,11 +3971,11 @@ export interface components { UrlCheckResponse: { /** @description Whether the URL is reachable */ ok: boolean; - /** @description HTTP status code (0 if request failed) */ + /** @description Status bucket: 200 (reachable), 400 (4xx), 500 (5xx), 0 (no response) */ status: number; /** @description Error message if URL is not reachable */ error?: string; - /** @description Favicon URL extracted from the page */ + /** @description Favicon URL (e.g. origin/favicon.ico) */ favicon?: string; }; GitHubCommitResponse: { @@ -4941,6 +4992,25 @@ export interface operations { }; }; }; + AdminController_getProjectsManifestSummary: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ProjectManifestSummaryResponse"]; + }; + }; + }; + }; AdminController_getProject: { parameters: { query?: never; @@ -5298,9 +5368,10 @@ export interface operations { AdminController_getTransactionLedger: { parameters: { query?: { - kind?: "ShopItem" | "EventTicket"; + kind?: "ShopItem" | "EventTicket" | "AdminAdjustment"; userId?: number; fulfilled?: boolean; + refunded?: boolean; limit?: number; }; header?: never; @@ -5470,25 +5541,6 @@ export interface operations { }; }; }; - AdminController_getProjectsManifestSummary: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProjectManifestSummaryResponse"]; - }; - }; - }; - }; AdminController_getPriorityUsers: { parameters: { query?: never; @@ -5640,6 +5692,31 @@ export interface operations { }; }; }; + AdminController_adjustUserHours: { + parameters: { + query?: never; + header?: never; + path: { + id: number; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["AdjustUserHoursDto"]; + }; + }; + responses: { + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["AdjustUserHoursResponse"]; + }; + }; + }; + }; AdminController_importCsv: { parameters: { query?: never; @@ -5892,7 +5969,7 @@ export interface operations { }; }; }; - ReviewerController_quickApproveSubmission: { + ReviewerController_previewSlackMessage: { parameters: { query?: never; header?: never; @@ -5903,17 +5980,16 @@ export interface operations { }; requestBody: { content: { - "application/json": components["schemas"]["QuickApproveDto"]; + "application/json": components["schemas"]["PreviewSlackMessageDto"]; }; }; responses: { + /** @description Preview Slack DM sent to the requesting reviewer */ 200: { headers: { [name: string]: unknown; }; - content: { - "application/json": components["schemas"]["ReviewResultResponse"]; - }; + content?: never; }; 201: { headers: { @@ -5923,7 +5999,7 @@ export interface operations { }; }; }; - ReviewerController_previewSlackMessage: { + ReviewerController_quickApproveSubmission: { parameters: { query?: never; header?: never; @@ -5934,11 +6010,7 @@ export interface operations { }; requestBody: { content: { - "application/json": { - userFeedback?: string; - approvedHours?: number; - approved?: boolean; - }; + "application/json": components["schemas"]["QuickApproveDto"]; }; }; responses: { @@ -5946,7 +6018,9 @@ export interface operations { headers: { [name: string]: unknown; }; - content?: never; + content: { + "application/json": components["schemas"]["ReviewResultResponse"]; + }; }; 201: { headers: { @@ -7189,7 +7263,7 @@ export interface operations { query: { /** @description URL to check */ url: string; - /** @description Check type: "url" (default) or "repo" (verify git repository) */ + /** @description Check type: "url" (default) or "repo" */ type?: string; }; header?: never; diff --git a/admin/src/routes/(app)/shop/+page.svelte b/admin/src/routes/(app)/shop/+page.svelte index 875cd5bd..9aa08d18 100644 --- a/admin/src/routes/(app)/shop/+page.svelte +++ b/admin/src/routes/(app)/shop/+page.svelte @@ -39,6 +39,7 @@ cost: string; regions: string[]; maxPerUser: string; + eventHoursReduction: string; }>({ shopId: null, name: '', @@ -46,7 +47,8 @@ imageUrl: '', cost: '', regions: [], - maxPerUser: '' + maxPerUser: '', + eventHoursReduction: '' }); let editingItemId = $state(null); let shopItemSaving = $state(false); @@ -322,7 +324,8 @@ imageUrl: '', cost: '', regions: [], - maxPerUser: '' + maxPerUser: '', + eventHoursReduction: '' }; editingItemId = null; shopItemError = ''; @@ -338,7 +341,8 @@ imageUrl: item.imageUrl || '', cost: item.cost.toString(), regions: item.regions ?? [], - maxPerUser: item.maxPerUser?.toString() || '' + maxPerUser: item.maxPerUser?.toString() || '', + eventHoursReduction: item.eventHoursReduction?.toString() ?? '' }; shopItemError = ''; shopItemSuccess = ''; @@ -356,13 +360,20 @@ return; } + const eventHoursReduction = shopItemForm.eventHoursReduction.trim() + ? parseFloat(shopItemForm.eventHoursReduction) + : editingItemId + ? null + : undefined; + const payload = { name: shopItemForm.name, description: shopItemForm.description || undefined, imageUrl: shopItemForm.imageUrl || undefined, cost: parseFloat(shopItemForm.cost), regions: shopItemForm.regions.length > 0 ? shopItemForm.regions : undefined, - maxPerUser: shopItemForm.maxPerUser ? parseInt(shopItemForm.maxPerUser) : undefined + maxPerUser: shopItemForm.maxPerUser ? parseInt(shopItemForm.maxPerUser) : undefined, + eventHoursReduction }; try { @@ -862,6 +873,24 @@ bind:value={shopItemForm.maxPerUser} /> +
+ + +

+ Hours credited toward the event goal when purchased (shop slug must match event slug). +

+
@@ -996,6 +1025,12 @@ >Max {item.maxPerUser}/user {/if} + {#if item.eventHoursReduction} + +{item.eventHoursReduction}h event credit + {/if} {#if item.variants && item.variants.length > 0} Promise; @@ -59,6 +60,7 @@ export class BalanceService { itemId = null, variantId = null, eventId = null, + eventHoursCredit = null, quantity = 1, enforceBalance = true, preCheck, @@ -92,6 +94,7 @@ export class BalanceService { itemId, variantId, eventId, + eventHoursCredit, itemDescription, cost, }; diff --git a/backend/src/events/dto/events-response.dto.ts b/backend/src/events/dto/events-response.dto.ts index fe966513..c8f5d75a 100644 --- a/backend/src/events/dto/events-response.dto.ts +++ b/backend/src/events/dto/events-response.dto.ts @@ -132,6 +132,12 @@ export class TicketStatusResponse { @ApiProperty() approvedHours: number; + + @ApiProperty({ + description: + 'Hours credited toward this event goal from event shop purchases (per-user)', + }) + eventHoursCredit: number; } export class TicketTransactionResponse { diff --git a/backend/src/events/events.service.ts b/backend/src/events/events.service.ts index 8e7cad81..4ec8e9ae 100644 --- a/backend/src/events/events.service.ts +++ b/backend/src/events/events.service.ts @@ -185,6 +185,22 @@ export class EventsService { } } + // ── Event hour progress ── + + async getEventHoursCredit(userId: number, eventSlug: string): Promise { + const result = await this.prisma.transaction.aggregate({ + where: { + userId, + kind: 'ShopItem', + refundedAt: null, + eventHoursCredit: { not: null }, + item: { shop: { slug: eventSlug } }, + }, + _sum: { eventHoursCredit: true }, + }); + return Math.round((result._sum.eventHoursCredit ?? 0) * 10) / 10; + } + // ── Ticketing ── async getTicketStatus(userId: number, slug: string) { @@ -204,8 +220,11 @@ export class EventsService { select: { transactionId: true }, }); - const { balance, totalApprovedHours } = - await this.balanceService.getUserBalance(userId); + const [{ balance, totalApprovedHours }, eventHoursCredit] = + await Promise.all([ + this.balanceService.getUserBalance(userId), + this.getEventHoursCredit(userId, event.slug), + ]); return { slug: event.slug, @@ -215,6 +234,7 @@ export class EventsService { hasTicket: !!hasTicketTxn, balance, approvedHours: Math.round(totalApprovedHours * 10) / 10, + eventHoursCredit, }; } @@ -245,13 +265,25 @@ export class EventsService { if (event.ticketThreshold !== null) { const { totalApprovedHours } = await this.balanceService.getUserBalance(userId); - if (totalApprovedHours < event.ticketThreshold) { + const eventHoursCredit = await this.getEventHoursCredit( + userId, + event.slug, + ); + const totalEligibleHours = totalApprovedHours + eventHoursCredit; + if (totalEligibleHours < event.ticketThreshold) { throw new BadRequestException( - `You need ${event.ticketThreshold} approved hours to buy a ticket. You have ${Math.round(totalApprovedHours * 10) / 10}.`, + `You need ${event.ticketThreshold} approved hours to buy a ticket. You have ${Math.round(totalEligibleHours * 10) / 10}.`, ); } } + // Calculate the net ticket cost after applying event hours credit + const eventHoursCredit = await this.getEventHoursCredit( + userId, + event.slug, + ); + const netTicketCost = Math.max(0, event.ticketCost! - eventHoursCredit); + let transaction; try { transaction = await this.prisma.$transaction(async (tx) => { @@ -261,7 +293,7 @@ export class EventsService { eventId: event.eventId, kind: 'EventTicket', itemDescription: `Ticket — ${event.title}`, - cost: event.ticketCost!, + cost: netTicketCost, }, }); await tx.pinnedEvent.upsert({ diff --git a/backend/src/shop/dto/create-item.dto.ts b/backend/src/shop/dto/create-item.dto.ts index e9af713b..b98ce5f3 100644 --- a/backend/src/shop/dto/create-item.dto.ts +++ b/backend/src/shop/dto/create-item.dto.ts @@ -33,4 +33,9 @@ export class CreateItemDto { @IsOptional() @Min(1) maxPerUser?: number; + + @IsNumber() + @IsOptional() + @Min(0) + eventHoursReduction?: number; } diff --git a/backend/src/shop/dto/shop-response.dto.ts b/backend/src/shop/dto/shop-response.dto.ts index fec535e0..16aa4e78 100644 --- a/backend/src/shop/dto/shop-response.dto.ts +++ b/backend/src/shop/dto/shop-response.dto.ts @@ -74,6 +74,14 @@ export class ShopItemResponse { @ApiProperty({ type: Number, nullable: true }) maxPerUser: number | null; + @ApiProperty({ + type: Number, + nullable: true, + description: + 'Hours credited toward the linked event hour goal when purchased (shop slug must match event slug)', + }) + eventHoursReduction: number | null; + @ApiProperty() isActive: boolean; diff --git a/backend/src/shop/dto/update-item.dto.ts b/backend/src/shop/dto/update-item.dto.ts index 72f102a3..fe1ee9bf 100644 --- a/backend/src/shop/dto/update-item.dto.ts +++ b/backend/src/shop/dto/update-item.dto.ts @@ -48,4 +48,9 @@ export class UpdateItemDto { @IsBoolean() @IsOptional() isActive?: boolean; + + @IsNumber() + @IsOptional() + @Min(0) + eventHoursReduction?: number | null; } diff --git a/backend/src/shop/shop.service.ts b/backend/src/shop/shop.service.ts index 48e75b5f..9d39f545 100644 --- a/backend/src/shop/shop.service.ts +++ b/backend/src/shop/shop.service.ts @@ -158,6 +158,7 @@ export class ShopService { imageUrl: createItemDto.imageUrl, cost: createItemDto.cost, regions: createItemDto.regions ?? [], + eventHoursReduction: createItemDto.eventHoursReduction ?? null, }, include: { shop: { select: { slug: true } }, @@ -338,8 +339,19 @@ export class ShopService { description += ` - ${item.description}`; } + let eventHoursCredit: number | null = null; + if (item.eventHoursReduction != null && item.eventHoursReduction > 0) { + const linkedEvent = await this.prisma.event.findUnique({ + where: { slug: item.shop.slug }, + select: { eventId: true }, + }); + if (linkedEvent) { + eventHoursCredit = item.eventHoursReduction; + } + } + console.log( - `[Shop Purchase] Creating transaction for userId: ${userId}, itemId: ${itemId}, unitCost: ${cost}, quantity: ${quantity}, total: ${cost * quantity}`, + `[Shop Purchase] Creating transaction for userId: ${userId}, itemId: ${itemId}, unitCost: ${cost}, quantity: ${quantity}, total: ${cost * quantity}, eventHoursCredit: ${eventHoursCredit ?? 'none'}`, ); const transaction = await this.balanceService.processPurchase({ userId, @@ -349,6 +361,7 @@ export class ShopService { itemDescription: description, itemId, variantId: variant?.variantId ?? null, + eventHoursCredit, preCheck: async (tx) => { if (maxPerUser !== null && maxPerUser > 0) { const count = await tx.transaction.count({ diff --git a/frontend/src/lib/api/schema.d.ts b/frontend/src/lib/api/schema.d.ts index 9014208e..69eec510 100644 --- a/frontend/src/lib/api/schema.d.ts +++ b/frontend/src/lib/api/schema.d.ts @@ -645,6 +645,22 @@ export interface paths { patch?: never; trace?: never; }; + "/api/admin/projects/manifest-summary": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["AdminController_getProjectsManifestSummary"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/api/admin/projects/{id}": { parameters: { query?: never; @@ -789,6 +805,22 @@ export interface paths { patch?: never; trace?: never; }; + "/api/admin/projects/{id}/joe-reset": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["AdminController_resetJoeAndRequeue"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/api/admin/stats": { parameters: { query?: never; @@ -901,6 +933,22 @@ export interface paths { patch?: never; trace?: never; }; + "/api/admin/reviewer-leaderboard/trigger": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["AdminController_triggerReviewerLeaderboard"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/api/admin/users/{id}/fraud-flag": { parameters: { query?: never; @@ -1093,6 +1141,22 @@ export interface paths { patch: operations["AdminController_updateUser"]; trace?: never; }; + "/api/admin/users/{id}/hours-adjustment": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["AdminController_adjustUserHours"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/api/admin/import/csv": { parameters: { query?: never; @@ -1269,6 +1333,22 @@ export interface paths { patch?: never; trace?: never; }; + "/api/reviewer/submissions/{id}/preview-slack-message": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["ReviewerController_previewSlackMessage"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/api/reviewer/submissions/{id}/quick-approve": { parameters: { query?: never; @@ -1916,7 +1996,7 @@ export interface paths { path?: never; cookie?: never; }; - /** Check if a URL is reachable */ + /** Check if a URL is reachable (forwarded to isolated worker) */ get: operations["UrlCheckController_checkUrl"]; put?: never; post?: never; @@ -2075,7 +2155,7 @@ export interface components { repoUrl?: string; /** @description README URL */ readmeUrl?: string; - /** @description Screenshot URL */ + /** @description Screenshot URL (Hack Club CDN only) */ screenshotUrl?: string; /** @description Linked Hackatime project names */ nowHackatimeProjects?: string[]; @@ -2225,7 +2305,7 @@ export interface components { readmeUrl?: string; /** * Format: uri - * @description Screenshot URL + * @description Screenshot URL (Hack Club CDN only) */ screenshotUrl?: string; /** @description Journal URL (for hardware projects) */ @@ -2512,6 +2592,18 @@ export interface components { user: components["schemas"]["TimelineActorResponse"]; timeline: components["schemas"]["TimelineEventResponse"][]; }; + ProjectManifestSummaryEntry: { + projectId: number; + /** @description Sum of hoursShipped across non-Horizons YSWS submissions registered for this project codeUrl on Manifest. */ + priorYswsHoursShipped: number; + /** @description Names of the non-Horizons YSWS programs this codeUrl has been submitted to. */ + priorYswsNames: string[]; + }; + ProjectManifestSummaryResponse: { + entries: components["schemas"]["ProjectManifestSummaryEntry"][]; + /** @description True when Manifest is configured and reachable. False means the entries array is empty because the lookup was skipped. */ + enabled: boolean; + }; UpdateAdminProjectDto: { projectTitle?: string; /** @enum {string} */ @@ -2525,7 +2617,7 @@ export interface components { readmeUrl?: string | null; /** Format: uri */ journalUrl?: string | null; - /** Format: uri */ + /** @description Hack Club CDN URL only */ screenshotUrl?: string | null; nowHackatimeProjects?: string[]; adminComment?: string | null; @@ -2740,6 +2832,10 @@ export interface components { /** @description True if a Slack DM was successfully dispatched. */ slackSent: boolean; }; + ResetJoeActionResponse: { + success: boolean; + project: components["schemas"]["FraudReviewQueueItemResponse"]; + }; StatsFunnelEventEntry: { eventId: number; title: string; @@ -2750,6 +2846,7 @@ export interface components { linkedHackatimeProject: number; project10PlusHours: number; atLeast1Submission: number; + submitted10PlusHours: number; atLeast1ApprovedHour: number; approved10Plus: number; canBuyTicket: number; @@ -2763,6 +2860,7 @@ export interface components { linkedHackatimeProject: number; project10PlusHours: number; atLeast1Submission: number; + submitted10PlusHours: number; atLeast1ApprovedHour: number; approved10Plus: number; canBuyTicket: number; @@ -2816,25 +2914,14 @@ export interface components { /** Format: date-time */ endDate: string | null; }; - StatsSignupQualificationModeCounts: { - engaged: number; - rsvped: number; - qualified: number; - }; - StatsSignupQualificationModes: { - approved: components["schemas"]["StatsSignupQualificationModeCounts"]; - shipped: components["schemas"]["StatsSignupQualificationModeCounts"]; - unshipped: components["schemas"]["StatsSignupQualificationModeCounts"]; - }; StatsSignupQualificationEntry: { eventId: number; title: string; slug: string; signedUp: number; engaged: number; - rsvped: number; - qualified: number; - modes: components["schemas"]["StatsSignupQualificationModes"]; + canBuyTicket: number; + boughtTicket: number; }; StatsSignupRoute: { originCountry: string; @@ -2948,6 +3035,16 @@ export interface components { date: string; value: number; }; + StatsSignupQualificationModeCounts: { + engaged: number; + rsvped: number; + qualified: number; + }; + StatsSignupQualificationModes: { + approved: components["schemas"]["StatsSignupQualificationModeCounts"]; + shipped: components["schemas"]["StatsSignupQualificationModeCounts"]; + unshipped: components["schemas"]["StatsSignupQualificationModeCounts"]; + }; EventStatsQualification: { signedUp: number; engaged: number; @@ -2990,13 +3087,15 @@ export interface components { LedgerEntryResponse: { transactionId: number; /** @enum {string} */ - kind: "ShopItem" | "EventTicket"; + kind: "ShopItem" | "EventTicket" | "AdminAdjustment"; itemDescription: string; cost: number; isFulfilled: boolean; /** Format: date-time */ fulfilledAt: string | null; /** Format: date-time */ + refundedAt: string | null; + /** Format: date-time */ createdAt: string; user: components["schemas"]["LedgerEntryUserSummary"]; item: components["schemas"]["LedgerEntryItemSummary"] | null; @@ -3007,6 +3106,7 @@ export interface components { totalSpent: number; shopCount: number; ticketCount: number; + adminAdjustmentCount: number; }; LedgerResponse: { entries: components["schemas"]["LedgerEntryResponse"][]; @@ -3023,6 +3123,13 @@ export interface components { /** Format: date-time */ lastReviewedAt: string | null; }; + TriggerReviewerLeaderboardResponse: { + posted: boolean; + dateLabel: string; + totalReviews: number; + reviewerCount: number; + message: string; + }; ToggleFraudFlagDto: { isFraud: boolean; }; @@ -3108,6 +3215,23 @@ export interface components { hackatimeStartDate: string | null; recalculatedProjects: number; }; + AdjustUserHoursDto: { + /** @description Hours to credit (positive) or deduct (negative) from the user. Cannot be zero. */ + hours: number; + /** @description Reason for the adjustment. Stored as the transaction description and visible to the user in their transaction history. */ + reason: string; + }; + AdjustUserHoursResponse: { + transactionId: number; + userId: number; + /** @description Signed adjustment in hours: positive when hours were credited to the user, negative when deducted. */ + hours: number; + reason: string; + /** @description New balance after the adjustment. */ + newBalance: number; + /** Format: date-time */ + createdAt: string; + }; ImportCsvSkipped: { row: number; email: string; @@ -3365,6 +3489,11 @@ export interface components { submissionId: number; status: string; }; + PreviewSlackMessageDto: { + userFeedback?: string; + approvedHours?: number; + approved?: boolean; + }; QuickApproveDto: { userFeedback?: string; hoursJustification?: string; @@ -3472,7 +3601,6 @@ export interface components { PurchaseItemDto: { itemId: number; variantId?: number; - quantity?: number; }; TransactionItemSummary: { itemId: number; @@ -3843,11 +3971,11 @@ export interface components { UrlCheckResponse: { /** @description Whether the URL is reachable */ ok: boolean; - /** @description HTTP status code (0 if request failed) */ + /** @description Status bucket: 200 (reachable), 400 (4xx), 500 (5xx), 0 (no response) */ status: number; /** @description Error message if URL is not reachable */ error?: string; - /** @description Favicon URL extracted from the page */ + /** @description Favicon URL (e.g. origin/favicon.ico) */ favicon?: string; }; GitHubCommitResponse: { @@ -4864,6 +4992,25 @@ export interface operations { }; }; }; + AdminController_getProjectsManifestSummary: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ProjectManifestSummaryResponse"]; + }; + }; + }; + }; AdminController_getProject: { parameters: { query?: never; @@ -5093,6 +5240,27 @@ export interface operations { }; }; }; + AdminController_resetJoeAndRequeue: { + parameters: { + query?: never; + header?: never; + path: { + id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ResetJoeActionResponse"]; + }; + }; + }; + }; AdminController_getStats: { parameters: { query?: never; @@ -5200,9 +5368,10 @@ export interface operations { AdminController_getTransactionLedger: { parameters: { query?: { - kind?: "ShopItem" | "EventTicket"; + kind?: "ShopItem" | "EventTicket" | "AdminAdjustment"; userId?: number; fulfilled?: boolean; + refunded?: boolean; limit?: number; }; header?: never; @@ -5240,6 +5409,25 @@ export interface operations { }; }; }; + AdminController_triggerReviewerLeaderboard: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TriggerReviewerLeaderboardResponse"]; + }; + }; + }; + }; AdminController_toggleUserFraudFlag: { parameters: { query?: never; @@ -5504,6 +5692,31 @@ export interface operations { }; }; }; + AdminController_adjustUserHours: { + parameters: { + query?: never; + header?: never; + path: { + id: number; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["AdjustUserHoursDto"]; + }; + }; + responses: { + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["AdjustUserHoursResponse"]; + }; + }; + }; + }; AdminController_importCsv: { parameters: { query?: never; @@ -5756,6 +5969,36 @@ export interface operations { }; }; }; + ReviewerController_previewSlackMessage: { + parameters: { + query?: never; + header?: never; + path: { + id: number; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["PreviewSlackMessageDto"]; + }; + }; + responses: { + /** @description Preview Slack DM sent to the requesting reviewer */ + 200: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 201: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; ReviewerController_quickApproveSubmission: { parameters: { query?: never; @@ -7020,7 +7263,7 @@ export interface operations { query: { /** @description URL to check */ url: string; - /** @description Check type: "url" (default) or "repo" (verify git repository) */ + /** @description Check type: "url" (default) or "repo" */ type?: string; }; header?: never; diff --git a/frontend/src/lib/components/cards/EventColumnCard.svelte b/frontend/src/lib/components/cards/EventColumnCard.svelte index 06627899..f036b5d0 100644 --- a/frontend/src/lib/components/cards/EventColumnCard.svelte +++ b/frontend/src/lib/components/cards/EventColumnCard.svelte @@ -19,6 +19,8 @@ /** Whether ticket purchase is currently open. */ ticketEnabled?: boolean; hasTicket?: boolean; + /** Hours credited toward the event goal from shop item purchases. */ + eventHoursCredit?: number; onclick?: (e: MouseEvent) => void; onmouseenter?: () => void; selected?: boolean; @@ -38,6 +40,7 @@ ticketCost = null, ticketEnabled = false, hasTicket = false, + eventHoursCredit = 0, onclick, onmouseenter, selected = false, @@ -64,11 +67,12 @@ // Threshold of approved hours hit and the purchase window is open. // Null threshold means no eligibility gate; only ticketCost+ticketEnabled // need to be set for the purchase prompt to show. + // Include eventHoursCredit from shop items in the eligibility calculation. if ( ticketCost !== null && ticketEnabled && !hasTicket && - (ticketThreshold === null || approvedDisplay >= ticketThreshold) + (ticketThreshold === null || (approvedDisplay + eventHoursCredit) >= ticketThreshold) ) { return 'buy-ticket'; } diff --git a/frontend/src/routes/app/+page.svelte b/frontend/src/routes/app/+page.svelte index eeeeb14a..48798968 100644 --- a/frontend/src/routes/app/+page.svelte +++ b/frontend/src/routes/app/+page.svelte @@ -118,6 +118,7 @@ let pinnedTicketCost = $state(null); let pinnedTicketEnabled = $state(false); let pinnedHasTicket = $state(false); + let pinnedEventHoursCredit = $state(0); // #horizons huddle state — populated by polling /api/huddles/status. When `huddleActive` // is true, a LiveHuddleCard is rendered beneath the community-events card. @@ -302,10 +303,14 @@ return { completed: debugCompleted, approved: debugApproved, pending: debugPending }; } // Live values: treat unapproved completed hours as pending until per-event ship lookup is wired. + const creditedCompleted = Math.min( + targetHours, + Math.round((completedHours + pinnedEventHoursCredit) * 10) / 10 + ); return { - completed: completedHours, + completed: creditedCompleted, approved: approvedHours, - pending: Math.max(0, completedHours - approvedHours), + pending: Math.max(0, creditedCompleted - approvedHours), }; }); @@ -340,9 +345,11 @@ ticketCost: number | null; ticketEnabled: boolean; hasTicket: boolean; + eventHoursCredit?: number; } | undefined; if (!data) return; + pinnedEventHoursCredit = Math.round((data.eventHoursCredit ?? 0) * 10) / 10; pinnedTicketThreshold = data.ticketThreshold; pinnedTicketCost = data.ticketCost; pinnedTicketEnabled = data.ticketEnabled; @@ -775,6 +782,7 @@ ticketCost={eventColumnTicketCost} ticketEnabled={eventColumnTicketEnabled} hasTicket={eventColumnHasTicket} + eventHoursCredit={pinnedEventHoursCredit} selected={nav.isSelected(COL_PINNED_EVENT, 0)} onmouseenter={() => handleCardHover(COL_PINNED_EVENT, 0)} onclick={(e) => { e.preventDefault(); navigateTo('/app/events'); }} diff --git a/frontend/src/routes/app/events/+page.svelte b/frontend/src/routes/app/events/+page.svelte index 8cc95db0..3da383c9 100644 --- a/frontend/src/routes/app/events/+page.svelte +++ b/frontend/src/routes/app/events/+page.svelte @@ -61,6 +61,7 @@ let purchasing = $state(null); let purchaseError = $state(null); let hasEventShopItems = $state(false); + let eventHoursCredit = $state(0); let showConfirmModal = $state(false); let confirmText = $state(''); @@ -94,8 +95,18 @@ const effectiveBalance = $derived( debugApprovedState === '' ? balance : effectiveApprovedHours ); + const effectiveEventHoursCredit = $derived( + debugApprovedState === '' ? eventHoursCredit : 0 + ); const effectiveCompletedHours = $derived( - debugApprovedState === '' ? completedHours : Math.max(effectiveApprovedHours, completedHours) + debugApprovedState === '' + ? effectiveHasTicket + ? targetHours + : Math.min( + targetHours, + Math.round((completedHours + effectiveEventHoursCredit) * 10) / 10 + ) + : Math.max(effectiveApprovedHours, completedHours) ); // Confirmation modal — activated when the user picks "Buy Ticket". The user @@ -130,9 +141,11 @@ hasTicket: boolean; balance: number; approvedHours: number; + eventHoursCredit?: number; } | undefined; if (!data) return; + eventHoursCredit = Math.round((data.eventHoursCredit ?? 0) * 10) / 10; ticketThreshold = data.ticketThreshold; ticketCost = data.ticketCost; hasTicket = data.hasTicket; diff --git a/frontend/src/routes/app/events/shop/[itemId]/+page.svelte b/frontend/src/routes/app/events/shop/[itemId]/+page.svelte index 4e4b95b6..75d3ca9e 100644 --- a/frontend/src/routes/app/events/shop/[itemId]/+page.svelte +++ b/frontend/src/routes/app/events/shop/[itemId]/+page.svelte @@ -16,6 +16,7 @@ regions: string[]; isActive: boolean; maxPerUser: number | null; + eventHoursReduction: number | null; variants: { variantId: number; name: string; cost: number }[]; } @@ -203,6 +204,11 @@

{item.cost}h · approx. {calcDays(item.cost)} days of coding

+ {#if item.eventHoursReduction} +

+ Credits {item.eventHoursReduction}h toward your {item.shopSlug} event goal +

+ {/if} {#if item.regions.length > 0}
{#each item.regions as region} diff --git a/package-lock.json b/package-lock.json index a6442346..242eb3af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "gateway" ], "dependencies": { - "pnpm": "^10.33.0", + "pnpm": "^10.34.1", "prisma": "^7.6.0" }, "devDependencies": { @@ -44,9 +44,7 @@ "helmet": "^8.0.0", "ioredis": "^5.8.1", "jose": "^6.1.3", - "mjml": "^4.16.1", - "node-forge": "^1.3.1", - "nodemailer": "^7.0.9", + "loops": "^6.3.0", "papaparse": "^5.5.3", "pg": "^8.18.0", "posthog-node": "^5.11.2", @@ -128,7 +126,7 @@ "version": "0.0.1", "dependencies": { "cobe": "^2.0.1", - "dompurify": "^3.3.3", + "dompurify": "^3.4.0", "gsap": "^3.14.2", "js-yaml": "^4.1.1", "marked": "^17.0.5", @@ -1043,6 +1041,7 @@ "version": "7.29.2", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -2396,6 +2395,7 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, "license": "ISC", "dependencies": { "string-width": "^5.1.2", @@ -2413,6 +2413,7 @@ "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -2425,6 +2426,7 @@ "version": "6.2.3", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -2437,12 +2439,14 @@ "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, "license": "MIT" }, "node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", @@ -2460,6 +2464,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^6.2.2" @@ -2475,6 +2480,7 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^6.1.0", @@ -4191,12 +4197,6 @@ "npm": ">=5.10.0" } }, - "node_modules/@one-ini/wasm": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", - "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", - "license": "MIT" - }, "node_modules/@opentelemetry/api": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz", @@ -4564,6 +4564,7 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, "license": "MIT", "optional": true, "engines": { @@ -7856,15 +7857,6 @@ "dev": true, "license": "Apache-2.0" }, - "node_modules/abbrev": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", - "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -7996,6 +7988,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -8021,6 +8014,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -8030,6 +8024,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -8055,6 +8050,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", @@ -8068,6 +8064,7 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -8294,6 +8291,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, "license": "MIT" }, "node_modules/bare-events": { @@ -8483,18 +8481,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -8558,12 +8544,6 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "license": "ISC" - }, "node_modules/brace-expansion": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", @@ -8801,16 +8781,6 @@ "node": ">=6" } }, - "node_modules/camel-case": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", - "integrity": "sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==", - "license": "MIT", - "dependencies": { - "no-case": "^2.2.0", - "upper-case": "^1.1.1" - } - }, "node_modules/camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -8908,44 +8878,6 @@ "pnpm": ">=8" } }, - "node_modules/cheerio": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", - "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", - "license": "MIT", - "dependencies": { - "cheerio-select": "^2.1.0", - "dom-serializer": "^2.0.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "htmlparser2": "^8.0.1", - "parse5": "^7.0.0", - "parse5-htmlparser2-tree-adapter": "^7.0.0" - }, - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/cheeriojs/cheerio?sponsor=1" - } - }, - "node_modules/cheerio-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", - "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-select": "^5.1.0", - "css-what": "^6.1.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, "node_modules/chokidar": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", @@ -9019,27 +8951,6 @@ "validator": "^13.15.22" } }, - "node_modules/clean-css": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.4.tgz", - "integrity": "sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==", - "license": "MIT", - "dependencies": { - "source-map": "~0.6.0" - }, - "engines": { - "node": ">= 4.0" - } - }, - "node_modules/clean-css/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", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -9096,6 +9007,7 @@ "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==", + "dev": true, "license": "ISC", "dependencies": { "string-width": "^4.2.0", @@ -9110,6 +9022,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -9180,6 +9093,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -9192,6 +9106,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, "license": "MIT" }, "node_modules/colorette": { @@ -9312,16 +9227,6 @@ "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", "license": "MIT" }, - "node_modules/config-chain": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", - "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", - "license": "MIT", - "dependencies": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" - } - }, "node_modules/consola": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", @@ -9498,34 +9403,6 @@ "node": ">= 8" } }, - "node_modules/css-select": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", - "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-what": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", - "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", - "license": "BSD-2-Clause", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, "node_modules/csstype": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", @@ -9765,12 +9642,6 @@ "node": ">=8" } }, - "node_modules/detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "license": "MIT" - }, "node_modules/devalue": { "version": "5.6.4", "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.4.tgz", @@ -9799,70 +9670,15 @@ "node": ">=0.3.1" } }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "BSD-2-Clause" - }, - "node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, "node_modules/dompurify": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.3.tgz", - "integrity": "sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==", + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.7.tgz", + "integrity": "sha512-2jBxDJY4RR06tQNy4w5FlFH7kfxsQZlufd0sbv+chfHCxeJwrFw2baUDsSwvBISD4K4RDbd0PTfy3uNXsR6siA==", "license": "(MPL-2.0 OR Apache-2.0)", "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, - "node_modules/domutils": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", - "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, "node_modules/dotenv": { "version": "16.6.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", @@ -9908,59 +9724,9 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, "license": "MIT" }, - "node_modules/editorconfig": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.7.tgz", - "integrity": "sha512-e0GOtq/aTQhVdNyDU9e02+wz9oDDM+SIOQxWME2QRjzRX5yyLAuHDE+0aE8vHb9XRC8XD37eO2u57+F09JqFhw==", - "license": "MIT", - "dependencies": { - "@one-ini/wasm": "0.1.1", - "commander": "^10.0.0", - "minimatch": "^9.0.1", - "semver": "^7.5.3" - }, - "bin": { - "editorconfig": "bin/editorconfig" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/editorconfig/node_modules/brace-expansion": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", - "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/editorconfig/node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "license": "MIT", - "engines": { - "node": ">=14" - } - }, - "node_modules/editorconfig/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.2" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -10001,6 +9767,7 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, "license": "MIT" }, "node_modules/empathic": { @@ -10035,18 +9802,6 @@ "node": ">=10.13.0" } }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, "node_modules/env-paths": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", @@ -10174,23 +9929,12 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" } }, - "node_modules/escape-goat": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-3.0.0.tgz", - "integrity": "sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -11104,6 +10848,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -11150,6 +10895,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" @@ -11485,15 +11231,6 @@ "node": ">= 0.4" } }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "license": "MIT", - "bin": { - "he": "bin/he" - } - }, "node_modules/helmet": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/helmet/-/helmet-8.1.0.tgz", @@ -11519,52 +11256,6 @@ "dev": true, "license": "MIT" }, - "node_modules/html-minifier": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-4.0.0.tgz", - "integrity": "sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==", - "license": "MIT", - "dependencies": { - "camel-case": "^3.0.0", - "clean-css": "^4.2.1", - "commander": "^2.19.0", - "he": "^1.2.0", - "param-case": "^2.1.1", - "relateurl": "^0.2.7", - "uglify-js": "^3.5.1" - }, - "bin": { - "html-minifier": "cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/html-minifier/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "license": "MIT" - }, - "node_modules/htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" - } - }, "node_modules/http-cache-semantics": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", @@ -11806,12 +11497,6 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "license": "ISC" - }, "node_modules/inspect-with-kind": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/inspect-with-kind/-/inspect-with-kind-1.0.5.tgz", @@ -11862,18 +11547,6 @@ "dev": true, "license": "MIT" }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-core-module": { "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", @@ -11903,6 +11576,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -12128,6 +11802,7 @@ "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" @@ -12941,124 +12616,27 @@ "url": "https://github.com/sponsors/panva" } }, - "node_modules/js-beautify": { - "version": "1.15.4", - "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz", - "integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==", + "node_modules/js-levenshtein": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", + "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", + "dev": true, "license": "MIT", - "dependencies": { - "config-chain": "^1.1.13", - "editorconfig": "^1.0.4", - "glob": "^10.4.2", - "js-cookie": "^3.0.5", - "nopt": "^7.2.1" - }, - "bin": { - "css-beautify": "js/bin/css-beautify.js", - "html-beautify": "js/bin/html-beautify.js", - "js-beautify": "js/bin/js-beautify.js" - }, "engines": { - "node": ">=14" + "node": ">=0.10.0" } }, - "node_modules/js-beautify/node_modules/brace-expansion": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", - "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/js-beautify/node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", - "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", - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/js-beautify/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC" - }, - "node_modules/js-beautify/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.2" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/js-beautify/node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/js-cookie": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", - "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", - "license": "MIT", - "engines": { - "node": ">=14" - } - }, - "node_modules/js-levenshtein": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", - "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, - "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==", + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "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==", "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -13140,34 +12718,6 @@ "graceful-fs": "^4.1.6" } }, - "node_modules/juice": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/juice/-/juice-10.0.1.tgz", - "integrity": "sha512-ZhJT1soxJCkOiO55/mz8yeBKTAJhRzX9WBO+16ZTqNTONnnVlUPyVBIzQ7lDRjaBdTbid+bAnyIon/GM3yp4cA==", - "license": "MIT", - "dependencies": { - "cheerio": "1.0.0-rc.12", - "commander": "^6.1.0", - "mensch": "^0.3.4", - "slick": "^1.12.2", - "web-resource-inliner": "^6.0.1" - }, - "bin": { - "juice": "bin/juice" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/juice/node_modules/commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -13607,11 +13157,14 @@ "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", "license": "Apache-2.0" }, - "node_modules/lower-case": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==", - "license": "MIT" + "node_modules/loops": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/loops/-/loops-6.3.0.tgz", + "integrity": "sha512-T5oc66bM90LS+yHxTv9WoYtsadJC94QIax25Avl/XCq/DTonqtNd3g1nmOfuAqDKhkZzXE8GuXmSRvSFIgq0Xg==", + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/lowercase-keys": { "version": "3.0.0", @@ -13746,12 +13299,6 @@ "node": ">= 4.0.0" } }, - "node_modules/mensch": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/mensch/-/mensch-0.3.4.tgz", - "integrity": "sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g==", - "license": "MIT" - }, "node_modules/merge-descriptors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", @@ -13820,635 +13367,84 @@ "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "license": "MIT", "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/mimic-response": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", - "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", - "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/mjml": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/mjml/-/mjml-4.18.0.tgz", - "integrity": "sha512-rQM4aqFRrNvV1k733e8hJSopBjZvoSdBpRYzNTMAN+As0jqJsO5eN0wTT2IFtfe4PREzzu5b06RkPiUQdd0IIg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4", - "mjml-cli": "4.18.0", - "mjml-core": "4.18.0", - "mjml-migrate": "4.18.0", - "mjml-preset-core": "4.18.0", - "mjml-validator": "4.18.0" - }, - "bin": { - "mjml": "bin/mjml" - } - }, - "node_modules/mjml-accordion": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/mjml-accordion/-/mjml-accordion-4.18.0.tgz", - "integrity": "sha512-9PUmy2JxIOGgAaVHvgVYX21nVAo3o/+wJckTTF/YTLGAqB+nm+44buxRzaXxVk7qXRwbCNfE8c8mlGVNh7vB1g==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4", - "lodash": "^4.17.21", - "mjml-core": "4.18.0" - } - }, - "node_modules/mjml-body": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/mjml-body/-/mjml-body-4.18.0.tgz", - "integrity": "sha512-34AwX70/7NkRIajPsa5j6NySRiNrlLatTKhiLwTVFiVtrEFlfCcbeMNmdVixI3Ldvs8209ZC6euaAnXDRyR1zw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4", - "lodash": "^4.17.21", - "mjml-core": "4.18.0" - } - }, - "node_modules/mjml-button": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/mjml-button/-/mjml-button-4.18.0.tgz", - "integrity": "sha512-ZsWMI0j7EcFCMqbqdVwMWhmsVc03FhmypWXokKopGhwySn4IAB4AOURonRmFrO7k6sDeQ+iJ9QtTu7jA+S8wmg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4", - "lodash": "^4.17.21", - "mjml-core": "4.18.0" - } - }, - "node_modules/mjml-carousel": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/mjml-carousel/-/mjml-carousel-4.18.0.tgz", - "integrity": "sha512-wY4g1CHCOoVSZuar7CLFon/qkPbICu71IT+6pa4BDwkAiaAMAemZPyy+a+iIUgdc8kHgSuHGsGf6PQzBSMWRZA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4", - "lodash": "^4.17.21", - "mjml-core": "4.18.0" - } - }, - "node_modules/mjml-cli": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/mjml-cli/-/mjml-cli-4.18.0.tgz", - "integrity": "sha512-N6CnA4o/q/VRnGPxTzvVnjAEcF7WUVVQGYfS9SPAp0qwyf7RysMmewdS9yN8GwXwZV6L2sKdn+3ANNi2FNsJ7w==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4", - "chokidar": "^3.0.0", - "glob": "^10.3.10", - "html-minifier": "^4.0.0", - "js-beautify": "^1.6.14", - "lodash": "^4.17.21", - "minimatch": "^9.0.3", - "mjml-core": "4.18.0", - "mjml-migrate": "4.18.0", - "mjml-parser-xml": "4.18.0", - "mjml-validator": "4.18.0", - "yargs": "^17.7.2" - }, - "bin": { - "mjml-cli": "bin/mjml" - } - }, - "node_modules/mjml-cli/node_modules/brace-expansion": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", - "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/mjml-cli/node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/mjml-cli/node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", - "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", - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/mjml-cli/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/mjml-cli/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC" - }, - "node_modules/mjml-cli/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.2" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/mjml-cli/node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/mjml-cli/node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/mjml-cli/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/mjml-column": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/mjml-column/-/mjml-column-4.18.0.tgz", - "integrity": "sha512-0QZ1whxbHUmJaRT8tW+wmr3fWZ/kpsHKAd24c7Z/N1Otm/U2G0T/FFEFJ6cB25X6ZN0K40QZ8L9gdLfiSVuRbA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4", - "lodash": "^4.17.21", - "mjml-core": "4.18.0" - } - }, - "node_modules/mjml-core": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/mjml-core/-/mjml-core-4.18.0.tgz", - "integrity": "sha512-yey72LszXvIo5p0R6DB+YU8er/nP2wPsqpLKQCB0H8vG0WRT1sbSUvnCUOkKGn7subuyWDTdzHKbQO3XYIOmvg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4", - "cheerio": "1.0.0-rc.12", - "detect-node": "^2.0.4", - "html-minifier": "^4.0.0", - "js-beautify": "^1.6.14", - "juice": "^10.0.0", - "lodash": "^4.17.21", - "mjml-migrate": "4.18.0", - "mjml-parser-xml": "4.18.0", - "mjml-validator": "4.18.0" - } - }, - "node_modules/mjml-divider": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/mjml-divider/-/mjml-divider-4.18.0.tgz", - "integrity": "sha512-FmGUVJqi4RYroh7y85vDx0aUKZgECkxHtMQ4pkLGQbZ2g93/Qt0Ek88DVCNJ5XwUAQQkE/TvrGMLHp3CIqpQ9Q==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4", - "lodash": "^4.17.21", - "mjml-core": "4.18.0" - } - }, - "node_modules/mjml-group": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/mjml-group/-/mjml-group-4.18.0.tgz", - "integrity": "sha512-28ABkXsKljBqj7XCC8GkQ94xz8HEU2XTyD+9LTlkDafzGp/MGJb8DcLh/7IkxCwqkQWyeMiDNLf1djsQ909Vxw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4", - "lodash": "^4.17.21", - "mjml-core": "4.18.0" - } - }, - "node_modules/mjml-head": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/mjml-head/-/mjml-head-4.18.0.tgz", - "integrity": "sha512-DS0adpIAsVMDIk2DOsHzjg+RNjQU0fF8jiVP9BmdRHVGrLPmpL9wIHZk2KvsKvZe7VaXXBijFt3DZ5/CQ/+D7Q==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4", - "lodash": "^4.17.21", - "mjml-core": "4.18.0" - } - }, - "node_modules/mjml-head-attributes": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/mjml-head-attributes/-/mjml-head-attributes-4.18.0.tgz", - "integrity": "sha512-nLzix1wrMnojE0RPGhk4iKqSRwHKjie2EPzgKT7CDzfqN+Ref03E5Q19x3cQTLgxvq3C3CnvCQBfnhoS3Eakug==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4", - "lodash": "^4.17.21", - "mjml-core": "4.18.0" - } - }, - "node_modules/mjml-head-breakpoint": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/mjml-head-breakpoint/-/mjml-head-breakpoint-4.18.0.tgz", - "integrity": "sha512-k6rwff+7i+vTQYJ/CjBfE20qNqPaW60IRH2x2oEPuCzmwDmoVWOcplJIuotSqIAdfwF9hLkICknisp1BpczVlQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4", - "lodash": "^4.17.21", - "mjml-core": "4.18.0" - } - }, - "node_modules/mjml-head-font": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/mjml-head-font/-/mjml-head-font-4.18.0.tgz", - "integrity": "sha512-ao8HB5nf+Dmxw4GO6lMMOlnj1lNZONai0GC9RobrZgPlghZw6hpURWGpkON7pQcy6XnOHwYwkV7Go/npzA2i7w==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4", - "lodash": "^4.17.21", - "mjml-core": "4.18.0" - } - }, - "node_modules/mjml-head-html-attributes": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/mjml-head-html-attributes/-/mjml-head-html-attributes-4.18.0.tgz", - "integrity": "sha512-xaQE1rthe0RrNotwEr71X1tE+QQ489Yc0ynMm3oNMrohDI/TaCeazx8GAHPMM7VLduDA8D4A5wkZ6PuEvlJu4w==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4", - "lodash": "^4.17.21", - "mjml-core": "4.18.0" - } - }, - "node_modules/mjml-head-preview": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/mjml-head-preview/-/mjml-head-preview-4.18.0.tgz", - "integrity": "sha512-2JvYqhbLyU/+Te6/1AXxzTNoHYCDYhXOVZP7wMvU4t7K34pXqyRUNO405atyHUY1MRafrl6RJ8cIx0x5vUX7PA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4", - "lodash": "^4.17.21", - "mjml-core": "4.18.0" - } - }, - "node_modules/mjml-head-style": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/mjml-head-style/-/mjml-head-style-4.18.0.tgz", - "integrity": "sha512-nEwDHkAqY3Fm7QWeAZc/a7MakZpXh6THfrE8/AWrfpgzTHrD/wihNUc09ztNpr6z/K1+JWgQfSF2BRc+X3P46g==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4", - "lodash": "^4.17.21", - "mjml-core": "4.18.0" - } - }, - "node_modules/mjml-head-title": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/mjml-head-title/-/mjml-head-title-4.18.0.tgz", - "integrity": "sha512-0Hm8o50rPMUQLSCOOa4D4pz9NajmCDccLvBYE4fwKdeUXjSJ6bwAYeMpveel8oNZMDUVJ4Hx+PskisEGHMHM2w==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4", - "lodash": "^4.17.21", - "mjml-core": "4.18.0" - } - }, - "node_modules/mjml-hero": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/mjml-hero/-/mjml-hero-4.18.0.tgz", - "integrity": "sha512-rujm0ROM4QGWw77vnl3NaVaCKXrT4xTSHeAnkHKiY5AuRf6HPTgEtutq5pdel/y6Q9GrmxvN3HRESum7tpJCJw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4", - "lodash": "^4.17.21", - "mjml-core": "4.18.0" - } - }, - "node_modules/mjml-image": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/mjml-image/-/mjml-image-4.18.0.tgz", - "integrity": "sha512-e09NkoYwvzMcTv7V6H5doWD6Te2E1y2EvOLQJoXKVdQpDwyBWGdfnZke0scJGdA58HLAB+0mLYogpLwmfLaP5Q==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4", - "lodash": "^4.17.21", - "mjml-core": "4.18.0" - } - }, - "node_modules/mjml-migrate": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/mjml-migrate/-/mjml-migrate-4.18.0.tgz", - "integrity": "sha512-qfNCgW9zhJIsbPyXFA5RT/WY4mlje3N0WhHHOsHc0nY89Q01DenyslUy9nLLGXwi4K5FHS58oCjwWbMhwDcj1w==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4", - "js-beautify": "^1.6.14", - "lodash": "^4.17.21", - "mjml-core": "4.18.0", - "mjml-parser-xml": "4.18.0", - "yargs": "^17.7.2" - }, - "bin": { - "migrate": "lib/cli.js" - } - }, - "node_modules/mjml-navbar": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/mjml-navbar/-/mjml-navbar-4.18.0.tgz", - "integrity": "sha512-uho/MS2tfNAe+V9u2X7NoCco34MDbdp30ETA8009Qo1VCP/D8lZ+s69WGRPu6hvN/Y2pzBgZly++CMg3qFZqBQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4", - "lodash": "^4.17.21", - "mjml-core": "4.18.0" - } - }, - "node_modules/mjml-parser-xml": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/mjml-parser-xml/-/mjml-parser-xml-4.18.0.tgz", - "integrity": "sha512-sHSsZg4afY1heThuJzxa1Kvfh/QzB7/9P5fFUHeVnnxb07ZTXnhXWA6YbobdND5/l9+5yjN5/UgqDZm3tIT4Uw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4", - "detect-node": "2.1.0", - "htmlparser2": "^9.1.0", - "lodash": "^4.17.21" - } - }, - "node_modules/mjml-parser-xml/node_modules/htmlparser2": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", - "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.1.0", - "entities": "^4.5.0" - } - }, - "node_modules/mjml-preset-core": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/mjml-preset-core/-/mjml-preset-core-4.18.0.tgz", - "integrity": "sha512-x3l8vMVtsaqM/jauMeZIN7HFD2t5A28J4U0o4849yIlRxiWguLFV5l3BL8Byol+YLkoLuT9PjaZs9RYv+FGfeg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4", - "mjml-accordion": "4.18.0", - "mjml-body": "4.18.0", - "mjml-button": "4.18.0", - "mjml-carousel": "4.18.0", - "mjml-column": "4.18.0", - "mjml-divider": "4.18.0", - "mjml-group": "4.18.0", - "mjml-head": "4.18.0", - "mjml-head-attributes": "4.18.0", - "mjml-head-breakpoint": "4.18.0", - "mjml-head-font": "4.18.0", - "mjml-head-html-attributes": "4.18.0", - "mjml-head-preview": "4.18.0", - "mjml-head-style": "4.18.0", - "mjml-head-title": "4.18.0", - "mjml-hero": "4.18.0", - "mjml-image": "4.18.0", - "mjml-navbar": "4.18.0", - "mjml-raw": "4.18.0", - "mjml-section": "4.18.0", - "mjml-social": "4.18.0", - "mjml-spacer": "4.18.0", - "mjml-table": "4.18.0", - "mjml-text": "4.18.0", - "mjml-wrapper": "4.18.0" - } - }, - "node_modules/mjml-raw": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/mjml-raw/-/mjml-raw-4.18.0.tgz", - "integrity": "sha512-F/kViAwXm3ccPP52kw++/mHQbcYbYYxC8JH15TZxH8GLVZkX5CGKgcBrHhDK7WoIlfEIsVRZ6IZdlHjH8vgyxw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4", - "lodash": "^4.17.21", - "mjml-core": "4.18.0" + "node": ">= 0.6" } }, - "node_modules/mjml-section": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/mjml-section/-/mjml-section-4.18.0.tgz", - "integrity": "sha512-bB8My9zvIEkTOxej+TrjEeaeRT0lsypGeRADtdrRZXeqUClkkuCnCXlsNKSLGT8ZRqjUqWRc5z8ubDOvGk2+Gg==", + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.4", - "lodash": "^4.17.21", - "mjml-core": "4.18.0" + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" } }, - "node_modules/mjml-social": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/mjml-social/-/mjml-social-4.18.0.tgz", - "integrity": "sha512-iAQc9g59L6L3VHDd55BxeIvk/zHkxflxmvuyYyOOvpmmKAvUBC//ULfpxiiM4yupofsThqFfrO+wc8d4kTRkbQ==", + "node_modules/mime-types/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4", - "lodash": "^4.17.21", - "mjml-core": "4.18.0" + "engines": { + "node": ">= 0.6" } }, - "node_modules/mjml-spacer": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/mjml-spacer/-/mjml-spacer-4.18.0.tgz", - "integrity": "sha512-FK/0f5IBiONgaRpwNBs7G8EbLdAbmYqcIfHR8O8tP4LipAChLQKHO9vX3vrRMGLBZZNTESLObcFSVWmA40Mfpw==", + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4", - "lodash": "^4.17.21", - "mjml-core": "4.18.0" + "engines": { + "node": ">=6" } }, - "node_modules/mjml-table": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/mjml-table/-/mjml-table-4.18.0.tgz", - "integrity": "sha512-vJysCPUL3CHcsQDAFpW+skzBtY0RYsmMBYswI4WX0B05GLKlOjXqpYOwcmAupWeGoBVL5r/t28ynu2PqnOlN3w==", + "node_modules/mimic-response": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", + "dev": true, "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4", - "lodash": "^4.17.21", - "mjml-core": "4.18.0" + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mjml-text": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/mjml-text/-/mjml-text-4.18.0.tgz", - "integrity": "sha512-hBLmF3JgveUKktKQFWHqHAr7qr92j1CxAvq7mtpDUgiWgyPFzqRX8mUsFYgZ7DmRxG4UE+Kzpt8/YFd9+E98lw==", - "license": "MIT", + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", "dependencies": { - "@babel/runtime": "^7.28.4", - "lodash": "^4.17.21", - "mjml-core": "4.18.0" + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "node_modules/mjml-validator": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/mjml-validator/-/mjml-validator-4.18.0.tgz", - "integrity": "sha512-JmpWAsNTUlAxJOz2zHYfF8Vod8OzM3Qp5JXtrVw5tivZQzq88ZfqVGuqsas51z0pp1/ilfD4lC17YGfGwKGyhA==", + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mjml-wrapper": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/mjml-wrapper/-/mjml-wrapper-4.18.0.tgz", - "integrity": "sha512-TZeOvLjIhXEK60rjWNiYhEYNlv5GKYahE+96ifcT5OGkWkRA0DsQDfp+6VI32OS5VxsfKq2h/UdERPlQijjpAQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4", - "lodash": "^4.17.21", - "mjml-core": "4.18.0", - "mjml-section": "4.18.0" + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" } }, "node_modules/module-details-from-path": { @@ -14602,15 +13598,6 @@ "dev": true, "license": "MIT" }, - "node_modules/no-case": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", - "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", - "license": "MIT", - "dependencies": { - "lower-case": "^1.1.1" - } - }, "node_modules/node-abort-controller": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", @@ -14635,41 +13622,12 @@ "lodash": "^4.17.21" } }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, "node_modules/node-fetch-native": { "version": "1.6.7", "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", "license": "MIT" }, - "node_modules/node-forge": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz", - "integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==", - "license": "(BSD-3-Clause OR GPL-2.0)", - "engines": { - "node": ">= 6.13.0" - } - }, "node_modules/node-gyp-build": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.9.0.tgz", @@ -14696,34 +13654,11 @@ "dev": true, "license": "MIT" }, - "node_modules/nodemailer": { - "version": "7.0.13", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.13.tgz", - "integrity": "sha512-PNDFSJdP+KFgdsG3ZzMXCgquO7I6McjY2vlqILjtJd0hy8wEvtugS9xKRF2NWlPNGxvLCXlTNIae4serI7dinw==", - "license": "MIT-0", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/nopt": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", - "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", - "license": "ISC", - "dependencies": { - "abbrev": "^2.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -14755,18 +13690,6 @@ "node": ">=8" } }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, "node_modules/nypm": { "version": "0.6.5", "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.5.tgz", @@ -15081,6 +14004,7 @@ "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==", + "dev": true, "license": "BlueOak-1.0.0" }, "node_modules/papaparse": { @@ -15089,15 +14013,6 @@ "integrity": "sha512-5QvjGxYVjxO59MGU2lHVYpRWBBtKHnlIAcSe1uNFCkkptUh63NFRj0FJQm7nR67puEruUci/ZkjmEFrjCAyP4A==", "license": "MIT" }, - "node_modules/param-case": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", - "integrity": "sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==", - "license": "MIT", - "dependencies": { - "no-case": "^2.2.0" - } - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -15130,43 +14045,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/parse5": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", - "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", - "license": "MIT", - "dependencies": { - "entities": "^6.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", - "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", - "license": "MIT", - "dependencies": { - "domhandler": "^5.0.3", - "parse5": "^7.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parse5/node_modules/entities": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -15506,9 +14384,9 @@ } }, "node_modules/pnpm": { - "version": "10.33.0", - "resolved": "https://registry.npmjs.org/pnpm/-/pnpm-10.33.0.tgz", - "integrity": "sha512-EFaLtKavtYyes2MNqQzJUWQXq+vT+rvmc58K55VyjaFJHp21pUTHatjrdXD1xLs9bGN7LLQb/c20f6gjyGSTGQ==", + "version": "10.34.1", + "resolved": "https://registry.npmjs.org/pnpm/-/pnpm-10.34.1.tgz", + "integrity": "sha512-tY+95tymapKVOAIVgfZItFcLbKGbGOfL1/LAenskRUFVOI2s3wjyrzZ46IptH+BPnWCd8kv1FzWgYOoEGzdKtw==", "license": "MIT", "bin": { "pnpm": "bin/pnpm.cjs", @@ -15738,12 +14616,6 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "license": "ISC" }, - "node_modules/proto-list": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", - "license": "ISC" - }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -15941,15 +14813,6 @@ "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", "license": "Apache-2.0" }, - "node_modules/relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, "node_modules/remeda": { "version": "2.33.4", "resolved": "https://registry.npmjs.org/remeda/-/remeda-2.33.4.tgz", @@ -15963,6 +14826,7 @@ "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==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -16297,6 +15161,7 @@ "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -16555,15 +15420,6 @@ "node": ">=8" } }, - "node_modules/slick": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/slick/-/slick-1.12.2.tgz", - "integrity": "sha512-4qdtOGcBjral6YIBCWJ0ljFSKNLz9KkhbWtuGvUyRowl1kxfuE1x/Z/aJcaiilpb3do9bl5K7/1h9XC5wWpY/A==", - "license": "MIT (http://mootools.net/license.txt)", - "engines": { - "node": "*" - } - }, "node_modules/sort-keys": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", @@ -16770,6 +15626,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -16785,6 +15642,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -16799,6 +15657,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -16812,6 +15671,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -17393,12 +16253,6 @@ "node": ">=6" } }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, "node_modules/tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -17717,7 +16571,9 @@ "version": "3.19.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, "license": "BSD-2-Clause", + "optional": true, "bin": { "uglifyjs": "bin/uglifyjs" }, @@ -17851,12 +16707,6 @@ "browserslist": ">= 4.21.0" } }, - "node_modules/upper-case": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", - "integrity": "sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==", - "license": "MIT" - }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -17925,15 +16775,6 @@ } } }, - "node_modules/valid-data-url": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/valid-data-url/-/valid-data-url-3.0.1.tgz", - "integrity": "sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, "node_modules/validator": { "version": "13.15.26", "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.26.tgz", @@ -18107,138 +16948,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/web-resource-inliner": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/web-resource-inliner/-/web-resource-inliner-6.0.1.tgz", - "integrity": "sha512-kfqDxt5dTB1JhqsCUQVFDj0rmY+4HLwGQIsLPbyrsN9y9WV/1oFDSx3BQ4GfCv9X+jVeQ7rouTqwK53rA/7t8A==", - "license": "MIT", - "dependencies": { - "ansi-colors": "^4.1.1", - "escape-goat": "^3.0.0", - "htmlparser2": "^5.0.0", - "mime": "^2.4.6", - "node-fetch": "^2.6.0", - "valid-data-url": "^3.0.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/web-resource-inliner/node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/web-resource-inliner/node_modules/dom-serializer/node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/web-resource-inliner/node_modules/domhandler": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz", - "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==", - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.0.1" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/web-resource-inliner/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/web-resource-inliner/node_modules/domutils/node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/web-resource-inliner/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "license": "BSD-2-Clause", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/web-resource-inliner/node_modules/htmlparser2": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-5.0.1.tgz", - "integrity": "sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==", - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^3.3.0", - "domutils": "^2.4.2", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/fb55/htmlparser2?sponsor=1" - } - }, - "node_modules/web-resource-inliner/node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, "node_modules/webpack": { "version": "5.105.4", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.4.tgz", @@ -18375,16 +17084,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -18437,6 +17136,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -18483,6 +17183,7 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, "license": "ISC", "engines": { "node": ">=10" @@ -18506,6 +17207,7 @@ "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, "license": "MIT", "dependencies": { "cliui": "^8.0.1", @@ -18524,6 +17226,7 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, "license": "ISC", "engines": { "node": ">=12" diff --git a/package.json b/package.json index 901cb37a..c679597c 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "concurrently": "^8.2.2" }, "dependencies": { - "pnpm": "^10.33.0", + "pnpm": "^10.34.1", "prisma": "^7.6.0" }, "keywords": [], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 811233f3..9f57d348 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -63,8 +63,8 @@ importers: .: dependencies: pnpm: - specifier: ^10.33.0 - version: 10.33.0 + specifier: ^10.34.1 + version: 10.34.1 prisma: specifier: ^7.6.0 version: 7.6.0(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) @@ -1891,7 +1891,7 @@ packages: peerDependencies: '@opentelemetry/api': ^1.0.0 '@sveltejs/vite-plugin-svelte': ^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0 - svelte: '>=5.51.5' + svelte: '>=5.53.5' typescript: ^5.3.3 || ^6.0.0 vite: ^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0 peerDependenciesMeta: @@ -1912,7 +1912,7 @@ packages: resolution: {integrity: sha512-ou/d51QSdTyN26D7h6dSpusAKaZkAiGM55/AKYi+9AGZw7q85hElbjK3kEyzXHhLSnRISHOYzVge6x0jRZ7DXA==} engines: {node: ^20.19 || ^22.12 || >=24} peerDependencies: - svelte: '>=5.51.5' + svelte: '>=5.53.5' vite: ^6.3.0 || ^7.0.0 '@swc/cli@0.7.10': @@ -4600,8 +4600,8 @@ packages: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} - pnpm@10.33.0: - resolution: {integrity: sha512-EFaLtKavtYyes2MNqQzJUWQXq+vT+rvmc58K55VyjaFJHp21pUTHatjrdXD1xLs9bGN7LLQb/c20f6gjyGSTGQ==} + pnpm@10.34.1: + resolution: {integrity: sha512-tY+95tymapKVOAIVgfZItFcLbKGbGOfL1/LAenskRUFVOI2s3wjyrzZ46IptH+BPnWCd8kv1FzWgYOoEGzdKtw==} engines: {node: '>=18.12'} hasBin: true @@ -5081,7 +5081,7 @@ packages: engines: {node: '>= 18.0.0'} hasBin: true peerDependencies: - svelte: '>=5.51.5' + svelte: '>=5.53.5' typescript: '>=5.0.0' svelte@5.55.1: @@ -9949,7 +9949,7 @@ snapshots: pluralize@8.0.0: {} - pnpm@10.33.0: {} + pnpm@10.34.1: {} postcss@8.5.6: dependencies: From b5742d058aabf89eee6a8e4a745dfe4a38095817 Mon Sep 17 00:00:00 2001 From: markalmax Date: Thu, 4 Jun 2026 13:44:41 +0300 Subject: [PATCH 2/2] fix: removed credit system and checked on buying instead --- backend/src/balance/balance.service.ts | 3 --- backend/src/events/events.service.ts | 27 +++++++++++++++++++++----- backend/src/shop/shop.service.ts | 4 +++- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/backend/src/balance/balance.service.ts b/backend/src/balance/balance.service.ts index f0346b75..97750627 100644 --- a/backend/src/balance/balance.service.ts +++ b/backend/src/balance/balance.service.ts @@ -47,7 +47,6 @@ export class BalanceService { itemId?: number | null; variantId?: number | null; eventId?: number | null; - eventHoursCredit?: number | null; quantity?: number; enforceBalance?: boolean; preCheck?: (tx: Prisma.TransactionClient) => Promise; @@ -60,7 +59,6 @@ export class BalanceService { itemId = null, variantId = null, eventId = null, - eventHoursCredit = null, quantity = 1, enforceBalance = true, preCheck, @@ -94,7 +92,6 @@ export class BalanceService { itemId, variantId, eventId, - eventHoursCredit, itemDescription, cost, }; diff --git a/backend/src/events/events.service.ts b/backend/src/events/events.service.ts index 4ec8e9ae..e3084821 100644 --- a/backend/src/events/events.service.ts +++ b/backend/src/events/events.service.ts @@ -277,12 +277,29 @@ export class EventsService { } } - // Calculate the net ticket cost after applying event hours credit - const eventHoursCredit = await this.getEventHoursCredit( - userId, - event.slug, + // Sum up all event hours credits for this specific event and reduce the ticket cost + const eventHoursCredits = await this.prisma.transaction.findMany({ + where: { + userId, + eventId: event.eventId, + eventHoursCredit: { not: null }, + refundedAt: null, + }, + select: { eventHoursCredit: true }, + }); + const totalEventHoursCredit = eventHoursCredits.reduce( + (sum, txn) => sum + (txn.eventHoursCredit ?? 0), + 0, ); - const netTicketCost = Math.max(0, event.ticketCost! - eventHoursCredit); + const netTicketCost = Math.max(0, event.ticketCost! - totalEventHoursCredit); + + // Verify the user has sufficient balance to pay for the ticket + const { balance } = await this.balanceService.getUserBalance(userId); + if (balance < netTicketCost) { + throw new BadRequestException( + `Insufficient balance. You need ${netTicketCost} hours to buy a ticket, but you have ${Math.round(balance * 10) / 10} hours.`, + ); + } let transaction; try { diff --git a/backend/src/shop/shop.service.ts b/backend/src/shop/shop.service.ts index 9d39f545..fdf054b2 100644 --- a/backend/src/shop/shop.service.ts +++ b/backend/src/shop/shop.service.ts @@ -340,6 +340,7 @@ export class ShopService { } let eventHoursCredit: number | null = null; + let linkedEventId: number | null = null; if (item.eventHoursReduction != null && item.eventHoursReduction > 0) { const linkedEvent = await this.prisma.event.findUnique({ where: { slug: item.shop.slug }, @@ -347,6 +348,7 @@ export class ShopService { }); if (linkedEvent) { eventHoursCredit = item.eventHoursReduction; + linkedEventId = linkedEvent.eventId; } } @@ -361,7 +363,7 @@ export class ShopService { itemDescription: description, itemId, variantId: variant?.variantId ?? null, - eventHoursCredit, + eventId: linkedEventId, preCheck: async (tx) => { if (maxPerUser !== null && maxPerUser > 0) { const count = await tx.transaction.count({