From 04121c7c280456d1d1341ecfca429685ac2cea07 Mon Sep 17 00:00:00 2001 From: laasyaaki Date: Sun, 3 Aug 2025 14:13:09 -0500 Subject: [PATCH 01/65] Manually added locations --- src/parser/diningParser.ts | 92 +++++++++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/src/parser/diningParser.ts b/src/parser/diningParser.ts index d966381..51a4d6b 100644 --- a/src/parser/diningParser.ts +++ b/src/parser/diningParser.ts @@ -32,7 +32,97 @@ export default class DiningParser { builder.overwriteLocationCoordinates(locationCoordinateOverwrites); } - return locationBuilders.map((builder) => builder.build()); + const locations = locationBuilders.map((builder) => builder.build()); + + locations.push({ + conceptId: 9998, + name: "Subway", + shortDescription: + "Off-Campus, Flex-only location serving sandwiches, wraps, and salads.", + description: + "Casual counter-serve chain for build-your-own sandwiches & salads, with health-conscious options. Accepts CMU Flex and DineX dollars", + url: "https://www.subway.com/en-us/", + location: "Off-Campus", + menu: "https://www.subway.com/en-us/menunutrition/menu", + coordinates: { lat: 40.44468, lng: -79.94888 }, + acceptsOnlineOrders: false, + times: [ + { + start: { day: 0, hour: 9, minute: 0 }, + end: { day: 0, hour: 22, minute: 0 }, + }, + { + start: { day: 1, hour: 7, minute: 0 }, + end: { day: 1, hour: 23, minute: 0 }, + }, + { + start: { day: 2, hour: 7, minute: 0 }, + end: { day: 2, hour: 23, minute: 0 }, + }, + { + start: { day: 3, hour: 7, minute: 0 }, + end: { day: 3, hour: 23, minute: 0 }, + }, + { + start: { day: 4, hour: 7, minute: 0 }, + end: { day: 4, hour: 23, minute: 0 }, + }, + { + start: { day: 5, hour: 7, minute: 0 }, + end: { day: 5, hour: 23, minute: 0 }, + }, + { + start: { day: 6, hour: 9, minute: 0 }, + end: { day: 6, hour: 23, minute: 0 }, + }, + ], + }); + + locations.push({ + conceptId: 9999, + name: "Vocelli Pizza", + shortDescription: + "Off-Campus, Flex-only location serving pizza and Italian dishes. Delivers to on-campus locations.", + description: + "Pittsburgh-based chain serving artisanal pizzas & other Italian sandwiches & salads. Accepts CMU Flex and DineX dollars and delivers to on-campus locations.", + url: "https://www.vocellipizza.com/", + location: "Off-Campus", + menu: "https://www.vocellipizza.com/menu", + coordinates: { lat: 40.454081, lng: -79.948794 }, + acceptsOnlineOrders: true, + times: [ + { + start: { day: 0, hour: 11, minute: 0 }, + end: { day: 0, hour: 22, minute: 0 }, + }, + { + start: { day: 1, hour: 11, minute: 0 }, + end: { day: 1, hour: 22, minute: 0 }, + }, + { + start: { day: 2, hour: 11, minute: 0 }, + end: { day: 2, hour: 22, minute: 0 }, + }, + { + start: { day: 3, hour: 11, minute: 0 }, + end: { day: 3, hour: 22, minute: 0 }, + }, + { + start: { day: 4, hour: 11, minute: 0 }, + end: { day: 5, hour: 0, minute: 0 }, + }, + { + start: { day: 5, hour: 11, minute: 0 }, + end: { day: 6, hour: 2, minute: 0 }, + }, + { + start: { day: 6, hour: 11, minute: 0 }, + end: { day: 0, hour: 2, minute: 0 }, + }, + ], + }); + + return locations; } private async initializeLocationBuildersFromMainPage(): Promise< From 259e8d17412fc6cb24b9cc40a830cc6628b9b614 Mon Sep 17 00:00:00 2001 From: laasyaaki Date: Sun, 3 Aug 2025 14:36:03 -0500 Subject: [PATCH 02/65] cleaned up a bit --- src/parser/diningParser.ts | 185 +++++++++++++++++++------------------ 1 file changed, 93 insertions(+), 92 deletions(-) diff --git a/src/parser/diningParser.ts b/src/parser/diningParser.ts index 51a4d6b..90eee75 100644 --- a/src/parser/diningParser.ts +++ b/src/parser/diningParser.ts @@ -19,9 +19,97 @@ export default class DiningParser { constructor() {} - async process(): Promise { + async process(includeManual: boolean = true): Promise { const locationBuilders = await this.initializeLocationBuildersFromMainPage(); + const manualLocations: ILocation[] = [ + { + conceptId: 9998, + name: "Subway", + shortDescription: + "Off-Campus, Flex-only location serving sandwiches, wraps, and salads.", + description: + "Casual counter-serve chain for build-your-own sandwiches & salads, with health-conscious options. Accepts CMU Flex and DineX dollars", + url: "https://www.subway.com/en-us/", + location: "Off-Campus", + menu: "https://www.subway.com/en-us/menunutrition/menu", + coordinates: { lat: 40.44468, lng: -79.94888 }, + acceptsOnlineOrders: false, + times: [ + { + start: { day: 0, hour: 9, minute: 0 }, + end: { day: 0, hour: 22, minute: 0 }, + }, + { + start: { day: 1, hour: 7, minute: 0 }, + end: { day: 1, hour: 23, minute: 0 }, + }, + { + start: { day: 2, hour: 7, minute: 0 }, + end: { day: 2, hour: 23, minute: 0 }, + }, + { + start: { day: 3, hour: 7, minute: 0 }, + end: { day: 3, hour: 23, minute: 0 }, + }, + { + start: { day: 4, hour: 7, minute: 0 }, + end: { day: 4, hour: 23, minute: 0 }, + }, + { + start: { day: 5, hour: 7, minute: 0 }, + end: { day: 5, hour: 23, minute: 0 }, + }, + { + start: { day: 6, hour: 9, minute: 0 }, + end: { day: 6, hour: 23, minute: 0 }, + }, + ], + }, + { + conceptId: 9999, + name: "Vocelli Pizza", + shortDescription: + "Off-Campus, Flex-only location serving pizza and Italian dishes. Delivers to on-campus locations.", + description: + "Pittsburgh-based chain serving artisanal pizzas & other Italian sandwiches & salads. Accepts CMU Flex and DineX dollars and delivers to on-campus locations.", + url: "https://www.vocellipizza.com/", + location: "Off-Campus", + menu: "https://www.vocellipizza.com/menu", + coordinates: { lat: 40.454081, lng: -79.948794 }, + acceptsOnlineOrders: true, + times: [ + { + start: { day: 0, hour: 11, minute: 0 }, + end: { day: 0, hour: 22, minute: 0 }, + }, + { + start: { day: 1, hour: 11, minute: 0 }, + end: { day: 1, hour: 22, minute: 0 }, + }, + { + start: { day: 2, hour: 11, minute: 0 }, + end: { day: 2, hour: 22, minute: 0 }, + }, + { + start: { day: 3, hour: 11, minute: 0 }, + end: { day: 3, hour: 22, minute: 0 }, + }, + { + start: { day: 4, hour: 11, minute: 0 }, + end: { day: 5, hour: 0, minute: 0 }, + }, + { + start: { day: 5, hour: 11, minute: 0 }, + end: { day: 6, hour: 2, minute: 0 }, + }, + { + start: { day: 6, hour: 11, minute: 0 }, + end: { day: 0, hour: 2, minute: 0 }, + }, + ], + }, + ]; const [specials, soups] = await this.fetchSpecials(); @@ -32,97 +120,10 @@ export default class DiningParser { builder.overwriteLocationCoordinates(locationCoordinateOverwrites); } - const locations = locationBuilders.map((builder) => builder.build()); - - locations.push({ - conceptId: 9998, - name: "Subway", - shortDescription: - "Off-Campus, Flex-only location serving sandwiches, wraps, and salads.", - description: - "Casual counter-serve chain for build-your-own sandwiches & salads, with health-conscious options. Accepts CMU Flex and DineX dollars", - url: "https://www.subway.com/en-us/", - location: "Off-Campus", - menu: "https://www.subway.com/en-us/menunutrition/menu", - coordinates: { lat: 40.44468, lng: -79.94888 }, - acceptsOnlineOrders: false, - times: [ - { - start: { day: 0, hour: 9, minute: 0 }, - end: { day: 0, hour: 22, minute: 0 }, - }, - { - start: { day: 1, hour: 7, minute: 0 }, - end: { day: 1, hour: 23, minute: 0 }, - }, - { - start: { day: 2, hour: 7, minute: 0 }, - end: { day: 2, hour: 23, minute: 0 }, - }, - { - start: { day: 3, hour: 7, minute: 0 }, - end: { day: 3, hour: 23, minute: 0 }, - }, - { - start: { day: 4, hour: 7, minute: 0 }, - end: { day: 4, hour: 23, minute: 0 }, - }, - { - start: { day: 5, hour: 7, minute: 0 }, - end: { day: 5, hour: 23, minute: 0 }, - }, - { - start: { day: 6, hour: 9, minute: 0 }, - end: { day: 6, hour: 23, minute: 0 }, - }, - ], - }); - - locations.push({ - conceptId: 9999, - name: "Vocelli Pizza", - shortDescription: - "Off-Campus, Flex-only location serving pizza and Italian dishes. Delivers to on-campus locations.", - description: - "Pittsburgh-based chain serving artisanal pizzas & other Italian sandwiches & salads. Accepts CMU Flex and DineX dollars and delivers to on-campus locations.", - url: "https://www.vocellipizza.com/", - location: "Off-Campus", - menu: "https://www.vocellipizza.com/menu", - coordinates: { lat: 40.454081, lng: -79.948794 }, - acceptsOnlineOrders: true, - times: [ - { - start: { day: 0, hour: 11, minute: 0 }, - end: { day: 0, hour: 22, minute: 0 }, - }, - { - start: { day: 1, hour: 11, minute: 0 }, - end: { day: 1, hour: 22, minute: 0 }, - }, - { - start: { day: 2, hour: 11, minute: 0 }, - end: { day: 2, hour: 22, minute: 0 }, - }, - { - start: { day: 3, hour: 11, minute: 0 }, - end: { day: 3, hour: 22, minute: 0 }, - }, - { - start: { day: 4, hour: 11, minute: 0 }, - end: { day: 5, hour: 0, minute: 0 }, - }, - { - start: { day: 5, hour: 11, minute: 0 }, - end: { day: 6, hour: 2, minute: 0 }, - }, - { - start: { day: 6, hour: 11, minute: 0 }, - end: { day: 0, hour: 2, minute: 0 }, - }, - ], - }); - - return locations; + return [ + ...locationBuilders.map((builder) => builder.build()), + ...(includeManual ? manualLocations : []), + ]; } private async initializeLocationBuildersFromMainPage(): Promise< From 612d35f64a9868d057401ecff25e99e8e2c4336e Mon Sep 17 00:00:00 2001 From: laasyaaki Date: Sun, 3 Aug 2025 14:44:38 -0500 Subject: [PATCH 03/65] latest --- src/parser/diningParser.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/parser/diningParser.ts b/src/parser/diningParser.ts index 90eee75..64601a2 100644 --- a/src/parser/diningParser.ts +++ b/src/parser/diningParser.ts @@ -19,7 +19,7 @@ export default class DiningParser { constructor() {} - async process(includeManual: boolean = true): Promise { + async process(): Promise { const locationBuilders = await this.initializeLocationBuildersFromMainPage(); const manualLocations: ILocation[] = [ @@ -122,7 +122,7 @@ export default class DiningParser { return [ ...locationBuilders.map((builder) => builder.build()), - ...(includeManual ? manualLocations : []), + ...manualLocations, ]; } From af470c89ef40edf02cb640fdbaeac22038ee8226 Mon Sep 17 00:00:00 2001 From: laasyaaki Date: Sun, 3 Aug 2025 15:23:22 -0500 Subject: [PATCH 04/65] seperated locations --- src/manualLocations.ts | 94 ++++++++++++++++++++++++++++++++++++++ src/parser/diningParser.ts | 93 +------------------------------------ src/server.ts | 4 +- 3 files changed, 98 insertions(+), 93 deletions(-) create mode 100644 src/manualLocations.ts diff --git a/src/manualLocations.ts b/src/manualLocations.ts new file mode 100644 index 0000000..3cc0262 --- /dev/null +++ b/src/manualLocations.ts @@ -0,0 +1,94 @@ +import { ILocation } from "types"; + +export const manualLocations: ILocation[] = [ + { + conceptId: 9998, + name: "Subway", + shortDescription: + "Off-Campus, Flex-only location serving sandwiches, wraps, and salads.", + description: + "Casual counter-serve chain for build-your-own sandwiches & salads, with health-conscious options. Accepts CMU Flex and DineX dollars", + url: "https://www.subway.com/en-us/", + location: "Off-Campus", + menu: "https://www.subway.com/en-us/menunutrition/menu", + coordinates: { lat: 40.44468, lng: -79.94888 }, + acceptsOnlineOrders: false, + times: [ + { + start: { day: 0, hour: 9, minute: 0 }, + end: { day: 0, hour: 22, minute: 0 }, + }, + { + start: { day: 1, hour: 7, minute: 0 }, + end: { day: 1, hour: 23, minute: 0 }, + }, + { + start: { day: 2, hour: 7, minute: 0 }, + end: { day: 2, hour: 23, minute: 0 }, + }, + { + start: { day: 3, hour: 7, minute: 0 }, + end: { day: 3, hour: 23, minute: 0 }, + }, + { + start: { day: 4, hour: 7, minute: 0 }, + end: { day: 4, hour: 23, minute: 0 }, + }, + { + start: { day: 5, hour: 7, minute: 0 }, + end: { day: 5, hour: 23, minute: 0 }, + }, + { + start: { day: 6, hour: 9, minute: 0 }, + end: { day: 6, hour: 23, minute: 0 }, + }, + ], + todaysSpecials: [], + todaysSoups: [], + }, + { + conceptId: 9999, + name: "Vocelli Pizza", + shortDescription: + "Off-Campus, Flex-only location serving pizza and Italian dishes. Delivers to on-campus locations.", + description: + "Pittsburgh-based chain serving artisanal pizzas & other Italian sandwiches & salads. Accepts CMU Flex and DineX dollars and delivers to on-campus locations.", + url: "https://www.vocellipizza.com/", + location: "Off-Campus", + menu: "https://www.vocellipizza.com/menu", + coordinates: { lat: 40.454081, lng: -79.948794 }, + acceptsOnlineOrders: true, + times: [ + { + start: { day: 0, hour: 11, minute: 0 }, + end: { day: 0, hour: 22, minute: 0 }, + }, + { + start: { day: 1, hour: 11, minute: 0 }, + end: { day: 1, hour: 22, minute: 0 }, + }, + { + start: { day: 2, hour: 11, minute: 0 }, + end: { day: 2, hour: 22, minute: 0 }, + }, + { + start: { day: 3, hour: 11, minute: 0 }, + end: { day: 3, hour: 22, minute: 0 }, + }, + { + start: { day: 4, hour: 11, minute: 0 }, + end: { day: 5, hour: 0, minute: 0 }, + }, + { + start: { day: 5, hour: 11, minute: 0 }, + end: { day: 6, hour: 2, minute: 0 }, + }, + { + start: { day: 6, hour: 11, minute: 0 }, + end: { day: 0, hour: 2, minute: 0 }, + }, + ], + todaysSpecials: [], + todaysSoups: [], + }, +]; diff --git a/src/parser/diningParser.ts b/src/parser/diningParser.ts index 64601a2..a220a46 100644 --- a/src/parser/diningParser.ts +++ b/src/parser/diningParser.ts @@ -22,94 +22,6 @@ export default class DiningParser { async process(): Promise { const locationBuilders = await this.initializeLocationBuildersFromMainPage(); - const manualLocations: ILocation[] = [ - { - conceptId: 9998, - name: "Subway", - shortDescription: - "Off-Campus, Flex-only location serving sandwiches, wraps, and salads.", - description: - "Casual counter-serve chain for build-your-own sandwiches & salads, with health-conscious options. Accepts CMU Flex and DineX dollars", - url: "https://www.subway.com/en-us/", - location: "Off-Campus", - menu: "https://www.subway.com/en-us/menunutrition/menu", - coordinates: { lat: 40.44468, lng: -79.94888 }, - acceptsOnlineOrders: false, - times: [ - { - start: { day: 0, hour: 9, minute: 0 }, - end: { day: 0, hour: 22, minute: 0 }, - }, - { - start: { day: 1, hour: 7, minute: 0 }, - end: { day: 1, hour: 23, minute: 0 }, - }, - { - start: { day: 2, hour: 7, minute: 0 }, - end: { day: 2, hour: 23, minute: 0 }, - }, - { - start: { day: 3, hour: 7, minute: 0 }, - end: { day: 3, hour: 23, minute: 0 }, - }, - { - start: { day: 4, hour: 7, minute: 0 }, - end: { day: 4, hour: 23, minute: 0 }, - }, - { - start: { day: 5, hour: 7, minute: 0 }, - end: { day: 5, hour: 23, minute: 0 }, - }, - { - start: { day: 6, hour: 9, minute: 0 }, - end: { day: 6, hour: 23, minute: 0 }, - }, - ], - }, - { - conceptId: 9999, - name: "Vocelli Pizza", - shortDescription: - "Off-Campus, Flex-only location serving pizza and Italian dishes. Delivers to on-campus locations.", - description: - "Pittsburgh-based chain serving artisanal pizzas & other Italian sandwiches & salads. Accepts CMU Flex and DineX dollars and delivers to on-campus locations.", - url: "https://www.vocellipizza.com/", - location: "Off-Campus", - menu: "https://www.vocellipizza.com/menu", - coordinates: { lat: 40.454081, lng: -79.948794 }, - acceptsOnlineOrders: true, - times: [ - { - start: { day: 0, hour: 11, minute: 0 }, - end: { day: 0, hour: 22, minute: 0 }, - }, - { - start: { day: 1, hour: 11, minute: 0 }, - end: { day: 1, hour: 22, minute: 0 }, - }, - { - start: { day: 2, hour: 11, minute: 0 }, - end: { day: 2, hour: 22, minute: 0 }, - }, - { - start: { day: 3, hour: 11, minute: 0 }, - end: { day: 3, hour: 22, minute: 0 }, - }, - { - start: { day: 4, hour: 11, minute: 0 }, - end: { day: 5, hour: 0, minute: 0 }, - }, - { - start: { day: 5, hour: 11, minute: 0 }, - end: { day: 6, hour: 2, minute: 0 }, - }, - { - start: { day: 6, hour: 11, minute: 0 }, - end: { day: 0, hour: 2, minute: 0 }, - }, - ], - }, - ]; const [specials, soups] = await this.fetchSpecials(); @@ -120,10 +32,7 @@ export default class DiningParser { builder.overwriteLocationCoordinates(locationCoordinateOverwrites); } - return [ - ...locationBuilders.map((builder) => builder.build()), - ...manualLocations, - ]; + return [...locationBuilders.map((builder) => builder.build())]; } private async initializeLocationBuildersFromMainPage(): Promise< diff --git a/src/server.ts b/src/server.ts index 0a27d48..99801e0 100644 --- a/src/server.ts +++ b/src/server.ts @@ -8,6 +8,7 @@ import { node } from "@elysiajs/node"; import { getDiffsBetweenLocationData } from "utils/diff"; import { getEmails } from "./db"; import LocationMerger from "utils/locationMerger"; +import { manualLocations } from "manualLocations"; let cachedLocations: ILocation[] = []; @@ -27,7 +28,8 @@ async function reload(): Promise { const locations = await parser.process(); locations.forEach((location) => locationMerger.addLocation(location)); } - const finalLocations = locationMerger.getMostFrequentLocations(); + let finalLocations = locationMerger.getMostFrequentLocations(); + finalLocations = [...finalLocations, ...manualLocations]; if (finalLocations.length === 0) { notifySlack(" No data scraped! Skipping"); } else { From d15c0fe79af6f2221246dcbee6f8cbe5126cff37 Mon Sep 17 00:00:00 2001 From: Laasya Aki <101072841+laasyaaki@users.noreply.github.com> Date: Sun, 3 Aug 2025 15:52:09 -0500 Subject: [PATCH 05/65] Revert "Manually added off campus flex only locations" --- src/manualLocations.ts | 94 -------------------------------------- src/parser/diningParser.ts | 2 +- src/server.ts | 4 +- 3 files changed, 2 insertions(+), 98 deletions(-) delete mode 100644 src/manualLocations.ts diff --git a/src/manualLocations.ts b/src/manualLocations.ts deleted file mode 100644 index 3cc0262..0000000 --- a/src/manualLocations.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { ILocation } from "types"; - -export const manualLocations: ILocation[] = [ - { - conceptId: 9998, - name: "Subway", - shortDescription: - "Off-Campus, Flex-only location serving sandwiches, wraps, and salads.", - description: - "Casual counter-serve chain for build-your-own sandwiches & salads, with health-conscious options. Accepts CMU Flex and DineX dollars", - url: "https://www.subway.com/en-us/", - location: "Off-Campus", - menu: "https://www.subway.com/en-us/menunutrition/menu", - coordinates: { lat: 40.44468, lng: -79.94888 }, - acceptsOnlineOrders: false, - times: [ - { - start: { day: 0, hour: 9, minute: 0 }, - end: { day: 0, hour: 22, minute: 0 }, - }, - { - start: { day: 1, hour: 7, minute: 0 }, - end: { day: 1, hour: 23, minute: 0 }, - }, - { - start: { day: 2, hour: 7, minute: 0 }, - end: { day: 2, hour: 23, minute: 0 }, - }, - { - start: { day: 3, hour: 7, minute: 0 }, - end: { day: 3, hour: 23, minute: 0 }, - }, - { - start: { day: 4, hour: 7, minute: 0 }, - end: { day: 4, hour: 23, minute: 0 }, - }, - { - start: { day: 5, hour: 7, minute: 0 }, - end: { day: 5, hour: 23, minute: 0 }, - }, - { - start: { day: 6, hour: 9, minute: 0 }, - end: { day: 6, hour: 23, minute: 0 }, - }, - ], - todaysSpecials: [], - todaysSoups: [], - }, - { - conceptId: 9999, - name: "Vocelli Pizza", - shortDescription: - "Off-Campus, Flex-only location serving pizza and Italian dishes. Delivers to on-campus locations.", - description: - "Pittsburgh-based chain serving artisanal pizzas & other Italian sandwiches & salads. Accepts CMU Flex and DineX dollars and delivers to on-campus locations.", - url: "https://www.vocellipizza.com/", - location: "Off-Campus", - menu: "https://www.vocellipizza.com/menu", - coordinates: { lat: 40.454081, lng: -79.948794 }, - acceptsOnlineOrders: true, - times: [ - { - start: { day: 0, hour: 11, minute: 0 }, - end: { day: 0, hour: 22, minute: 0 }, - }, - { - start: { day: 1, hour: 11, minute: 0 }, - end: { day: 1, hour: 22, minute: 0 }, - }, - { - start: { day: 2, hour: 11, minute: 0 }, - end: { day: 2, hour: 22, minute: 0 }, - }, - { - start: { day: 3, hour: 11, minute: 0 }, - end: { day: 3, hour: 22, minute: 0 }, - }, - { - start: { day: 4, hour: 11, minute: 0 }, - end: { day: 5, hour: 0, minute: 0 }, - }, - { - start: { day: 5, hour: 11, minute: 0 }, - end: { day: 6, hour: 2, minute: 0 }, - }, - { - start: { day: 6, hour: 11, minute: 0 }, - end: { day: 0, hour: 2, minute: 0 }, - }, - ], - todaysSpecials: [], - todaysSoups: [], - }, -]; diff --git a/src/parser/diningParser.ts b/src/parser/diningParser.ts index a220a46..d966381 100644 --- a/src/parser/diningParser.ts +++ b/src/parser/diningParser.ts @@ -32,7 +32,7 @@ export default class DiningParser { builder.overwriteLocationCoordinates(locationCoordinateOverwrites); } - return [...locationBuilders.map((builder) => builder.build())]; + return locationBuilders.map((builder) => builder.build()); } private async initializeLocationBuildersFromMainPage(): Promise< diff --git a/src/server.ts b/src/server.ts index 99801e0..0a27d48 100644 --- a/src/server.ts +++ b/src/server.ts @@ -8,7 +8,6 @@ import { node } from "@elysiajs/node"; import { getDiffsBetweenLocationData } from "utils/diff"; import { getEmails } from "./db"; import LocationMerger from "utils/locationMerger"; -import { manualLocations } from "manualLocations"; let cachedLocations: ILocation[] = []; @@ -28,8 +27,7 @@ async function reload(): Promise { const locations = await parser.process(); locations.forEach((location) => locationMerger.addLocation(location)); } - let finalLocations = locationMerger.getMostFrequentLocations(); - finalLocations = [...finalLocations, ...manualLocations]; + const finalLocations = locationMerger.getMostFrequentLocations(); if (finalLocations.length === 0) { notifySlack(" No data scraped! Skipping"); } else { From 12cbaf67bf5fbda577c620c9e5223519adfb375c Mon Sep 17 00:00:00 2001 From: Laasya Aki <101072841+laasyaaki@users.noreply.github.com> Date: Sun, 3 Aug 2025 15:52:09 -0500 Subject: [PATCH 06/65] Revert "Manually added off campus flex only locations" --- src/manualLocations.ts | 94 -------------------------------------- src/parser/diningParser.ts | 2 +- src/server.ts | 4 +- 3 files changed, 2 insertions(+), 98 deletions(-) delete mode 100644 src/manualLocations.ts diff --git a/src/manualLocations.ts b/src/manualLocations.ts deleted file mode 100644 index 3cc0262..0000000 --- a/src/manualLocations.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { ILocation } from "types"; - -export const manualLocations: ILocation[] = [ - { - conceptId: 9998, - name: "Subway", - shortDescription: - "Off-Campus, Flex-only location serving sandwiches, wraps, and salads.", - description: - "Casual counter-serve chain for build-your-own sandwiches & salads, with health-conscious options. Accepts CMU Flex and DineX dollars", - url: "https://www.subway.com/en-us/", - location: "Off-Campus", - menu: "https://www.subway.com/en-us/menunutrition/menu", - coordinates: { lat: 40.44468, lng: -79.94888 }, - acceptsOnlineOrders: false, - times: [ - { - start: { day: 0, hour: 9, minute: 0 }, - end: { day: 0, hour: 22, minute: 0 }, - }, - { - start: { day: 1, hour: 7, minute: 0 }, - end: { day: 1, hour: 23, minute: 0 }, - }, - { - start: { day: 2, hour: 7, minute: 0 }, - end: { day: 2, hour: 23, minute: 0 }, - }, - { - start: { day: 3, hour: 7, minute: 0 }, - end: { day: 3, hour: 23, minute: 0 }, - }, - { - start: { day: 4, hour: 7, minute: 0 }, - end: { day: 4, hour: 23, minute: 0 }, - }, - { - start: { day: 5, hour: 7, minute: 0 }, - end: { day: 5, hour: 23, minute: 0 }, - }, - { - start: { day: 6, hour: 9, minute: 0 }, - end: { day: 6, hour: 23, minute: 0 }, - }, - ], - todaysSpecials: [], - todaysSoups: [], - }, - { - conceptId: 9999, - name: "Vocelli Pizza", - shortDescription: - "Off-Campus, Flex-only location serving pizza and Italian dishes. Delivers to on-campus locations.", - description: - "Pittsburgh-based chain serving artisanal pizzas & other Italian sandwiches & salads. Accepts CMU Flex and DineX dollars and delivers to on-campus locations.", - url: "https://www.vocellipizza.com/", - location: "Off-Campus", - menu: "https://www.vocellipizza.com/menu", - coordinates: { lat: 40.454081, lng: -79.948794 }, - acceptsOnlineOrders: true, - times: [ - { - start: { day: 0, hour: 11, minute: 0 }, - end: { day: 0, hour: 22, minute: 0 }, - }, - { - start: { day: 1, hour: 11, minute: 0 }, - end: { day: 1, hour: 22, minute: 0 }, - }, - { - start: { day: 2, hour: 11, minute: 0 }, - end: { day: 2, hour: 22, minute: 0 }, - }, - { - start: { day: 3, hour: 11, minute: 0 }, - end: { day: 3, hour: 22, minute: 0 }, - }, - { - start: { day: 4, hour: 11, minute: 0 }, - end: { day: 5, hour: 0, minute: 0 }, - }, - { - start: { day: 5, hour: 11, minute: 0 }, - end: { day: 6, hour: 2, minute: 0 }, - }, - { - start: { day: 6, hour: 11, minute: 0 }, - end: { day: 0, hour: 2, minute: 0 }, - }, - ], - todaysSpecials: [], - todaysSoups: [], - }, -]; diff --git a/src/parser/diningParser.ts b/src/parser/diningParser.ts index a220a46..d966381 100644 --- a/src/parser/diningParser.ts +++ b/src/parser/diningParser.ts @@ -32,7 +32,7 @@ export default class DiningParser { builder.overwriteLocationCoordinates(locationCoordinateOverwrites); } - return [...locationBuilders.map((builder) => builder.build())]; + return locationBuilders.map((builder) => builder.build()); } private async initializeLocationBuildersFromMainPage(): Promise< diff --git a/src/server.ts b/src/server.ts index 99801e0..0a27d48 100644 --- a/src/server.ts +++ b/src/server.ts @@ -8,7 +8,6 @@ import { node } from "@elysiajs/node"; import { getDiffsBetweenLocationData } from "utils/diff"; import { getEmails } from "./db"; import LocationMerger from "utils/locationMerger"; -import { manualLocations } from "manualLocations"; let cachedLocations: ILocation[] = []; @@ -28,8 +27,7 @@ async function reload(): Promise { const locations = await parser.process(); locations.forEach((location) => locationMerger.addLocation(location)); } - let finalLocations = locationMerger.getMostFrequentLocations(); - finalLocations = [...finalLocations, ...manualLocations]; + const finalLocations = locationMerger.getMostFrequentLocations(); if (finalLocations.length === 0) { notifySlack(" No data scraped! Skipping"); } else { From b41a6eb42b5b1e0645654aaaed21411889086918 Mon Sep 17 00:00:00 2001 From: Eric Xu Date: Mon, 11 Aug 2025 11:22:01 -0400 Subject: [PATCH 07/65] fix: coalesce intervals if gap is 1 min, and do wrap-around coalescing (#190) * fix: coalesce intervals if gap is 1 min, and do wrap-around coalescing * fix: unparseable tokens should be ignored * chore: remove extraneous comment * fix: redo the merging algo --- src/containers/timeBuilder.ts | 25 +++-- src/types.ts | 6 +- src/utils/diff.ts | 2 +- src/utils/timeUtils.ts | 52 ++++++--- tests/integration.test.ts | 199 +++++++++++++++++++++++++++++++++- 5 files changed, 255 insertions(+), 29 deletions(-) diff --git a/src/containers/timeBuilder.ts b/src/containers/timeBuilder.ts index 2f14cb8..8f04347 100644 --- a/src/containers/timeBuilder.ts +++ b/src/containers/timeBuilder.ts @@ -28,8 +28,7 @@ export function getTimeRangesFromString(rowHTML: Element) { } function getTimeAttributesFromRow(rowHTML: Element) { - const { day, date, timeSlots } = tokenizeTimeRow(rowHTML); - return getTimeInfoWithRawAttributes([day, date, ...timeSlots]); + return getTimeInfoWithRawAttributes(tokenizeTimeRow(rowHTML)); } function tokenizeTimeRow(rowHTML: Element) { @@ -37,12 +36,13 @@ function tokenizeTimeRow(rowHTML: Element) { let day = $("strong").text(); const dataStr = $.text().replace(/\s\s+/g, " ").replace(day, "").trim(); let [date, time] = dataStr.split(/,(.+)/); + if (date === undefined || time === undefined) return []; day = (day.charAt(0).toUpperCase() + day.slice(1).toLowerCase()).trim(); date = (date.charAt(0).toUpperCase() + date.slice(1).toLowerCase()).trim(); time = time.toUpperCase().trim(); const timeSlots = time.split(/[,;]/).map((slot) => slot.trim()); - return { day, date, timeSlots }; + return [day, date, ...timeSlots]; } function getTimeInfoWithRawAttributes(tokens: string[]) { @@ -92,6 +92,13 @@ function resolveAttributeConflicts( closed: input.closed, }; } + if (input.twentyFour) { + return { + day: input.day, + date: input.date, + times: [{ start: { hour: 0, minute: 0 }, end: { hour: 23, minute: 59 } }], + }; + } if (input.times && input.times.length > 0) { return { day: input.day, @@ -102,18 +109,22 @@ function resolveAttributeConflicts( return { day: input.day, date: input.date, - times: [{ start: { hour: 0, minute: 0 }, end: { hour: 23, minute: 59 } }], + times: [], }; } function getTimeRangesFromTimeRow(time: ITimeRowAttributes) { if (time.day === undefined) { - throw new Error("Cannot convert when day is not set"); + notifySlack( + ` Cannot convert time attribute: ${JSON.stringify( + time + )} since day is not set` + ); + return []; } const allRanges: ITimeRange[] = []; for (const range of time.times ?? []) { - rollBack12AmEndTime(range); - + rollBack12AmEndTime(range); // not sure why this was added, but it doesn't hurt I guess (I suppose the only case this actively helps is if the time string is 12:00 AM - 12:00 AM) const shouldSpillToNextDay = range.start.hour * 60 + range.start.minute > range.end.hour * 60 + range.end.minute; diff --git a/src/types.ts b/src/types.ts index f2624ec..0a5cef5 100644 --- a/src/types.ts +++ b/src/types.ts @@ -17,15 +17,15 @@ export interface ISpecial { description: string; } -export interface ITimeMoment { +export interface ITimeSlot { day: DayOfTheWeek; hour: number; minute: number; } export interface ITimeRange { - start: ITimeMoment; - end: ITimeMoment; + start: ITimeSlot; + end: ITimeSlot; } export interface ICoordinate { lat: number; diff --git a/src/utils/diff.ts b/src/utils/diff.ts index abc0865..50f992e 100644 --- a/src/utils/diff.ts +++ b/src/utils/diff.ts @@ -4,7 +4,7 @@ type T = { [key: string]: T } | T[] | string | number | undefined; export function getObjDiffs( prevObject: T, newObject: T, - path: string = "~" + path: string = "~" // ~uwu senpai ): string[] { let diffs: string[] = []; if (typeof prevObject === "object" && typeof newObject === "object") { diff --git a/src/utils/timeUtils.ts b/src/utils/timeUtils.ts index ef4f793..beccb11 100644 --- a/src/utils/timeUtils.ts +++ b/src/utils/timeUtils.ts @@ -1,4 +1,4 @@ -import { DayOfTheWeek, ITimeMoment, ITimeRange } from "types"; +import { DayOfTheWeek, ITimeSlot, ITimeRange } from "types"; export function getNextDay(day: DayOfTheWeek): DayOfTheWeek { const weekdays: DayOfTheWeek[] = [ @@ -13,42 +13,66 @@ export function getNextDay(day: DayOfTheWeek): DayOfTheWeek { return weekdays[(weekdays.indexOf(day) + 1) % 7]; } -export function getMinutesSinceStartOfSunday(timeMoment: ITimeMoment) { - return timeMoment.day * (24 * 60) + timeMoment.hour * 60 + timeMoment.minute; +export function getMinutesSinceStartOfSunday(timeSlot: ITimeSlot) { + return timeSlot.day * (24 * 60) + timeSlot.hour * 60 + timeSlot.minute; } /** * - * @param moment1 - * @param moment2 + * @param timeSlot1 + * @param timeSlot2 * @returns Delta in minutes of moment1 - moment2 */ -export function compareTimeMoments(moment1: ITimeMoment, moment2: ITimeMoment) { +export function compareTimeSlots(timeSlot1: ITimeSlot, timeSlot2: ITimeSlot) { return ( - getMinutesSinceStartOfSunday(moment1) - - getMinutesSinceStartOfSunday(moment2) + getMinutesSinceStartOfSunday(timeSlot1) - + getMinutesSinceStartOfSunday(timeSlot2) ); } export function sortAndMergeTimeRanges(timeRanges: ITimeRange[]) { - timeRanges.sort((range1, range2) => - compareTimeMoments(range1.start, range2.start) - ); + const MINUTES_IN_A_WEEK = 60 * 24 * 7; + const unwrappedTimeRanges = timeRanges + .flatMap((rng) => { + if (compareTimeSlots(rng.start, rng.end) > 0) { + // unwrap the wrapped interval + return [ + { start: { day: 0, hour: 0, minute: 0 }, end: rng.end }, + { start: rng.start, end: { day: 6, hour: 23, minute: 59 } }, + ]; + } else { + return [rng]; + } + }) + .sort((range1, range2) => compareTimeSlots(range1.start, range2.start)); const mergedRanges: ITimeRange[] = []; - for (const timeRange of timeRanges) { + for (const timeRange of unwrappedTimeRanges) { const lastTimeRange = mergedRanges.length ? mergedRanges[mergedRanges.length - 1] : undefined; if ( lastTimeRange && - compareTimeMoments(lastTimeRange.end, timeRange.start) >= 0 + compareTimeSlots(lastTimeRange.end, timeRange.start) >= -1 // we overlap 1-minute disjoint intervals as well (ex. 2:00 PM - 2:59 PM will get merged with 3:00PM - 4:00PM as 2:00PM - 4:00PM) ) { - if (compareTimeMoments(timeRange.end, lastTimeRange.end) > 0) { + if (compareTimeSlots(timeRange.end, lastTimeRange.end) > 0) { lastTimeRange.end = timeRange.end; // join current range with last range } } else { mergedRanges.push(timeRange); } } + // merge the last day with the first day if needed + if (mergedRanges.length >= 2) { + const lastRange = mergedRanges[mergedRanges.length - 1]; + const firstRange = mergedRanges[0]; + if ( + getMinutesSinceStartOfSunday(lastRange.end) === MINUTES_IN_A_WEEK - 1 && + getMinutesSinceStartOfSunday(firstRange.start) === 0 + ) { + lastRange.end = firstRange.end; + mergedRanges.shift(); + } + } + return mergedRanges; } diff --git a/tests/integration.test.ts b/tests/integration.test.ts index 3008e83..304f6ff 100644 --- a/tests/integration.test.ts +++ b/tests/integration.test.ts @@ -84,11 +84,160 @@ describe("time edge cases", () => { [Thur]: "24 hRs", [Fri]: "24 hours", }); + await queryParserAndAssertTimingsCorrect([[Tue, 0, 0, Fri, 23, 59]]); + }); + test("all day every day", async () => { + setUpTimingTest({ + [Mon]: "OPEN 24 HOURS", + [Tue]: "OPEN 24 HOURS", + [Wed]: "OPEN 24 HRS", + [Thur]: "24 hRs", + [Fri]: "24 hours", + [Sat]: "24 hours", + [Sun]: "24 hours", + }); + await queryParserAndAssertTimingsCorrect([[Sun, 0, 0, Sat, 23, 59]]); // this is the reason why we leave the other return type that represents open every day as this. (backwards compatibility, mostly) + }); + test("all day every day but slightly different", async () => { + setUpTimingTest({ + [Mon]: "OPEN 24 HOURS", + [Tue]: "OPEN 24 HOURS", + [Wed]: "OPEN 24 HRS", + [Thur]: "12:00 AM - 11:59 PM", + [Fri]: "24 hours", + [Sat]: "12:00 AM - 2:59 AM, 3:00 AM - 11:59 PM", + [Sun]: "24 hours", + }); + await queryParserAndAssertTimingsCorrect([[Sun, 0, 0, Sat, 23, 59]]); + }); + test("empty string", async () => { + setUpTimingTest({ + [Mon]: "OPEN 24 HOURS", + [Tue]: "", + [Wed]: "", + [Thur]: "", + [Fri]: "", + [Sat]: "", + [Sun]: "24 hours", + }); + await queryParserAndAssertTimingsCorrect([[Sun, 0, 0, Mon, 23, 59]]); + }); + test("loop-back time coalescing (wrapping on saturday, but it overlaps with sunday)", async () => { + setUpTimingTest({ + [Mon]: "", + [Tue]: "", + [Wed]: "", + [Thur]: "", + [Fri]: "", + [Sat]: "7:00 AM - 2:00 AM", + [Sun]: "1:00 AM - 5:00 PM", // sunday is represented as 0 + }); + await queryParserAndAssertTimingsCorrect([[Sat, 7, 0, Sun, 17, 0]]); + }); + test("loop-back time coalescing (wrapping on saturday, but it overlaps with multiple ranges on sunday)", async () => { + setUpTimingTest({ + [Mon]: "", + [Tue]: "", + [Wed]: "", + [Thur]: "", + [Fri]: "", + [Sat]: "7:00 AM - 2:00 AM", + [Sun]: "12:00AM - 12:35 AM, 1:00 AM - 5:00 PM", // sunday is represented as 0 + }); + await queryParserAndAssertTimingsCorrect([[Sat, 7, 0, Sun, 17, 0]]); + }); + test("open all week, gone wrong", async () => { + setUpTimingTest({ + [Sun]: "OPEN 24 HOURS", + [Mon]: "OPEN 24 HOURS", + [Tue]: "OPEN 24 HOURS", + [Wed]: "OPEN 24 HOURS", + [Thur]: "OPEN 24 HOURS", + [Fri]: "OPEN 24 HOURS", + [Sat]: "12:00 AM - 10:00 AM, 9:00 AM - 2:00 AM", + }); + await queryParserAndAssertTimingsCorrect([[Sun, 0, 0, Sat, 23, 59]]); // this should be the default return value if it's open all week + }); + test("open all week, gone wrong", async () => { + setUpTimingTest({ + [Sun]: "12:00 AM - 12:05 AM, 12:10 AM - 11:59 PM", + [Mon]: "OPEN 24 HOURS", + [Tue]: "OPEN 24 HOURS", + [Wed]: "OPEN 24 HOURS", + [Thur]: "OPEN 24 HOURS", + [Fri]: "OPEN 24 HOURS", + [Sat]: "12:00 AM - 10:00 AM, 9:00 AM - 2:00 AM", + }); + await queryParserAndAssertTimingsCorrect([[Sun, 0, 0, Sat, 23, 59]]); // this should be the default return value if it's open all week + }); + test("wrapping on thursday, but it overlaps with friday", async () => { + setUpTimingTest({ + [Mon]: "", + [Tue]: "", + [Wed]: "", + [Thur]: "7:00 AM - 2:00 AM", + [Fri]: "1:00 AM - 5:00 PM", + [Sat]: "", + [Sun]: "", + }); + await queryParserAndAssertTimingsCorrect([[Thur, 7, 0, Fri, 17, 0]]); + }); + test("some combination of wrap-around", async () => { + setUpTimingTest({ + [Mon]: "", + [Tue]: "", + [Wed]: "open 24 hours", + [Thur]: "7:00 AM - 2:00 AM", + [Fri]: "1:00 AM - 5:00 PM", + [Sat]: "", + [Sun]: "open 24 hours", + }); await queryParserAndAssertTimingsCorrect([ - [Tue, 0, 0, Tue, 23, 59], + [Sun, 0, 0, Sun, 23, 59], [Wed, 0, 0, Wed, 23, 59], - [Thur, 0, 0, Thur, 23, 59], - [Fri, 0, 0, Fri, 23, 59], + [Thur, 7, 0, Fri, 17, 0], + ]); + }); + test("open nearly all week, but Dining Services has truly lost it", async () => { + setUpTimingTest({ + [Sun]: "12:00 AM - 12:05 AM, 9:00 AM - 11:59 PM", + [Mon]: "12:00 AM - 3:05 AM, 3:00 AM - 2:00 AM", + [Tue]: "1:00 AM - 9:00 PM, 9:01 PM - 12:00 AM, 12:00 AM - 3:00 PM", + [Wed]: "OPEN 24 HOURS", + [Thur]: "OPEN 24 HOURS", + [Fri]: "OPEN 24 HOURS, mooo", + [Sat]: "12:00 AM - 10:00 AM, 9:00 AM - 7:05 AM", + }); + await queryParserAndAssertTimingsCorrect([[Sun, 9, 0, Sun, 7, 5]]); + }); // tests literally everything + test("open nearly all week, but Dining Services has truly lost it", async () => { + setUpTimingTest({ + [Sun]: "12:05 AM - 12:10 AM, 9:00 AM - 11:59 PM", + [Mon]: "12:00 AM - 3:05 AM, 3:00 AM - 2:00 AM", + [Tue]: "1:00 AM - 9:00 PM, 9:01 PM - 12:00 AM, 12:00 AM - 3:00 PM", + [Wed]: "OPEN 24 HOURS", + [Thur]: "OPEN 24 HOURS", + [Fri]: "OPEN 24 HOURS, mooo", + [Sat]: "12:00 AM - 10:00 AM, 9:00 AM - 12:02 AM", + }); + await queryParserAndAssertTimingsCorrect([ + [Sun, 0, 5, Sun, 0, 10], + [Sun, 9, 0, Sun, 0, 2], + ]); + }); // tests literally everything + test("degenerate open times", async () => { + setUpTimingTest({ + [Mon]: "", + [Tue]: "", + [Wed]: "", + [Thur]: "2:00 AM - 2:00 AM", + [Fri]: "1:00 AM - 1:00 AM", + [Sat]: "", + [Sun]: "", + }); + await queryParserAndAssertTimingsCorrect([ + [Thur, 2, 0, Thur, 2, 0], + [Fri, 1, 0, Fri, 1, 0], ]); }); test("single time", async () => { @@ -136,7 +285,7 @@ describe("time edge cases", () => { [Sat, 16, 0, Sat, 21, 0], ]); }); - test("12AM", async () => { + test("12AM (tests the 12:00 AM -> 11:59 PM shift)", async () => { setUpTimingTest({ [Mon]: "12:00 AM - 12:00 AM", [Tue]: "2:00 AM - 12:00 AM", @@ -182,4 +331,46 @@ describe("time edge cases", () => { [Fri, 19, 0, Fri, 23, 59], ]); }); + test("partial all day", async () => { + setUpTimingTest({ + [Wed]: "open 24 hours", + [Thur]: "open 24 hours", + [Fri]: "open 24 hours", + }); + await queryParserAndAssertTimingsCorrect([[Wed, 0, 0, Fri, 23, 59]]); + }); + test("partial all day, over the weekend", async () => { + setUpTimingTest({ + [Sat]: "open 24 hours", + [Sun]: "open 24 hours", + [Mon]: "open 24 hours", + }); + await queryParserAndAssertTimingsCorrect([[Sat, 0, 0, Mon, 23, 59]]); + }); + test("partial all day, over the weekend", async () => { + setUpTimingTest({ + [Sat]: "7:00 AM - 12:01 AM", + [Sun]: "open 24 hours", + [Mon]: "open 24 hours", + }); + await queryParserAndAssertTimingsCorrect([[Sat, 7, 0, Mon, 23, 59]]); + }); + test("another one", async () => { + setUpTimingTest({ + [Sat]: "7:00 AM - 12:01 AM", + }); + await queryParserAndAssertTimingsCorrect([[Sat, 7, 0, Sun, 0, 1]]); + }); + test("unparseable token", async () => { + setUpTimingTest({ + [Mon]: "mooooo", + }); + await queryParserAndAssertTimingsCorrect([]); + }); + test("24 hours should override other times", async () => { + setUpTimingTest({ + [Mon]: "OPEN 24 HOURS, 2:00 AM - 3:00 AM", + }); + await queryParserAndAssertTimingsCorrect([[Mon, 0, 0, Mon, 23, 59]]); + }); }); From 510cd179cb69cb28bc3c9736be788e26022e2196 Mon Sep 17 00:00:00 2001 From: JackHurew Date: Tue, 12 Aug 2025 06:25:58 -0400 Subject: [PATCH 08/65] SQL dashboard phase 2 completed-basic ability to overread scrapes with our own information. --- src/containers/locationBuilder.ts | 14 ++++++++++++ src/containers/overrides.ts | 21 +++++++++++++++++ src/db.ts | 38 +++++++++++++++++++++++++++++++ src/parser/diningParser.ts | 10 ++++++++ src/server.ts | 3 +++ 5 files changed, 86 insertions(+) create mode 100644 src/containers/overrides.ts diff --git a/src/containers/locationBuilder.ts b/src/containers/locationBuilder.ts index 2aa5c92..e01a193 100644 --- a/src/containers/locationBuilder.ts +++ b/src/containers/locationBuilder.ts @@ -10,6 +10,7 @@ import { ITimeRange, } from "../types"; import { sortAndMergeTimeRanges } from "utils/timeUtils"; +import { ChangeOverride } from "./overrides"; /** * For building the location data structure @@ -140,4 +141,17 @@ export default class LocationBuilder { todaysSoups: this.soups, }; } + + applyOverride(override: ChangeOverride) { + // Only apply if conceptId matches (optional guard) + if (this.conceptId !== override.conceptid) return; + if (override.name !== null) this.name = override.name; + if (override.description !== null) this.description = override.description; + if (override.shortdescription !== null) + this.shortDescription = override.shortdescription; + if (override.times !== null) this.times = override.times; + if (override.menu !== null) this.menu = override.menu; + if (override.accepts_online_orders !== null) + this.acceptsOnlineOrders = override.accepts_online_orders; + } } diff --git a/src/containers/overrides.ts b/src/containers/overrides.ts new file mode 100644 index 0000000..e366efb --- /dev/null +++ b/src/containers/overrides.ts @@ -0,0 +1,21 @@ +import type { ITimeRange } from "types"; + +/* +Separated from locationBuilder for cleanliness. +- Format used to export changes from SQL database to concept cards +- go to db.ts to change SQL export command ("getChanges(...)") +- go to locationBuilder.ts to change manner of overrides ("applyOverride(...)") + -Currently, if it detects anything other than null + -it gets overridden completely +- go to diningParser.ts to change way it is being parsed +*/ + +export interface ChangeOverride { + conceptid: number; + name?: string; + description?: string; + shortdescription?: string; + times?: ITimeRange[]; + menu?: string; + accepts_online_orders?: boolean; +} diff --git a/src/db.ts b/src/db.ts index 4b4cc8c..6153d76 100644 --- a/src/db.ts +++ b/src/db.ts @@ -2,6 +2,7 @@ import { Pool } from "pg"; import "dotenv/config"; import { env } from "env"; +import { ITimeRange } from "types"; let pool: Pool | null = null; @@ -24,3 +25,40 @@ export async function getEmails(): Promise<{ name: string; email: string }[]> { email: row.email.replace(/^mailto:/, ""), })); } + +export async function getChanges(): Promise< + { + conceptid: number; + name: string; + description: string; + shortdescription: string; + times: ITimeRange[]; + menu: string; + accepts_online_orders: boolean; + }[] +> { + const dbPool = getPool(); + const result = await dbPool.query( + "select conceptid, name, description, shortdescription,times,menu,accepts_online_orders from dashboard_changes" + ); + // Remove 'mailto:' if present + return result.rows.map( + (row: { + conceptid: number; + name: string; + description: string; + shortdescription: string; + times: ITimeRange[]; + menu: string; + accepts_online_orders: boolean; + }) => ({ + conceptid: row.conceptid, + name: row.name, + description: row.description, + shortdescription: row.shortdescription, + times: row.times, + menu: row.menu, + accepts_online_orders: row.accepts_online_orders, + }) + ); +} diff --git a/src/parser/diningParser.ts b/src/parser/diningParser.ts index d966381..547fa76 100644 --- a/src/parser/diningParser.ts +++ b/src/parser/diningParser.ts @@ -4,6 +4,7 @@ import LocationBuilder from "../containers/locationBuilder"; import { retrieveSpecials } from "../containers/specials/specialsBuilder"; import { ILocation, ISpecial } from "types"; import locationCoordinateOverwrites from "overwrites/locationCoordinateOverwrites"; +import { getChanges } from "../db"; /** * Retrieves the HTML from the CMU Dining website and parses the information @@ -23,6 +24,8 @@ export default class DiningParser { const locationBuilders = await this.initializeLocationBuildersFromMainPage(); + const overrides = await getChanges(); + const [specials, soups] = await this.fetchSpecials(); for (const builder of locationBuilders) { @@ -30,6 +33,13 @@ export default class DiningParser { builder.setSoup(soups); builder.setSpecials(specials); builder.overwriteLocationCoordinates(locationCoordinateOverwrites); + + const override = overrides.find( + (o) => o.conceptid === builder.getConceptId() + ); + if (override) { + builder.applyOverride(override); + } } return locationBuilders.map((builder) => builder.build()); diff --git a/src/server.ts b/src/server.ts index 0a27d48..7542670 100644 --- a/src/server.ts +++ b/src/server.ts @@ -7,6 +7,7 @@ import { notifySlack } from "utils/slack"; import { node } from "@elysiajs/node"; import { getDiffsBetweenLocationData } from "utils/diff"; import { getEmails } from "./db"; +import { getChanges } from "./db"; import LocationMerger from "utils/locationMerger"; let cachedLocations: ILocation[] = []; @@ -97,6 +98,8 @@ app.get("/locations/time/:day/:hour/:min", ({ params: { day, hour, min } }) => { }); app.get("/api/emails", getEmails); +app.get("/api/changes", getChanges); + app.post( "/api/sendSlackMessage", async ({ body: { message } }) => { From e3437b9f322d2b7b000dcffae07bb3ff9c67b822 Mon Sep 17 00:00:00 2001 From: JackHurew Date: Fri, 15 Aug 2025 20:18:38 -0500 Subject: [PATCH 09/65] Everyone Loves Drizzle --- bun.lock | 228 ++++++++++++++++++++++++++++-- drizzle.config.ts | 11 ++ package.json | 17 ++- src/containers/locationBuilder.ts | 21 ++- src/containers/overrides.ts | 21 --- src/db.ts | 83 +++++------ src/parser/diningParser.ts | 2 +- src/schema.ts | 25 ++++ 8 files changed, 317 insertions(+), 91 deletions(-) create mode 100644 drizzle.config.ts delete mode 100644 src/containers/overrides.ts create mode 100644 src/schema.ts diff --git a/bun.lock b/bun.lock index 9cbb75c..aba6f10 100644 --- a/bun.lock +++ b/bun.lock @@ -4,11 +4,15 @@ "": { "name": "dining-api", "dependencies": { - "@elysiajs/cors": "^1.3.3", - "@elysiajs/node": "^1.3.0", + "@elysiajs/cors": "1.3.3", + "@elysiajs/node": "1.3.0", + "@types/express": "^5.0.3", "axios": "^1.10.0", "cheerio": "^1.0.0-rc.12", - "elysia": "^1.3.5", + "drizzle-kit": "^0.31.4", + "drizzle-orm": "^0.44.4", + "elysia": "1.3.5", + "express": "^5.1.0", "pg": "^8.16.3", "zod": "^3.25.67", }, @@ -20,7 +24,7 @@ "@types/bun": "^1.1.1", "@types/jest": "^29.5.14", "@types/node": "^24.0.14", - "@types/pg": "^8.11.2", + "@types/pg": "^8.15.5", "babel-jest": "^29.7.0", "bun-types": "^1.1.7", "dotenv": "^17.2.0", @@ -254,10 +258,18 @@ "@bcoe/v8-coverage": ["@bcoe/v8-coverage@0.2.3", "", {}, "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw=="], + "@borewit/text-codec": ["@borewit/text-codec@0.1.1", "", {}, "sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA=="], + + "@drizzle-team/brocli": ["@drizzle-team/brocli@0.10.2", "", {}, "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w=="], + "@elysiajs/cors": ["@elysiajs/cors@1.3.3", "", { "peerDependencies": { "elysia": ">= 1.3.0" } }, "sha512-mYIU6PyMM6xIJuj7d27Vt0/wuzVKIEnFPjcvlkyd7t/m9xspAG37cwNjFxVOnyvY43oOd2I/oW2DB85utXpA2Q=="], "@elysiajs/node": ["@elysiajs/node@1.3.0", "", { "dependencies": { "@hono/node-server": "^1.14.3" }, "peerDependencies": { "elysia": ">= 1.3.3" } }, "sha512-AA9rnL/FOBklxtJjFpUDBVZLuiKddDaV1KgnKawHzj5VgN99SzE+V0h1MOhp+8jlo2KQQJO/3aD3upyGgrfohQ=="], + "@esbuild-kit/core-utils": ["@esbuild-kit/core-utils@3.3.2", "", { "dependencies": { "esbuild": "~0.18.20", "source-map-support": "^0.5.21" } }, "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ=="], + + "@esbuild-kit/esm-loader": ["@esbuild-kit/esm-loader@2.6.5", "", { "dependencies": { "@esbuild-kit/core-utils": "^3.3.2", "get-tsconfig": "^4.7.0" } }, "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA=="], + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.7", "", { "os": "aix", "cpu": "ppc64" }, "sha512-uD0kKFHh6ETr8TqEtaAcV+dn/2qnYbH/+8wGEdY70Qf7l1l/jmBUbrmQqwiPKAQE6cOQ7dTj6Xr0HzQDGHyceQ=="], "@esbuild/android-arm": ["@esbuild/android-arm@0.25.7", "", { "os": "android", "cpu": "arm" }, "sha512-Jhuet0g1k9rAJHrXGIh7sFknFuT4sfytYZpZpuZl7YKDhnPByVAm5oy2LEBmMbuYf3ejWVYCc2seX81Mk+madA=="], @@ -310,7 +322,7 @@ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.7", "", { "os": "win32", "cpu": "x64" }, "sha512-FaPsAHTwm+1Gfvn37Eg3E5HIpfR3i6x1AIcla/MkqAIupD4BW3MrSeUqfoTzwwJhk3WE2/KqUn4/eenEJC76VA=="], - "@hono/node-server": ["@hono/node-server@1.14.4", "", { "peerDependencies": { "hono": "^4" } }, "sha512-DnxpshhYewr2q9ZN8ez/M5mmc3sucr8CT1sIgIy1bkeUXut9XWDkqHoFHRhWIQgkYnKpVRxunyhK7WzpJeJ6qQ=="], + "@hono/node-server": ["@hono/node-server@1.18.2", "", { "peerDependencies": { "hono": "^4" } }, "sha512-icgNvC0vRYivzyuSSaUv9ttcwtN8fDyd1k3AOIBDJgYd84tXRZSS6na8X54CY/oYoFTNhEmZraW/Rb9XYwX4KA=="], "@istanbuljs/load-nyc-config": ["@istanbuljs/load-nyc-config@1.1.0", "", { "dependencies": { "camelcase": "^5.3.1", "find-up": "^4.1.0", "get-package-type": "^0.1.0", "js-yaml": "^3.13.1", "resolve-from": "^5.0.0" } }, "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ=="], @@ -398,7 +410,7 @@ "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.45.1", "", { "os": "win32", "cpu": "x64" }, "sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA=="], - "@sinclair/typebox": ["@sinclair/typebox@0.34.37", "", {}, "sha512-2TRuQVgQYfy+EzHRTIvkhv2ADEouJ2xNS/Vq+W5EuuewBdOrvATvljZTxHWZSTYr2sTjTHpGvucaGAt67S2akw=="], + "@sinclair/typebox": ["@sinclair/typebox@0.34.38", "", {}, "sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA=="], "@sinonjs/commons": ["@sinonjs/commons@3.0.1", "", { "dependencies": { "type-detect": "4.0.8" } }, "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ=="], @@ -416,12 +428,22 @@ "@types/babel__traverse": ["@types/babel__traverse@7.20.6", "", { "dependencies": { "@babel/types": "^7.20.7" } }, "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg=="], + "@types/body-parser": ["@types/body-parser@1.19.6", "", { "dependencies": { "@types/connect": "*", "@types/node": "*" } }, "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g=="], + "@types/bun": ["@types/bun@1.2.2", "", { "dependencies": { "bun-types": "1.2.2" } }, "sha512-tr74gdku+AEDN5ergNiBnplr7hpDp3V1h7fqI2GcR/rsUaM39jpSeKH0TFibRvU0KwniRx5POgaYnaXbk0hU+w=="], + "@types/connect": ["@types/connect@3.4.38", "", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="], + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + "@types/express": ["@types/express@5.0.3", "", { "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^5.0.0", "@types/serve-static": "*" } }, "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw=="], + + "@types/express-serve-static-core": ["@types/express-serve-static-core@5.0.7", "", { "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, "sha512-R+33OsgWw7rOhD1emjU7dzCDHucJrgJXMA5PYCzJxVil0dsyx5iBEPHqpPfiKNJQb7lZ1vxwoLR4Z87bBUpeGQ=="], + "@types/graceful-fs": ["@types/graceful-fs@4.1.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ=="], + "@types/http-errors": ["@types/http-errors@2.0.5", "", {}, "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg=="], + "@types/istanbul-lib-coverage": ["@types/istanbul-lib-coverage@2.0.6", "", {}, "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w=="], "@types/istanbul-lib-report": ["@types/istanbul-lib-report@3.0.3", "", { "dependencies": { "@types/istanbul-lib-coverage": "*" } }, "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA=="], @@ -430,9 +452,19 @@ "@types/jest": ["@types/jest@29.5.14", "", { "dependencies": { "expect": "^29.0.0", "pretty-format": "^29.0.0" } }, "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ=="], + "@types/mime": ["@types/mime@1.3.5", "", {}, "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w=="], + "@types/node": ["@types/node@24.0.14", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-4zXMWD91vBLGRtHK3YbIoFMia+1nqEz72coM42C5ETjnNCa/heoj7NT1G67iAfOqMmcfhuCZ4uNpyz8EjlAejw=="], - "@types/pg": ["@types/pg@8.15.4", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-I6UNVBAoYbvuWkkU3oosC8yxqH21f4/Jc4DK71JLG3dT2mdlGe1z+ep/LQGXaKaOgcvUrsQoPRqfgtMcvZiJhg=="], + "@types/pg": ["@types/pg@8.15.5", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-LF7lF6zWEKxuT3/OR8wAZGzkg4ENGXFNyiV/JeOt9z5B+0ZVwbql9McqX5c/WStFq1GaGso7H1AzP/qSzmlCKQ=="], + + "@types/qs": ["@types/qs@6.14.0", "", {}, "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ=="], + + "@types/range-parser": ["@types/range-parser@1.2.7", "", {}, "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ=="], + + "@types/send": ["@types/send@0.17.5", "", { "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w=="], + + "@types/serve-static": ["@types/serve-static@1.15.8", "", { "dependencies": { "@types/http-errors": "*", "@types/node": "*", "@types/send": "*" } }, "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg=="], "@types/stack-utils": ["@types/stack-utils@2.0.3", "", {}, "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw=="], @@ -442,6 +474,8 @@ "@types/yargs-parser": ["@types/yargs-parser@21.0.3", "", {}, "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ=="], + "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], + "ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="], "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], @@ -476,6 +510,8 @@ "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + "body-parser": ["body-parser@2.2.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.0", "http-errors": "^2.0.0", "iconv-lite": "^0.6.3", "on-finished": "^2.4.1", "qs": "^6.14.0", "raw-body": "^3.0.0", "type-is": "^2.0.0" } }, "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg=="], + "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="], "brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="], @@ -492,6 +528,12 @@ "bun-types": ["bun-types@1.2.2", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-RCbMH5elr9gjgDGDhkTTugA21XtJAy/9jkKe/G3WR2q17VPGhcquf9Sir6uay9iW+7P/BV0CAHA1XlHXMAVKHg=="], + "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], "camelcase": ["camelcase@5.3.1", "", {}, "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="], @@ -524,10 +566,16 @@ "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + "content-disposition": ["content-disposition@1.0.0", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg=="], + + "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], "cookie": ["cookie@1.0.2", "", {}, "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="], + "cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="], + "core-js-compat": ["core-js-compat@3.40.0", "", { "dependencies": { "browserslist": "^4.24.3" } }, "sha512-0XEDpr5y5mijvw8Lbc6E5AkjrHfp7eEoPlu36SWeAbcL8fn1G1ANe8DBlo2XoNN89oVpxWwOjYIPVzR4ZvsKCQ=="], "create-jest": ["create-jest@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "chalk": "^4.0.0", "exit": "^0.1.2", "graceful-fs": "^4.2.9", "jest-config": "^29.7.0", "jest-util": "^29.7.0", "prompts": "^2.0.1" }, "bin": { "create-jest": "bin/create-jest.js" } }, "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q=="], @@ -546,6 +594,8 @@ "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], + "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], + "detect-newline": ["detect-newline@3.1.0", "", {}, "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA=="], "diff-sequences": ["diff-sequences@29.6.3", "", {}, "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q=="], @@ -564,6 +614,14 @@ "dotenv-expand": ["dotenv-expand@10.0.0", "", {}, "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A=="], + "drizzle-kit": ["drizzle-kit@0.31.4", "", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.25.4", "esbuild-register": "^3.5.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-tCPWVZWZqWVx2XUsVpJRnH9Mx0ClVOf5YUHerZ5so1OKSlqww4zy1R5ksEdGRcO3tM3zj0PYN6V48TbQCL1RfA=="], + + "drizzle-orm": ["drizzle-orm@0.44.4", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1.13", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@upstash/redis": ">=1.34.7", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@upstash/redis", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-ZyzKFpTC/Ut3fIqc2c0dPZ6nhchQXriTsqTNs4ayRgl6sZcFlMs9QZKPSHXK4bdOf41GHGWf+FrpcDDYwW+W6Q=="], + + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], + "ejs": ["ejs@3.1.10", "", { "dependencies": { "jake": "^10.8.5" }, "bin": { "ejs": "bin/cli.js" } }, "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA=="], "electron-to-chromium": ["electron-to-chromium@1.5.90", "", {}, "sha512-C3PN4aydfW91Natdyd449Kw+BzhLmof6tzy5W1pFC5SpQxVXT+oyiyOG9AgYYSN9OdA/ik3YkCrpwqI8ug5Tug=="], @@ -574,16 +632,28 @@ "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], + "encoding-sniffer": ["encoding-sniffer@0.2.0", "", { "dependencies": { "iconv-lite": "^0.6.3", "whatwg-encoding": "^3.1.1" } }, "sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg=="], "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], "error-ex": ["error-ex@1.3.2", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g=="], + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + "esbuild": ["esbuild@0.25.7", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.7", "@esbuild/android-arm": "0.25.7", "@esbuild/android-arm64": "0.25.7", "@esbuild/android-x64": "0.25.7", "@esbuild/darwin-arm64": "0.25.7", "@esbuild/darwin-x64": "0.25.7", "@esbuild/freebsd-arm64": "0.25.7", "@esbuild/freebsd-x64": "0.25.7", "@esbuild/linux-arm": "0.25.7", "@esbuild/linux-arm64": "0.25.7", "@esbuild/linux-ia32": "0.25.7", "@esbuild/linux-loong64": "0.25.7", "@esbuild/linux-mips64el": "0.25.7", "@esbuild/linux-ppc64": "0.25.7", "@esbuild/linux-riscv64": "0.25.7", "@esbuild/linux-s390x": "0.25.7", "@esbuild/linux-x64": "0.25.7", "@esbuild/netbsd-arm64": "0.25.7", "@esbuild/netbsd-x64": "0.25.7", "@esbuild/openbsd-arm64": "0.25.7", "@esbuild/openbsd-x64": "0.25.7", "@esbuild/openharmony-arm64": "0.25.7", "@esbuild/sunos-x64": "0.25.7", "@esbuild/win32-arm64": "0.25.7", "@esbuild/win32-ia32": "0.25.7", "@esbuild/win32-x64": "0.25.7" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-daJB0q2dmTzo90L9NjRaohhRWrCzYxWNFTjEi72/h+p5DcY3yn4MacWfDakHmaBaDzDiuLJsCh0+6LK/iX+c+Q=="], + "esbuild-register": ["esbuild-register@3.6.0", "", { "dependencies": { "debug": "^4.3.4" }, "peerDependencies": { "esbuild": ">=0.12 <1" } }, "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg=="], + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], + "escape-string-regexp": ["escape-string-regexp@2.0.0", "", {}, "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="], "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], @@ -592,6 +662,8 @@ "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], + "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], + "exact-mirror": ["exact-mirror@0.1.2", "", { "peerDependencies": { "@sinclair/typebox": "^0.34.15" }, "optionalPeers": ["@sinclair/typebox"] }, "sha512-wFCPCDLmHbKGUb8TOi/IS7jLsgR8WVDGtDK3CzcB4Guf/weq7G+I+DkXiRSZfbemBFOxOINKpraM6ml78vo8Zw=="], "execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="], @@ -600,6 +672,8 @@ "expect": ["expect@29.7.0", "", { "dependencies": { "@jest/expect-utils": "^29.7.0", "jest-get-type": "^29.6.3", "jest-matcher-utils": "^29.7.0", "jest-message-util": "^29.7.0", "jest-util": "^29.7.0" } }, "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw=="], + "express": ["express@5.1.0", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA=="], + "fast-decode-uri-component": ["fast-decode-uri-component@1.0.1", "", {}, "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg=="], "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], @@ -614,12 +688,18 @@ "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + "finalhandler": ["finalhandler@2.1.0", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q=="], + "find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="], "follow-redirects": ["follow-redirects@1.15.9", "", {}, "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="], "form-data": ["form-data@4.0.1", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "mime-types": "^2.1.12" } }, "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw=="], + "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], + + "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], + "fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="], "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], @@ -630,8 +710,12 @@ "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + "get-package-type": ["get-package-type@0.1.0", "", {}, "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q=="], + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + "get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], "get-tsconfig": ["get-tsconfig@4.10.1", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ=="], @@ -640,18 +724,24 @@ "globals": ["globals@11.12.0", "", {}, "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="], + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], - "hono": ["hono@4.8.3", "", {}, "sha512-jYZ6ZtfWjzBdh8H/0CIFfCBHaFL75k+KMzaM177hrWWm2TWL39YMYaJgB74uK/niRc866NMlH9B8uCvIo284WQ=="], + "hono": ["hono@4.9.1", "", {}, "sha512-qfvdJ42t6CQE0N/iSCa8KsW8SQqYD67YB+TYbwPHlnALvX+s7ynh8otR1NEk5jXtUg73gpV/B82OSufDmwtX3w=="], "html-escaper": ["html-escaper@2.0.2", "", {}, "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg=="], "htmlparser2": ["htmlparser2@9.1.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.1.0", "entities": "^4.5.0" } }, "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ=="], + "http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="], + "human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="], "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], @@ -666,6 +756,8 @@ "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], + "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], + "is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="], "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], @@ -676,6 +768,8 @@ "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="], + "is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], @@ -774,13 +868,19 @@ "makeerror": ["makeerror@1.0.12", "", { "dependencies": { "tmpl": "1.0.5" } }, "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg=="], + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="], + + "merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="], + "merge-stream": ["merge-stream@2.0.0", "", {}, "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="], "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], - "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], - "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + "mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="], "mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], @@ -792,6 +892,8 @@ "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], + "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], + "node-int64": ["node-int64@0.4.0", "", {}, "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw=="], "node-releases": ["node-releases@2.0.19", "", {}, "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw=="], @@ -804,6 +906,10 @@ "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="], + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], + + "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], + "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], "onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], @@ -824,6 +930,8 @@ "parse5-parser-stream": ["parse5-parser-stream@7.1.2", "", { "dependencies": { "parse5": "^7.0.0" } }, "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow=="], + "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], "path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="], @@ -832,6 +940,8 @@ "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], + "path-to-regexp": ["path-to-regexp@8.2.0", "", {}, "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ=="], + "pg": ["pg@8.16.3", "", { "dependencies": { "pg-connection-string": "^2.9.1", "pg-pool": "^3.10.1", "pg-protocol": "^1.10.3", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "optionalDependencies": { "pg-cloudflare": "^1.2.7" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw=="], "pg-cloudflare": ["pg-cloudflare@1.2.7", "", {}, "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg=="], @@ -868,10 +978,18 @@ "prompts": ["prompts@2.4.2", "", { "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" } }, "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q=="], + "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], + "proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], "pure-rand": ["pure-rand@6.1.0", "", {}, "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA=="], + "qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="], + + "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], + + "raw-body": ["raw-body@3.0.0", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.6.3", "unpipe": "1.0.0" } }, "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g=="], + "react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], "regenerate": ["regenerate@1.4.2", "", {}, "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A=="], @@ -902,14 +1020,32 @@ "rollup": ["rollup@4.45.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.45.1", "@rollup/rollup-android-arm64": "4.45.1", "@rollup/rollup-darwin-arm64": "4.45.1", "@rollup/rollup-darwin-x64": "4.45.1", "@rollup/rollup-freebsd-arm64": "4.45.1", "@rollup/rollup-freebsd-x64": "4.45.1", "@rollup/rollup-linux-arm-gnueabihf": "4.45.1", "@rollup/rollup-linux-arm-musleabihf": "4.45.1", "@rollup/rollup-linux-arm64-gnu": "4.45.1", "@rollup/rollup-linux-arm64-musl": "4.45.1", "@rollup/rollup-linux-loongarch64-gnu": "4.45.1", "@rollup/rollup-linux-powerpc64le-gnu": "4.45.1", "@rollup/rollup-linux-riscv64-gnu": "4.45.1", "@rollup/rollup-linux-riscv64-musl": "4.45.1", "@rollup/rollup-linux-s390x-gnu": "4.45.1", "@rollup/rollup-linux-x64-gnu": "4.45.1", "@rollup/rollup-linux-x64-musl": "4.45.1", "@rollup/rollup-win32-arm64-msvc": "4.45.1", "@rollup/rollup-win32-ia32-msvc": "4.45.1", "@rollup/rollup-win32-x64-msvc": "4.45.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw=="], + "router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="], + + "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], "semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "send": ["send@1.2.0", "", { "dependencies": { "debug": "^4.3.5", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.0", "mime-types": "^3.0.1", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.1" } }, "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw=="], + + "serve-static": ["serve-static@2.2.0", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ=="], + + "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], + + "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="], + + "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], + + "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], "sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="], @@ -926,6 +1062,8 @@ "stack-utils": ["stack-utils@2.0.6", "", { "dependencies": { "escape-string-regexp": "^2.0.0" } }, "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ=="], + "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], + "string-length": ["string-length@4.0.2", "", { "dependencies": { "char-regex": "^1.0.2", "strip-ansi": "^6.0.0" } }, "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ=="], "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -938,7 +1076,7 @@ "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], - "strtok3": ["strtok3@10.3.1", "", { "dependencies": { "@tokenizer/token": "^0.3.0" } }, "sha512-3JWEZM6mfix/GCJBBUrkA8p2Id2pBkyTkVCJKto55w080QBKZ+8R171fGrbiSp+yMO/u6F8/yUh7K4V9K+YCnw=="], + "strtok3": ["strtok3@10.3.4", "", { "dependencies": { "@tokenizer/token": "^0.3.0" } }, "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg=="], "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], @@ -950,7 +1088,9 @@ "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], - "token-types": ["token-types@6.0.3", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-IKJ6EzuPPWtKtEIEPpIdXv9j5j2LGJEYk0CKY2efgKoYKLBiZdh6iQkLVBow/CB3phyWAWCyk+bZeaimJn6uRQ=="], + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], + + "token-types": ["token-types@6.1.1", "", { "dependencies": { "@borewit/text-codec": "^0.1.0", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ=="], "ts-jest": ["ts-jest@29.2.5", "", { "dependencies": { "bs-logger": "^0.2.6", "ejs": "^3.1.10", "fast-json-stable-stringify": "^2.1.0", "jest-util": "^29.0.0", "json5": "^2.2.3", "lodash.memoize": "^4.1.2", "make-error": "^1.3.6", "semver": "^7.6.3", "yargs-parser": "^21.1.1" }, "peerDependencies": { "@babel/core": ">=7.0.0-beta.0 <8", "@jest/transform": "^29.0.0", "@jest/types": "^29.0.0", "babel-jest": "^29.0.0", "jest": "^29.0.0", "typescript": ">=4.3 <6" }, "optionalPeers": ["@babel/core", "@jest/transform", "@jest/types", "babel-jest"], "bin": { "ts-jest": "cli.js" } }, "sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA=="], @@ -962,9 +1102,11 @@ "type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="], + "type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="], + "typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], - "uint8array-extras": ["uint8array-extras@1.4.0", "", {}, "sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ=="], + "uint8array-extras": ["uint8array-extras@1.4.1", "", {}, "sha512-+NWHrac9dvilNgme+gP4YrBSumsaMZP0fNBtXXFIf33RLLKEcBUKaQZ7ULUbS0sBfcjxIZ4V96OTRkCbM7hxpw=="], "undici": ["undici@6.21.1", "", {}, "sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ=="], @@ -978,10 +1120,14 @@ "unicode-property-aliases-ecmascript": ["unicode-property-aliases-ecmascript@2.1.0", "", {}, "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w=="], + "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], + "update-browserslist-db": ["update-browserslist-db@1.1.2", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg=="], "v8-to-istanbul": ["v8-to-istanbul@9.3.0", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", "convert-source-map": "^2.0.0" } }, "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA=="], + "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], + "walker": ["walker@1.0.8", "", { "dependencies": { "makeerror": "1.0.12" } }, "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ=="], "whatwg-encoding": ["whatwg-encoding@3.1.1", "", { "dependencies": { "iconv-lite": "0.6.3" } }, "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ=="], @@ -1010,6 +1156,10 @@ "zod": ["zod@3.25.67", "", {}, "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw=="], + "@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="], + + "@esbuild-kit/core-utils/source-map-support": ["source-map-support@0.5.21", "", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="], + "@jest/console/@types/node": ["@types/node@22.13.0", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA=="], "@jest/core/@types/node": ["@types/node@22.13.0", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA=="], @@ -1038,8 +1188,14 @@ "dotenv-cli/dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], + "express/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], + "filelist/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="], + "form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + + "http-errors/statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="], + "jest-circus/@types/node": ["@types/node@22.13.0", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA=="], "jest-environment-node/@types/node": ["@types/node@22.13.0", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA=="], @@ -1078,6 +1234,50 @@ "wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.18.20", "", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.18.20", "", { "os": "android", "cpu": "arm64" }, "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.18.20", "", { "os": "android", "cpu": "x64" }, "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.18.20", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.18.20", "", { "os": "darwin", "cpu": "x64" }, "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.18.20", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.18.20", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.18.20", "", { "os": "linux", "cpu": "arm" }, "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.18.20", "", { "os": "linux", "cpu": "arm64" }, "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.18.20", "", { "os": "linux", "cpu": "ia32" }, "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.18.20", "", { "os": "linux", "cpu": "ppc64" }, "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.18.20", "", { "os": "linux", "cpu": "s390x" }, "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.18.20", "", { "os": "linux", "cpu": "x64" }, "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.18.20", "", { "os": "none", "cpu": "x64" }, "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.18.20", "", { "os": "openbsd", "cpu": "x64" }, "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.18.20", "", { "os": "sunos", "cpu": "x64" }, "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.18.20", "", { "os": "win32", "cpu": "arm64" }, "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.18.20", "", { "os": "win32", "cpu": "ia32" }, "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.18.20", "", { "os": "win32", "cpu": "x64" }, "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ=="], + "@jest/console/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], "@jest/core/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], @@ -1100,6 +1300,8 @@ "filelist/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], + "form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + "jest-circus/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], "jest-environment-node/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], diff --git a/drizzle.config.ts b/drizzle.config.ts new file mode 100644 index 0000000..80bf4c8 --- /dev/null +++ b/drizzle.config.ts @@ -0,0 +1,11 @@ +import type { Config } from 'drizzle-kit'; +import { env } from './src/env'; + +export default { + schema: './src/schema.ts', + out: './drizzle', + dialect: 'postgresql', + dbCredentials: { + url: env.DATABASE_URL, + }, +} satisfies Config; \ No newline at end of file diff --git a/package.json b/package.json index cd9c379..f9b3a07 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,10 @@ "test-watch": "AXIOS_RETRY_INTERVAL_MS=0 IN_TEST_MODE=true SLACK_WEBHOOK_URL=/ jest --watch", "dev": "dotenv -- tsx watch src/server.ts", "start": "NODE_ENV=production dotenv -- node dist/server.js", - "build": "rollup -c" + "build": "rollup -c", + "db:generate": "drizzle-kit generate", + "db:migrate": "drizzle-kit migrate", + "db:studio": "drizzle-kit studio" }, "repository": { "type": "git", @@ -20,11 +23,15 @@ }, "homepage": "https://github.com/ScottyLabs/dining-api#readme", "dependencies": { - "@elysiajs/cors": "^1.3.3", - "@elysiajs/node": "^1.3.0", + "@elysiajs/cors": "1.3.3", + "@elysiajs/node": "1.3.0", + "@types/express": "^5.0.3", "axios": "^1.10.0", "cheerio": "^1.0.0-rc.12", - "elysia": "^1.3.5", + "drizzle-kit": "^0.31.4", + "drizzle-orm": "^0.44.4", + "elysia": "1.3.5", + "express": "^5.1.0", "pg": "^8.16.3", "zod": "^3.25.67" }, @@ -36,7 +43,7 @@ "@types/bun": "^1.1.1", "@types/jest": "^29.5.14", "@types/node": "^24.0.14", - "@types/pg": "^8.11.2", + "@types/pg": "^8.15.5", "babel-jest": "^29.7.0", "bun-types": "^1.1.7", "dotenv": "^17.2.0", diff --git a/src/containers/locationBuilder.ts b/src/containers/locationBuilder.ts index e01a193..0eca62e 100644 --- a/src/containers/locationBuilder.ts +++ b/src/containers/locationBuilder.ts @@ -10,7 +10,6 @@ import { ITimeRange, } from "../types"; import { sortAndMergeTimeRanges } from "utils/timeUtils"; -import { ChangeOverride } from "./overrides"; /** * For building the location data structure @@ -142,16 +141,16 @@ export default class LocationBuilder { }; } - applyOverride(override: ChangeOverride) { + applyOverride(override: ILocation) { // Only apply if conceptId matches (optional guard) - if (this.conceptId !== override.conceptid) return; - if (override.name !== null) this.name = override.name; - if (override.description !== null) this.description = override.description; - if (override.shortdescription !== null) - this.shortDescription = override.shortdescription; - if (override.times !== null) this.times = override.times; - if (override.menu !== null) this.menu = override.menu; - if (override.accepts_online_orders !== null) - this.acceptsOnlineOrders = override.accepts_online_orders; + if (this.conceptId !== override.conceptId && override.conceptId !== undefined) return; + if (override.name !== null && override.name !== undefined && override.name !== "") this.name = override.name; + if (override.description !== null && override.description !== undefined && override.description !== "") this.description = override.description; + if (override.shortDescription !== null && override.shortDescription !== undefined) + this.shortDescription = override.shortDescription; + if (override.times !== null && override.times !== undefined) this.times = override.times; + if (override.menu !== null && override.menu !== undefined) this.menu = override.menu; + if (override.acceptsOnlineOrders !== null && override.acceptsOnlineOrders !== undefined) + this.acceptsOnlineOrders = override.acceptsOnlineOrders; } } diff --git a/src/containers/overrides.ts b/src/containers/overrides.ts deleted file mode 100644 index e366efb..0000000 --- a/src/containers/overrides.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { ITimeRange } from "types"; - -/* -Separated from locationBuilder for cleanliness. -- Format used to export changes from SQL database to concept cards -- go to db.ts to change SQL export command ("getChanges(...)") -- go to locationBuilder.ts to change manner of overrides ("applyOverride(...)") - -Currently, if it detects anything other than null - -it gets overridden completely -- go to diningParser.ts to change way it is being parsed -*/ - -export interface ChangeOverride { - conceptid: number; - name?: string; - description?: string; - shortdescription?: string; - times?: ITimeRange[]; - menu?: string; - accepts_online_orders?: boolean; -} diff --git a/src/db.ts b/src/db.ts index 6153d76..d91c37d 100644 --- a/src/db.ts +++ b/src/db.ts @@ -1,8 +1,10 @@ // db.ts -import { Pool } from "pg"; -import "dotenv/config"; -import { env } from "env"; -import { ITimeRange } from "types"; +import { drizzle } from 'drizzle-orm/node-postgres'; +import { Pool } from 'pg'; +import 'dotenv/config'; +import { env } from 'env'; +import { emails, dashboardChanges } from './schema'; +import { ILocation } from 'types'; let pool: Pool | null = null; @@ -16,49 +18,50 @@ function getPool(): Pool { return pool; } +function getDb() { + return drizzle(getPool(), { schema: { emails, dashboardChanges } }); +} + export async function getEmails(): Promise<{ name: string; email: string }[]> { - const dbPool = getPool(); - const result = await dbPool.query("SELECT name, email FROM emails"); + const db = getDb(); + const result = await db.select({ + name: emails.name, + email: emails.email, + }).from(emails); + // Remove 'mailto:' if present - return result.rows.map((row: { name: string; email: string }) => ({ + return result.map((row) => ({ name: row.name, email: row.email.replace(/^mailto:/, ""), })); } -export async function getChanges(): Promise< - { - conceptid: number; - name: string; - description: string; - shortdescription: string; - times: ITimeRange[]; - menu: string; - accepts_online_orders: boolean; - }[] -> { - const dbPool = getPool(); - const result = await dbPool.query( - "select conceptid, name, description, shortdescription,times,menu,accepts_online_orders from dashboard_changes" - ); - // Remove 'mailto:' if present - return result.rows.map( - (row: { - conceptid: number; - name: string; - description: string; - shortdescription: string; - times: ITimeRange[]; - menu: string; - accepts_online_orders: boolean; - }) => ({ - conceptid: row.conceptid, + +export async function getChanges(): Promise { + const db = getDb(); + const result = await db.select({ + conceptid: dashboardChanges.conceptid, + name: dashboardChanges.name, + description: dashboardChanges.description, + shortdescription: dashboardChanges.shortdescription, + times: dashboardChanges.times, + menu: dashboardChanges.menu, + accepts_online_orders: dashboardChanges.accepts_online_orders, + }).from(dashboardChanges); + + return result.map((row) => ({ + conceptId: row.conceptid, name: row.name, + shortDescription: row.shortdescription || undefined, description: row.description, - shortdescription: row.shortdescription, + url: `https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/${row.conceptid}`, + menu: row.menu || undefined, + location: "Unknown", + coordinates: undefined, + acceptsOnlineOrders: row.accepts_online_orders, times: row.times, - menu: row.menu, - accepts_online_orders: row.accepts_online_orders, - }) - ); -} + todaysSpecials: undefined, + todaysSoups: undefined, + })); + } + \ No newline at end of file diff --git a/src/parser/diningParser.ts b/src/parser/diningParser.ts index 547fa76..6ba7834 100644 --- a/src/parser/diningParser.ts +++ b/src/parser/diningParser.ts @@ -35,7 +35,7 @@ export default class DiningParser { builder.overwriteLocationCoordinates(locationCoordinateOverwrites); const override = overrides.find( - (o) => o.conceptid === builder.getConceptId() + (o) => o.conceptId === builder.getConceptId() ); if (override) { builder.applyOverride(override); diff --git a/src/schema.ts b/src/schema.ts new file mode 100644 index 0000000..83ed204 --- /dev/null +++ b/src/schema.ts @@ -0,0 +1,25 @@ +import { pgTable, text, integer, boolean, jsonb } from 'drizzle-orm/pg-core'; +import { ITimeRange } from './types'; + +// Emails table schema +export const emails = pgTable('emails', { + name: text('name').notNull(), + email: text('email').notNull(), +}); + +// Dashboard changes table schema +export const dashboardChanges = pgTable('dashboard_changes', { + conceptid: integer('conceptid').notNull(), + name: text('name').notNull(), + description: text('description').notNull(), + shortdescription: text('shortdescription').notNull(), + times: jsonb('times').$type().notNull(), + menu: text('menu').notNull(), + accepts_online_orders: boolean('accepts_online_orders').notNull(), +}); + +// Type exports for use in other files +export type Email = typeof emails.$inferSelect; +export type NewEmail = typeof emails.$inferInsert; +export type DashboardChange = typeof dashboardChanges.$inferSelect; +export type NewDashboardChange = typeof dashboardChanges.$inferInsert; \ No newline at end of file From e9ff38695ad855863fa4a86f3473133e66875acd Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Fri, 22 Aug 2025 17:04:48 -0400 Subject: [PATCH 10/65] feat: capital grains override --- bun.lock | 6 +++ package.json | 2 + src/containers/locationBuilder.ts | 11 ++++-- src/containers/time/parsedTimeForDate.ts | 2 + src/containers/timeBuilder.ts | 49 +++++++++++++++++++++--- src/overwrites/timeOverwrites.ts | 39 +++++++++++++++++++ src/parser/diningParser.ts | 30 +++++++++++---- src/server.ts | 7 +++- src/utils/requestUtils.ts | 26 +++++++++---- 9 files changed, 145 insertions(+), 27 deletions(-) create mode 100644 src/overwrites/timeOverwrites.ts diff --git a/bun.lock b/bun.lock index 9cbb75c..d5dc3da 100644 --- a/bun.lock +++ b/bun.lock @@ -6,9 +6,11 @@ "dependencies": { "@elysiajs/cors": "^1.3.3", "@elysiajs/node": "^1.3.0", + "@types/luxon": "^3.7.1", "axios": "^1.10.0", "cheerio": "^1.0.0-rc.12", "elysia": "^1.3.5", + "luxon": "^3.7.1", "pg": "^8.16.3", "zod": "^3.25.67", }, @@ -430,6 +432,8 @@ "@types/jest": ["@types/jest@29.5.14", "", { "dependencies": { "expect": "^29.0.0", "pretty-format": "^29.0.0" } }, "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ=="], + "@types/luxon": ["@types/luxon@3.7.1", "", {}, "sha512-H3iskjFIAn5SlJU7OuxUmTEpebK6TKB8rxZShDslBMZJ5u9S//KM1sbdAisiSrqwLQncVjnpi2OK2J51h+4lsg=="], + "@types/node": ["@types/node@24.0.14", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-4zXMWD91vBLGRtHK3YbIoFMia+1nqEz72coM42C5ETjnNCa/heoj7NT1G67iAfOqMmcfhuCZ4uNpyz8EjlAejw=="], "@types/pg": ["@types/pg@8.15.4", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-I6UNVBAoYbvuWkkU3oosC8yxqH21f4/Jc4DK71JLG3dT2mdlGe1z+ep/LQGXaKaOgcvUrsQoPRqfgtMcvZiJhg=="], @@ -768,6 +772,8 @@ "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], + "luxon": ["luxon@3.7.1", "", {}, "sha512-RkRWjA926cTvz5rAb1BqyWkKbbjzCGchDUIKMCUvNi17j6f6j8uHGDV82Aqcqtzd+icoYpELmG3ksgGiFNNcNg=="], + "make-dir": ["make-dir@4.0.0", "", { "dependencies": { "semver": "^7.5.3" } }, "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw=="], "make-error": ["make-error@1.3.6", "", {}, "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="], diff --git a/package.json b/package.json index cd9c379..4214c5a 100644 --- a/package.json +++ b/package.json @@ -22,9 +22,11 @@ "dependencies": { "@elysiajs/cors": "^1.3.3", "@elysiajs/node": "^1.3.0", + "@types/luxon": "^3.7.1", "axios": "^1.10.0", "cheerio": "^1.0.0-rc.12", "elysia": "^1.3.5", + "luxon": "^3.7.1", "pg": "^8.16.3", "zod": "^3.25.67" }, diff --git a/src/containers/locationBuilder.ts b/src/containers/locationBuilder.ts index 2aa5c92..0c4ba4e 100644 --- a/src/containers/locationBuilder.ts +++ b/src/containers/locationBuilder.ts @@ -10,6 +10,7 @@ import { ITimeRange, } from "../types"; import { sortAndMergeTimeRanges } from "utils/timeUtils"; +import { ITimeOverwrites } from "overwrites/timeOverwrites"; /** * For building the location data structure @@ -77,11 +78,12 @@ export default class LocationBuilder { return { lat: parseFloat(latitude), lng: parseFloat(longitude) }; } - async populateDetailedInfo() { + async populateDetailedInfo(timeSlotOverwrites: ITimeOverwrites) { const conceptURL = this.getConceptLink(); if (!conceptURL) return; - const $ = load(await getHTMLResponse(conceptURL)); + const { body, serverDate } = await getHTMLResponse(conceptURL); + const $ = load(body); this.url = conceptURL.toString(); this.description = $("div.description p").text().trim(); this.menu = $("div.navItems > a#getMenu").attr("href"); @@ -96,7 +98,9 @@ export default class LocationBuilder { const nextSevenDays = $("ul.schedule").find("li").toArray(); this.times = sortAndMergeTimeRanges( - nextSevenDays.flatMap((rowHTML) => getTimeRangesFromString(rowHTML)) + nextSevenDays.flatMap((rowHTML) => + getTimeRangesFromString(rowHTML, serverDate, timeSlotOverwrites) + ) ); } getConceptLink() { @@ -105,7 +109,6 @@ export default class LocationBuilder { } getConceptId() { - if (this.conceptId === undefined) return undefined; return this.conceptId; } diff --git a/src/containers/time/parsedTimeForDate.ts b/src/containers/time/parsedTimeForDate.ts index 787d783..6f68f6e 100644 --- a/src/containers/time/parsedTimeForDate.ts +++ b/src/containers/time/parsedTimeForDate.ts @@ -2,7 +2,9 @@ import { MonthOfTheYear } from "types"; import ParsedTimeBase from "./parsedTimeBase"; export interface IParsedTimeDate { + /** 1-12 */ month: MonthOfTheYear; + /* 1-31 */ date: number; } diff --git a/src/containers/timeBuilder.ts b/src/containers/timeBuilder.ts index 8f04347..378d942 100644 --- a/src/containers/timeBuilder.ts +++ b/src/containers/timeBuilder.ts @@ -7,6 +7,8 @@ import { IParsedTimeDate } from "./time/parsedTimeForDate"; import { DayOfTheWeek, ITimeRange, TimeInfoType } from "types"; import { parseToken } from "utils/parseTimeToken"; import { notifySlack } from "utils/slack"; +import { DateTime } from "luxon"; +import { ITimeOverwrites } from "overwrites/timeOverwrites"; interface ITimeRowAttributes { day?: DayOfTheWeek; @@ -16,22 +18,56 @@ interface ITimeRowAttributes { closed?: boolean; twentyFour?: boolean; } - /** * * @param rowString ex. Monday, September 09, 7:30 AM - 10:00 AM, 11:00 AM - 2:00 PM, 4:30 PM - 8:30 PM + * @param timeScraped Server time when rowHTML was obtained. we'll use this to derive the year for the row entry (and use that to check for relevant overrides) */ -export function getTimeRangesFromString(rowHTML: Element) { +export function getTimeRangesFromString( + rowHTML: Element, + timeScraped: DateTime, + overwrites: ITimeOverwrites +) { let timeRowInfo: ITimeRowAttributes = getTimeAttributesFromRow(rowHTML); + if (timeRowInfo.date) { + const { date, month } = timeRowInfo.date; + + let twoDigitYear = timeScraped.year % 100; + if ( + month < timeScraped.month || + (month === timeScraped.month && date < timeScraped.day) + ) { + // we are in the next year if the row date is before `timeScraped` (note: this requires `timeScraped` to be accurate and in the correct timezone, EST) + twoDigitYear++; + } + + const overrideEntry = overwrites[`${month}/${date}/${twoDigitYear}`]; + if (overrideEntry !== undefined) { + timeRowInfo = getTimeAttributesFromRow(rowHTML, overrideEntry); // yes, we are reparsing the string again, but we need a first pass to get the date + } + } + timeRowInfo = resolveAttributeConflicts(timeRowInfo); return getTimeRangesFromTimeRow(timeRowInfo); } -function getTimeAttributesFromRow(rowHTML: Element) { - return getTimeInfoWithRawAttributes(tokenizeTimeRow(rowHTML)); +/** + * + * @param rowHTML + * @param timeSlotOverrides ex. [7:00 AM - 3:00 PM, 4:00 PM - 6:00 PM] (the string should be formatted exactly how the dining site formats the string) + * Whatever is put here completely overrides the original time slot values. + * @returns + */ +function getTimeAttributesFromRow( + rowHTML: Element, + timeSlotOverrides?: string[] +) { + return getTimeInfoWithRawAttributes( + tokenizeTimeRow(rowHTML, timeSlotOverrides) + ); } -function tokenizeTimeRow(rowHTML: Element) { +function tokenizeTimeRow(rowHTML: Element, timeSlotOverrides?: string[]) { const $ = load(rowHTML); let day = $("strong").text(); const dataStr = $.text().replace(/\s\s+/g, " ").replace(day, "").trim(); @@ -41,7 +77,8 @@ function tokenizeTimeRow(rowHTML: Element) { day = (day.charAt(0).toUpperCase() + day.slice(1).toLowerCase()).trim(); date = (date.charAt(0).toUpperCase() + date.slice(1).toLowerCase()).trim(); time = time.toUpperCase().trim(); - const timeSlots = time.split(/[,;]/).map((slot) => slot.trim()); + const timeSlots = + timeSlotOverrides ?? time.split(/[,;]/).map((slot) => slot.trim()); return [day, date, ...timeSlots]; } diff --git a/src/overwrites/timeOverwrites.ts b/src/overwrites/timeOverwrites.ts new file mode 100644 index 0000000..8691ae6 --- /dev/null +++ b/src/overwrites/timeOverwrites.ts @@ -0,0 +1,39 @@ +export type ITimeOverwrites = { [date: string]: string[] | undefined }; + +export type IAllTimeOverwrites = { + [conceptId: string]: ITimeOverwrites; +}; + +/** + * key is in the format of MM/DD/YY (omit the first digit of the month if it's 0. same thing for day. (ex: Miku day is represented as 3/9/25)) + * NOTE: There is no coalescing between the existing time string for that day and the overwritten time string. + * If you choose to overwrite that day, you must do so completely. + */ +export const timeSlotOverwrites: IAllTimeOverwrites = { + // capital grains override + // won't be open until 9/13 + 179: { + "8/22/25": ["CLOSED"], + "8/23/25": ["CLOSED"], + "8/24/25": ["CLOSED"], + "8/25/25": ["CLOSED"], + "8/26/25": ["CLOSED"], + "8/27/25": ["CLOSED"], + "8/28/25": ["CLOSED"], + "8/29/25": ["CLOSED"], + "8/30/25": ["CLOSED"], + "8/31/25": ["CLOSED"], + "9/1/25": ["CLOSED"], + "9/2/25": ["CLOSED"], + "9/3/25": ["CLOSED"], + "9/4/25": ["CLOSED"], + "9/5/25": ["CLOSED"], + "9/6/25": ["CLOSED"], + "9/7/25": ["CLOSED"], + "9/8/25": ["CLOSED"], + "9/9/25": ["CLOSED"], + "9/10/25": ["CLOSED"], + "9/11/25": ["CLOSED"], + "9/12/25": ["CLOSED"], + }, +}; diff --git a/src/parser/diningParser.ts b/src/parser/diningParser.ts index d966381..062397e 100644 --- a/src/parser/diningParser.ts +++ b/src/parser/diningParser.ts @@ -2,8 +2,8 @@ import { getHTMLResponse } from "../utils/requestUtils"; import { load } from "cheerio"; import LocationBuilder from "../containers/locationBuilder"; import { retrieveSpecials } from "../containers/specials/specialsBuilder"; -import { ILocation, ISpecial } from "types"; -import locationCoordinateOverwrites from "overwrites/locationCoordinateOverwrites"; +import { ILocation, ILocationCoordinateOverwrites, ISpecial } from "types"; +import { IAllTimeOverwrites } from "overwrites/timeOverwrites"; /** * Retrieves the HTML from the CMU Dining website and parses the information @@ -16,8 +16,16 @@ export default class DiningParser { "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Specials"; static readonly DINING_SOUPS_URL = "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Soups"; + locationOverwrites: ILocationCoordinateOverwrites; + timeSlotOverwrites: IAllTimeOverwrites; - constructor() {} + constructor( + locationOverwrites: ILocationCoordinateOverwrites, + timeSlotOverwrites: IAllTimeOverwrites + ) { + this.locationOverwrites = locationOverwrites; + this.timeSlotOverwrites = timeSlotOverwrites; + } async process(): Promise { const locationBuilders = @@ -26,10 +34,12 @@ export default class DiningParser { const [specials, soups] = await this.fetchSpecials(); for (const builder of locationBuilders) { - await builder.populateDetailedInfo(); + await builder.populateDetailedInfo( + this.timeSlotOverwrites[builder.getConceptId() ?? -1] ?? [] + ); builder.setSoup(soups); builder.setSpecials(specials); - builder.overwriteLocationCoordinates(locationCoordinateOverwrites); + builder.overwriteLocationCoordinates(this.locationOverwrites); } return locationBuilders.map((builder) => builder.build()); @@ -38,7 +48,7 @@ export default class DiningParser { private async initializeLocationBuildersFromMainPage(): Promise< LocationBuilder[] > { - const mainPageHTML = await getHTMLResponse( + const { body: mainPageHTML } = await getHTMLResponse( new URL(DiningParser.DINING_URL) ); const mainContainer = load(mainPageHTML)("div.conceptCards"); @@ -55,10 +65,14 @@ export default class DiningParser { > { return await Promise.all([ retrieveSpecials( - await getHTMLResponse(new URL(DiningParser.DINING_SPECIALS_URL)) + ( + await getHTMLResponse(new URL(DiningParser.DINING_SPECIALS_URL)) + ).body ), retrieveSpecials( - await getHTMLResponse(new URL(DiningParser.DINING_SOUPS_URL)) + ( + await getHTMLResponse(new URL(DiningParser.DINING_SOUPS_URL)) + ).body ), ]); } diff --git a/src/server.ts b/src/server.ts index 0a27d48..f925e20 100644 --- a/src/server.ts +++ b/src/server.ts @@ -8,13 +8,18 @@ import { node } from "@elysiajs/node"; import { getDiffsBetweenLocationData } from "utils/diff"; import { getEmails } from "./db"; import LocationMerger from "utils/locationMerger"; +import locationCoordinateOverwrites from "overwrites/locationCoordinateOverwrites"; +import { timeSlotOverwrites } from "overwrites/timeOverwrites"; let cachedLocations: ILocation[] = []; async function reload(): Promise { const now = new Date(); console.log(`Reloading Dining API: ${now}`); - const parser = new DiningParser(); + const parser = new DiningParser( + locationCoordinateOverwrites, + timeSlotOverwrites + ); const locationMerger = new LocationMerger(); for (let i = 0; i < env.NUMBER_OF_SCRAPES; i++) { diff --git a/src/utils/requestUtils.ts b/src/utils/requestUtils.ts index c197e6d..937ac60 100644 --- a/src/utils/requestUtils.ts +++ b/src/utils/requestUtils.ts @@ -1,15 +1,17 @@ import axios from "axios"; import { notifySlack } from "./slack"; import { env } from "env"; - +import { DateTime } from "luxon"; const wait = (ms: number) => { return new Promise((re) => setTimeout(re, ms)); }; - -export async function getHTMLResponse( - url: URL, - retriesLeft = 4 -): Promise { +/** + * + * @param url + * @param retriesLeft + * @returns the serverDate returned is in EST time, since that's the timezone that the dining server operates in (ex. at midnight est, the 7-day times shift to the next day.) + */ +export async function getHTMLResponse(url: URL, retriesLeft = 4) { try { if (!env.IN_TEST_MODE) console.log(`Scraping ${url}`); const response = await axios.get(url.toString()); @@ -19,8 +21,16 @@ export async function getHTMLResponse( html: response.data, url: url.toString(), }); - - return response.data; + console.log(response.headers.date, new Date()); + const attemptedParsedDate = DateTime.fromRFC2822( + response.headers.date + ).setZone("America/New_York"); + return { + body: response.data, + serverDate: attemptedParsedDate.isValid + ? attemptedParsedDate + : (DateTime.now().setZone("America/New_York") as DateTime), // date should always be valid... + }; } catch (err: any) { notifySlack(`Error scraping ${url}\n${err.stack}`); if (retriesLeft > 0) { From 1e4c2d01ae4f704f0b1a4d2c29b5c31ec65da795 Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Fri, 22 Aug 2025 17:07:17 -0400 Subject: [PATCH 11/65] chore: remove console.log --- src/utils/requestUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/requestUtils.ts b/src/utils/requestUtils.ts index 937ac60..780ca18 100644 --- a/src/utils/requestUtils.ts +++ b/src/utils/requestUtils.ts @@ -21,7 +21,7 @@ export async function getHTMLResponse(url: URL, retriesLeft = 4) { html: response.data, url: url.toString(), }); - console.log(response.headers.date, new Date()); + const attemptedParsedDate = DateTime.fromRFC2822( response.headers.date ).setZone("America/New_York"); From 9ad006d04cea851d41a780bfd7c16af349b5d497 Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Fri, 22 Aug 2025 19:42:39 -0400 Subject: [PATCH 12/65] fix: fix existing tests --- src/parser/diningParser.ts | 23 ++++++++++++++--------- src/server.ts | 6 +++--- tests/integration.test.ts | 5 ++++- tests/mockAxios.ts | 11 ++++++++++- 4 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/parser/diningParser.ts b/src/parser/diningParser.ts index 062397e..e2a715e 100644 --- a/src/parser/diningParser.ts +++ b/src/parser/diningParser.ts @@ -16,15 +16,20 @@ export default class DiningParser { "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Specials"; static readonly DINING_SOUPS_URL = "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Soups"; - locationOverwrites: ILocationCoordinateOverwrites; - timeSlotOverwrites: IAllTimeOverwrites; + locationCoordinateOverwrites: ILocationCoordinateOverwrites = {}; + timeSlotOverwrites: IAllTimeOverwrites = {}; - constructor( - locationOverwrites: ILocationCoordinateOverwrites, - timeSlotOverwrites: IAllTimeOverwrites - ) { - this.locationOverwrites = locationOverwrites; - this.timeSlotOverwrites = timeSlotOverwrites; + constructor(overwrites?: { + locationCoordinateOverwrites?: ILocationCoordinateOverwrites; + timeSlotOverwrites?: IAllTimeOverwrites; + }) { + if (overwrites?.locationCoordinateOverwrites !== undefined) { + this.locationCoordinateOverwrites = + overwrites.locationCoordinateOverwrites; + } + if (overwrites?.timeSlotOverwrites !== undefined) { + this.timeSlotOverwrites = overwrites.timeSlotOverwrites; + } } async process(): Promise { @@ -39,7 +44,7 @@ export default class DiningParser { ); builder.setSoup(soups); builder.setSpecials(specials); - builder.overwriteLocationCoordinates(this.locationOverwrites); + builder.overwriteLocationCoordinates(this.locationCoordinateOverwrites); } return locationBuilders.map((builder) => builder.build()); diff --git a/src/server.ts b/src/server.ts index f925e20..e293055 100644 --- a/src/server.ts +++ b/src/server.ts @@ -16,10 +16,10 @@ let cachedLocations: ILocation[] = []; async function reload(): Promise { const now = new Date(); console.log(`Reloading Dining API: ${now}`); - const parser = new DiningParser( + const parser = new DiningParser({ locationCoordinateOverwrites, - timeSlotOverwrites - ); + timeSlotOverwrites, + }); const locationMerger = new LocationMerger(); for (let i = 0; i < env.NUMBER_OF_SCRAPES; i++) { diff --git a/tests/integration.test.ts b/tests/integration.test.ts index 304f6ff..44ed95d 100644 --- a/tests/integration.test.ts +++ b/tests/integration.test.ts @@ -1,3 +1,4 @@ +import locationCoordinateOverwrites from "overwrites/locationCoordinateOverwrites"; import DiningParser from "../src/parser/diningParser"; import { expectedLocationData } from "./expectedData"; @@ -26,7 +27,9 @@ test("the whole thing, including locationOverwrites", async () => { ? `html/concepts/${conceptId}.html` : "html/blank.html", }); - const parser = new DiningParser(); + const parser = new DiningParser({ + locationCoordinateOverwrites: locationCoordinateOverwrites, + }); const parsedLocationData = await parser.process(); expect(parsedLocationData).toStrictEqual(expectedLocationData); }); diff --git a/tests/mockAxios.ts b/tests/mockAxios.ts index dd20ef1..935f355 100644 --- a/tests/mockAxios.ts +++ b/tests/mockAxios.ts @@ -1,3 +1,4 @@ +import { DateTime } from "luxon"; import { getFileContent, last } from "./utils"; import axios from "axios"; @@ -19,14 +20,19 @@ export function mockAxiosGETMethod({ specialsHTML, soupsHTML, conceptHTML, + serverDate, }: { conceptListHTML?: string; specialsHTML?: string; soupsHTML?: string; conceptHTML?: (id: string) => string | undefined; + serverDate?: DateTime; }) { (axios.get as jest.Mock).mockImplementation(async (url: string) => { - return { data: getHTML(url) }; + return { + data: getHTML(url), + headers: { date: (serverDate ?? DateTime.now()).toRFC2822() }, + }; }); const getHTML = (url: string) => { @@ -48,11 +54,13 @@ export function mockAxiosGETMethodWithFilePaths({ specialsFilePath, soupsFilePath, getConceptFilePath, + serverDate, }: { conceptListFilePath?: string; specialsFilePath?: string; soupsFilePath?: string; getConceptFilePath?: (conceptId: string) => string; + serverDate?: DateTime; }) { mockAxiosGETMethod({ conceptListHTML: getFileContent(conceptListFilePath), @@ -62,5 +70,6 @@ export function mockAxiosGETMethodWithFilePaths({ getConceptFilePath ? getFileContent(getConceptFilePath(conceptId)) : undefined, + serverDate, }); } From 54fd9b26c15798bad0d7062842e4d86da9141194 Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Fri, 22 Aug 2025 21:43:28 -0400 Subject: [PATCH 13/65] tests: add tests for overwrites --- .../concepts/113-completely-customizable.html | 96 ++++++++ tests/integration.test.ts | 208 ++++++++++++++++++ tests/mockTimings.ts | 49 ++++- 3 files changed, 350 insertions(+), 3 deletions(-) create mode 100644 tests/html/concepts/113-completely-customizable.html diff --git a/tests/html/concepts/113-completely-customizable.html b/tests/html/concepts/113-completely-customizable.html new file mode 100644 index 0000000..b315640 --- /dev/null +++ b/tests/html/concepts/113-completely-customizable.html @@ -0,0 +1,96 @@ + + + + + + + + + + Concept + + + + +
+ +
+ +
+ + +
+ + An error has occurred. This application may no longer respond until reloaded. + + + Reload + 🗙 +
+ + + + + \ No newline at end of file diff --git a/tests/integration.test.ts b/tests/integration.test.ts index 44ed95d..76a0dec 100644 --- a/tests/integration.test.ts +++ b/tests/integration.test.ts @@ -13,7 +13,9 @@ import { Fri, Sat, Sun, + setUpArbitraryTest, } from "./mockTimings"; +import { DateTime } from "luxon"; jest.mock("axios"); @@ -377,3 +379,209 @@ describe("time edge cases", () => { await queryParserAndAssertTimingsCorrect([[Mon, 0, 0, Mon, 23, 59]]); }); }); + +describe("test time overwrites", () => { + test("no overwrites, more-so testing setUpArbitraryTest functionality", async () => { + setUpArbitraryTest( + [ + ["Friday", "August 22", "7:00 AM - 9:00 PM"], + ["Saturday", "August 23", "7:00 AM - 9:00 PM"], + ["Sunday", "August 24", "7:00 AM - 9:00 PM"], + ["Monday", "August 25", "7:00 AM - 9:00 PM"], + ["Tuesday", "August 26", "7:00 AM - 9:00 PM"], + ["Wednesday", "August 27", "7:00 AM - 9:00 PM"], + ["Thursday", "August 28", "7:00 AM - 9:00 PM"], + ], + DateTime.fromObject({ year: 2025, month: 8, day: 22 }) + ); + const diningParser = new DiningParser(); + await queryParserAndAssertTimingsCorrect( + [ + [Sun, 7, 0, Sun, 21, 0], + [Mon, 7, 0, Mon, 21, 0], + [Tue, 7, 0, Tue, 21, 0], + [Wed, 7, 0, Wed, 21, 0], + [Thur, 7, 0, Thur, 21, 0], + [Fri, 7, 0, Fri, 21, 0], + [Sat, 7, 0, Sat, 21, 0], + ], + diningParser + ); + }); + test("basic", async () => { + setUpArbitraryTest( + [ + ["Friday", "August 22", "7:00 AM - 9:00 PM"], + ["Saturday", "August 23", "7:00 AM - 9:00 PM"], + ["Sunday", "August 24", "7:00 AM - 9:00 PM"], + ["Monday", "August 25", "7:00 AM - 9:00 PM"], + ["Tuesday", "August 26", "7:00 AM - 9:00 PM"], + ["Wednesday", "August 27", "7:00 AM - 9:00 PM"], + ["Thursday", "August 28", "7:00 AM - 9:00 PM"], + ], + DateTime.fromObject({ year: 2025, month: 8, day: 22 }) + ); + const diningParser = new DiningParser({ + timeSlotOverwrites: { + 113: { + "8/23/25": ["CLOSED"], + }, + }, + }); + await queryParserAndAssertTimingsCorrect( + [ + [Sun, 7, 0, Sun, 21, 0], + [Mon, 7, 0, Mon, 21, 0], + [Tue, 7, 0, Tue, 21, 0], + [Wed, 7, 0, Wed, 21, 0], + [Thur, 7, 0, Thur, 21, 0], + [Fri, 7, 0, Fri, 21, 0], + ], + diningParser + ); + }); + test("basic", async () => { + setUpArbitraryTest( + [ + ["Friday", "August 22", "7:00 AM - 9:00 PM"], + ["Saturday", "August 23", "7:00 AM - 9:00 PM"], + ["Sunday", "August 24", "7:00 AM - 9:00 PM"], + ["Monday", "August 25", "7:00 AM - 9:00 PM"], + ["Tuesday", "August 26", "7:00 AM - 9:00 PM"], + ["Wednesday", "August 27", "7:00 AM - 9:00 PM"], + ["Thursday", "August 28", "7:00 AM - 9:00 PM"], + ], + DateTime.fromObject({ year: 2025, month: 8, day: 22 }) + ); + const diningParser = new DiningParser({ + timeSlotOverwrites: { + 113: { + "8/23/25": ["2:00 AM - 3:00 AM"], + }, + }, + }); + await queryParserAndAssertTimingsCorrect( + [ + [Sun, 7, 0, Sun, 21, 0], + [Mon, 7, 0, Mon, 21, 0], + [Tue, 7, 0, Tue, 21, 0], + [Wed, 7, 0, Wed, 21, 0], + [Thur, 7, 0, Thur, 21, 0], + [Fri, 7, 0, Fri, 21, 0], + [Sat, 2, 0, Sat, 3, 0], + ], + diningParser + ); + }); + test("overwrite everything", async () => { + setUpArbitraryTest( + [ + ["Friday", "August 22", "7:00 AM - 9:00 PM"], + ["Saturday", "August 23", "7:00 AM - 9:00 PM"], + ["Sunday", "August 24", "7:00 AM - 9:00 PM"], + ["Monday", "August 25", "7:00 AM - 9:00 PM"], + ["Tuesday", "August 26", "7:00 AM - 9:00 PM"], + ["Wednesday", "August 27", "7:00 AM - 9:00 PM"], + ["Thursday", "August 28", "7:00 AM - 9:00 PM"], + ], + DateTime.fromObject({ year: 2025, month: 8, day: 22 }) + ); + const diningParser = new DiningParser({ + timeSlotOverwrites: { + 113: { + "8/22/25": ["2:00 AM - 3:00 AM"], + "8/23/25": ["2:00 AM - 3:00 AM"], + "8/24/25": ["2:00 AM - 3:00 AM"], + "8/25/25": ["2:00 AM - 3:00 AM"], + "8/26/25": ["2:00 AM - 3:00 AM"], + "8/27/25": ["2:00 AM - 3:00 AM"], + "8/28/25": ["2:00 AM - 3:00 AM"], + }, + }, + }); + await queryParserAndAssertTimingsCorrect( + [ + [Sun, 2, 0, Sun, 3, 0], + [Mon, 2, 0, Mon, 3, 0], + [Tue, 2, 0, Tue, 3, 0], + [Wed, 2, 0, Wed, 3, 0], + [Thur, 2, 0, Thur, 3, 0], + [Fri, 2, 0, Fri, 3, 0], + [Sat, 2, 0, Sat, 3, 0], + ], + diningParser + ); + }); + test("overwrite with coalescing", async () => { + setUpArbitraryTest( + [ + ["Friday", "August 22", "7:00 AM - 9:00 PM"], + ["Saturday", "August 23", "7:00 AM - 9:00 PM"], + ["Sunday", "August 24", "7:00 AM - 9:00 PM"], + ["Monday", "August 25", "7:00 AM - 9:00 PM"], + ["Tuesday", "August 26", "7:00 AM - 9:00 PM"], + ["Wednesday", "August 27", "7:00 AM - 9:00 PM"], + ["Thursday", "August 28", "7:00 AM - 9:00 PM"], + ], + DateTime.fromObject({ year: 2025, month: 8, day: 22 }) + ); + const diningParser = new DiningParser({ + timeSlotOverwrites: { + 113: { + "8/23/25": ["11:00 AM - 10:00 AM"], + }, + }, + }); + await queryParserAndAssertTimingsCorrect( + [ + [Mon, 7, 0, Mon, 21, 0], + [Tue, 7, 0, Tue, 21, 0], + [Wed, 7, 0, Wed, 21, 0], + [Thur, 7, 0, Thur, 21, 0], + [Fri, 7, 0, Fri, 21, 0], + [Sat, 11, 0, Sun, 21, 0], + ], + diningParser + ); + }); + test("overwrite everything, into the New Years", async () => { + setUpArbitraryTest( + [ + ["Tuesday", "December 30", "7:00 AM - 9:00 PM"], + ["Wednesday", "December 31", "7:00 AM - 9:00 PM"], + ["Thursday", "January 1", "7:00 AM - 9:00 PM"], + ["Friday", "January 2", "7:00 AM - 9:00 PM"], + ["Saturday", "January 3", "7:00 AM - 9:00 PM"], + ["Sunday", "January 4", "7:00 AM - 9:00 PM"], + ["Monday", "January 5", "7:00 AM - 9:00 PM"], + ], + DateTime.fromObject({ year: 2025, month: 12, day: 30 }) + ); + const diningParser = new DiningParser({ + timeSlotOverwrites: { + 113: { + "8/23/25": ["2:00 AM - 10:00 AM"], + "12/30/25": ["2:00 AM - 10:00 AM"], + "12/31/25": ["2:00 AM - 10:00 AM"], + "1/1/26": ["2:00 AM - 10:00 AM"], // typing this date just gave me an existential crisis. Thanks a lot. + "1/2/26": ["2:00 AM - 10:00 AM"], + "1/3/26": ["2:00 AM - 10:00 AM"], + "1/4/26": ["2:00 AM - 10:00 AM"], + "1/5/26": ["2:00 AM - 10:00 AM"], + }, + }, + }); + await queryParserAndAssertTimingsCorrect( + [ + [Sun, 2, 0, Sun, 10, 0], + [Mon, 2, 0, Mon, 10, 0], + [Tue, 2, 0, Tue, 10, 0], + [Wed, 2, 0, Wed, 10, 0], + [Thur, 2, 0, Thur, 10, 0], + [Fri, 2, 0, Fri, 10, 0], + [Sat, 2, 0, Sat, 10, 0], + ], + diningParser + ); + }); +}); diff --git a/tests/mockTimings.ts b/tests/mockTimings.ts index c5b8b14..bf5dd0a 100644 --- a/tests/mockTimings.ts +++ b/tests/mockTimings.ts @@ -1,3 +1,4 @@ +import { DateTime } from "luxon"; import DiningParser from "../src/parser/diningParser"; import { mockAxiosGETMethod } from "./mockAxios"; import { getFileContent } from "./utils"; @@ -22,13 +23,14 @@ export const Mon = DayOfTheWeek.MONDAY, /** * * @param times [startDay,startHour,startMinute,endDay,endHour,endMinute][] (order matters for input! Sunday comes first) + * @param parser In case you want to pass in a custom parser with overwrites or something. If nothing is passed in, a default parser is assumed * The only reason start and end are bundled into one array is because prettier will autoformat it to two lines otherwise */ export async function queryParserAndAssertTimingsCorrect( - times: [DayOfTheWeek, number, number, DayOfTheWeek, number, number][] + times: [DayOfTheWeek, number, number, DayOfTheWeek, number, number][], + parser?: DiningParser ) { - const parser = new DiningParser(); - const result = await parser.process(); + const result = await (parser ?? new DiningParser()).process(); expect(result.length).toBe(1); expect(result[0].times).toStrictEqual( times.map((time) => { @@ -88,3 +90,44 @@ export function setUpTimingTest( }, }); } + +/** + * concept id is 113 + * @param data + * @param serverDate (time the server "processed" the request. This value matters for overwrite times) + */ +export function setUpArbitraryTest( + data: [ + [string, string, string], + [string, string, string], + [string, string, string], + [string, string, string], + [string, string, string], + [string, string, string], + [string, string, string] + ], + serverDate: DateTime +) { + const fillInHtml = (html: string) => { + const A_CHAR_CODE = "A".charCodeAt(0); + for (let i = 0; i < 7; i++) { + for (let j = 0; j < 3; j++) { + const square = `[${String.fromCharCode(A_CHAR_CODE + i)}${j + 1}]`; + html = html.replace(square, data[i][j]); + } + } + return html; + }; + mockAxiosGETMethod({ + conceptListHTML: getFileContent("html/listconcepts-just-113.html"), + soupsHTML: getFileContent("html/blank.html"), + specialsHTML: getFileContent("html/blank.html"), + conceptHTML: (id) => { + expect(id).toBe("113"); + return fillInHtml( + getFileContent("html/concepts/113-completely-customizable.html") + ); + }, + serverDate, + }); +} From 56b33b725ef84e7074fa625dfbe301c67265635b Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Fri, 22 Aug 2025 21:52:10 -0400 Subject: [PATCH 14/65] fix: notify when response date header becomes unparseable --- src/utils/requestUtils.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/utils/requestUtils.ts b/src/utils/requestUtils.ts index 780ca18..2d44fd5 100644 --- a/src/utils/requestUtils.ts +++ b/src/utils/requestUtils.ts @@ -25,6 +25,11 @@ export async function getHTMLResponse(url: URL, retriesLeft = 4) { const attemptedParsedDate = DateTime.fromRFC2822( response.headers.date ).setZone("America/New_York"); + if (!attemptedParsedDate.isValid) { + notifySlack( + ` Ran into unparseable date response header! url: ${url} response headers: ${response.headers.date}` + ); + } return { body: response.data, serverDate: attemptedParsedDate.isValid From 9e27cb57bbdddeb0f41fab5587799006530d0e95 Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Mon, 25 Aug 2025 21:19:32 -0400 Subject: [PATCH 15/65] fix: tests --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cd9c379..cf8908a 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Carnegie Mellon University Dining API", "main": "server.ts", "scripts": { - "test": "AXIOS_RETRY_INTERVAL_MS=0 IN_TEST_MODE=true SLACK_WEBHOOK_URL=/ DATABASE_URL=/ jest --coverage", + "test": "AXIOS_RETRY_INTERVAL_MS=0 IN_TEST_MODE=true SLACK_BACKEND_WEBHOOK_URL=/ SLACK_FRONTEND_WEBHOOK_URL=/ DATABASE_URL=/ jest --coverage", "test-watch": "AXIOS_RETRY_INTERVAL_MS=0 IN_TEST_MODE=true SLACK_WEBHOOK_URL=/ jest --watch", "dev": "dotenv -- tsx watch src/server.ts", "start": "NODE_ENV=production dotenv -- node dist/server.js", From e2e18ecb55294211027d6bbe1bf393b0cce6e501 Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Thu, 4 Sep 2025 20:00:06 -0400 Subject: [PATCH 16/65] refactor: rework the entire parsing algorithm to better accomodate time slot overrides --- src/containers/locationBuilder.ts | 13 +- src/containers/time/parsedTimeForDate.ts | 5 + src/containers/time/parsedTimeForDay.ts | 21 ++- src/containers/timeBuilder.ts | 212 +++++++++-------------- src/overwrites/timeOverwrites.ts | 44 ++--- src/parser/diningParser.ts | 4 +- src/types.ts | 17 +- src/utils/parseTimeToken.ts | 22 +-- src/utils/slack.ts | 5 +- src/utils/timeUtils.ts | 18 +- tests/integration.test.ts | 186 +++++++++++++++++--- tests/mockAxios.ts | 6 +- tests/mockTimings.ts | 5 + 13 files changed, 318 insertions(+), 240 deletions(-) diff --git a/src/containers/locationBuilder.ts b/src/containers/locationBuilder.ts index 0c4ba4e..9fbf552 100644 --- a/src/containers/locationBuilder.ts +++ b/src/containers/locationBuilder.ts @@ -1,7 +1,7 @@ import { getHTMLResponse } from "utils/requestUtils"; import { load } from "cheerio"; import type { Element } from "domhandler"; -import { getTimeRangesFromString } from "./timeBuilder"; +import { getAllTimeSlotsFromSchedule } from "./timeBuilder"; import { ICoordinate, ILocation, @@ -9,7 +9,6 @@ import { ISpecial, ITimeRange, } from "../types"; -import { sortAndMergeTimeRanges } from "utils/timeUtils"; import { ITimeOverwrites } from "overwrites/timeOverwrites"; /** @@ -78,7 +77,7 @@ export default class LocationBuilder { return { lat: parseFloat(latitude), lng: parseFloat(longitude) }; } - async populateDetailedInfo(timeSlotOverwrites: ITimeOverwrites) { + async populateDetailedInfo(timeSlotOverwrites: ITimeOverwrites = {}) { const conceptURL = this.getConceptLink(); if (!conceptURL) return; @@ -97,10 +96,10 @@ export default class LocationBuilder { } const nextSevenDays = $("ul.schedule").find("li").toArray(); - this.times = sortAndMergeTimeRanges( - nextSevenDays.flatMap((rowHTML) => - getTimeRangesFromString(rowHTML, serverDate, timeSlotOverwrites) - ) + this.times = getAllTimeSlotsFromSchedule( + nextSevenDays, + serverDate.year, + timeSlotOverwrites ); } getConceptLink() { diff --git a/src/containers/time/parsedTimeForDate.ts b/src/containers/time/parsedTimeForDate.ts index 6f68f6e..7f8af4e 100644 --- a/src/containers/time/parsedTimeForDate.ts +++ b/src/containers/time/parsedTimeForDate.ts @@ -38,6 +38,11 @@ export default class ParsedTimeForDate extends ParsedTimeBase { } } +/** + * Throws error when failed + * @param monthStr + * @returns + */ export function convertMonthStringToEnum(monthStr: string): MonthOfTheYear { const normalizedMonth = monthStr.trim().toLowerCase(); switch (normalizedMonth) { diff --git a/src/containers/time/parsedTimeForDay.ts b/src/containers/time/parsedTimeForDay.ts index e729a1e..e0dd808 100644 --- a/src/containers/time/parsedTimeForDay.ts +++ b/src/containers/time/parsedTimeForDay.ts @@ -1,11 +1,10 @@ -import { DayOfTheWeek } from "types"; import ParsedTimeBase from "./parsedTimeBase"; /** - * For parsing a string representing a day to a day of the week enum + * For parsing a string representing a day to a day of the week (0-6) */ export default class ParsedTimeForDay extends ParsedTimeBase { - declare value: DayOfTheWeek; + declare value: number; parse() { this.value = convertDayStringToEnum(this.input); @@ -13,31 +12,31 @@ export default class ParsedTimeForDay extends ParsedTimeBase { } } -export function convertDayStringToEnum(dayStr: string): DayOfTheWeek { +export function convertDayStringToEnum(dayStr: string): number { const normalizedDay = dayStr.trim().toLowerCase(); switch (normalizedDay) { case "sunday": case "sun": - return DayOfTheWeek.SUNDAY; + return 0; case "monday": case "mon": - return DayOfTheWeek.MONDAY; + return 1; case "tuesday": case "tue": - return DayOfTheWeek.TUESDAY; + return 2; case "wednesday": case "wed": - return DayOfTheWeek.WEDNESDAY; + return 3; case "thursday": case "thu": case "thurs": - return DayOfTheWeek.THURSDAY; + return 4; case "friday": case "fri": - return DayOfTheWeek.FRIDAY; + return 5; case "saturday": case "sat": - return DayOfTheWeek.SATURDAY; + return 6; default: throw new Error(`Invalid Day: ${dayStr}`); } diff --git a/src/containers/timeBuilder.ts b/src/containers/timeBuilder.ts index 378d942..6dc42da 100644 --- a/src/containers/timeBuilder.ts +++ b/src/containers/timeBuilder.ts @@ -1,166 +1,126 @@ import { load } from "cheerio"; import type { Element } from "domhandler"; -import { getNextDay } from "../utils/timeUtils"; +import { sortAndMergeTimeRanges } from "../utils/timeUtils"; import { IParsedTimeRange } from "./time/parsedTime"; -import { IParsedTimeDate } from "./time/parsedTimeForDate"; -import { DayOfTheWeek, ITimeRange, TimeInfoType } from "types"; +import { convertMonthStringToEnum } from "./time/parsedTimeForDate"; +import { ITimeRange, TimeInfoType } from "types"; import { parseToken } from "utils/parseTimeToken"; import { notifySlack } from "utils/slack"; import { DateTime } from "luxon"; import { ITimeOverwrites } from "overwrites/timeOverwrites"; +import ParsedTimeForDayOfWeek from "./time/parsedTimeForDay"; -interface ITimeRowAttributes { - day?: DayOfTheWeek; - date?: IParsedTimeDate; - /** Multiple times in the same day (ex. https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/180) */ - times?: IParsedTimeRange[]; - closed?: boolean; - twentyFour?: boolean; -} /** * - * @param rowString ex. Monday, September 09, 7:30 AM - 10:00 AM, 11:00 AM - 2:00 PM, 4:30 PM - 8:30 PM - * @param timeScraped Server time when rowHTML was obtained. we'll use this to derive the year for the row entry (and use that to check for relevant overrides) + * @param schedule + * @param currentYear Current year, XXXX + * @returns */ -export function getTimeRangesFromString( - rowHTML: Element, - timeScraped: DateTime, - overwrites: ITimeOverwrites +export function getAllTimeSlotsFromSchedule( + schedule: Element[], + currentYear: number, + scheduleOverwrites: ITimeOverwrites ) { - let timeRowInfo: ITimeRowAttributes = getTimeAttributesFromRow(rowHTML); - if (timeRowInfo.date) { - const { date, month } = timeRowInfo.date; + const allTimeSlots: ITimeRange[] = []; + let prevMonth = -1; + for (const rowHTML of schedule) { + const $ = load(rowHTML); + try { + const [date, timeSlotsForThatDay] = $.text() + .replaceAll("\n", "") + .replace(/\s\s+/g, " ") + .split(/,(.*)/); // splits only on first comma + const [dayOfWeek, month, day] = date.trim().split(" "); + if ( + dayOfWeek === undefined || + month === undefined || + day === undefined || + date === undefined || + timeSlotsForThatDay === undefined + ) + continue; + const parsedMonth = convertMonthStringToEnum(month); + const parsedDay = parseInt(day); + const parsedDayOfWeek = new ParsedTimeForDayOfWeek(dayOfWeek).parse() + .value; + if (parsedMonth < prevMonth) { + // new year + currentYear++; + } + prevMonth = parsedMonth; - let twoDigitYear = timeScraped.year % 100; - if ( - month < timeScraped.month || - (month === timeScraped.month && date < timeScraped.day) - ) { - // we are in the next year if the row date is before `timeScraped` (note: this requires `timeScraped` to be accurate and in the correct timezone, EST) - twoDigitYear++; - } + const rowFullDateString = `${parsedMonth}/${parsedDay}/${currentYear}`; + const rowDate = DateTime.fromFormat(rowFullDateString, "M/d/y"); - const overrideEntry = overwrites[`${month}/${date}/${twoDigitYear}`]; - if (overrideEntry !== undefined) { - timeRowInfo = getTimeAttributesFromRow(rowHTML, overrideEntry); // yes, we are reparsing the string again, but we need a first pass to get the date + if (!rowDate.isValid) { + notifySlack( + ` Cannot parse date ${rowFullDateString}, derived from ${$.text()}` + ); + continue; + } + const parsedTimeSlots = parseTimeSlots( + scheduleOverwrites[rowFullDateString] ?? + timeSlotsForThatDay.split(/[,;]/).map((slot) => slot.trim()) + ); + const parsedTimeSlotsWithDateInfo = augmentAndEditTimeRangesWithDateInfo( + parsedTimeSlots, + parsedDayOfWeek // we could use rowDate.weekday % 7, but that would break a good number of tests that did not rely on serverDate to get the weekday... + ); + allTimeSlots.push(...parsedTimeSlotsWithDateInfo); + } catch (e) { + notifySlack( + ` Failed to parse row ${$.text()} ${e} ${(e as any).stack}` + ); } } - - timeRowInfo = resolveAttributeConflicts(timeRowInfo); - return getTimeRangesFromTimeRow(timeRowInfo); + return sortAndMergeTimeRanges(allTimeSlots); } - /** * - * @param rowHTML - * @param timeSlotOverrides ex. [7:00 AM - 3:00 PM, 4:00 PM - 6:00 PM] (the string should be formatted exactly how the dining site formats the string) - * Whatever is put here completely overrides the original time slot values. + * @param timeString The actual time slots (ex. '10:30 AM - 8:00 PM' in the string 'Tuesday September 09, 10:30 AM - 8:00 PM') * @returns */ -function getTimeAttributesFromRow( - rowHTML: Element, - timeSlotOverrides?: string[] -) { - return getTimeInfoWithRawAttributes( - tokenizeTimeRow(rowHTML, timeSlotOverrides) - ); -} - -function tokenizeTimeRow(rowHTML: Element, timeSlotOverrides?: string[]) { - const $ = load(rowHTML); - let day = $("strong").text(); - const dataStr = $.text().replace(/\s\s+/g, " ").replace(day, "").trim(); - let [date, time] = dataStr.split(/,(.+)/); - if (date === undefined || time === undefined) return []; - - day = (day.charAt(0).toUpperCase() + day.slice(1).toLowerCase()).trim(); - date = (date.charAt(0).toUpperCase() + date.slice(1).toLowerCase()).trim(); - time = time.toUpperCase().trim(); - const timeSlots = - timeSlotOverrides ?? time.split(/[,;]/).map((slot) => slot.trim()); - return [day, date, ...timeSlots]; -} +function parseTimeSlots(timeSlotStrings: string[]) { + const timeRanges: IParsedTimeRange[] = []; -function getTimeInfoWithRawAttributes(tokens: string[]) { - const timeInfo: ITimeRowAttributes = {}; - - for (const token of tokens) { + for (const token of timeSlotStrings) { try { const { type: timeInfoType, value } = parseToken(token); switch (timeInfoType) { - case TimeInfoType.DAY: - timeInfo.day = value; - break; - case TimeInfoType.DATE: - timeInfo.date = value; - break; case TimeInfoType.TIME: - if (timeInfo.times !== undefined) { - timeInfo.times.push(value); - } else { - timeInfo.times = [value]; - } - break; - case TimeInfoType.CLOSED: - timeInfo.closed = true; + timeRanges.push(value); break; case TimeInfoType.TWENTYFOURHOURS: - timeInfo.twentyFour = true; - break; + timeRanges.push({ + start: { hour: 0, minute: 0 }, + end: { hour: 23, minute: 59 }, + }); } } catch (err: any) { notifySlack( - ` Failed to parse token \`${token}\` from list of tokens \`${tokens}\`\n${err.stack}` + ` Failed to parse token \`${token}\` from time slot \`${timeSlotStrings.join( + "|" + )}\`\n${err.stack}` ); continue; } } - return timeInfo; -} - -function resolveAttributeConflicts( - input: ITimeRowAttributes -): ITimeRowAttributes { - if (input.closed) { - return { - day: input.day, - date: input.date, - closed: input.closed, - }; - } - if (input.twentyFour) { - return { - day: input.day, - date: input.date, - times: [{ start: { hour: 0, minute: 0 }, end: { hour: 23, minute: 59 } }], - }; - } - if (input.times && input.times.length > 0) { - return { - day: input.day, - date: input.date, - times: input.times, - }; - } - return { - day: input.day, - date: input.date, - times: [], - }; + return timeRanges; } -function getTimeRangesFromTimeRow(time: ITimeRowAttributes) { - if (time.day === undefined) { - notifySlack( - ` Cannot convert time attribute: ${JSON.stringify( - time - )} since day is not set` - ); - return []; - } +/** + * + * @param timeRanges + * @param day (0-6, with Sunday being 0) + * @returns + */ +function augmentAndEditTimeRangesWithDateInfo( + timeRanges: IParsedTimeRange[], + day: number +) { const allRanges: ITimeRange[] = []; - for (const range of time.times ?? []) { + for (const range of timeRanges) { rollBack12AmEndTime(range); // not sure why this was added, but it doesn't hurt I guess (I suppose the only case this actively helps is if the time string is 12:00 AM - 12:00 AM) const shouldSpillToNextDay = range.start.hour * 60 + range.start.minute > @@ -168,12 +128,12 @@ function getTimeRangesFromTimeRow(time: ITimeRowAttributes) { allRanges.push({ start: { - day: time.day, + day: day, hour: range.start.hour, minute: range.start.minute, }, end: { - day: shouldSpillToNextDay ? getNextDay(time.day) : time.day, + day: shouldSpillToNextDay ? (day + 1) % 7 : day, hour: range.end.hour, minute: range.end.minute, }, diff --git a/src/overwrites/timeOverwrites.ts b/src/overwrites/timeOverwrites.ts index 8691ae6..26d5a17 100644 --- a/src/overwrites/timeOverwrites.ts +++ b/src/overwrites/timeOverwrites.ts @@ -13,27 +13,27 @@ export const timeSlotOverwrites: IAllTimeOverwrites = { // capital grains override // won't be open until 9/13 179: { - "8/22/25": ["CLOSED"], - "8/23/25": ["CLOSED"], - "8/24/25": ["CLOSED"], - "8/25/25": ["CLOSED"], - "8/26/25": ["CLOSED"], - "8/27/25": ["CLOSED"], - "8/28/25": ["CLOSED"], - "8/29/25": ["CLOSED"], - "8/30/25": ["CLOSED"], - "8/31/25": ["CLOSED"], - "9/1/25": ["CLOSED"], - "9/2/25": ["CLOSED"], - "9/3/25": ["CLOSED"], - "9/4/25": ["CLOSED"], - "9/5/25": ["CLOSED"], - "9/6/25": ["CLOSED"], - "9/7/25": ["CLOSED"], - "9/8/25": ["CLOSED"], - "9/9/25": ["CLOSED"], - "9/10/25": ["CLOSED"], - "9/11/25": ["CLOSED"], - "9/12/25": ["CLOSED"], + "8/22/2025": ["CLOSED"], + "8/23/2025": ["CLOSED"], + "8/24/2025": ["CLOSED"], + "8/25/2025": ["CLOSED"], + "8/26/2025": ["CLOSED"], + "8/27/2025": ["CLOSED"], + "8/28/2025": ["CLOSED"], + "8/29/2025": ["CLOSED"], + "8/30/2025": ["CLOSED"], + "8/31/2025": ["CLOSED"], + "9/1/2025": ["CLOSED"], + "9/2/2025": ["CLOSED"], + "9/3/2025": ["CLOSED"], + "9/4/2025": ["CLOSED"], + "9/5/2025": ["CLOSED"], + "9/6/2025": ["CLOSED"], + "9/7/2025": ["CLOSED"], + "9/8/2025": ["CLOSED"], + "9/9/2025": ["CLOSED"], + "9/10/2025": ["CLOSED"], + "9/11/2025": ["CLOSED"], + "9/12/2025": ["CLOSED"], }, }; diff --git a/src/parser/diningParser.ts b/src/parser/diningParser.ts index e2a715e..53fed04 100644 --- a/src/parser/diningParser.ts +++ b/src/parser/diningParser.ts @@ -40,7 +40,9 @@ export default class DiningParser { for (const builder of locationBuilders) { await builder.populateDetailedInfo( - this.timeSlotOverwrites[builder.getConceptId() ?? -1] ?? [] + builder.getConceptId() !== undefined + ? this.timeSlotOverwrites[builder.getConceptId()!] + : undefined ); builder.setSoup(soups); builder.setSpecials(specials); diff --git a/src/types.ts b/src/types.ts index 0a5cef5..e9e77bb 100644 --- a/src/types.ts +++ b/src/types.ts @@ -18,7 +18,10 @@ export interface ISpecial { } export interface ITimeSlot { - day: DayOfTheWeek; + /** + * 0 - 6 where 0 = Sunday + */ + day: number; hour: number; minute: number; } @@ -36,16 +39,6 @@ export interface ILocationCoordinateOverwrites { [conceptId: string]: ICoordinate; } -export enum DayOfTheWeek { - SUNDAY = 0, - MONDAY = 1, - TUESDAY = 2, - WEDNESDAY = 3, - THURSDAY = 4, - FRIDAY = 5, - SATURDAY = 6, -} - export enum MonthOfTheYear { JANUARY = 1, FEBRUARY = 2, @@ -62,8 +55,6 @@ export enum MonthOfTheYear { } export enum TimeInfoType { - DAY = "DAY", - DATE = "DATE", TIME = "TIME", CLOSED = "CLOSED", TWENTYFOURHOURS = "TWENTYFOURHOURS", diff --git a/src/utils/parseTimeToken.ts b/src/utils/parseTimeToken.ts index 8b03078..98eee63 100644 --- a/src/utils/parseTimeToken.ts +++ b/src/utils/parseTimeToken.ts @@ -1,28 +1,10 @@ import ParsedTime from "containers/time/parsedTime"; -import ParsedTimeForDate, { - convertMonthStringToEnum, -} from "containers/time/parsedTimeForDate"; -import ParsedTimeForDay, { - convertDayStringToEnum, -} from "containers/time/parsedTimeForDay"; +import { convertMonthStringToEnum } from "containers/time/parsedTimeForDate"; +import { convertDayStringToEnum } from "containers/time/parsedTimeForDay"; import { TimeInfoType } from "types"; export function parseToken(token: string) { token = token.trim().toLowerCase(); - if (isDay(token)) { - return { - type: TimeInfoType.DAY, - value: new ParsedTimeForDay(token).parse().value, - } as const; - } - - const testMonth = token.split(/\s/)[0]; - if (isMonth(testMonth)) { - return { - type: TimeInfoType.DATE, - value: new ParsedTimeForDate(token).parse().value, - } as const; - } if ( token === "24 hours" || token === "24 hrs" || diff --git a/src/utils/slack.ts b/src/utils/slack.ts index e7e9d59..c3c9052 100644 --- a/src/utils/slack.ts +++ b/src/utils/slack.ts @@ -4,7 +4,10 @@ async function wait(ms: number) { return new Promise((resolve) => setTimeout(resolve, ms)); } export async function notifySlack(message: string) { - if (env.IN_TEST_MODE) return; + if (env.IN_TEST_MODE) { + console.log("would've notified slack with message", message); + return; + } console.log("Sending message to slack:", message); try { await axios.post( diff --git a/src/utils/timeUtils.ts b/src/utils/timeUtils.ts index beccb11..e4704c0 100644 --- a/src/utils/timeUtils.ts +++ b/src/utils/timeUtils.ts @@ -1,17 +1,4 @@ -import { DayOfTheWeek, ITimeSlot, ITimeRange } from "types"; - -export function getNextDay(day: DayOfTheWeek): DayOfTheWeek { - const weekdays: DayOfTheWeek[] = [ - DayOfTheWeek.SUNDAY, - DayOfTheWeek.MONDAY, - DayOfTheWeek.TUESDAY, - DayOfTheWeek.WEDNESDAY, - DayOfTheWeek.THURSDAY, - DayOfTheWeek.FRIDAY, - DayOfTheWeek.SATURDAY, - ]; // ordered by time - return weekdays[(weekdays.indexOf(day) + 1) % 7]; -} +import { ITimeSlot, ITimeRange } from "types"; export function getMinutesSinceStartOfSunday(timeSlot: ITimeSlot) { return timeSlot.day * (24 * 60) + timeSlot.hour * 60 + timeSlot.minute; @@ -31,7 +18,7 @@ export function compareTimeSlots(timeSlot1: ITimeSlot, timeSlot2: ITimeSlot) { export function sortAndMergeTimeRanges(timeRanges: ITimeRange[]) { const MINUTES_IN_A_WEEK = 60 * 24 * 7; - const unwrappedTimeRanges = timeRanges + const unwrappedTimeRanges = [...timeRanges] .flatMap((rng) => { if (compareTimeSlots(rng.start, rng.end) > 0) { // unwrap the wrapped interval @@ -46,6 +33,7 @@ export function sortAndMergeTimeRanges(timeRanges: ITimeRange[]) { .sort((range1, range2) => compareTimeSlots(range1.start, range2.start)); const mergedRanges: ITimeRange[] = []; + // merge overlapping ranges for (const timeRange of unwrappedTimeRanges) { const lastTimeRange = mergedRanges.length ? mergedRanges[mergedRanges.length - 1] diff --git a/tests/integration.test.ts b/tests/integration.test.ts index 76a0dec..9020cbf 100644 --- a/tests/integration.test.ts +++ b/tests/integration.test.ts @@ -1,4 +1,3 @@ -import locationCoordinateOverwrites from "overwrites/locationCoordinateOverwrites"; import DiningParser from "../src/parser/diningParser"; import { expectedLocationData } from "./expectedData"; @@ -28,9 +27,143 @@ test("the whole thing, including locationOverwrites", async () => { ["92", "110", "113", "175", "108", "168"].includes(conceptId) // note that location 168 does not have a location overwrite and thus uses the one provided on the page ? `html/concepts/${conceptId}.html` : "html/blank.html", + serverDate: DateTime.fromObject({ + year: 2024, + month: 8, + day: 5, + }) as DateTime, }); const parser = new DiningParser({ - locationCoordinateOverwrites: locationCoordinateOverwrites, + locationCoordinateOverwrites: { + "136": { + lat: 40.44497677044086, + lng: -79.94526274161514, + }, + "134": { + lat: 40.44496677039454, + lng: -79.94543510284873, + }, + "127": { + lat: 40.44296, + lng: -79.941815, + }, + "175": { + lat: 40.44492728615915, + lng: -79.94544766519252, + }, + "185": { + lat: 40.44494190940949, + lng: -79.94547335554091, + }, + "154": { + lat: 40.44493626367144, + lng: -79.94534102575214, + }, + "173": { + lat: 40.44490020783157, + lng: -79.94540910407714, + }, + "192": { + lat: 40.44494958989201, + lng: -79.94541940780638, + }, + "155": { + lat: 40.44269873724883, + lng: -79.94664155857618, + }, + "82": { + lat: 40.44260716039899, + lng: -79.93995513782087, + }, + "98": { + lat: 40.44245836757329, + lng: -79.94002835619484, + }, + "178": { + lat: 40.44265640089745, + lng: -79.94021168805847, + }, + "114": { + lat: 40.44253705946464, + lng: -79.9400539411368, + }, + "95": { + lat: 40.44110394047497, + lng: -79.94341681666283, + }, + "103": { + lat: 40.44294406631636, + lng: -79.9421613966706, + }, + "91": { + lat: 40.44306965961788, + lng: -79.94207835253889, + }, + "174": { + lat: 40.44336219896886, + lng: -79.94196740444092, + }, + "113": { + lat: 40.44392022644891, + lng: -79.94220130436851, + }, + "108": { + lat: 40.44313373573427, + lng: -79.94252833667237, + }, + "138": { + lat: 40.44319390927541, + lng: -79.9420529542194, + }, + "184": { + lat: 40.4432203633545, + lng: -79.94203823851873, + }, + "193": { + lat: 40.44326107687846, + lng: -79.94202626834755, + }, + "88": { + lat: 40.44322866937478, + lng: -79.94252749706692, + }, + "148": { + lat: 40.44618769931711, + lng: -79.95093385349946, + }, + "110": { + lat: 40.44326419955131, + lng: -79.94551330831828, + }, + "84": { + lat: 40.44161664158368, + lng: -79.94285872206078, + }, + "188": { + lat: 40.44538605085632, + lng: -79.9430188095375, + }, + "190": { + lat: 40.44541493684331, + lng: -79.94298366401661, + }, + "115": { + lat: 40.44347617300122, + lng: -79.94480928001676, + }, + "92": { + lat: 40.44144439971656, + lng: -79.94195512801193, + }, + "94": { + lat: 40.44259053115122, + lng: -79.94597744494271, + }, + "180": { + lat: 40.44414285761781, + lng: -79.93888579754876, + }, + }, }); const parsedLocationData = await parser.process(); expect(parsedLocationData).toStrictEqual(expectedLocationData); @@ -44,6 +177,11 @@ test("specials for The Exchange", async () => { ["92", "110", "113", "175", "108"].includes(conceptId) ? `html/concepts/${conceptId}.html` : "html/blank.html", + serverDate: DateTime.fromObject({ + year: 2024, + month: 8, + day: 5, + }) as DateTime, }); const parser = new DiningParser(); expect((await parser.process()).map((data) => data.todaysSpecials)).toEqual( @@ -70,7 +208,13 @@ test("specials for The Exchange", async () => { test( "parser throws on repeated axios error", async () => { - mockAxiosGETMethodWithFilePaths({}); + mockAxiosGETMethodWithFilePaths({ + serverDate: DateTime.fromObject({ + year: 2024, + month: 8, + day: 5, + }) as DateTime, + }); const parser = new DiningParser(); await expect(async () => { @@ -424,7 +568,7 @@ describe("test time overwrites", () => { const diningParser = new DiningParser({ timeSlotOverwrites: { 113: { - "8/23/25": ["CLOSED"], + "8/23/2025": ["CLOSED"], }, }, }); @@ -456,7 +600,7 @@ describe("test time overwrites", () => { const diningParser = new DiningParser({ timeSlotOverwrites: { 113: { - "8/23/25": ["2:00 AM - 3:00 AM"], + "8/23/2025": ["2:00 AM - 3:00 AM"], }, }, }); @@ -489,13 +633,13 @@ describe("test time overwrites", () => { const diningParser = new DiningParser({ timeSlotOverwrites: { 113: { - "8/22/25": ["2:00 AM - 3:00 AM"], - "8/23/25": ["2:00 AM - 3:00 AM"], - "8/24/25": ["2:00 AM - 3:00 AM"], - "8/25/25": ["2:00 AM - 3:00 AM"], - "8/26/25": ["2:00 AM - 3:00 AM"], - "8/27/25": ["2:00 AM - 3:00 AM"], - "8/28/25": ["2:00 AM - 3:00 AM"], + "8/22/2025": ["2:00 AM - 3:00 AM"], + "8/23/2025": ["2:00 AM - 3:00 AM"], + "8/24/2025": ["2:00 AM - 3:00 AM"], + "8/25/2025": ["2:00 AM - 3:00 AM"], + "8/26/2025": ["2:00 AM - 3:00 AM"], + "8/27/2025": ["2:00 AM - 3:00 AM"], + "8/28/2025": ["2:00 AM - 3:00 AM"], }, }, }); @@ -528,7 +672,7 @@ describe("test time overwrites", () => { const diningParser = new DiningParser({ timeSlotOverwrites: { 113: { - "8/23/25": ["11:00 AM - 10:00 AM"], + "8/23/2025": ["11:00 AM - 10:00 AM"], }, }, }); @@ -560,14 +704,14 @@ describe("test time overwrites", () => { const diningParser = new DiningParser({ timeSlotOverwrites: { 113: { - "8/23/25": ["2:00 AM - 10:00 AM"], - "12/30/25": ["2:00 AM - 10:00 AM"], - "12/31/25": ["2:00 AM - 10:00 AM"], - "1/1/26": ["2:00 AM - 10:00 AM"], // typing this date just gave me an existential crisis. Thanks a lot. - "1/2/26": ["2:00 AM - 10:00 AM"], - "1/3/26": ["2:00 AM - 10:00 AM"], - "1/4/26": ["2:00 AM - 10:00 AM"], - "1/5/26": ["2:00 AM - 10:00 AM"], + "8/23/2025": ["2:00 AM - 10:00 AM"], + "12/30/2025": ["2:00 AM - 10:00 AM"], + "12/31/2025": ["2:00 AM - 10:00 AM"], + "1/1/2026": ["2:00 AM - 10:00 AM"], // typing this date just gave me an existential crisis. Thanks a lot. + "1/2/2026": ["2:00 AM - 10:00 AM"], + "1/3/2026": ["2:00 AM - 10:00 AM"], + "1/4/2026": ["2:00 AM - 10:00 AM"], + "1/5/2026": ["2:00 AM - 10:00 AM"], }, }, }); diff --git a/tests/mockAxios.ts b/tests/mockAxios.ts index 935f355..fcd1ef2 100644 --- a/tests/mockAxios.ts +++ b/tests/mockAxios.ts @@ -26,12 +26,12 @@ export function mockAxiosGETMethod({ specialsHTML?: string; soupsHTML?: string; conceptHTML?: (id: string) => string | undefined; - serverDate?: DateTime; + serverDate: DateTime; }) { (axios.get as jest.Mock).mockImplementation(async (url: string) => { return { data: getHTML(url), - headers: { date: (serverDate ?? DateTime.now()).toRFC2822() }, + headers: { date: serverDate.toRFC2822() }, }; }); @@ -60,7 +60,7 @@ export function mockAxiosGETMethodWithFilePaths({ specialsFilePath?: string; soupsFilePath?: string; getConceptFilePath?: (conceptId: string) => string; - serverDate?: DateTime; + serverDate: DateTime; }) { mockAxiosGETMethod({ conceptListHTML: getFileContent(conceptListFilePath), diff --git a/tests/mockTimings.ts b/tests/mockTimings.ts index bf5dd0a..fcdeca7 100644 --- a/tests/mockTimings.ts +++ b/tests/mockTimings.ts @@ -88,6 +88,11 @@ export function setUpTimingTest( getFileContent("html/concepts/113-tests.html") ); }, + serverDate: DateTime.fromObject({ + year: 2024, + month: 9, + day: 9, + }) as DateTime, // this was the date when the test page was scraped }); } From b1ea9a9066d65d4dbd9402b56f4abaf0b838469b Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Thu, 4 Sep 2025 20:06:35 -0400 Subject: [PATCH 17/65] chore: update time slot overwrite description --- src/overwrites/timeOverwrites.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/overwrites/timeOverwrites.ts b/src/overwrites/timeOverwrites.ts index 26d5a17..6cd8051 100644 --- a/src/overwrites/timeOverwrites.ts +++ b/src/overwrites/timeOverwrites.ts @@ -5,7 +5,7 @@ export type IAllTimeOverwrites = { }; /** - * key is in the format of MM/DD/YY (omit the first digit of the month if it's 0. same thing for day. (ex: Miku day is represented as 3/9/25)) + * key is in the format of MM/DD/YYYY (omit the first digit of the month if it's 0. same thing for day. (ex: Miku day is represented as 3/9/25)) * NOTE: There is no coalescing between the existing time string for that day and the overwritten time string. * If you choose to overwrite that day, you must do so completely. */ From 63e016c38783491c553526f0a6f68f0cbe52f8dc Mon Sep 17 00:00:00 2001 From: JackHurew Date: Fri, 12 Sep 2025 12:56:52 -0400 Subject: [PATCH 18/65] Dockerfile (Sorry this took so long) --- Dockerfile | 11 +++++-- README.md | 2 ++ docker-compose.yml | 37 +++++++++++++++++++++++ init.sql | 75 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 5 +++- src/db.ts | 18 +++++------ src/schema.ts | 8 +---- 7 files changed, 136 insertions(+), 20 deletions(-) create mode 100644 docker-compose.yml create mode 100644 init.sql diff --git a/Dockerfile b/Dockerfile index 0d58ec5..41398e6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,15 @@ FROM oven/bun:latest -WORKDIR /runtime -COPY . /runtime +WORKDIR /app + +COPY package.json bun.lock* ./ RUN bun install + +COPY . . + RUN bun run build EXPOSE 5010 -CMD bun run start + +CMD ["bun", "run", "start"] diff --git a/README.md b/README.md index 9d14e8e..0b075e4 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,8 @@ Note: To add new dependencies, use `bun add dependency-name`. To remove dependen Build: `docker build -f Dockerfile . -t dining` Run the server: `docker run -p 127.0.0.1:5010:5010 dining` Run bash inside it (for debugging): `docker run --rm -it --entrypoint bash -p 127.0.0.1:5010:5010 dining` +Close dockerfile: `docker-compose down --volumes` +Open dockerfile: `docker-compose up --build` ## Under the hood diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..51cbd5c --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,37 @@ +name: dining-api + +services: + postgres: + image: postgres:17.5 + container_name: dining-api-db + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: donotuseinproduction + POSTGRES_DB: dining_api + ports: + - "127.0.0.1:5433:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + - ./init.sql:/docker-entrypoint-initdb.d/init.sql + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 5s + timeout: 5s + retries: 10 + + api: + build: . + container_name: dining-api-server + ports: + - "5010:5010" + env_file: + - .env + environment: + - DATABASE_URL=postgresql://postgres:donotuseinproduction@postgres:5432/dining_api + depends_on: + postgres: + condition: service_healthy + restart: unless-stopped + +volumes: + postgres_data: \ No newline at end of file diff --git a/init.sql b/init.sql new file mode 100644 index 0000000..271ca9e --- /dev/null +++ b/init.sql @@ -0,0 +1,75 @@ +CREATE TABLE IF NOT EXISTS emails ( + name TEXT NOT NULL, + email TEXT NOT NULL +); + +create table dashboard_changes ( + conceptid integer primary key, +name TEXT, +description TEXT, +shortdescription TEXT, +times JSONB, +menu TEXT, +accepts_online_orders boolean +); + +insert into emails (name, email) values ('Bulgaria','bulgaria@un.org'), +('Czech Republic','czech@un.org'), +('Slovakia','slovakia@un.org'), +('Poland','poland@un.org'), +('Hungary','hungary@un.org'), +('Romania','romania@un.org'); + +INSERT INTO dashboard_changes ( + conceptid, + shortdescription +) VALUES ( + 110, + 'Bulgaria' +); + +INSERT INTO dashboard_changes + (conceptid, name, description, shortdescription, menu, times) +VALUES ( + 109, + 'Dockerfile test', + $$This is in SQL +This is the description +I hope you like SQL +I like SQL +SQL is fun +Is Drizzle? +idk$$, + 'This is a test admin override. Add these to override API data.', + 'https://www.youtube.com/watch?v=eYMdw4z1me8', + '[ + { + "start": { "day": 0, "hour": 12, "minute": 0 }, + "end": { "day": 0, "hour": 20, "minute": 0 } + }, +{ + "start": { "day": 0, "hour": 21, "minute": 0 }, + "end": { "day": 0, "hour": 22, "minute": 0 } + }, + { + "start": { "day": 1, "hour": 0, "minute": 30 }, + "end": { "day": 1, "hour": 23, "minute": 0 } + }, + { + "start": { "day": 2, "hour": 19, "minute": 30 }, + "end": { "day": 2, "hour": 20, "minute": 0 } + }, + { + "start": { "day": 3, "hour": 10, "minute": 30 }, + "end": { "day": 3, "hour": 13, "minute": 0 } + }, + { + "start": { "day": 4, "hour": 10, "minute": 30 }, + "end": { "day": 4, "hour": 10, "minute": 40 } + }, + { + "start": { "day": 5, "hour": 10, "minute": 30 }, + "end": { "day": 5, "hour": 16, "minute": 0 } + } + ]'::jsonb +); diff --git a/package.json b/package.json index f9b3a07..eb34345 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,10 @@ "build": "rollup -c", "db:generate": "drizzle-kit generate", "db:migrate": "drizzle-kit migrate", - "db:studio": "drizzle-kit studio" + "db:studio": "drizzle-kit studio", + "db:up": "docker-compose up -d", + "db:down": "docker-compose down", + "dev:local": "docker-compose up -d && dotenv -- tsx watch src/server.ts" }, "repository": { "type": "git", diff --git a/src/db.ts b/src/db.ts index d91c37d..faee9e0 100644 --- a/src/db.ts +++ b/src/db.ts @@ -9,14 +9,14 @@ import { ILocation } from 'types'; let pool: Pool | null = null; function getPool(): Pool { - if (pool === null) { - pool = new Pool({ - connectionString: env.DATABASE_URL, - ssl: { rejectUnauthorized: false }, // Required by Railway - }); + if (pool === null) { + pool = new Pool({ + connectionString: env.DATABASE_URL, + ssl: false, + }); + } + return pool; } - return pool; -} function getDb() { return drizzle(getPool(), { schema: { emails, dashboardChanges } }); @@ -52,10 +52,10 @@ export async function getChanges(): Promise { return result.map((row) => ({ conceptId: row.conceptid, name: row.name, - shortDescription: row.shortdescription || undefined, + shortDescription: row.shortdescription, description: row.description, url: `https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/${row.conceptid}`, - menu: row.menu || undefined, + menu: row.menu, location: "Unknown", coordinates: undefined, acceptsOnlineOrders: row.accepts_online_orders, diff --git a/src/schema.ts b/src/schema.ts index 83ed204..18c56ab 100644 --- a/src/schema.ts +++ b/src/schema.ts @@ -16,10 +16,4 @@ export const dashboardChanges = pgTable('dashboard_changes', { times: jsonb('times').$type().notNull(), menu: text('menu').notNull(), accepts_online_orders: boolean('accepts_online_orders').notNull(), -}); - -// Type exports for use in other files -export type Email = typeof emails.$inferSelect; -export type NewEmail = typeof emails.$inferInsert; -export type DashboardChange = typeof dashboardChanges.$inferSelect; -export type NewDashboardChange = typeof dashboardChanges.$inferInsert; \ No newline at end of file +}); \ No newline at end of file From b6a710ac90da7ff297e1d39b9196bb539da1e262 Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Mon, 15 Sep 2025 13:30:27 -0400 Subject: [PATCH 19/65] fix: change original Dockerfile to match Railway version, upd readme --- Dockerfile | 10 ++---- README.md | 15 ++++----- docker-compose.yml | 2 +- src/db.ts | 81 ++++++++++++++++++++++++---------------------- 4 files changed, 52 insertions(+), 56 deletions(-) diff --git a/Dockerfile b/Dockerfile index 41398e6..fb933db 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,15 +1,9 @@ FROM oven/bun:latest -WORKDIR /app - -COPY package.json bun.lock* ./ - -RUN bun install - +WORKDIR /runtime COPY . . - +RUN bun install RUN bun run build EXPOSE 5010 - CMD ["bun", "run", "start"] diff --git a/README.md b/README.md index 0b075e4..cec0704 100644 --- a/README.md +++ b/README.md @@ -30,18 +30,17 @@ Now install the API's dependencies by 'cd'-ing into the root of the repository a bun install ``` -Then, you can run the server with `bun dev` and it should work! You can also use -`bun run dev` since `bun dev` is its shorthand version. +To start a local instance of the database used by the dining api, run `docker-compose up --build -d postgres` + +Then, you can run the server with `bun dev` (or `bun run dev`) and it should work! Note: To add new dependencies, use `bun add dependency-name`. To remove dependencies, use `bun remove dependency-name`. Run `bun outdated` to see what dependencies are outdated and `bun update` to update all outdated dependencies to the latest version. -## Testing the Dockerfile +## Testing the production build of the backend -Build: `docker build -f Dockerfile . -t dining` -Run the server: `docker run -p 127.0.0.1:5010:5010 dining` -Run bash inside it (for debugging): `docker run --rm -it --entrypoint bash -p 127.0.0.1:5010:5010 dining` -Close dockerfile: `docker-compose down --volumes` -Open dockerfile: `docker-compose up --build` +Build and run db + api: `docker-compose up --build` +Run bash inside it (for debugging): `docker run --rm -it --entrypoint bash dining-api-server` +Close dockerfile + delete volumes: `docker-compose down --volumes` ## Under the hood diff --git a/docker-compose.yml b/docker-compose.yml index 51cbd5c..3c59320 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,7 +19,7 @@ services: timeout: 5s retries: 10 - api: + server: build: . container_name: dining-api-server ports: diff --git a/src/db.ts b/src/db.ts index faee9e0..588e650 100644 --- a/src/db.ts +++ b/src/db.ts @@ -1,22 +1,22 @@ // db.ts -import { drizzle } from 'drizzle-orm/node-postgres'; -import { Pool } from 'pg'; -import 'dotenv/config'; -import { env } from 'env'; -import { emails, dashboardChanges } from './schema'; -import { ILocation } from 'types'; +import { drizzle } from "drizzle-orm/node-postgres"; +import { Pool } from "pg"; +import "dotenv/config"; +import { env } from "env"; +import { emails, dashboardChanges } from "./schema"; +import { ILocation } from "types"; let pool: Pool | null = null; function getPool(): Pool { - if (pool === null) { - pool = new Pool({ - connectionString: env.DATABASE_URL, - ssl: false, - }); - } - return pool; + if (pool === null) { + pool = new Pool({ + connectionString: env.DATABASE_URL, + ssl: false, + }); } + return pool; +} function getDb() { return drizzle(getPool(), { schema: { emails, dashboardChanges } }); @@ -24,11 +24,13 @@ function getDb() { export async function getEmails(): Promise<{ name: string; email: string }[]> { const db = getDb(); - const result = await db.select({ - name: emails.name, - email: emails.email, - }).from(emails); - + const result = await db + .select({ + name: emails.name, + email: emails.email, + }) + .from(emails); + // Remove 'mailto:' if present return result.map((row) => ({ name: row.name, @@ -36,10 +38,10 @@ export async function getEmails(): Promise<{ name: string; email: string }[]> { })); } - export async function getChanges(): Promise { - const db = getDb(); - const result = await db.select({ + const db = getDb(); + const result = await db + .select({ conceptid: dashboardChanges.conceptid, name: dashboardChanges.name, description: dashboardChanges.description, @@ -47,21 +49,22 @@ export async function getChanges(): Promise { times: dashboardChanges.times, menu: dashboardChanges.menu, accepts_online_orders: dashboardChanges.accepts_online_orders, - }).from(dashboardChanges); - - return result.map((row) => ({ - conceptId: row.conceptid, - name: row.name, - shortDescription: row.shortdescription, - description: row.description, - url: `https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/${row.conceptid}`, - menu: row.menu, - location: "Unknown", - coordinates: undefined, - acceptsOnlineOrders: row.accepts_online_orders, - times: row.times, - todaysSpecials: undefined, - todaysSoups: undefined, - })); - } - \ No newline at end of file + }) + .from(dashboardChanges) + .catch(() => []); + + return result.map((row) => ({ + conceptId: row.conceptid, + name: row.name, + shortDescription: row.shortdescription, + description: row.description, + url: `https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/${row.conceptid}`, + menu: row.menu, + location: "Unknown", + coordinates: undefined, + acceptsOnlineOrders: row.accepts_online_orders, + times: row.times, + todaysSpecials: undefined, + todaysSoups: undefined, + })); +} From 60ff9b5e826347d6f5025fc6331ff6762283c07d Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Tue, 16 Sep 2025 11:30:33 -0400 Subject: [PATCH 20/65] feat: automatic db migrations, move overwrite logic directly under api endpoint, create bun and pnpm dockerfiles --- .env.test | 5 ++ Dockerfile | 9 -- Dockerfile.bun | 9 ++ Dockerfile.pnpm | 19 ++++ Dockerfile.pnpm.simple | 12 +++ README.md | 17 ++-- docker-compose.yml | 14 +-- drizzle.config.ts | 15 ++-- drizzle/0000_wise_firestar.sql | 19 ++++ drizzle/meta/0000_snapshot.json | 140 ++++++++++++++++++++++++++++++ drizzle/meta/_journal.json | 13 +++ init.sql | 75 ---------------- package.json | 13 ++- run-bun.sh | 2 + run-pnpm.sh | 3 + src/containers/locationBuilder.ts | 30 +------ src/db.ts | 70 --------------- src/db/db.ts | 13 +++ src/db/query.ts | 72 +++++++++++++++ src/db/schema.ts | 32 +++++++ src/db/seed.ts | 27 ++++++ src/parser/diningParser.ts | 12 --- src/schema.ts | 19 ---- src/server.ts | 67 ++++++++------ tests/expectedData.ts | 56 ++++++------ 25 files changed, 468 insertions(+), 295 deletions(-) create mode 100644 .env.test delete mode 100644 Dockerfile create mode 100644 Dockerfile.bun create mode 100644 Dockerfile.pnpm create mode 100644 Dockerfile.pnpm.simple create mode 100644 drizzle/0000_wise_firestar.sql create mode 100644 drizzle/meta/0000_snapshot.json create mode 100644 drizzle/meta/_journal.json delete mode 100644 init.sql create mode 100644 run-bun.sh create mode 100644 run-pnpm.sh delete mode 100644 src/db.ts create mode 100644 src/db/db.ts create mode 100644 src/db/query.ts create mode 100644 src/db/schema.ts create mode 100644 src/db/seed.ts delete mode 100644 src/schema.ts diff --git a/.env.test b/.env.test new file mode 100644 index 0000000..61ba3cb --- /dev/null +++ b/.env.test @@ -0,0 +1,5 @@ +# placeholder variables used for running tests +AXIOS_RETRY_INTERVAL_MS=0 +IN_TEST_MODE=true +SLACK_WEBHOOK_URL=/ +DATABASE_URL=/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index fb933db..0000000 --- a/Dockerfile +++ /dev/null @@ -1,9 +0,0 @@ -FROM oven/bun:latest - -WORKDIR /runtime -COPY . . -RUN bun install -RUN bun run build - -EXPOSE 5010 -CMD ["bun", "run", "start"] diff --git a/Dockerfile.bun b/Dockerfile.bun new file mode 100644 index 0000000..31460d1 --- /dev/null +++ b/Dockerfile.bun @@ -0,0 +1,9 @@ +# for starting up the dining api (used both in local dev and railway builds) +FROM oven/bun:latest + +WORKDIR /runtime +COPY . . +RUN bun install +RUN bun run build +EXPOSE 5010 +CMD ["./run-bun.sh"] \ No newline at end of file diff --git a/Dockerfile.pnpm b/Dockerfile.pnpm new file mode 100644 index 0000000..3d77acc --- /dev/null +++ b/Dockerfile.pnpm @@ -0,0 +1,19 @@ +FROM node:24-slim AS base +ENV PNPM_HOME="/pnpm" +ENV PATH="$PNPM_HOME:$PATH" +RUN corepack enable +COPY . /app +WORKDIR /app + +FROM base AS prod-deps +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod + +FROM base AS build +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install +RUN pnpm run build + +FROM base +COPY --from=prod-deps /app/node_modules /app/node_modules +COPY --from=build /app/dist /app/dist +EXPOSE 5010 +CMD ["sh","./run-pnpm.sh"] diff --git a/Dockerfile.pnpm.simple b/Dockerfile.pnpm.simple new file mode 100644 index 0000000..dfde470 --- /dev/null +++ b/Dockerfile.pnpm.simple @@ -0,0 +1,12 @@ +FROM node:24-slim AS base +ENV PNPM_HOME="/pnpm" +ENV PATH="$PNPM_HOME:$PATH" +RUN corepack enable +COPY . /app +WORKDIR /app + +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install +RUN pnpm run build + +EXPOSE 5010 +CMD ["sh","./run-pnpm.sh"] diff --git a/README.md b/README.md index cec0704..b3429bb 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ > [!IMPORTANT] > Make sure `bun` is on the latest version! (Earlier versions are rather buggy) Run `bun upgrade` to update. -This Dining API scrapes location data from the CMU dining sites and distributes it as a RESTful API. Access the API [here](https://apis.scottylabs.org/dining/). +This Dining API scrapes location data from the CMU dining sites and distributes it as a RESTful API. Access the API [here](https://dining.apis.scottylabs.org/). To build and deploy the service, you'll need [Bun](https://bun.sh), which you should install beforehand. @@ -30,15 +30,20 @@ Now install the API's dependencies by 'cd'-ing into the root of the repository a bun install ``` -To start a local instance of the database used by the dining api, run `docker-compose up --build -d postgres` - -Then, you can run the server with `bun dev` (or `bun run dev`) and it should work! +Start your local database with `db:start` and then start the server with `bun dev` (or `bun run dev`) and it should work, assuming you have the correct env variables. (To see the contents of the database, I recommend using DBeaver. You can also run `bun db:studio` to start up drizzle studio) Note: To add new dependencies, use `bun add dependency-name`. To remove dependencies, use `bun remove dependency-name`. Run `bun outdated` to see what dependencies are outdated and `bun update` to update all outdated dependencies to the latest version. -## Testing the production build of the backend +## Database schema changes (important!) + +When you make changes to the database schema, be sure to run `bun db:push` to keep your local db in sync. + +Before merging your PR, be sure to run `bun db:generate` to generate a migration file, which will then be automatically applied to the staging and production databases when deployed. + +To test if the migration files work, you can run `bun run-prod`, which will spin up a production version of the server and a postgres database mounted on a new volume. The server is created using the same Dockerfile used in our Railway deployments, so if it works locally, it (probably) works in production as well. + +## Extra docker commands -Build and run db + api: `docker-compose up --build` Run bash inside it (for debugging): `docker run --rm -it --entrypoint bash dining-api-server` Close dockerfile + delete volumes: `docker-compose down --volumes` diff --git a/docker-compose.yml b/docker-compose.yml index 3c59320..f53d5a9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,10 +9,9 @@ services: POSTGRES_PASSWORD: donotuseinproduction POSTGRES_DB: dining_api ports: - - "127.0.0.1:5433:5432" + - "127.0.0.1:5432:5432" volumes: - - postgres_data:/var/lib/postgresql/data - - ./init.sql:/docker-entrypoint-initdb.d/init.sql + - ${VOL_SRC}:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s @@ -20,18 +19,19 @@ services: retries: 10 server: - build: . + build: + dockerfile: Dockerfile.pnpm.simple container_name: dining-api-server ports: - "5010:5010" env_file: - .env environment: - - DATABASE_URL=postgresql://postgres:donotuseinproduction@postgres:5432/dining_api + - DATABASE_URL=postgresql://postgres:donotuseinproduction@dining-api-db:5432/dining_api depends_on: postgres: condition: service_healthy restart: unless-stopped - volumes: - postgres_data: \ No newline at end of file + postgres_data: + postgres_dev_data: \ No newline at end of file diff --git a/drizzle.config.ts b/drizzle.config.ts index 80bf4c8..21277b1 100644 --- a/drizzle.config.ts +++ b/drizzle.config.ts @@ -1,11 +1,12 @@ -import type { Config } from 'drizzle-kit'; -import { env } from './src/env'; +import { defineConfig } from "drizzle-kit"; +import { env } from "./src/env"; -export default { - schema: './src/schema.ts', - out: './drizzle', - dialect: 'postgresql', +export default defineConfig({ + out: "./drizzle", + schema: "./src/db/schema.ts", + dialect: "postgresql", dbCredentials: { url: env.DATABASE_URL, }, -} satisfies Config; \ No newline at end of file + verbose: true, +}); diff --git a/drizzle/0000_wise_firestar.sql b/drizzle/0000_wise_firestar.sql new file mode 100644 index 0000000..81dca73 --- /dev/null +++ b/drizzle/0000_wise_firestar.sql @@ -0,0 +1,19 @@ +CREATE TABLE "emails" ( + "id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "emails_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1), + "name" text NOT NULL, + "email" text NOT NULL +); +--> statement-breakpoint +CREATE TABLE "overwrites_table" ( + "concept_id" integer PRIMARY KEY NOT NULL, + "name" text, + "description" text, + "short_description" text, + "url" text, + "menu" text, + "location" text, + "coordinateLat" numeric, + "coordinateLng" numeric, + "accepts_online_orders" boolean, + "times" jsonb +); diff --git a/drizzle/meta/0000_snapshot.json b/drizzle/meta/0000_snapshot.json new file mode 100644 index 0000000..aa1fbfb --- /dev/null +++ b/drizzle/meta/0000_snapshot.json @@ -0,0 +1,140 @@ +{ + "id": "51fd9b98-60b4-4660-92b5-fa6adeb2b033", + "prevId": "00000000-0000-0000-0000-000000000000", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.emails": { + "name": "emails", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "emails_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.overwrites_table": { + "name": "overwrites_table", + "schema": "", + "columns": { + "concept_id": { + "name": "concept_id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "short_description": { + "name": "short_description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "menu": { + "name": "menu", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "coordinateLat": { + "name": "coordinateLat", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "coordinateLng": { + "name": "coordinateLng", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "accepts_online_orders": { + "name": "accepts_online_orders", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "times": { + "name": "times", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json new file mode 100644 index 0000000..47b6d21 --- /dev/null +++ b/drizzle/meta/_journal.json @@ -0,0 +1,13 @@ +{ + "version": "7", + "dialect": "postgresql", + "entries": [ + { + "idx": 0, + "version": "7", + "when": 1757973116166, + "tag": "0000_wise_firestar", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/init.sql b/init.sql deleted file mode 100644 index 271ca9e..0000000 --- a/init.sql +++ /dev/null @@ -1,75 +0,0 @@ -CREATE TABLE IF NOT EXISTS emails ( - name TEXT NOT NULL, - email TEXT NOT NULL -); - -create table dashboard_changes ( - conceptid integer primary key, -name TEXT, -description TEXT, -shortdescription TEXT, -times JSONB, -menu TEXT, -accepts_online_orders boolean -); - -insert into emails (name, email) values ('Bulgaria','bulgaria@un.org'), -('Czech Republic','czech@un.org'), -('Slovakia','slovakia@un.org'), -('Poland','poland@un.org'), -('Hungary','hungary@un.org'), -('Romania','romania@un.org'); - -INSERT INTO dashboard_changes ( - conceptid, - shortdescription -) VALUES ( - 110, - 'Bulgaria' -); - -INSERT INTO dashboard_changes - (conceptid, name, description, shortdescription, menu, times) -VALUES ( - 109, - 'Dockerfile test', - $$This is in SQL -This is the description -I hope you like SQL -I like SQL -SQL is fun -Is Drizzle? -idk$$, - 'This is a test admin override. Add these to override API data.', - 'https://www.youtube.com/watch?v=eYMdw4z1me8', - '[ - { - "start": { "day": 0, "hour": 12, "minute": 0 }, - "end": { "day": 0, "hour": 20, "minute": 0 } - }, -{ - "start": { "day": 0, "hour": 21, "minute": 0 }, - "end": { "day": 0, "hour": 22, "minute": 0 } - }, - { - "start": { "day": 1, "hour": 0, "minute": 30 }, - "end": { "day": 1, "hour": 23, "minute": 0 } - }, - { - "start": { "day": 2, "hour": 19, "minute": 30 }, - "end": { "day": 2, "hour": 20, "minute": 0 } - }, - { - "start": { "day": 3, "hour": 10, "minute": 30 }, - "end": { "day": 3, "hour": 13, "minute": 0 } - }, - { - "start": { "day": 4, "hour": 10, "minute": 30 }, - "end": { "day": 4, "hour": 10, "minute": 40 } - }, - { - "start": { "day": 5, "hour": 10, "minute": 30 }, - "end": { "day": 5, "hour": 16, "minute": 0 } - } - ]'::jsonb -); diff --git a/package.json b/package.json index eb34345..9cdf33d 100644 --- a/package.json +++ b/package.json @@ -4,17 +4,16 @@ "description": "Carnegie Mellon University Dining API", "main": "server.ts", "scripts": { - "test": "AXIOS_RETRY_INTERVAL_MS=0 IN_TEST_MODE=true SLACK_WEBHOOK_URL=/ DATABASE_URL=/ jest --coverage", - "test-watch": "AXIOS_RETRY_INTERVAL_MS=0 IN_TEST_MODE=true SLACK_WEBHOOK_URL=/ jest --watch", "dev": "dotenv -- tsx watch src/server.ts", - "start": "NODE_ENV=production dotenv -- node dist/server.js", "build": "rollup -c", + "start": "NODE_ENV=production node dist/server.js", + "test": "dotenv -e .env.test -- jest --coverage", + "run-prod": "VOL_SRC=postgres_data docker-compose up --build", + "db:start": "VOL_SRC=postgres_dev_data docker-compose up -d postgres --build", + "db:push": "drizzle-kit push", "db:generate": "drizzle-kit generate", "db:migrate": "drizzle-kit migrate", - "db:studio": "drizzle-kit studio", - "db:up": "docker-compose up -d", - "db:down": "docker-compose down", - "dev:local": "docker-compose up -d && dotenv -- tsx watch src/server.ts" + "db:studio": "drizzle-kit studio" }, "repository": { "type": "git", diff --git a/run-bun.sh b/run-bun.sh new file mode 100644 index 0000000..b210528 --- /dev/null +++ b/run-bun.sh @@ -0,0 +1,2 @@ +echo "running db migrations..." +bun db:migrate && bun run start \ No newline at end of file diff --git a/run-pnpm.sh b/run-pnpm.sh new file mode 100644 index 0000000..5218878 --- /dev/null +++ b/run-pnpm.sh @@ -0,0 +1,3 @@ +echo "running db migrations..." +pnpm db:migrate && pnpm run start +# we stack them like this so if the migration fails, the server never starts \ No newline at end of file diff --git a/src/containers/locationBuilder.ts b/src/containers/locationBuilder.ts index 0eca62e..58ceaf7 100644 --- a/src/containers/locationBuilder.ts +++ b/src/containers/locationBuilder.ts @@ -2,13 +2,7 @@ import { getHTMLResponse } from "utils/requestUtils"; import { load } from "cheerio"; import type { Element } from "domhandler"; import { getTimeRangesFromString } from "./timeBuilder"; -import { - ICoordinate, - ILocation, - ILocationCoordinateOverwrites, - ISpecial, - ITimeRange, -} from "../types"; +import { ICoordinate, ILocation, ISpecial, ITimeRange } from "../types"; import { sortAndMergeTimeRanges } from "utils/timeUtils"; /** @@ -41,15 +35,6 @@ export default class LocationBuilder { this.shortDescription = load(card)("div.description").text().trim(); } - overwriteLocationCoordinates(overwrites: ILocationCoordinateOverwrites) { - if ( - this.conceptId !== undefined && - overwrites[this.conceptId] !== undefined - ) { - this.coordinates = overwrites[this.conceptId]; - } - } - setSoup(soupList: Record) { if ( this.conceptId !== undefined && @@ -140,17 +125,4 @@ export default class LocationBuilder { todaysSoups: this.soups, }; } - - applyOverride(override: ILocation) { - // Only apply if conceptId matches (optional guard) - if (this.conceptId !== override.conceptId && override.conceptId !== undefined) return; - if (override.name !== null && override.name !== undefined && override.name !== "") this.name = override.name; - if (override.description !== null && override.description !== undefined && override.description !== "") this.description = override.description; - if (override.shortDescription !== null && override.shortDescription !== undefined) - this.shortDescription = override.shortDescription; - if (override.times !== null && override.times !== undefined) this.times = override.times; - if (override.menu !== null && override.menu !== undefined) this.menu = override.menu; - if (override.acceptsOnlineOrders !== null && override.acceptsOnlineOrders !== undefined) - this.acceptsOnlineOrders = override.acceptsOnlineOrders; - } } diff --git a/src/db.ts b/src/db.ts deleted file mode 100644 index 588e650..0000000 --- a/src/db.ts +++ /dev/null @@ -1,70 +0,0 @@ -// db.ts -import { drizzle } from "drizzle-orm/node-postgres"; -import { Pool } from "pg"; -import "dotenv/config"; -import { env } from "env"; -import { emails, dashboardChanges } from "./schema"; -import { ILocation } from "types"; - -let pool: Pool | null = null; - -function getPool(): Pool { - if (pool === null) { - pool = new Pool({ - connectionString: env.DATABASE_URL, - ssl: false, - }); - } - return pool; -} - -function getDb() { - return drizzle(getPool(), { schema: { emails, dashboardChanges } }); -} - -export async function getEmails(): Promise<{ name: string; email: string }[]> { - const db = getDb(); - const result = await db - .select({ - name: emails.name, - email: emails.email, - }) - .from(emails); - - // Remove 'mailto:' if present - return result.map((row) => ({ - name: row.name, - email: row.email.replace(/^mailto:/, ""), - })); -} - -export async function getChanges(): Promise { - const db = getDb(); - const result = await db - .select({ - conceptid: dashboardChanges.conceptid, - name: dashboardChanges.name, - description: dashboardChanges.description, - shortdescription: dashboardChanges.shortdescription, - times: dashboardChanges.times, - menu: dashboardChanges.menu, - accepts_online_orders: dashboardChanges.accepts_online_orders, - }) - .from(dashboardChanges) - .catch(() => []); - - return result.map((row) => ({ - conceptId: row.conceptid, - name: row.name, - shortDescription: row.shortdescription, - description: row.description, - url: `https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/${row.conceptid}`, - menu: row.menu, - location: "Unknown", - coordinates: undefined, - acceptsOnlineOrders: row.accepts_online_orders, - times: row.times, - todaysSpecials: undefined, - todaysSoups: undefined, - })); -} diff --git a/src/db/db.ts b/src/db/db.ts new file mode 100644 index 0000000..558c7bb --- /dev/null +++ b/src/db/db.ts @@ -0,0 +1,13 @@ +import { drizzle } from "drizzle-orm/node-postgres"; +import { Pool } from "pg"; +import { env } from "env"; +import * as schema from "./schema"; + +const pool: Pool = new Pool({ + connectionString: env.DATABASE_URL, + ssl: false, +}); + +export const db = drizzle(pool, { + schema, +}); diff --git a/src/db/query.ts b/src/db/query.ts new file mode 100644 index 0000000..0690c8e --- /dev/null +++ b/src/db/query.ts @@ -0,0 +1,72 @@ +import { emailTable, overwritesTable } from "./schema"; +import { ILocation } from "types"; +import { db } from "./db"; +import { notifySlack } from "utils/slack"; + +export async function getEmails(): Promise<{ name: string; email: string }[]> { + const result = await db + .select({ + name: emailTable.name, + email: emailTable.email, + }) + .from(emailTable); + + // Remove 'mailto:' if present + return result.map((row) => ({ + name: row.name, + email: row.email.replace(/^mailto:/, ""), + })); +} + +/** + * + * @returns object that maps ids to overrides, where each value in the override map is guaranteed to be non-null. (important!!) + */ +export async function getOverrides() { + const overrides = await db + .select() + .from(overwritesTable) + .catch((e) => { + notifySlack(` Failed to fetch overwrites with error ${e}`); + return []; + }); + + const idToOverrideMap = overrides.reduce<{ + [conceptId in string]?: Partial; + }>((accumulator, curLocation) => { + const reformattedObjWithNonNullFields: Partial = { + ...(curLocation.acceptsOnlineOrders !== null && { + acceptsOnlineOrders: curLocation.acceptsOnlineOrders, + }), + ...(curLocation.description !== null && { + description: curLocation.description, + }), + ...(curLocation.location !== null && { location: curLocation.location }), + ...(curLocation.menu !== null && { menu: curLocation.menu }), + ...(curLocation.name !== null && { name: curLocation.name }), + ...(curLocation.shortDescription !== null && { + shortDescription: curLocation.shortDescription, + }), + ...(curLocation.times !== null && { times: curLocation.times }), + ...(curLocation.url !== null && { url: curLocation.url }), + ...(curLocation.coordinateLat !== null && + curLocation.coordinateLng !== null && { + coordinates: { + lat: curLocation.coordinateLat, + lng: curLocation.coordinateLng, + }, + }), + }; + return { + ...accumulator, + [curLocation.conceptId]: { + ...Object.fromEntries( + Object.entries(reformattedObjWithNonNullFields).filter( + ([_, v]) => v !== null + ) + ), + }, + }; + }, {}); + return idToOverrideMap; +} diff --git a/src/db/schema.ts b/src/db/schema.ts new file mode 100644 index 0000000..d53a385 --- /dev/null +++ b/src/db/schema.ts @@ -0,0 +1,32 @@ +import { + pgTable, + text, + integer, + boolean, + jsonb, + decimal, +} from "drizzle-orm/pg-core"; +import { ITimeRange } from "../types"; + +export const emailTable = pgTable("emails", { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + name: text("name").notNull(), + email: text("email").notNull(), +}); + +/** + * Includes everything in ILocation except for soups and specials + */ +export const overwritesTable = pgTable("overwrites_table", { + conceptId: integer("concept_id").notNull().primaryKey(), + name: text("name"), + description: text("description"), + shortDescription: text("short_description"), + url: text("url"), + menu: text("menu"), + location: text("location"), + coordinateLat: decimal({ mode: "number", scale: 30 }), + coordinateLng: decimal({ mode: "number", scale: 30 }), + acceptsOnlineOrders: boolean("accepts_online_orders"), + times: jsonb("times").$type(), +}); diff --git a/src/db/seed.ts b/src/db/seed.ts new file mode 100644 index 0000000..4f364af --- /dev/null +++ b/src/db/seed.ts @@ -0,0 +1,27 @@ +import { db } from "./db"; +import { emailTable } from "./schema"; + +export async function populateEmails() { + await db.insert(emailTable).values([ + { + email: "czech@un.org", + name: "Czech Republic", + }, + { + email: "slovakia@un.org", + name: "Slovakia", + }, + { + email: "poland@un.org", + name: "Poland", + }, + { + email: "hungary@un.org", + name: "Hungary", + }, + { + email: "romania@un.org", + name: "Romania", + }, + ]); +} diff --git a/src/parser/diningParser.ts b/src/parser/diningParser.ts index 6ba7834..b7cf56a 100644 --- a/src/parser/diningParser.ts +++ b/src/parser/diningParser.ts @@ -3,8 +3,6 @@ import { load } from "cheerio"; import LocationBuilder from "../containers/locationBuilder"; import { retrieveSpecials } from "../containers/specials/specialsBuilder"; import { ILocation, ISpecial } from "types"; -import locationCoordinateOverwrites from "overwrites/locationCoordinateOverwrites"; -import { getChanges } from "../db"; /** * Retrieves the HTML from the CMU Dining website and parses the information @@ -24,22 +22,12 @@ export default class DiningParser { const locationBuilders = await this.initializeLocationBuildersFromMainPage(); - const overrides = await getChanges(); - const [specials, soups] = await this.fetchSpecials(); for (const builder of locationBuilders) { await builder.populateDetailedInfo(); builder.setSoup(soups); builder.setSpecials(specials); - builder.overwriteLocationCoordinates(locationCoordinateOverwrites); - - const override = overrides.find( - (o) => o.conceptId === builder.getConceptId() - ); - if (override) { - builder.applyOverride(override); - } } return locationBuilders.map((builder) => builder.build()); diff --git a/src/schema.ts b/src/schema.ts deleted file mode 100644 index 18c56ab..0000000 --- a/src/schema.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { pgTable, text, integer, boolean, jsonb } from 'drizzle-orm/pg-core'; -import { ITimeRange } from './types'; - -// Emails table schema -export const emails = pgTable('emails', { - name: text('name').notNull(), - email: text('email').notNull(), -}); - -// Dashboard changes table schema -export const dashboardChanges = pgTable('dashboard_changes', { - conceptid: integer('conceptid').notNull(), - name: text('name').notNull(), - description: text('description').notNull(), - shortdescription: text('shortdescription').notNull(), - times: jsonb('times').$type().notNull(), - menu: text('menu').notNull(), - accepts_online_orders: boolean('accepts_online_orders').notNull(), -}); \ No newline at end of file diff --git a/src/server.ts b/src/server.ts index 7542670..fcdde02 100644 --- a/src/server.ts +++ b/src/server.ts @@ -6,12 +6,13 @@ import { env } from "env"; import { notifySlack } from "utils/slack"; import { node } from "@elysiajs/node"; import { getDiffsBetweenLocationData } from "utils/diff"; -import { getEmails } from "./db"; -import { getChanges } from "./db"; import LocationMerger from "utils/locationMerger"; +import { getEmails, getOverrides } from "db/query"; let cachedLocations: ILocation[] = []; - +function getCachedLocations() { + return applyOverrides(cachedLocations); +} async function reload(): Promise { const now = new Date(); console.log(`Reloading Dining API: ${now}`); @@ -46,6 +47,17 @@ async function reload(): Promise { } } +async function applyOverrides(locations: ILocation[]): Promise { + const overrides = await getOverrides(); + return locations.map((location) => { + const overrideData = overrides[location.conceptId]; + if (overrideData === undefined) return location; + return { + ...location, + ...overrideData, + }; + }); +} export const app = new Elysia({ adapter: node() }); // I don't trust bun (as a runtime) enough (Eric Xu - 7/18/2025). This may change in the future, but bun is currently NOT a full drop-in replacement for node and is still rather unstable from personal experience app.onError(({ error, path, code }) => { @@ -65,10 +77,10 @@ app.get("/", () => { return "ScottyLabs Dining API"; }); -app.get("/locations", () => ({ locations: cachedLocations })); +app.get("/locations", async () => ({ locations: await getCachedLocations() })); -app.get("/location/:name", ({ params: { name } }) => { - const filteredLocation = cachedLocations.filter((location) => { +app.get("/location/:name", async ({ params: { name } }) => { + const filteredLocation = (await getCachedLocations()).filter((location) => { return location.name?.toLowerCase().includes(name.toLowerCase()); }); return { @@ -76,29 +88,32 @@ app.get("/location/:name", ({ params: { name } }) => { }; }); -app.get("/locations/time/:day/:hour/:min", ({ params: { day, hour, min } }) => { - const result = cachedLocations.filter((el) => { - let returning = false; - el.times.forEach((element) => { - const startMins = - element.start.day * 1440 + - element.start.hour * 60 + - element.start.minute; - const endMins = - element.end.day * 1440 + element.end.hour * 60 + element.end.minute; - const currentMins = - parseInt(day) * 1440 + parseInt(hour) * 60 + parseInt(min); - if (currentMins >= startMins && currentMins < endMins) { - returning = true; - } +app.get( + "/locations/time/:day/:hour/:min", + async ({ params: { day, hour, min } }) => { + const result = (await getCachedLocations()).filter((el) => { + let returning = false; + el.times.forEach((element) => { + const startMins = + element.start.day * 1440 + + element.start.hour * 60 + + element.start.minute; + const endMins = + element.end.day * 1440 + element.end.hour * 60 + element.end.minute; + const currentMins = + parseInt(day) * 1440 + parseInt(hour) * 60 + parseInt(min); + if (currentMins >= startMins && currentMins < endMins) { + returning = true; + } + }); + return returning; }); - return returning; - }); - return { locations: result }; -}); + return { locations: result }; + } +); app.get("/api/emails", getEmails); -app.get("/api/changes", getChanges); +app.get("/api/changes", getOverrides); app.post( "/api/sendSlackMessage", diff --git a/tests/expectedData.ts b/tests/expectedData.ts index 9730609..5a9d02f 100644 --- a/tests/expectedData.ts +++ b/tests/expectedData.ts @@ -9,7 +9,7 @@ export const expectedLocationData = [ url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/113", location: "Cohon Center, Second floor", menu: "https://web.archive.org/web/20230806004812/https://apps.studentaffairs.cmu.edu/dining/dashboard_images/Production/menus/113/abp-menu6.pdf", - coordinates: { lat: 40.44392022644891, lng: -79.94220130436851 }, + coordinates: { lat: 40.44, lng: -79.94 }, acceptsOnlineOrders: true, times: [ { @@ -52,7 +52,7 @@ export const expectedLocationData = [ url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/184", location: "", menu: undefined, - coordinates: { lat: 40.4432203633545, lng: -79.94203823851873 }, + coordinates: undefined, acceptsOnlineOrders: false, times: [], todaysSpecials: undefined, @@ -67,7 +67,7 @@ export const expectedLocationData = [ url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/95", location: "", menu: undefined, - coordinates: { lat: 40.44110394047497, lng: -79.94341681666283 }, + coordinates: undefined, acceptsOnlineOrders: false, times: [], todaysSpecials: undefined, @@ -81,7 +81,7 @@ export const expectedLocationData = [ url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/134", location: "", menu: undefined, - coordinates: { lat: 40.44496677039454, lng: -79.94543510284873 }, + coordinates: undefined, acceptsOnlineOrders: false, times: [], todaysSpecials: undefined, @@ -95,7 +95,7 @@ export const expectedLocationData = [ url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/178", location: "", menu: undefined, - coordinates: { lat: 40.44265640089745, lng: -79.94021168805847 }, + coordinates: undefined, acceptsOnlineOrders: false, times: [], todaysSpecials: undefined, @@ -110,7 +110,7 @@ export const expectedLocationData = [ url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/88", location: "", menu: undefined, - coordinates: { lat: 40.44322866937478, lng: -79.94252749706692 }, + coordinates: undefined, acceptsOnlineOrders: false, times: [], todaysSpecials: undefined, @@ -125,7 +125,7 @@ export const expectedLocationData = [ url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/103", location: "", menu: undefined, - coordinates: { lat: 40.44294406631636, lng: -79.9421613966706 }, + coordinates: undefined, acceptsOnlineOrders: false, times: [], todaysSpecials: undefined, @@ -141,7 +141,7 @@ export const expectedLocationData = [ url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/92", location: "Posner Hall, 1st Floor", menu: "https://web.archive.org/web/20240721001349/https://apps.studentaffairs.cmu.edu/dining/dashboard_images/Production/menus/92/menu-exchange-2024-25-v2.pdf", - coordinates: { lat: 40.44144439971656, lng: -79.94195512801193 }, + coordinates: { lat: 40.441354, lng: -79.942125 }, acceptsOnlineOrders: false, times: [ { @@ -206,7 +206,7 @@ export const expectedLocationData = [ url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/173", location: "", menu: undefined, - coordinates: { lat: 40.44490020783157, lng: -79.94540910407714 }, + coordinates: undefined, acceptsOnlineOrders: false, times: [], todaysSpecials: undefined, @@ -221,7 +221,7 @@ export const expectedLocationData = [ url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/91", location: "", menu: undefined, - coordinates: { lat: 40.44306965961788, lng: -79.94207835253889 }, + coordinates: undefined, acceptsOnlineOrders: false, times: [], todaysSpecials: undefined, @@ -251,7 +251,7 @@ export const expectedLocationData = [ url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/110", location: "Newell-Simon Atrium", menu: "https://web.archive.org/web/20240901003526/https://apps.studentaffairs.cmu.edu/dining/dashboard_images/Production/menus/110/Fall Menus 2024 (25).pdf", - coordinates: { lat: 40.44326419955131, lng: -79.94551330831828 }, + coordinates: { lat: 40.4433922, lng: -79.9455957 }, acceptsOnlineOrders: true, times: [ { @@ -291,7 +291,7 @@ export const expectedLocationData = [ url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/115", location: "", menu: undefined, - coordinates: { lat: 40.44347617300122, lng: -79.94480928001676 }, + coordinates: undefined, acceptsOnlineOrders: false, times: [], todaysSpecials: undefined, @@ -306,7 +306,7 @@ export const expectedLocationData = [ url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/136", location: "", menu: undefined, - coordinates: { lat: 40.44497677044086, lng: -79.94526274161514 }, + coordinates: undefined, acceptsOnlineOrders: false, times: [], todaysSpecials: undefined, @@ -321,7 +321,7 @@ export const expectedLocationData = [ url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/127", location: "", menu: undefined, - coordinates: { lat: 40.44296, lng: -79.941815 }, + coordinates: undefined, acceptsOnlineOrders: false, times: [], todaysSpecials: undefined, @@ -336,7 +336,7 @@ export const expectedLocationData = [ url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/94", location: "", menu: undefined, - coordinates: { lat: 40.44259053115122, lng: -79.94597744494271 }, + coordinates: undefined, acceptsOnlineOrders: false, times: [], todaysSpecials: undefined, @@ -366,7 +366,7 @@ export const expectedLocationData = [ url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/174", location: "", menu: undefined, - coordinates: { lat: 40.44336219896886, lng: -79.94196740444092 }, + coordinates: undefined, acceptsOnlineOrders: false, times: [], todaysSpecials: undefined, @@ -381,7 +381,7 @@ export const expectedLocationData = [ url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/108", location: "Cohon Center, Second floor", menu: undefined, - coordinates: { lat: 40.44313373573427, lng: -79.94252833667237 }, + coordinates: { lat: 40.4429602, lng: -79.9418151 }, acceptsOnlineOrders: false, times: [ { @@ -625,7 +625,7 @@ export const expectedLocationData = [ url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/180", location: "", menu: undefined, - coordinates: { lat: 40.44414285761781, lng: -79.93888579754876 }, + coordinates: undefined, acceptsOnlineOrders: false, times: [], todaysSpecials: undefined, @@ -640,7 +640,7 @@ export const expectedLocationData = [ url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/190", location: "", menu: undefined, - coordinates: { lat: 40.44541493684331, lng: -79.94298366401661 }, + coordinates: undefined, acceptsOnlineOrders: false, times: [], todaysSpecials: undefined, @@ -655,7 +655,7 @@ export const expectedLocationData = [ url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/188", location: "", menu: undefined, - coordinates: { lat: 40.44538605085632, lng: -79.9430188095375 }, + coordinates: undefined, acceptsOnlineOrders: false, times: [], todaysSpecials: undefined, @@ -670,7 +670,7 @@ export const expectedLocationData = [ url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/148", location: "", menu: undefined, - coordinates: { lat: 40.44618769931711, lng: -79.95093385349946 }, + coordinates: undefined, acceptsOnlineOrders: false, times: [], todaysSpecials: undefined, @@ -684,7 +684,7 @@ export const expectedLocationData = [ url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/82", location: "", menu: undefined, - coordinates: { lat: 40.44260716039899, lng: -79.93995513782087 }, + coordinates: undefined, acceptsOnlineOrders: false, times: [], todaysSpecials: undefined, @@ -735,7 +735,7 @@ export const expectedLocationData = [ url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/114", location: "", menu: undefined, - coordinates: { lat: 40.44253705946464, lng: -79.9400539411368 }, + coordinates: undefined, acceptsOnlineOrders: false, times: [], todaysSpecials: undefined, @@ -750,7 +750,7 @@ export const expectedLocationData = [ url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/185", location: "", menu: undefined, - coordinates: { lat: 40.44494190940949, lng: -79.94547335554091 }, + coordinates: undefined, acceptsOnlineOrders: false, times: [], todaysSpecials: undefined, @@ -765,7 +765,7 @@ export const expectedLocationData = [ url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/138", location: "", menu: undefined, - coordinates: { lat: 40.44319390927541, lng: -79.9420529542194 }, + coordinates: undefined, acceptsOnlineOrders: false, times: [], todaysSpecials: undefined, @@ -779,7 +779,7 @@ export const expectedLocationData = [ url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/98", location: "", menu: undefined, - coordinates: { lat: 40.44245836757329, lng: -79.94002835619484 }, + coordinates: undefined, acceptsOnlineOrders: false, times: [], todaysSpecials: undefined, @@ -794,7 +794,7 @@ export const expectedLocationData = [ url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/155", location: "", menu: undefined, - coordinates: { lat: 40.44269873724883, lng: -79.94664155857618 }, + coordinates: undefined, acceptsOnlineOrders: false, times: [], todaysSpecials: undefined, @@ -809,7 +809,7 @@ export const expectedLocationData = [ url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/84", location: "", menu: undefined, - coordinates: { lat: 40.44161664158368, lng: -79.94285872206078 }, + coordinates: undefined, acceptsOnlineOrders: false, times: [], todaysSpecials: undefined, From ebb0ac278d4321ea0d3f4158280a46b617c139db Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Sat, 20 Sep 2025 16:00:45 -0400 Subject: [PATCH 21/65] chore: finalize migration from bun to pnpm --- .github/dependabot.yml | 12 +- .github/workflows/test.yml | 20 +- Dockerfile.bun | 9 - Dockerfile.pnpm | 4 +- README.md | 23 +- bun.lock | 1323 -------- bun.lockb | Bin 285615 -> 0 bytes docker-compose.yml | 2 +- package.json | 4 +- pnpm-lock.yaml | 5861 ++++++++++++++++++++++++++++++++++++ run-bun.sh | 2 - src/server.ts | 2 +- tests/integration.test.ts | 3 +- tsconfig.json | 2 +- 14 files changed, 5900 insertions(+), 1367 deletions(-) delete mode 100644 Dockerfile.bun delete mode 100644 bun.lock delete mode 100755 bun.lockb create mode 100644 pnpm-lock.yaml delete mode 100644 run-bun.sh diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 4ba8a1a..3020a73 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,14 +1,14 @@ version: 2 updates: # Enable version updates for npm - - package-ecosystem: 'npm' + - package-ecosystem: "pnpm" # Look for `package.json` and `lock` files in the `root` directory - directory: '/' + directory: "/" # Check the npm registry for updates every day (weekdays) schedule: - interval: 'daily' - - package-ecosystem: 'github-actions' - directory: '/' + interval: "daily" + - package-ecosystem: "github-actions" + directory: "/" schedule: # Check for updates to GitHub Actions every weekday - interval: 'daily' + interval: "daily" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c900dea..fd7bca7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,11 +6,21 @@ on: jobs: test: runs-on: ubuntu-latest + strategy: + matrix: + node-version: [24] steps: - uses: actions/checkout@v4 - - uses: oven-sh/setup-bun@v2 # Setup bun + - name: Install pnpm + uses: pnpm/action-setup@v4 with: - bun-version: latest - - run: bun install - - name: Run bun test - run: bun run test + version: 10 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: "pnpm" + - name: Install dependencies + run: pnpm install --frozen-lockfile + - name: run tests + run: pnpm test diff --git a/Dockerfile.bun b/Dockerfile.bun deleted file mode 100644 index 31460d1..0000000 --- a/Dockerfile.bun +++ /dev/null @@ -1,9 +0,0 @@ -# for starting up the dining api (used both in local dev and railway builds) -FROM oven/bun:latest - -WORKDIR /runtime -COPY . . -RUN bun install -RUN bun run build -EXPOSE 5010 -CMD ["./run-bun.sh"] \ No newline at end of file diff --git a/Dockerfile.pnpm b/Dockerfile.pnpm index 3d77acc..9fd7b5d 100644 --- a/Dockerfile.pnpm +++ b/Dockerfile.pnpm @@ -6,10 +6,10 @@ COPY . /app WORKDIR /app FROM base AS prod-deps -RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile FROM base AS build -RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile RUN pnpm run build FROM base diff --git a/README.md b/README.md index b3429bb..76b3892 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,8 @@ # Dining API -> [!IMPORTANT] -> Make sure `bun` is on the latest version! (Earlier versions are rather buggy) Run `bun upgrade` to update. - This Dining API scrapes location data from the CMU dining sites and distributes it as a RESTful API. Access the API [here](https://dining.apis.scottylabs.org/). -To build and deploy the service, you'll need [Bun](https://bun.sh), +To build and deploy the service, you'll need [pnpm](https://pnpm.io/), which you should install beforehand. Then, clone this repository to your computer by running @@ -27,20 +24,18 @@ If you already have the node_modules folder or package-lock.json from previous v Now install the API's dependencies by 'cd'-ing into the root of the repository and running: ``` -bun install +pnpm install ``` -Start your local database with `db:start` and then start the server with `bun dev` (or `bun run dev`) and it should work, assuming you have the correct env variables. (To see the contents of the database, I recommend using DBeaver. You can also run `bun db:studio` to start up drizzle studio) - -Note: To add new dependencies, use `bun add dependency-name`. To remove dependencies, use `bun remove dependency-name`. Run `bun outdated` to see what dependencies are outdated and `bun update` to update all outdated dependencies to the latest version. +Start your local database with `pnpm db:start` and then start the server with `pnpm dev` and it should work, assuming you have the correct env variables. (To see the contents of the database, I recommend using DBeaver. You can also run `pnpm db:studio` to start up drizzle studio) ## Database schema changes (important!) -When you make changes to the database schema, be sure to run `bun db:push` to keep your local db in sync. +When you make changes to the database schema, be sure to run `pnpm db:push` to keep your local db in sync. -Before merging your PR, be sure to run `bun db:generate` to generate a migration file, which will then be automatically applied to the staging and production databases when deployed. +Before merging your PR, be sure to run `pnpm db:generate` to generate a migration file, which will then be automatically applied to the staging and production databases when deployed. -To test if the migration files work, you can run `bun run-prod`, which will spin up a production version of the server and a postgres database mounted on a new volume. The server is created using the same Dockerfile used in our Railway deployments, so if it works locally, it (probably) works in production as well. +To test if the migration files work, you can run `pnpm run-prod`, which will spin up a production version of the server and a postgres database mounted on a new volume. The server is created using the same Dockerfile used in our Railway deployments, so if it works locally, it (probably) works in production as well. ## Extra docker commands @@ -53,4 +48,8 @@ We get the entire list of locations from `DINING_URL`, fetch location specifics ## Before submitting a PR -- Make sure all tests pass with `bun run test` or `bun run test --watch` for watch mode. (NOTE! `bun test` does something different and does NOT work!) +- Make sure all tests pass with `pnpm test` or `pnpm test --watch` for watch mode. + +## Random notes + +the "cheerio" package is pinned at version "1.0.0-rc.12" because newer versions seem to be incompatible with jest. diff --git a/bun.lock b/bun.lock deleted file mode 100644 index aba6f10..0000000 --- a/bun.lock +++ /dev/null @@ -1,1323 +0,0 @@ -{ - "lockfileVersion": 1, - "workspaces": { - "": { - "name": "dining-api", - "dependencies": { - "@elysiajs/cors": "1.3.3", - "@elysiajs/node": "1.3.0", - "@types/express": "^5.0.3", - "axios": "^1.10.0", - "cheerio": "^1.0.0-rc.12", - "drizzle-kit": "^0.31.4", - "drizzle-orm": "^0.44.4", - "elysia": "1.3.5", - "express": "^5.1.0", - "pg": "^8.16.3", - "zod": "^3.25.67", - }, - "devDependencies": { - "@babel/core": "^7.25.2", - "@babel/preset-env": "^7.25.4", - "@babel/preset-typescript": "^7.24.7", - "@rollup/plugin-typescript": "^12.1.4", - "@types/bun": "^1.1.1", - "@types/jest": "^29.5.14", - "@types/node": "^24.0.14", - "@types/pg": "^8.15.5", - "babel-jest": "^29.7.0", - "bun-types": "^1.1.7", - "dotenv": "^17.2.0", - "dotenv-cli": "^8.0.0", - "jest": "^29.7.0", - "npm-check-updates": "^18.0.1", - "rollup": "^4.45.1", - "ts-jest": "^29.2.5", - "tslib": "^2.8.1", - "tsx": "^4.20.3", - }, - "peerDependencies": { - "typescript": "^5.8.3", - }, - }, - }, - "packages": { - "@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="], - - "@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="], - - "@babel/compat-data": ["@babel/compat-data@7.26.5", "", {}, "sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg=="], - - "@babel/core": ["@babel/core@7.26.7", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.2", "@babel/generator": "^7.26.5", "@babel/helper-compilation-targets": "^7.26.5", "@babel/helper-module-transforms": "^7.26.0", "@babel/helpers": "^7.26.7", "@babel/parser": "^7.26.7", "@babel/template": "^7.25.9", "@babel/traverse": "^7.26.7", "@babel/types": "^7.26.7", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-SRijHmF0PSPgLIBYlWnG0hyeJLwXE2CgpsXaMOrtt2yp9/86ALw6oUlj9KYuZ0JN07T4eBMVIW4li/9S1j2BGA=="], - - "@babel/generator": ["@babel/generator@7.26.5", "", { "dependencies": { "@babel/parser": "^7.26.5", "@babel/types": "^7.26.5", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" } }, "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw=="], - - "@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.25.9", "", { "dependencies": { "@babel/types": "^7.25.9" } }, "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g=="], - - "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.26.5", "", { "dependencies": { "@babel/compat-data": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA=="], - - "@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.25.9", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-member-expression-to-functions": "^7.25.9", "@babel/helper-optimise-call-expression": "^7.25.9", "@babel/helper-replace-supers": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", "@babel/traverse": "^7.25.9", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ=="], - - "@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.26.3", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "regexpu-core": "^6.2.0", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-G7ZRb40uUgdKOQqPLjfD12ZmGA54PzqDFUv2BKImnC9QIfGhIHKvVML0oN8IUiDq4iRqpq74ABpvOaerfWdong=="], - - "@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.6.3", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", "debug": "^4.1.1", "lodash.debounce": "^4.0.8", "resolve": "^1.14.2" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg=="], - - "@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.25.9", "", { "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" } }, "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ=="], - - "@babel/helper-module-imports": ["@babel/helper-module-imports@7.25.9", "", { "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" } }, "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw=="], - - "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.26.0", "", { "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9", "@babel/traverse": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw=="], - - "@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.25.9", "", { "dependencies": { "@babel/types": "^7.25.9" } }, "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ=="], - - "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.26.5", "", {}, "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg=="], - - "@babel/helper-remap-async-to-generator": ["@babel/helper-remap-async-to-generator@7.25.9", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-wrap-function": "^7.25.9", "@babel/traverse": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw=="], - - "@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.26.5", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.25.9", "@babel/helper-optimise-call-expression": "^7.25.9", "@babel/traverse": "^7.26.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg=="], - - "@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.25.9", "", { "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" } }, "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA=="], - - "@babel/helper-string-parser": ["@babel/helper-string-parser@7.25.9", "", {}, "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA=="], - - "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.25.9", "", {}, "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="], - - "@babel/helper-validator-option": ["@babel/helper-validator-option@7.25.9", "", {}, "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw=="], - - "@babel/helper-wrap-function": ["@babel/helper-wrap-function@7.25.9", "", { "dependencies": { "@babel/template": "^7.25.9", "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" } }, "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g=="], - - "@babel/helpers": ["@babel/helpers@7.26.7", "", { "dependencies": { "@babel/template": "^7.25.9", "@babel/types": "^7.26.7" } }, "sha512-8NHiL98vsi0mbPQmYAGWwfcFaOy4j2HY49fXJCfuDcdE7fMIsH9a7GdaeXpIBsbT7307WU8KCMp5pUVDNL4f9A=="], - - "@babel/parser": ["@babel/parser@7.26.7", "", { "dependencies": { "@babel/types": "^7.26.7" }, "bin": "./bin/babel-parser.js" }, "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w=="], - - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": ["@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/traverse": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g=="], - - "@babel/plugin-bugfix-safari-class-field-initializer-scope": ["@babel/plugin-bugfix-safari-class-field-initializer-scope@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw=="], - - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ["@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug=="], - - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ["@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", "@babel/plugin-transform-optional-chaining": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.13.0" } }, "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g=="], - - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": ["@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/traverse": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg=="], - - "@babel/plugin-proposal-private-property-in-object": ["@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2", "", { "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w=="], - - "@babel/plugin-syntax-async-generators": ["@babel/plugin-syntax-async-generators@7.8.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw=="], - - "@babel/plugin-syntax-bigint": ["@babel/plugin-syntax-bigint@7.8.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg=="], - - "@babel/plugin-syntax-class-properties": ["@babel/plugin-syntax-class-properties@7.12.13", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.12.13" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA=="], - - "@babel/plugin-syntax-class-static-block": ["@babel/plugin-syntax-class-static-block@7.14.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw=="], - - "@babel/plugin-syntax-import-assertions": ["@babel/plugin-syntax-import-assertions@7.26.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg=="], - - "@babel/plugin-syntax-import-attributes": ["@babel/plugin-syntax-import-attributes@7.26.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A=="], - - "@babel/plugin-syntax-import-meta": ["@babel/plugin-syntax-import-meta@7.10.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g=="], - - "@babel/plugin-syntax-json-strings": ["@babel/plugin-syntax-json-strings@7.8.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA=="], - - "@babel/plugin-syntax-jsx": ["@babel/plugin-syntax-jsx@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA=="], - - "@babel/plugin-syntax-logical-assignment-operators": ["@babel/plugin-syntax-logical-assignment-operators@7.10.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig=="], - - "@babel/plugin-syntax-nullish-coalescing-operator": ["@babel/plugin-syntax-nullish-coalescing-operator@7.8.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ=="], - - "@babel/plugin-syntax-numeric-separator": ["@babel/plugin-syntax-numeric-separator@7.10.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug=="], - - "@babel/plugin-syntax-object-rest-spread": ["@babel/plugin-syntax-object-rest-spread@7.8.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA=="], - - "@babel/plugin-syntax-optional-catch-binding": ["@babel/plugin-syntax-optional-catch-binding@7.8.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q=="], - - "@babel/plugin-syntax-optional-chaining": ["@babel/plugin-syntax-optional-chaining@7.8.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg=="], - - "@babel/plugin-syntax-private-property-in-object": ["@babel/plugin-syntax-private-property-in-object@7.14.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg=="], - - "@babel/plugin-syntax-top-level-await": ["@babel/plugin-syntax-top-level-await@7.14.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw=="], - - "@babel/plugin-syntax-typescript": ["@babel/plugin-syntax-typescript@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ=="], - - "@babel/plugin-syntax-unicode-sets-regex": ["@babel/plugin-syntax-unicode-sets-regex@7.18.6", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg=="], - - "@babel/plugin-transform-arrow-functions": ["@babel/plugin-transform-arrow-functions@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg=="], - - "@babel/plugin-transform-async-generator-functions": ["@babel/plugin-transform-async-generator-functions@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-remap-async-to-generator": "^7.25.9", "@babel/traverse": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw=="], - - "@babel/plugin-transform-async-to-generator": ["@babel/plugin-transform-async-to-generator@7.25.9", "", { "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-remap-async-to-generator": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ=="], - - "@babel/plugin-transform-block-scoped-functions": ["@babel/plugin-transform-block-scoped-functions@7.26.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.26.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ=="], - - "@babel/plugin-transform-block-scoping": ["@babel/plugin-transform-block-scoping@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg=="], - - "@babel/plugin-transform-class-properties": ["@babel/plugin-transform-class-properties@7.25.9", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q=="], - - "@babel/plugin-transform-class-static-block": ["@babel/plugin-transform-class-static-block@7.26.0", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.12.0" } }, "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ=="], - - "@babel/plugin-transform-classes": ["@babel/plugin-transform-classes@7.25.9", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-compilation-targets": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-replace-supers": "^7.25.9", "@babel/traverse": "^7.25.9", "globals": "^11.1.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg=="], - - "@babel/plugin-transform-computed-properties": ["@babel/plugin-transform-computed-properties@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/template": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA=="], - - "@babel/plugin-transform-destructuring": ["@babel/plugin-transform-destructuring@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ=="], - - "@babel/plugin-transform-dotall-regex": ["@babel/plugin-transform-dotall-regex@7.25.9", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA=="], - - "@babel/plugin-transform-duplicate-keys": ["@babel/plugin-transform-duplicate-keys@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw=="], - - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": ["@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.25.9", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog=="], - - "@babel/plugin-transform-dynamic-import": ["@babel/plugin-transform-dynamic-import@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg=="], - - "@babel/plugin-transform-exponentiation-operator": ["@babel/plugin-transform-exponentiation-operator@7.26.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ=="], - - "@babel/plugin-transform-export-namespace-from": ["@babel/plugin-transform-export-namespace-from@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww=="], - - "@babel/plugin-transform-for-of": ["@babel/plugin-transform-for-of@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A=="], - - "@babel/plugin-transform-function-name": ["@babel/plugin-transform-function-name@7.25.9", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", "@babel/traverse": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA=="], - - "@babel/plugin-transform-json-strings": ["@babel/plugin-transform-json-strings@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw=="], - - "@babel/plugin-transform-literals": ["@babel/plugin-transform-literals@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ=="], - - "@babel/plugin-transform-logical-assignment-operators": ["@babel/plugin-transform-logical-assignment-operators@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q=="], - - "@babel/plugin-transform-member-expression-literals": ["@babel/plugin-transform-member-expression-literals@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA=="], - - "@babel/plugin-transform-modules-amd": ["@babel/plugin-transform-modules-amd@7.25.9", "", { "dependencies": { "@babel/helper-module-transforms": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw=="], - - "@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.26.3", "", { "dependencies": { "@babel/helper-module-transforms": "^7.26.0", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ=="], - - "@babel/plugin-transform-modules-systemjs": ["@babel/plugin-transform-modules-systemjs@7.25.9", "", { "dependencies": { "@babel/helper-module-transforms": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9", "@babel/traverse": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA=="], - - "@babel/plugin-transform-modules-umd": ["@babel/plugin-transform-modules-umd@7.25.9", "", { "dependencies": { "@babel/helper-module-transforms": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw=="], - - "@babel/plugin-transform-named-capturing-groups-regex": ["@babel/plugin-transform-named-capturing-groups-regex@7.25.9", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA=="], - - "@babel/plugin-transform-new-target": ["@babel/plugin-transform-new-target@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ=="], - - "@babel/plugin-transform-nullish-coalescing-operator": ["@babel/plugin-transform-nullish-coalescing-operator@7.26.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.26.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw=="], - - "@babel/plugin-transform-numeric-separator": ["@babel/plugin-transform-numeric-separator@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q=="], - - "@babel/plugin-transform-object-rest-spread": ["@babel/plugin-transform-object-rest-spread@7.25.9", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", "@babel/plugin-transform-parameters": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg=="], - - "@babel/plugin-transform-object-super": ["@babel/plugin-transform-object-super@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-replace-supers": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A=="], - - "@babel/plugin-transform-optional-catch-binding": ["@babel/plugin-transform-optional-catch-binding@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g=="], - - "@babel/plugin-transform-optional-chaining": ["@babel/plugin-transform-optional-chaining@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A=="], - - "@babel/plugin-transform-parameters": ["@babel/plugin-transform-parameters@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g=="], - - "@babel/plugin-transform-private-methods": ["@babel/plugin-transform-private-methods@7.25.9", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw=="], - - "@babel/plugin-transform-private-property-in-object": ["@babel/plugin-transform-private-property-in-object@7.25.9", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw=="], - - "@babel/plugin-transform-property-literals": ["@babel/plugin-transform-property-literals@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA=="], - - "@babel/plugin-transform-regenerator": ["@babel/plugin-transform-regenerator@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "regenerator-transform": "^0.15.2" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg=="], - - "@babel/plugin-transform-regexp-modifiers": ["@babel/plugin-transform-regexp-modifiers@7.26.0", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw=="], - - "@babel/plugin-transform-reserved-words": ["@babel/plugin-transform-reserved-words@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg=="], - - "@babel/plugin-transform-shorthand-properties": ["@babel/plugin-transform-shorthand-properties@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng=="], - - "@babel/plugin-transform-spread": ["@babel/plugin-transform-spread@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A=="], - - "@babel/plugin-transform-sticky-regex": ["@babel/plugin-transform-sticky-regex@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA=="], - - "@babel/plugin-transform-template-literals": ["@babel/plugin-transform-template-literals@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw=="], - - "@babel/plugin-transform-typeof-symbol": ["@babel/plugin-transform-typeof-symbol@7.26.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.26.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-jfoTXXZTgGg36BmhqT3cAYK5qkmqvJpvNrPhaK/52Vgjhw4Rq29s9UqpWWV0D6yuRmgiFH/BUVlkl96zJWqnaw=="], - - "@babel/plugin-transform-typescript": ["@babel/plugin-transform-typescript@7.26.7", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", "@babel/plugin-syntax-typescript": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-5cJurntg+AT+cgelGP9Bt788DKiAw9gIMSMU2NJrLAilnj0m8WZWUNZPSLOmadYsujHutpgElO+50foX+ib/Wg=="], - - "@babel/plugin-transform-unicode-escapes": ["@babel/plugin-transform-unicode-escapes@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q=="], - - "@babel/plugin-transform-unicode-property-regex": ["@babel/plugin-transform-unicode-property-regex@7.25.9", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg=="], - - "@babel/plugin-transform-unicode-regex": ["@babel/plugin-transform-unicode-regex@7.25.9", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA=="], - - "@babel/plugin-transform-unicode-sets-regex": ["@babel/plugin-transform-unicode-sets-regex@7.25.9", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ=="], - - "@babel/preset-env": ["@babel/preset-env@7.26.7", "", { "dependencies": { "@babel/compat-data": "^7.26.5", "@babel/helper-compilation-targets": "^7.26.5", "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.9", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.9", "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.9", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-import-assertions": "^7.26.0", "@babel/plugin-syntax-import-attributes": "^7.26.0", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.25.9", "@babel/plugin-transform-async-generator-functions": "^7.25.9", "@babel/plugin-transform-async-to-generator": "^7.25.9", "@babel/plugin-transform-block-scoped-functions": "^7.26.5", "@babel/plugin-transform-block-scoping": "^7.25.9", "@babel/plugin-transform-class-properties": "^7.25.9", "@babel/plugin-transform-class-static-block": "^7.26.0", "@babel/plugin-transform-classes": "^7.25.9", "@babel/plugin-transform-computed-properties": "^7.25.9", "@babel/plugin-transform-destructuring": "^7.25.9", "@babel/plugin-transform-dotall-regex": "^7.25.9", "@babel/plugin-transform-duplicate-keys": "^7.25.9", "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", "@babel/plugin-transform-dynamic-import": "^7.25.9", "@babel/plugin-transform-exponentiation-operator": "^7.26.3", "@babel/plugin-transform-export-namespace-from": "^7.25.9", "@babel/plugin-transform-for-of": "^7.25.9", "@babel/plugin-transform-function-name": "^7.25.9", "@babel/plugin-transform-json-strings": "^7.25.9", "@babel/plugin-transform-literals": "^7.25.9", "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", "@babel/plugin-transform-member-expression-literals": "^7.25.9", "@babel/plugin-transform-modules-amd": "^7.25.9", "@babel/plugin-transform-modules-commonjs": "^7.26.3", "@babel/plugin-transform-modules-systemjs": "^7.25.9", "@babel/plugin-transform-modules-umd": "^7.25.9", "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", "@babel/plugin-transform-new-target": "^7.25.9", "@babel/plugin-transform-nullish-coalescing-operator": "^7.26.6", "@babel/plugin-transform-numeric-separator": "^7.25.9", "@babel/plugin-transform-object-rest-spread": "^7.25.9", "@babel/plugin-transform-object-super": "^7.25.9", "@babel/plugin-transform-optional-catch-binding": "^7.25.9", "@babel/plugin-transform-optional-chaining": "^7.25.9", "@babel/plugin-transform-parameters": "^7.25.9", "@babel/plugin-transform-private-methods": "^7.25.9", "@babel/plugin-transform-private-property-in-object": "^7.25.9", "@babel/plugin-transform-property-literals": "^7.25.9", "@babel/plugin-transform-regenerator": "^7.25.9", "@babel/plugin-transform-regexp-modifiers": "^7.26.0", "@babel/plugin-transform-reserved-words": "^7.25.9", "@babel/plugin-transform-shorthand-properties": "^7.25.9", "@babel/plugin-transform-spread": "^7.25.9", "@babel/plugin-transform-sticky-regex": "^7.25.9", "@babel/plugin-transform-template-literals": "^7.25.9", "@babel/plugin-transform-typeof-symbol": "^7.26.7", "@babel/plugin-transform-unicode-escapes": "^7.25.9", "@babel/plugin-transform-unicode-property-regex": "^7.25.9", "@babel/plugin-transform-unicode-regex": "^7.25.9", "@babel/plugin-transform-unicode-sets-regex": "^7.25.9", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.10", "babel-plugin-polyfill-corejs3": "^0.10.6", "babel-plugin-polyfill-regenerator": "^0.6.1", "core-js-compat": "^3.38.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Ycg2tnXwixaXOVb29rana8HNPgLVBof8qqtNQ9LE22IoyZboQbGSxI6ZySMdW3K5nAe6gu35IaJefUJflhUFTQ=="], - - "@babel/preset-modules": ["@babel/preset-modules@0.1.6-no-external-plugins", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/types": "^7.4.4", "esutils": "^2.0.2" }, "peerDependencies": { "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" } }, "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA=="], - - "@babel/preset-typescript": ["@babel/preset-typescript@7.26.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-validator-option": "^7.25.9", "@babel/plugin-syntax-jsx": "^7.25.9", "@babel/plugin-transform-modules-commonjs": "^7.25.9", "@babel/plugin-transform-typescript": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-NMk1IGZ5I/oHhoXEElcm+xUnL/szL6xflkFZmoEU9xj1qSJXpiS7rsspYo92B4DRCDvZn2erT5LdsCeXAKNCkg=="], - - "@babel/runtime": ["@babel/runtime@7.26.7", "", { "dependencies": { "regenerator-runtime": "^0.14.0" } }, "sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ=="], - - "@babel/template": ["@babel/template@7.25.9", "", { "dependencies": { "@babel/code-frame": "^7.25.9", "@babel/parser": "^7.25.9", "@babel/types": "^7.25.9" } }, "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg=="], - - "@babel/traverse": ["@babel/traverse@7.26.7", "", { "dependencies": { "@babel/code-frame": "^7.26.2", "@babel/generator": "^7.26.5", "@babel/parser": "^7.26.7", "@babel/template": "^7.25.9", "@babel/types": "^7.26.7", "debug": "^4.3.1", "globals": "^11.1.0" } }, "sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA=="], - - "@babel/types": ["@babel/types@7.26.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" } }, "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg=="], - - "@bcoe/v8-coverage": ["@bcoe/v8-coverage@0.2.3", "", {}, "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw=="], - - "@borewit/text-codec": ["@borewit/text-codec@0.1.1", "", {}, "sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA=="], - - "@drizzle-team/brocli": ["@drizzle-team/brocli@0.10.2", "", {}, "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w=="], - - "@elysiajs/cors": ["@elysiajs/cors@1.3.3", "", { "peerDependencies": { "elysia": ">= 1.3.0" } }, "sha512-mYIU6PyMM6xIJuj7d27Vt0/wuzVKIEnFPjcvlkyd7t/m9xspAG37cwNjFxVOnyvY43oOd2I/oW2DB85utXpA2Q=="], - - "@elysiajs/node": ["@elysiajs/node@1.3.0", "", { "dependencies": { "@hono/node-server": "^1.14.3" }, "peerDependencies": { "elysia": ">= 1.3.3" } }, "sha512-AA9rnL/FOBklxtJjFpUDBVZLuiKddDaV1KgnKawHzj5VgN99SzE+V0h1MOhp+8jlo2KQQJO/3aD3upyGgrfohQ=="], - - "@esbuild-kit/core-utils": ["@esbuild-kit/core-utils@3.3.2", "", { "dependencies": { "esbuild": "~0.18.20", "source-map-support": "^0.5.21" } }, "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ=="], - - "@esbuild-kit/esm-loader": ["@esbuild-kit/esm-loader@2.6.5", "", { "dependencies": { "@esbuild-kit/core-utils": "^3.3.2", "get-tsconfig": "^4.7.0" } }, "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA=="], - - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.7", "", { "os": "aix", "cpu": "ppc64" }, "sha512-uD0kKFHh6ETr8TqEtaAcV+dn/2qnYbH/+8wGEdY70Qf7l1l/jmBUbrmQqwiPKAQE6cOQ7dTj6Xr0HzQDGHyceQ=="], - - "@esbuild/android-arm": ["@esbuild/android-arm@0.25.7", "", { "os": "android", "cpu": "arm" }, "sha512-Jhuet0g1k9rAJHrXGIh7sFknFuT4sfytYZpZpuZl7YKDhnPByVAm5oy2LEBmMbuYf3ejWVYCc2seX81Mk+madA=="], - - "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.7", "", { "os": "android", "cpu": "arm64" }, "sha512-p0ohDnwyIbAtztHTNUTzN5EGD/HJLs1bwysrOPgSdlIA6NDnReoVfoCyxG6W1d85jr2X80Uq5KHftyYgaK9LPQ=="], - - "@esbuild/android-x64": ["@esbuild/android-x64@0.25.7", "", { "os": "android", "cpu": "x64" }, "sha512-mMxIJFlSgVK23HSsII3ZX9T2xKrBCDGyk0qiZnIW10LLFFtZLkFD6imZHu7gUo2wkNZwS9Yj3mOtZD3ZPcjCcw=="], - - "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-jyOFLGP2WwRwxM8F1VpP6gcdIJc8jq2CUrURbbTouJoRO7XCkU8GdnTDFIHdcifVBT45cJlOYsZ1kSlfbKjYUQ=="], - - "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-m9bVWqZCwQ1BthruifvG64hG03zzz9gE2r/vYAhztBna1/+qXiHyP9WgnyZqHgGeXoimJPhAmxfbeU+nMng6ZA=="], - - "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.7", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-Bss7P4r6uhr3kDzRjPNEnTm/oIBdTPRNQuwaEFWT/uvt6A1YzK/yn5kcx5ZxZ9swOga7LqeYlu7bDIpDoS01bA=="], - - "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.7", "", { "os": "freebsd", "cpu": "x64" }, "sha512-S3BFyjW81LXG7Vqmr37ddbThrm3A84yE7ey/ERBlK9dIiaWgrjRlre3pbG7txh1Uaxz8N7wGGQXmC9zV+LIpBQ=="], - - "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.7", "", { "os": "linux", "cpu": "arm" }, "sha512-JZMIci/1m5vfQuhKoFXogCKVYVfYQmoZJg8vSIMR4TUXbF+0aNlfXH3DGFEFMElT8hOTUF5hisdZhnrZO/bkDw=="], - - "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-HfQZQqrNOfS1Okn7PcsGUqHymL1cWGBslf78dGvtrj8q7cN3FkapFgNA4l/a5lXDwr7BqP2BSO6mz9UremNPbg=="], - - "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.7", "", { "os": "linux", "cpu": "ia32" }, "sha512-9Jex4uVpdeofiDxnwHRgen+j6398JlX4/6SCbbEFEXN7oMO2p0ueLN+e+9DdsdPLUdqns607HmzEFnxwr7+5wQ=="], - - "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.7", "", { "os": "linux", "cpu": "none" }, "sha512-TG1KJqjBlN9IHQjKVUYDB0/mUGgokfhhatlay8aZ/MSORMubEvj/J1CL8YGY4EBcln4z7rKFbsH+HeAv0d471w=="], - - "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.7", "", { "os": "linux", "cpu": "none" }, "sha512-Ty9Hj/lx7ikTnhOfaP7ipEm/ICcBv94i/6/WDg0OZ3BPBHhChsUbQancoWYSO0WNkEiSW5Do4febTTy4x1qYQQ=="], - - "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.7", "", { "os": "linux", "cpu": "ppc64" }, "sha512-MrOjirGQWGReJl3BNQ58BLhUBPpWABnKrnq8Q/vZWWwAB1wuLXOIxS2JQ1LT3+5T+3jfPh0tyf5CpbyQHqnWIQ=="], - - "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.7", "", { "os": "linux", "cpu": "none" }, "sha512-9pr23/pqzyqIZEZmQXnFyqp3vpa+KBk5TotfkzGMqpw089PGm0AIowkUppHB9derQzqniGn3wVXgck19+oqiOw=="], - - "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.7", "", { "os": "linux", "cpu": "s390x" }, "sha512-4dP11UVGh9O6Y47m8YvW8eoA3r8qL2toVZUbBKyGta8j6zdw1cn9F/Rt59/Mhv0OgY68pHIMjGXWOUaykCnx+w=="], - - "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.7", "", { "os": "linux", "cpu": "x64" }, "sha512-ghJMAJTdw/0uhz7e7YnpdX1xVn7VqA0GrWrAO2qKMuqbvgHT2VZiBv1BQ//VcHsPir4wsL3P2oPggfKPzTKoCA=="], - - "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.7", "", { "os": "none", "cpu": "arm64" }, "sha512-bwXGEU4ua45+u5Ci/a55B85KWaDSRS8NPOHtxy2e3etDjbz23wlry37Ffzapz69JAGGc4089TBo+dGzydQmydg=="], - - "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.7", "", { "os": "none", "cpu": "x64" }, "sha512-tUZRvLtgLE5OyN46sPSYlgmHoBS5bx2URSrgZdW1L1teWPYVmXh+QN/sKDqkzBo/IHGcKcHLKDhBeVVkO7teEA=="], - - "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.7", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-bTJ50aoC+WDlDGBReWYiObpYvQfMjBNlKztqoNUL0iUkYtwLkBQQeEsTq/I1KyjsKA5tyov6VZaPb8UdD6ci6Q=="], - - "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.7", "", { "os": "openbsd", "cpu": "x64" }, "sha512-TA9XfJrgzAipFUU895jd9j2SyDh9bbNkK2I0gHcvqb/o84UeQkBpi/XmYX3cO1q/9hZokdcDqQxIi6uLVrikxg=="], - - "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.7", "", { "os": "none", "cpu": "arm64" }, "sha512-5VTtExUrWwHHEUZ/N+rPlHDwVFQ5aME7vRJES8+iQ0xC/bMYckfJ0l2n3yGIfRoXcK/wq4oXSItZAz5wslTKGw=="], - - "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.7", "", { "os": "sunos", "cpu": "x64" }, "sha512-umkbn7KTxsexhv2vuuJmj9kggd4AEtL32KodkJgfhNOHMPtQ55RexsaSrMb+0+jp9XL4I4o2y91PZauVN4cH3A=="], - - "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.7", "", { "os": "win32", "cpu": "arm64" }, "sha512-j20JQGP/gz8QDgzl5No5Gr4F6hurAZvtkFxAKhiv2X49yi/ih8ECK4Y35YnjlMogSKJk931iNMcd35BtZ4ghfw=="], - - "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.7", "", { "os": "win32", "cpu": "ia32" }, "sha512-4qZ6NUfoiiKZfLAXRsvFkA0hoWVM+1y2bSHXHkpdLAs/+r0LgwqYohmfZCi985c6JWHhiXP30mgZawn/XrqAkQ=="], - - "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.7", "", { "os": "win32", "cpu": "x64" }, "sha512-FaPsAHTwm+1Gfvn37Eg3E5HIpfR3i6x1AIcla/MkqAIupD4BW3MrSeUqfoTzwwJhk3WE2/KqUn4/eenEJC76VA=="], - - "@hono/node-server": ["@hono/node-server@1.18.2", "", { "peerDependencies": { "hono": "^4" } }, "sha512-icgNvC0vRYivzyuSSaUv9ttcwtN8fDyd1k3AOIBDJgYd84tXRZSS6na8X54CY/oYoFTNhEmZraW/Rb9XYwX4KA=="], - - "@istanbuljs/load-nyc-config": ["@istanbuljs/load-nyc-config@1.1.0", "", { "dependencies": { "camelcase": "^5.3.1", "find-up": "^4.1.0", "get-package-type": "^0.1.0", "js-yaml": "^3.13.1", "resolve-from": "^5.0.0" } }, "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ=="], - - "@istanbuljs/schema": ["@istanbuljs/schema@0.1.3", "", {}, "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA=="], - - "@jest/console": ["@jest/console@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "jest-message-util": "^29.7.0", "jest-util": "^29.7.0", "slash": "^3.0.0" } }, "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg=="], - - "@jest/core": ["@jest/core@29.7.0", "", { "dependencies": { "@jest/console": "^29.7.0", "@jest/reporters": "^29.7.0", "@jest/test-result": "^29.7.0", "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "ci-info": "^3.2.0", "exit": "^0.1.2", "graceful-fs": "^4.2.9", "jest-changed-files": "^29.7.0", "jest-config": "^29.7.0", "jest-haste-map": "^29.7.0", "jest-message-util": "^29.7.0", "jest-regex-util": "^29.6.3", "jest-resolve": "^29.7.0", "jest-resolve-dependencies": "^29.7.0", "jest-runner": "^29.7.0", "jest-runtime": "^29.7.0", "jest-snapshot": "^29.7.0", "jest-util": "^29.7.0", "jest-validate": "^29.7.0", "jest-watcher": "^29.7.0", "micromatch": "^4.0.4", "pretty-format": "^29.7.0", "slash": "^3.0.0", "strip-ansi": "^6.0.0" }, "peerDependencies": { "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" }, "optionalPeers": ["node-notifier"] }, "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg=="], - - "@jest/environment": ["@jest/environment@29.7.0", "", { "dependencies": { "@jest/fake-timers": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "jest-mock": "^29.7.0" } }, "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw=="], - - "@jest/expect": ["@jest/expect@29.7.0", "", { "dependencies": { "expect": "^29.7.0", "jest-snapshot": "^29.7.0" } }, "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ=="], - - "@jest/expect-utils": ["@jest/expect-utils@29.7.0", "", { "dependencies": { "jest-get-type": "^29.6.3" } }, "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA=="], - - "@jest/fake-timers": ["@jest/fake-timers@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@sinonjs/fake-timers": "^10.0.2", "@types/node": "*", "jest-message-util": "^29.7.0", "jest-mock": "^29.7.0", "jest-util": "^29.7.0" } }, "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ=="], - - "@jest/globals": ["@jest/globals@29.7.0", "", { "dependencies": { "@jest/environment": "^29.7.0", "@jest/expect": "^29.7.0", "@jest/types": "^29.6.3", "jest-mock": "^29.7.0" } }, "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ=="], - - "@jest/reporters": ["@jest/reporters@29.7.0", "", { "dependencies": { "@bcoe/v8-coverage": "^0.2.3", "@jest/console": "^29.7.0", "@jest/test-result": "^29.7.0", "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "@types/node": "*", "chalk": "^4.0.0", "collect-v8-coverage": "^1.0.0", "exit": "^0.1.2", "glob": "^7.1.3", "graceful-fs": "^4.2.9", "istanbul-lib-coverage": "^3.0.0", "istanbul-lib-instrument": "^6.0.0", "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.1.3", "jest-message-util": "^29.7.0", "jest-util": "^29.7.0", "jest-worker": "^29.7.0", "slash": "^3.0.0", "string-length": "^4.0.1", "strip-ansi": "^6.0.0", "v8-to-istanbul": "^9.0.1" }, "peerDependencies": { "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" }, "optionalPeers": ["node-notifier"] }, "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg=="], - - "@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="], - - "@jest/source-map": ["@jest/source-map@29.6.3", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.18", "callsites": "^3.0.0", "graceful-fs": "^4.2.9" } }, "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw=="], - - "@jest/test-result": ["@jest/test-result@29.7.0", "", { "dependencies": { "@jest/console": "^29.7.0", "@jest/types": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" } }, "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA=="], - - "@jest/test-sequencer": ["@jest/test-sequencer@29.7.0", "", { "dependencies": { "@jest/test-result": "^29.7.0", "graceful-fs": "^4.2.9", "jest-haste-map": "^29.7.0", "slash": "^3.0.0" } }, "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw=="], - - "@jest/transform": ["@jest/transform@29.7.0", "", { "dependencies": { "@babel/core": "^7.11.6", "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "babel-plugin-istanbul": "^6.1.1", "chalk": "^4.0.0", "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", "jest-haste-map": "^29.7.0", "jest-regex-util": "^29.6.3", "jest-util": "^29.7.0", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", "write-file-atomic": "^4.0.2" } }, "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw=="], - - "@jest/types": ["@jest/types@29.6.3", "", { "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", "@types/yargs": "^17.0.8", "chalk": "^4.0.0" } }, "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw=="], - - "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="], - - "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], - - "@jridgewell/set-array": ["@jridgewell/set-array@1.2.1", "", {}, "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="], - - "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="], - - "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="], - - "@rollup/plugin-typescript": ["@rollup/plugin-typescript@12.1.4", "", { "dependencies": { "@rollup/pluginutils": "^5.1.0", "resolve": "^1.22.1" }, "peerDependencies": { "rollup": "^2.14.0||^3.0.0||^4.0.0", "tslib": "*", "typescript": ">=3.7.0" }, "optionalPeers": ["rollup", "tslib"] }, "sha512-s5Hx+EtN60LMlDBvl5f04bEiFZmAepk27Q+mr85L/00zPDn1jtzlTV6FWn81MaIwqfWzKxmOJrBWHU6vtQyedQ=="], - - "@rollup/pluginutils": ["@rollup/pluginutils@5.2.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw=="], - - "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.45.1", "", { "os": "android", "cpu": "arm" }, "sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA=="], - - "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.45.1", "", { "os": "android", "cpu": "arm64" }, "sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ=="], - - "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.45.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA=="], - - "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.45.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og=="], - - "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.45.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g=="], - - "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.45.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A=="], - - "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.45.1", "", { "os": "linux", "cpu": "arm" }, "sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q=="], - - "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.45.1", "", { "os": "linux", "cpu": "arm" }, "sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q=="], - - "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.45.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw=="], - - "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.45.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog=="], - - "@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.45.1", "", { "os": "linux", "cpu": "none" }, "sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg=="], - - "@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.45.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg=="], - - "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.45.1", "", { "os": "linux", "cpu": "none" }, "sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw=="], - - "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.45.1", "", { "os": "linux", "cpu": "none" }, "sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA=="], - - "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.45.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw=="], - - "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.45.1", "", { "os": "linux", "cpu": "x64" }, "sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw=="], - - "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.45.1", "", { "os": "linux", "cpu": "x64" }, "sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw=="], - - "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.45.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg=="], - - "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.45.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw=="], - - "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.45.1", "", { "os": "win32", "cpu": "x64" }, "sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA=="], - - "@sinclair/typebox": ["@sinclair/typebox@0.34.38", "", {}, "sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA=="], - - "@sinonjs/commons": ["@sinonjs/commons@3.0.1", "", { "dependencies": { "type-detect": "4.0.8" } }, "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ=="], - - "@sinonjs/fake-timers": ["@sinonjs/fake-timers@10.3.0", "", { "dependencies": { "@sinonjs/commons": "^3.0.0" } }, "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA=="], - - "@tokenizer/inflate": ["@tokenizer/inflate@0.2.7", "", { "dependencies": { "debug": "^4.4.0", "fflate": "^0.8.2", "token-types": "^6.0.0" } }, "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg=="], - - "@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="], - - "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], - - "@types/babel__generator": ["@types/babel__generator@7.6.8", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw=="], - - "@types/babel__template": ["@types/babel__template@7.4.4", "", { "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A=="], - - "@types/babel__traverse": ["@types/babel__traverse@7.20.6", "", { "dependencies": { "@babel/types": "^7.20.7" } }, "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg=="], - - "@types/body-parser": ["@types/body-parser@1.19.6", "", { "dependencies": { "@types/connect": "*", "@types/node": "*" } }, "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g=="], - - "@types/bun": ["@types/bun@1.2.2", "", { "dependencies": { "bun-types": "1.2.2" } }, "sha512-tr74gdku+AEDN5ergNiBnplr7hpDp3V1h7fqI2GcR/rsUaM39jpSeKH0TFibRvU0KwniRx5POgaYnaXbk0hU+w=="], - - "@types/connect": ["@types/connect@3.4.38", "", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="], - - "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], - - "@types/express": ["@types/express@5.0.3", "", { "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^5.0.0", "@types/serve-static": "*" } }, "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw=="], - - "@types/express-serve-static-core": ["@types/express-serve-static-core@5.0.7", "", { "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, "sha512-R+33OsgWw7rOhD1emjU7dzCDHucJrgJXMA5PYCzJxVil0dsyx5iBEPHqpPfiKNJQb7lZ1vxwoLR4Z87bBUpeGQ=="], - - "@types/graceful-fs": ["@types/graceful-fs@4.1.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ=="], - - "@types/http-errors": ["@types/http-errors@2.0.5", "", {}, "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg=="], - - "@types/istanbul-lib-coverage": ["@types/istanbul-lib-coverage@2.0.6", "", {}, "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w=="], - - "@types/istanbul-lib-report": ["@types/istanbul-lib-report@3.0.3", "", { "dependencies": { "@types/istanbul-lib-coverage": "*" } }, "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA=="], - - "@types/istanbul-reports": ["@types/istanbul-reports@3.0.4", "", { "dependencies": { "@types/istanbul-lib-report": "*" } }, "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ=="], - - "@types/jest": ["@types/jest@29.5.14", "", { "dependencies": { "expect": "^29.0.0", "pretty-format": "^29.0.0" } }, "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ=="], - - "@types/mime": ["@types/mime@1.3.5", "", {}, "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w=="], - - "@types/node": ["@types/node@24.0.14", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-4zXMWD91vBLGRtHK3YbIoFMia+1nqEz72coM42C5ETjnNCa/heoj7NT1G67iAfOqMmcfhuCZ4uNpyz8EjlAejw=="], - - "@types/pg": ["@types/pg@8.15.5", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-LF7lF6zWEKxuT3/OR8wAZGzkg4ENGXFNyiV/JeOt9z5B+0ZVwbql9McqX5c/WStFq1GaGso7H1AzP/qSzmlCKQ=="], - - "@types/qs": ["@types/qs@6.14.0", "", {}, "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ=="], - - "@types/range-parser": ["@types/range-parser@1.2.7", "", {}, "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ=="], - - "@types/send": ["@types/send@0.17.5", "", { "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w=="], - - "@types/serve-static": ["@types/serve-static@1.15.8", "", { "dependencies": { "@types/http-errors": "*", "@types/node": "*", "@types/send": "*" } }, "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg=="], - - "@types/stack-utils": ["@types/stack-utils@2.0.3", "", {}, "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw=="], - - "@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="], - - "@types/yargs": ["@types/yargs@17.0.33", "", { "dependencies": { "@types/yargs-parser": "*" } }, "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA=="], - - "@types/yargs-parser": ["@types/yargs-parser@21.0.3", "", {}, "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ=="], - - "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], - - "ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="], - - "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - - "ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], - - "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], - - "argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], - - "async": ["async@3.2.6", "", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="], - - "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], - - "axios": ["axios@1.10.0", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } }, "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw=="], - - "babel-jest": ["babel-jest@29.7.0", "", { "dependencies": { "@jest/transform": "^29.7.0", "@types/babel__core": "^7.1.14", "babel-plugin-istanbul": "^6.1.1", "babel-preset-jest": "^29.6.3", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "slash": "^3.0.0" }, "peerDependencies": { "@babel/core": "^7.8.0" } }, "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg=="], - - "babel-plugin-istanbul": ["babel-plugin-istanbul@6.1.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@istanbuljs/load-nyc-config": "^1.0.0", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-instrument": "^5.0.4", "test-exclude": "^6.0.0" } }, "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA=="], - - "babel-plugin-jest-hoist": ["babel-plugin-jest-hoist@29.6.3", "", { "dependencies": { "@babel/template": "^7.3.3", "@babel/types": "^7.3.3", "@types/babel__core": "^7.1.14", "@types/babel__traverse": "^7.0.6" } }, "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg=="], - - "babel-plugin-polyfill-corejs2": ["babel-plugin-polyfill-corejs2@0.4.12", "", { "dependencies": { "@babel/compat-data": "^7.22.6", "@babel/helper-define-polyfill-provider": "^0.6.3", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og=="], - - "babel-plugin-polyfill-corejs3": ["babel-plugin-polyfill-corejs3@0.10.6", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.2", "core-js-compat": "^3.38.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA=="], - - "babel-plugin-polyfill-regenerator": ["babel-plugin-polyfill-regenerator@0.6.3", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.3" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q=="], - - "babel-preset-current-node-syntax": ["babel-preset-current-node-syntax@1.1.0", "", { "dependencies": { "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-bigint": "^7.8.3", "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-import-attributes": "^7.24.7", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", "@babel/plugin-syntax-numeric-separator": "^7.10.4", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", "@babel/plugin-syntax-optional-chaining": "^7.8.3", "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw=="], - - "babel-preset-jest": ["babel-preset-jest@29.6.3", "", { "dependencies": { "babel-plugin-jest-hoist": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA=="], - - "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], - - "body-parser": ["body-parser@2.2.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.0", "http-errors": "^2.0.0", "iconv-lite": "^0.6.3", "on-finished": "^2.4.1", "qs": "^6.14.0", "raw-body": "^3.0.0", "type-is": "^2.0.0" } }, "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg=="], - - "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="], - - "brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="], - - "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], - - "browserslist": ["browserslist@4.24.4", "", { "dependencies": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" } }, "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A=="], - - "bs-logger": ["bs-logger@0.2.6", "", { "dependencies": { "fast-json-stable-stringify": "2.x" } }, "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog=="], - - "bser": ["bser@2.1.1", "", { "dependencies": { "node-int64": "^0.4.0" } }, "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ=="], - - "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], - - "bun-types": ["bun-types@1.2.2", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-RCbMH5elr9gjgDGDhkTTugA21XtJAy/9jkKe/G3WR2q17VPGhcquf9Sir6uay9iW+7P/BV0CAHA1XlHXMAVKHg=="], - - "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], - - "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], - - "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], - - "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], - - "camelcase": ["camelcase@5.3.1", "", {}, "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="], - - "caniuse-lite": ["caniuse-lite@1.0.30001696", "", {}, "sha512-pDCPkvzfa39ehJtJ+OwGT/2yvT2SbjfHhiIW2LWOAcMQ7BzwxT/XuyUp4OTOd0XFWA6BKw0JalnBHgSi5DGJBQ=="], - - "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], - - "char-regex": ["char-regex@1.0.2", "", {}, "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw=="], - - "cheerio": ["cheerio@1.0.0", "", { "dependencies": { "cheerio-select": "^2.1.0", "dom-serializer": "^2.0.0", "domhandler": "^5.0.3", "domutils": "^3.1.0", "encoding-sniffer": "^0.2.0", "htmlparser2": "^9.1.0", "parse5": "^7.1.2", "parse5-htmlparser2-tree-adapter": "^7.0.0", "parse5-parser-stream": "^7.1.2", "undici": "^6.19.5", "whatwg-mimetype": "^4.0.0" } }, "sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww=="], - - "cheerio-select": ["cheerio-select@2.1.0", "", { "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" } }, "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g=="], - - "ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], - - "cjs-module-lexer": ["cjs-module-lexer@1.4.3", "", {}, "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q=="], - - "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], - - "co": ["co@4.6.0", "", {}, "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ=="], - - "collect-v8-coverage": ["collect-v8-coverage@1.0.2", "", {}, "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q=="], - - "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], - - "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], - - "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], - - "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], - - "content-disposition": ["content-disposition@1.0.0", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg=="], - - "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], - - "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], - - "cookie": ["cookie@1.0.2", "", {}, "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="], - - "cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="], - - "core-js-compat": ["core-js-compat@3.40.0", "", { "dependencies": { "browserslist": "^4.24.3" } }, "sha512-0XEDpr5y5mijvw8Lbc6E5AkjrHfp7eEoPlu36SWeAbcL8fn1G1ANe8DBlo2XoNN89oVpxWwOjYIPVzR4ZvsKCQ=="], - - "create-jest": ["create-jest@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "chalk": "^4.0.0", "exit": "^0.1.2", "graceful-fs": "^4.2.9", "jest-config": "^29.7.0", "jest-util": "^29.7.0", "prompts": "^2.0.1" }, "bin": { "create-jest": "bin/create-jest.js" } }, "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q=="], - - "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], - - "css-select": ["css-select@5.1.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg=="], - - "css-what": ["css-what@6.1.0", "", {}, "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw=="], - - "debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], - - "dedent": ["dedent@1.5.3", "", { "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, "optionalPeers": ["babel-plugin-macros"] }, "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ=="], - - "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="], - - "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], - - "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], - - "detect-newline": ["detect-newline@3.1.0", "", {}, "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA=="], - - "diff-sequences": ["diff-sequences@29.6.3", "", {}, "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q=="], - - "dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="], - - "domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="], - - "domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="], - - "domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="], - - "dotenv": ["dotenv@17.2.0", "", {}, "sha512-Q4sgBT60gzd0BB0lSyYD3xM4YxrXA9y4uBDof1JNYGzOXrQdQ6yX+7XIAqoFOGQFOTK1D3Hts5OllpxMDZFONQ=="], - - "dotenv-cli": ["dotenv-cli@8.0.0", "", { "dependencies": { "cross-spawn": "^7.0.6", "dotenv": "^16.3.0", "dotenv-expand": "^10.0.0", "minimist": "^1.2.6" }, "bin": { "dotenv": "cli.js" } }, "sha512-aLqYbK7xKOiTMIRf1lDPbI+Y+Ip/wo5k3eyp6ePysVaSqbyxjyK3dK35BTxG+rmd7djf5q2UPs4noPNH+cj0Qw=="], - - "dotenv-expand": ["dotenv-expand@10.0.0", "", {}, "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A=="], - - "drizzle-kit": ["drizzle-kit@0.31.4", "", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.25.4", "esbuild-register": "^3.5.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-tCPWVZWZqWVx2XUsVpJRnH9Mx0ClVOf5YUHerZ5so1OKSlqww4zy1R5ksEdGRcO3tM3zj0PYN6V48TbQCL1RfA=="], - - "drizzle-orm": ["drizzle-orm@0.44.4", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1.13", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@upstash/redis": ">=1.34.7", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@upstash/redis", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-ZyzKFpTC/Ut3fIqc2c0dPZ6nhchQXriTsqTNs4ayRgl6sZcFlMs9QZKPSHXK4bdOf41GHGWf+FrpcDDYwW+W6Q=="], - - "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], - - "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], - - "ejs": ["ejs@3.1.10", "", { "dependencies": { "jake": "^10.8.5" }, "bin": { "ejs": "bin/cli.js" } }, "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA=="], - - "electron-to-chromium": ["electron-to-chromium@1.5.90", "", {}, "sha512-C3PN4aydfW91Natdyd449Kw+BzhLmof6tzy5W1pFC5SpQxVXT+oyiyOG9AgYYSN9OdA/ik3YkCrpwqI8ug5Tug=="], - - "elysia": ["elysia@1.3.5", "", { "dependencies": { "cookie": "^1.0.2", "exact-mirror": "0.1.2", "fast-decode-uri-component": "^1.0.1" }, "optionalDependencies": { "@sinclair/typebox": "^0.34.33", "openapi-types": "^12.1.3" }, "peerDependencies": { "file-type": ">= 20.0.0", "typescript": ">= 5.0.0" } }, "sha512-XVIKXlKFwUT7Sta8GY+wO5reD9I0rqAEtaz1Z71UgJb61csYt8Q3W9al8rtL5RgumuRR8e3DNdzlUN9GkC4KDw=="], - - "emittery": ["emittery@0.13.1", "", {}, "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ=="], - - "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], - - "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], - - "encoding-sniffer": ["encoding-sniffer@0.2.0", "", { "dependencies": { "iconv-lite": "^0.6.3", "whatwg-encoding": "^3.1.1" } }, "sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg=="], - - "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], - - "error-ex": ["error-ex@1.3.2", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g=="], - - "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], - - "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], - - "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], - - "esbuild": ["esbuild@0.25.7", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.7", "@esbuild/android-arm": "0.25.7", "@esbuild/android-arm64": "0.25.7", "@esbuild/android-x64": "0.25.7", "@esbuild/darwin-arm64": "0.25.7", "@esbuild/darwin-x64": "0.25.7", "@esbuild/freebsd-arm64": "0.25.7", "@esbuild/freebsd-x64": "0.25.7", "@esbuild/linux-arm": "0.25.7", "@esbuild/linux-arm64": "0.25.7", "@esbuild/linux-ia32": "0.25.7", "@esbuild/linux-loong64": "0.25.7", "@esbuild/linux-mips64el": "0.25.7", "@esbuild/linux-ppc64": "0.25.7", "@esbuild/linux-riscv64": "0.25.7", "@esbuild/linux-s390x": "0.25.7", "@esbuild/linux-x64": "0.25.7", "@esbuild/netbsd-arm64": "0.25.7", "@esbuild/netbsd-x64": "0.25.7", "@esbuild/openbsd-arm64": "0.25.7", "@esbuild/openbsd-x64": "0.25.7", "@esbuild/openharmony-arm64": "0.25.7", "@esbuild/sunos-x64": "0.25.7", "@esbuild/win32-arm64": "0.25.7", "@esbuild/win32-ia32": "0.25.7", "@esbuild/win32-x64": "0.25.7" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-daJB0q2dmTzo90L9NjRaohhRWrCzYxWNFTjEi72/h+p5DcY3yn4MacWfDakHmaBaDzDiuLJsCh0+6LK/iX+c+Q=="], - - "esbuild-register": ["esbuild-register@3.6.0", "", { "dependencies": { "debug": "^4.3.4" }, "peerDependencies": { "esbuild": ">=0.12 <1" } }, "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg=="], - - "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], - - "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], - - "escape-string-regexp": ["escape-string-regexp@2.0.0", "", {}, "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="], - - "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], - - "estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], - - "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], - - "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], - - "exact-mirror": ["exact-mirror@0.1.2", "", { "peerDependencies": { "@sinclair/typebox": "^0.34.15" }, "optionalPeers": ["@sinclair/typebox"] }, "sha512-wFCPCDLmHbKGUb8TOi/IS7jLsgR8WVDGtDK3CzcB4Guf/weq7G+I+DkXiRSZfbemBFOxOINKpraM6ml78vo8Zw=="], - - "execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="], - - "exit": ["exit@0.1.2", "", {}, "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ=="], - - "expect": ["expect@29.7.0", "", { "dependencies": { "@jest/expect-utils": "^29.7.0", "jest-get-type": "^29.6.3", "jest-matcher-utils": "^29.7.0", "jest-message-util": "^29.7.0", "jest-util": "^29.7.0" } }, "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw=="], - - "express": ["express@5.1.0", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA=="], - - "fast-decode-uri-component": ["fast-decode-uri-component@1.0.1", "", {}, "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg=="], - - "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], - - "fb-watchman": ["fb-watchman@2.0.2", "", { "dependencies": { "bser": "2.1.1" } }, "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA=="], - - "fflate": ["fflate@0.8.2", "", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="], - - "file-type": ["file-type@21.0.0", "", { "dependencies": { "@tokenizer/inflate": "^0.2.7", "strtok3": "^10.2.2", "token-types": "^6.0.0", "uint8array-extras": "^1.4.0" } }, "sha512-ek5xNX2YBYlXhiUXui3D/BXa3LdqPmoLJ7rqEx2bKJ7EAUEfmXgW0Das7Dc6Nr9MvqaOnIqiPV0mZk/r/UpNAg=="], - - "filelist": ["filelist@1.0.4", "", { "dependencies": { "minimatch": "^5.0.1" } }, "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q=="], - - "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], - - "finalhandler": ["finalhandler@2.1.0", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q=="], - - "find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="], - - "follow-redirects": ["follow-redirects@1.15.9", "", {}, "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="], - - "form-data": ["form-data@4.0.1", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "mime-types": "^2.1.12" } }, "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw=="], - - "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], - - "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], - - "fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="], - - "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], - - "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], - - "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], - - "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], - - "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], - - "get-package-type": ["get-package-type@0.1.0", "", {}, "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q=="], - - "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], - - "get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], - - "get-tsconfig": ["get-tsconfig@4.10.1", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ=="], - - "glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], - - "globals": ["globals@11.12.0", "", {}, "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="], - - "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], - - "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], - - "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], - - "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], - - "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], - - "hono": ["hono@4.9.1", "", {}, "sha512-qfvdJ42t6CQE0N/iSCa8KsW8SQqYD67YB+TYbwPHlnALvX+s7ynh8otR1NEk5jXtUg73gpV/B82OSufDmwtX3w=="], - - "html-escaper": ["html-escaper@2.0.2", "", {}, "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg=="], - - "htmlparser2": ["htmlparser2@9.1.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.1.0", "entities": "^4.5.0" } }, "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ=="], - - "http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="], - - "human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="], - - "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], - - "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], - - "import-local": ["import-local@3.2.0", "", { "dependencies": { "pkg-dir": "^4.2.0", "resolve-cwd": "^3.0.0" }, "bin": { "import-local-fixture": "fixtures/cli.js" } }, "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA=="], - - "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], - - "inflight": ["inflight@1.0.6", "", { "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA=="], - - "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], - - "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], - - "is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="], - - "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], - - "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], - - "is-generator-fn": ["is-generator-fn@2.1.0", "", {}, "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ=="], - - "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], - - "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="], - - "is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], - - "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], - - "istanbul-lib-coverage": ["istanbul-lib-coverage@3.2.2", "", {}, "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg=="], - - "istanbul-lib-instrument": ["istanbul-lib-instrument@5.2.1", "", { "dependencies": { "@babel/core": "^7.12.3", "@babel/parser": "^7.14.7", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.2.0", "semver": "^6.3.0" } }, "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg=="], - - "istanbul-lib-report": ["istanbul-lib-report@3.0.1", "", { "dependencies": { "istanbul-lib-coverage": "^3.0.0", "make-dir": "^4.0.0", "supports-color": "^7.1.0" } }, "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw=="], - - "istanbul-lib-source-maps": ["istanbul-lib-source-maps@4.0.1", "", { "dependencies": { "debug": "^4.1.1", "istanbul-lib-coverage": "^3.0.0", "source-map": "^0.6.1" } }, "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw=="], - - "istanbul-reports": ["istanbul-reports@3.1.7", "", { "dependencies": { "html-escaper": "^2.0.0", "istanbul-lib-report": "^3.0.0" } }, "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g=="], - - "jake": ["jake@10.9.2", "", { "dependencies": { "async": "^3.2.3", "chalk": "^4.0.2", "filelist": "^1.0.4", "minimatch": "^3.1.2" }, "bin": { "jake": "bin/cli.js" } }, "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA=="], - - "jest": ["jest@29.7.0", "", { "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", "import-local": "^3.0.2", "jest-cli": "^29.7.0" }, "peerDependencies": { "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" }, "optionalPeers": ["node-notifier"], "bin": { "jest": "bin/jest.js" } }, "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw=="], - - "jest-changed-files": ["jest-changed-files@29.7.0", "", { "dependencies": { "execa": "^5.0.0", "jest-util": "^29.7.0", "p-limit": "^3.1.0" } }, "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w=="], - - "jest-circus": ["jest-circus@29.7.0", "", { "dependencies": { "@jest/environment": "^29.7.0", "@jest/expect": "^29.7.0", "@jest/test-result": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "co": "^4.6.0", "dedent": "^1.0.0", "is-generator-fn": "^2.0.0", "jest-each": "^29.7.0", "jest-matcher-utils": "^29.7.0", "jest-message-util": "^29.7.0", "jest-runtime": "^29.7.0", "jest-snapshot": "^29.7.0", "jest-util": "^29.7.0", "p-limit": "^3.1.0", "pretty-format": "^29.7.0", "pure-rand": "^6.0.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" } }, "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw=="], - - "jest-cli": ["jest-cli@29.7.0", "", { "dependencies": { "@jest/core": "^29.7.0", "@jest/test-result": "^29.7.0", "@jest/types": "^29.6.3", "chalk": "^4.0.0", "create-jest": "^29.7.0", "exit": "^0.1.2", "import-local": "^3.0.2", "jest-config": "^29.7.0", "jest-util": "^29.7.0", "jest-validate": "^29.7.0", "yargs": "^17.3.1" }, "peerDependencies": { "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" }, "optionalPeers": ["node-notifier"], "bin": { "jest": "bin/jest.js" } }, "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg=="], - - "jest-config": ["jest-config@29.7.0", "", { "dependencies": { "@babel/core": "^7.11.6", "@jest/test-sequencer": "^29.7.0", "@jest/types": "^29.6.3", "babel-jest": "^29.7.0", "chalk": "^4.0.0", "ci-info": "^3.2.0", "deepmerge": "^4.2.2", "glob": "^7.1.3", "graceful-fs": "^4.2.9", "jest-circus": "^29.7.0", "jest-environment-node": "^29.7.0", "jest-get-type": "^29.6.3", "jest-regex-util": "^29.6.3", "jest-resolve": "^29.7.0", "jest-runner": "^29.7.0", "jest-util": "^29.7.0", "jest-validate": "^29.7.0", "micromatch": "^4.0.4", "parse-json": "^5.2.0", "pretty-format": "^29.7.0", "slash": "^3.0.0", "strip-json-comments": "^3.1.1" }, "peerDependencies": { "@types/node": "*", "ts-node": ">=9.0.0" }, "optionalPeers": ["@types/node", "ts-node"] }, "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ=="], - - "jest-diff": ["jest-diff@29.7.0", "", { "dependencies": { "chalk": "^4.0.0", "diff-sequences": "^29.6.3", "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" } }, "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw=="], - - "jest-docblock": ["jest-docblock@29.7.0", "", { "dependencies": { "detect-newline": "^3.0.0" } }, "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g=="], - - "jest-each": ["jest-each@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "chalk": "^4.0.0", "jest-get-type": "^29.6.3", "jest-util": "^29.7.0", "pretty-format": "^29.7.0" } }, "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ=="], - - "jest-environment-node": ["jest-environment-node@29.7.0", "", { "dependencies": { "@jest/environment": "^29.7.0", "@jest/fake-timers": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "jest-mock": "^29.7.0", "jest-util": "^29.7.0" } }, "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw=="], - - "jest-get-type": ["jest-get-type@29.6.3", "", {}, "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw=="], - - "jest-haste-map": ["jest-haste-map@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "graceful-fs": "^4.2.9", "jest-regex-util": "^29.6.3", "jest-util": "^29.7.0", "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "walker": "^1.0.8" }, "optionalDependencies": { "fsevents": "^2.3.2" } }, "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA=="], - - "jest-leak-detector": ["jest-leak-detector@29.7.0", "", { "dependencies": { "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" } }, "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw=="], - - "jest-matcher-utils": ["jest-matcher-utils@29.7.0", "", { "dependencies": { "chalk": "^4.0.0", "jest-diff": "^29.7.0", "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" } }, "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g=="], - - "jest-message-util": ["jest-message-util@29.7.0", "", { "dependencies": { "@babel/code-frame": "^7.12.13", "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", "pretty-format": "^29.7.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" } }, "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w=="], - - "jest-mock": ["jest-mock@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "jest-util": "^29.7.0" } }, "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw=="], - - "jest-pnp-resolver": ["jest-pnp-resolver@1.2.3", "", { "peerDependencies": { "jest-resolve": "*" }, "optionalPeers": ["jest-resolve"] }, "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w=="], - - "jest-regex-util": ["jest-regex-util@29.6.3", "", {}, "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg=="], - - "jest-resolve": ["jest-resolve@29.7.0", "", { "dependencies": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "jest-haste-map": "^29.7.0", "jest-pnp-resolver": "^1.2.2", "jest-util": "^29.7.0", "jest-validate": "^29.7.0", "resolve": "^1.20.0", "resolve.exports": "^2.0.0", "slash": "^3.0.0" } }, "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA=="], - - "jest-resolve-dependencies": ["jest-resolve-dependencies@29.7.0", "", { "dependencies": { "jest-regex-util": "^29.6.3", "jest-snapshot": "^29.7.0" } }, "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA=="], - - "jest-runner": ["jest-runner@29.7.0", "", { "dependencies": { "@jest/console": "^29.7.0", "@jest/environment": "^29.7.0", "@jest/test-result": "^29.7.0", "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "emittery": "^0.13.1", "graceful-fs": "^4.2.9", "jest-docblock": "^29.7.0", "jest-environment-node": "^29.7.0", "jest-haste-map": "^29.7.0", "jest-leak-detector": "^29.7.0", "jest-message-util": "^29.7.0", "jest-resolve": "^29.7.0", "jest-runtime": "^29.7.0", "jest-util": "^29.7.0", "jest-watcher": "^29.7.0", "jest-worker": "^29.7.0", "p-limit": "^3.1.0", "source-map-support": "0.5.13" } }, "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ=="], - - "jest-runtime": ["jest-runtime@29.7.0", "", { "dependencies": { "@jest/environment": "^29.7.0", "@jest/fake-timers": "^29.7.0", "@jest/globals": "^29.7.0", "@jest/source-map": "^29.6.3", "@jest/test-result": "^29.7.0", "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "cjs-module-lexer": "^1.0.0", "collect-v8-coverage": "^1.0.0", "glob": "^7.1.3", "graceful-fs": "^4.2.9", "jest-haste-map": "^29.7.0", "jest-message-util": "^29.7.0", "jest-mock": "^29.7.0", "jest-regex-util": "^29.6.3", "jest-resolve": "^29.7.0", "jest-snapshot": "^29.7.0", "jest-util": "^29.7.0", "slash": "^3.0.0", "strip-bom": "^4.0.0" } }, "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ=="], - - "jest-snapshot": ["jest-snapshot@29.7.0", "", { "dependencies": { "@babel/core": "^7.11.6", "@babel/generator": "^7.7.2", "@babel/plugin-syntax-jsx": "^7.7.2", "@babel/plugin-syntax-typescript": "^7.7.2", "@babel/types": "^7.3.3", "@jest/expect-utils": "^29.7.0", "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0", "chalk": "^4.0.0", "expect": "^29.7.0", "graceful-fs": "^4.2.9", "jest-diff": "^29.7.0", "jest-get-type": "^29.6.3", "jest-matcher-utils": "^29.7.0", "jest-message-util": "^29.7.0", "jest-util": "^29.7.0", "natural-compare": "^1.4.0", "pretty-format": "^29.7.0", "semver": "^7.5.3" } }, "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw=="], - - "jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], - - "jest-validate": ["jest-validate@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "camelcase": "^6.2.0", "chalk": "^4.0.0", "jest-get-type": "^29.6.3", "leven": "^3.1.0", "pretty-format": "^29.7.0" } }, "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw=="], - - "jest-watcher": ["jest-watcher@29.7.0", "", { "dependencies": { "@jest/test-result": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "emittery": "^0.13.1", "jest-util": "^29.7.0", "string-length": "^4.0.1" } }, "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g=="], - - "jest-worker": ["jest-worker@29.7.0", "", { "dependencies": { "@types/node": "*", "jest-util": "^29.7.0", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" } }, "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw=="], - - "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], - - "js-yaml": ["js-yaml@3.14.1", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g=="], - - "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], - - "json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="], - - "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], - - "kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], - - "leven": ["leven@3.1.0", "", {}, "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A=="], - - "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], - - "locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], - - "lodash.debounce": ["lodash.debounce@4.0.8", "", {}, "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="], - - "lodash.memoize": ["lodash.memoize@4.1.2", "", {}, "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag=="], - - "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], - - "make-dir": ["make-dir@4.0.0", "", { "dependencies": { "semver": "^7.5.3" } }, "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw=="], - - "make-error": ["make-error@1.3.6", "", {}, "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="], - - "makeerror": ["makeerror@1.0.12", "", { "dependencies": { "tmpl": "1.0.5" } }, "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg=="], - - "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], - - "media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="], - - "merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="], - - "merge-stream": ["merge-stream@2.0.0", "", {}, "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="], - - "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], - - "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], - - "mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="], - - "mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], - - "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], - - "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], - - "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], - - "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], - - "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], - - "node-int64": ["node-int64@0.4.0", "", {}, "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw=="], - - "node-releases": ["node-releases@2.0.19", "", {}, "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw=="], - - "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], - - "npm-check-updates": ["npm-check-updates@18.0.1", "", { "bin": { "ncu": "build/cli.js", "npm-check-updates": "build/cli.js" } }, "sha512-MO7mLp/8nm6kZNLLyPgz4gHmr9tLoU+pWPLdXuGAx+oZydBHkHWN0ibTonsrfwC2WEQNIQxuZagYwB67JQpAuw=="], - - "npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="], - - "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="], - - "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], - - "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], - - "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], - - "onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], - - "openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="], - - "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], - - "p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="], - - "p-try": ["p-try@2.2.0", "", {}, "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="], - - "parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="], - - "parse5": ["parse5@7.2.1", "", { "dependencies": { "entities": "^4.5.0" } }, "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ=="], - - "parse5-htmlparser2-tree-adapter": ["parse5-htmlparser2-tree-adapter@7.1.0", "", { "dependencies": { "domhandler": "^5.0.3", "parse5": "^7.0.0" } }, "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g=="], - - "parse5-parser-stream": ["parse5-parser-stream@7.1.2", "", { "dependencies": { "parse5": "^7.0.0" } }, "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow=="], - - "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], - - "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], - - "path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="], - - "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - - "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], - - "path-to-regexp": ["path-to-regexp@8.2.0", "", {}, "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ=="], - - "pg": ["pg@8.16.3", "", { "dependencies": { "pg-connection-string": "^2.9.1", "pg-pool": "^3.10.1", "pg-protocol": "^1.10.3", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "optionalDependencies": { "pg-cloudflare": "^1.2.7" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw=="], - - "pg-cloudflare": ["pg-cloudflare@1.2.7", "", {}, "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg=="], - - "pg-connection-string": ["pg-connection-string@2.9.1", "", {}, "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w=="], - - "pg-int8": ["pg-int8@1.0.1", "", {}, "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="], - - "pg-pool": ["pg-pool@3.10.1", "", { "peerDependencies": { "pg": ">=8.0" } }, "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg=="], - - "pg-protocol": ["pg-protocol@1.10.3", "", {}, "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ=="], - - "pg-types": ["pg-types@2.2.0", "", { "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", "postgres-bytea": "~1.0.0", "postgres-date": "~1.0.4", "postgres-interval": "^1.1.0" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="], - - "pgpass": ["pgpass@1.0.5", "", { "dependencies": { "split2": "^4.1.0" } }, "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug=="], - - "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], - - "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], - - "pirates": ["pirates@4.0.6", "", {}, "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg=="], - - "pkg-dir": ["pkg-dir@4.2.0", "", { "dependencies": { "find-up": "^4.0.0" } }, "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ=="], - - "postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="], - - "postgres-bytea": ["postgres-bytea@1.0.0", "", {}, "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w=="], - - "postgres-date": ["postgres-date@1.0.7", "", {}, "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="], - - "postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="], - - "pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], - - "prompts": ["prompts@2.4.2", "", { "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" } }, "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q=="], - - "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], - - "proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], - - "pure-rand": ["pure-rand@6.1.0", "", {}, "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA=="], - - "qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="], - - "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], - - "raw-body": ["raw-body@3.0.0", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.6.3", "unpipe": "1.0.0" } }, "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g=="], - - "react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], - - "regenerate": ["regenerate@1.4.2", "", {}, "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A=="], - - "regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.0", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA=="], - - "regenerator-runtime": ["regenerator-runtime@0.14.1", "", {}, "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="], - - "regenerator-transform": ["regenerator-transform@0.15.2", "", { "dependencies": { "@babel/runtime": "^7.8.4" } }, "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg=="], - - "regexpu-core": ["regexpu-core@6.2.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.0", "regjsgen": "^0.8.0", "regjsparser": "^0.12.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.1.0" } }, "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA=="], - - "regjsgen": ["regjsgen@0.8.0", "", {}, "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q=="], - - "regjsparser": ["regjsparser@0.12.0", "", { "dependencies": { "jsesc": "~3.0.2" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ=="], - - "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], - - "resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="], - - "resolve-cwd": ["resolve-cwd@3.0.0", "", { "dependencies": { "resolve-from": "^5.0.0" } }, "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg=="], - - "resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="], - - "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], - - "resolve.exports": ["resolve.exports@2.0.3", "", {}, "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A=="], - - "rollup": ["rollup@4.45.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.45.1", "@rollup/rollup-android-arm64": "4.45.1", "@rollup/rollup-darwin-arm64": "4.45.1", "@rollup/rollup-darwin-x64": "4.45.1", "@rollup/rollup-freebsd-arm64": "4.45.1", "@rollup/rollup-freebsd-x64": "4.45.1", "@rollup/rollup-linux-arm-gnueabihf": "4.45.1", "@rollup/rollup-linux-arm-musleabihf": "4.45.1", "@rollup/rollup-linux-arm64-gnu": "4.45.1", "@rollup/rollup-linux-arm64-musl": "4.45.1", "@rollup/rollup-linux-loongarch64-gnu": "4.45.1", "@rollup/rollup-linux-powerpc64le-gnu": "4.45.1", "@rollup/rollup-linux-riscv64-gnu": "4.45.1", "@rollup/rollup-linux-riscv64-musl": "4.45.1", "@rollup/rollup-linux-s390x-gnu": "4.45.1", "@rollup/rollup-linux-x64-gnu": "4.45.1", "@rollup/rollup-linux-x64-musl": "4.45.1", "@rollup/rollup-win32-arm64-msvc": "4.45.1", "@rollup/rollup-win32-ia32-msvc": "4.45.1", "@rollup/rollup-win32-x64-msvc": "4.45.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw=="], - - "router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="], - - "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], - - "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], - - "semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - - "send": ["send@1.2.0", "", { "dependencies": { "debug": "^4.3.5", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.0", "mime-types": "^3.0.1", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.1" } }, "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw=="], - - "serve-static": ["serve-static@2.2.0", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ=="], - - "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], - - "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], - - "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - - "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], - - "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="], - - "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], - - "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], - - "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], - - "sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="], - - "slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], - - "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], - - "source-map-support": ["source-map-support@0.5.13", "", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w=="], - - "split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="], - - "sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="], - - "stack-utils": ["stack-utils@2.0.6", "", { "dependencies": { "escape-string-regexp": "^2.0.0" } }, "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ=="], - - "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], - - "string-length": ["string-length@4.0.2", "", { "dependencies": { "char-regex": "^1.0.2", "strip-ansi": "^6.0.0" } }, "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ=="], - - "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], - - "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - - "strip-bom": ["strip-bom@4.0.0", "", {}, "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w=="], - - "strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="], - - "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], - - "strtok3": ["strtok3@10.3.4", "", { "dependencies": { "@tokenizer/token": "^0.3.0" } }, "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg=="], - - "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - - "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], - - "test-exclude": ["test-exclude@6.0.0", "", { "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", "minimatch": "^3.0.4" } }, "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w=="], - - "tmpl": ["tmpl@1.0.5", "", {}, "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw=="], - - "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], - - "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], - - "token-types": ["token-types@6.1.1", "", { "dependencies": { "@borewit/text-codec": "^0.1.0", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ=="], - - "ts-jest": ["ts-jest@29.2.5", "", { "dependencies": { "bs-logger": "^0.2.6", "ejs": "^3.1.10", "fast-json-stable-stringify": "^2.1.0", "jest-util": "^29.0.0", "json5": "^2.2.3", "lodash.memoize": "^4.1.2", "make-error": "^1.3.6", "semver": "^7.6.3", "yargs-parser": "^21.1.1" }, "peerDependencies": { "@babel/core": ">=7.0.0-beta.0 <8", "@jest/transform": "^29.0.0", "@jest/types": "^29.0.0", "babel-jest": "^29.0.0", "jest": "^29.0.0", "typescript": ">=4.3 <6" }, "optionalPeers": ["@babel/core", "@jest/transform", "@jest/types", "babel-jest"], "bin": { "ts-jest": "cli.js" } }, "sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA=="], - - "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - - "tsx": ["tsx@4.20.3", "", { "dependencies": { "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ=="], - - "type-detect": ["type-detect@4.0.8", "", {}, "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g=="], - - "type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="], - - "type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="], - - "typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], - - "uint8array-extras": ["uint8array-extras@1.4.1", "", {}, "sha512-+NWHrac9dvilNgme+gP4YrBSumsaMZP0fNBtXXFIf33RLLKEcBUKaQZ7ULUbS0sBfcjxIZ4V96OTRkCbM7hxpw=="], - - "undici": ["undici@6.21.1", "", {}, "sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ=="], - - "undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="], - - "unicode-canonical-property-names-ecmascript": ["unicode-canonical-property-names-ecmascript@2.0.1", "", {}, "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg=="], - - "unicode-match-property-ecmascript": ["unicode-match-property-ecmascript@2.0.0", "", { "dependencies": { "unicode-canonical-property-names-ecmascript": "^2.0.0", "unicode-property-aliases-ecmascript": "^2.0.0" } }, "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q=="], - - "unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.0", "", {}, "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg=="], - - "unicode-property-aliases-ecmascript": ["unicode-property-aliases-ecmascript@2.1.0", "", {}, "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w=="], - - "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], - - "update-browserslist-db": ["update-browserslist-db@1.1.2", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg=="], - - "v8-to-istanbul": ["v8-to-istanbul@9.3.0", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", "convert-source-map": "^2.0.0" } }, "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA=="], - - "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], - - "walker": ["walker@1.0.8", "", { "dependencies": { "makeerror": "1.0.12" } }, "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ=="], - - "whatwg-encoding": ["whatwg-encoding@3.1.1", "", { "dependencies": { "iconv-lite": "0.6.3" } }, "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ=="], - - "whatwg-mimetype": ["whatwg-mimetype@4.0.0", "", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="], - - "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], - - "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], - - "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], - - "write-file-atomic": ["write-file-atomic@4.0.2", "", { "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" } }, "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg=="], - - "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="], - - "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], - - "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], - - "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], - - "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], - - "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], - - "zod": ["zod@3.25.67", "", {}, "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw=="], - - "@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="], - - "@esbuild-kit/core-utils/source-map-support": ["source-map-support@0.5.21", "", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="], - - "@jest/console/@types/node": ["@types/node@22.13.0", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA=="], - - "@jest/core/@types/node": ["@types/node@22.13.0", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA=="], - - "@jest/environment/@types/node": ["@types/node@22.13.0", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA=="], - - "@jest/fake-timers/@types/node": ["@types/node@22.13.0", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA=="], - - "@jest/reporters/@types/node": ["@types/node@22.13.0", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA=="], - - "@jest/reporters/istanbul-lib-instrument": ["istanbul-lib-instrument@6.0.3", "", { "dependencies": { "@babel/core": "^7.23.9", "@babel/parser": "^7.23.9", "@istanbuljs/schema": "^0.1.3", "istanbul-lib-coverage": "^3.2.0", "semver": "^7.5.4" } }, "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q=="], - - "@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="], - - "@jest/types/@types/node": ["@types/node@22.13.0", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA=="], - - "@types/graceful-fs/@types/node": ["@types/node@22.13.0", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA=="], - - "@types/ws/@types/node": ["@types/node@22.13.0", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA=="], - - "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - - "bun-types/@types/node": ["@types/node@22.13.0", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA=="], - - "chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - - "dotenv-cli/dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], - - "express/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], - - "filelist/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="], - - "form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], - - "http-errors/statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="], - - "jest-circus/@types/node": ["@types/node@22.13.0", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA=="], - - "jest-environment-node/@types/node": ["@types/node@22.13.0", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA=="], - - "jest-haste-map/@types/node": ["@types/node@22.13.0", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA=="], - - "jest-mock/@types/node": ["@types/node@22.13.0", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA=="], - - "jest-runner/@types/node": ["@types/node@22.13.0", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA=="], - - "jest-runtime/@types/node": ["@types/node@22.13.0", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA=="], - - "jest-snapshot/semver": ["semver@7.7.0", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ=="], - - "jest-util/@types/node": ["@types/node@22.13.0", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA=="], - - "jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - - "jest-validate/camelcase": ["camelcase@6.3.0", "", {}, "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA=="], - - "jest-watcher/@types/node": ["@types/node@22.13.0", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA=="], - - "jest-worker/@types/node": ["@types/node@22.13.0", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA=="], - - "jest-worker/supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="], - - "make-dir/semver": ["semver@7.7.0", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ=="], - - "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - - "p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], - - "regjsparser/jsesc": ["jsesc@3.0.2", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g=="], - - "ts-jest/semver": ["semver@7.7.0", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ=="], - - "wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - - "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.18.20", "", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="], - - "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.18.20", "", { "os": "android", "cpu": "arm64" }, "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ=="], - - "@esbuild-kit/core-utils/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.18.20", "", { "os": "android", "cpu": "x64" }, "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg=="], - - "@esbuild-kit/core-utils/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.18.20", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA=="], - - "@esbuild-kit/core-utils/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.18.20", "", { "os": "darwin", "cpu": "x64" }, "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ=="], - - "@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.18.20", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw=="], - - "@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.18.20", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ=="], - - "@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.18.20", "", { "os": "linux", "cpu": "arm" }, "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg=="], - - "@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.18.20", "", { "os": "linux", "cpu": "arm64" }, "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA=="], - - "@esbuild-kit/core-utils/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.18.20", "", { "os": "linux", "cpu": "ia32" }, "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA=="], - - "@esbuild-kit/core-utils/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg=="], - - "@esbuild-kit/core-utils/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ=="], - - "@esbuild-kit/core-utils/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.18.20", "", { "os": "linux", "cpu": "ppc64" }, "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA=="], - - "@esbuild-kit/core-utils/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A=="], - - "@esbuild-kit/core-utils/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.18.20", "", { "os": "linux", "cpu": "s390x" }, "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ=="], - - "@esbuild-kit/core-utils/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.18.20", "", { "os": "linux", "cpu": "x64" }, "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w=="], - - "@esbuild-kit/core-utils/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.18.20", "", { "os": "none", "cpu": "x64" }, "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A=="], - - "@esbuild-kit/core-utils/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.18.20", "", { "os": "openbsd", "cpu": "x64" }, "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg=="], - - "@esbuild-kit/core-utils/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.18.20", "", { "os": "sunos", "cpu": "x64" }, "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ=="], - - "@esbuild-kit/core-utils/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.18.20", "", { "os": "win32", "cpu": "arm64" }, "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg=="], - - "@esbuild-kit/core-utils/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.18.20", "", { "os": "win32", "cpu": "ia32" }, "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g=="], - - "@esbuild-kit/core-utils/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.18.20", "", { "os": "win32", "cpu": "x64" }, "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ=="], - - "@jest/console/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], - - "@jest/core/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], - - "@jest/environment/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], - - "@jest/fake-timers/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], - - "@jest/reporters/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], - - "@jest/reporters/istanbul-lib-instrument/semver": ["semver@7.7.0", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ=="], - - "@jest/types/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], - - "@types/graceful-fs/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], - - "@types/ws/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], - - "bun-types/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], - - "filelist/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], - - "form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], - - "jest-circus/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], - - "jest-environment-node/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], - - "jest-haste-map/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], - - "jest-mock/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], - - "jest-runner/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], - - "jest-runtime/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], - - "jest-util/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], - - "jest-watcher/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], - - "jest-worker/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], - } -} diff --git a/bun.lockb b/bun.lockb deleted file mode 100755 index 3acc9cd08bc07737432a05c87fee85d1172ee8a8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 285615 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

r~{vH~0*54CWHhw~fj3dbaqMaPpA=`mNyp5aC zt<%Ck2cMurQcn)oOV$HQOwH(9!cT45r32x(eZjyX|JFy^Jbm75YENez_iF1pBPei& z^ttyQ_n{fIl8}&8t_6e+g0%z=N$33GkH3BKuu*3LLTedFv^OARk>)KtdF+Z;1E)5q zl#MrM;7z2cz>PJXYwJH-hrV&;AKwB;&YEjbf~51<=#DSEb!6PXk{UFzYO$zi-yhyD zN1jqW7Z4f!NfTV^#eIn18p7g|e znolKf?@7qEv-(V(arOa@b&65{IUqEGFUnsYo7rl_XqG6i^ma=~m)5t9+H_rGn&%|7 zLlP1=@96Y;g`+;uAx9--Ta#aXu0=IyA4B5Qgz+cs{pyD2Zru6wrv|&zNJ5@|=Zn*R zTz@S^k0s7I64L3fCmtVK7NQwLLb4^K-n?69zH{>j2Apny(7e$1q`_mqSlskZ9j7-S zWR5sf`Y7grEFVo~Wb1toFFl%gzdCsvlDF{^ zGI_|Wle3n;wNr=O1IWq1*>U@al{RfyOg_29nFk2P9B#kk#K6l_znQK>)OJ)YH#OJl zvEVO__#uWQIcsvR?i`e-nW;tbk&(Au(sP84qaGy`OE?P6ly*A5;&v>+dEwrp4yW= z6sZRXC1l}|k5{NT zlrDxQoCk^%?>3q>W*kE(%GItVAgutY+4_cI^|!iL2?!FpUA+N07m(4_y9O5RsY=!b zJkyOVQ9y{d1APbI`(yne=^Mv3a$Pe3X$HuU8>b9554^y|23<7Iv5^rysWP$D?kbKdo@EhzitULEHmK#1#Y{$Cf&*z?QZ zI*tdB3jjIe!$-VbmOUVNV-D_-)V^x}>(58(pL0;hSqVrMa8?!n(&ExbR$VUYAsKuP z$T@(#de^M8CVc#gkSOvH+f&rXG!}6Cjg~uUb?TfZP1qgF(?pPt#W;n!(`u@~AEFhi zYgFUwI?fq%($>nAx|WL3VL5(skl5ntDNEG7?8+T=yYBDA?P17ZK*+*g^-b`K)~~%J zdK+Fq2bUiZ8YP!rH}JY^r_Ed|a7anx64LmO;{8wbYECO;yrz0A#}7lM0EaAW&lmpP zwR706ff}`J*8_mi_}Tm8qmTB_+Rz&i>2WOqq#+=#{IzfFzmGLUdTFZxp;2MmRG-~Z!p94brmOEkEBZnreBzq0sh-!w>9mbsY}5Xqs7{)lfi|vp3JV8&M|L@?2f36)`Iuej}V(om+c(E#o-FfxV=NF$dLE=EGQd?TD zQX7<|`M~0wo?I1J>j6x*>+XrdQyuZy&u#iFXiW1q_d>R7DIk=0ySwkW6RsUNiy{~j zq8>lidJO}k&_se*#;WtA

-V~T2mvfvbLjSUDWnXA-DF& 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 LZ}0j)`tSb%R#DNp diff --git a/docker-compose.yml b/docker-compose.yml index f53d5a9..c5a8658 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -20,7 +20,7 @@ services: server: build: - dockerfile: Dockerfile.pnpm.simple + dockerfile: Dockerfile.pnpm container_name: dining-api-server ports: - "5010:5010" diff --git a/package.json b/package.json index 9cdf33d..9f47e02 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "@elysiajs/node": "1.3.0", "@types/express": "^5.0.3", "axios": "^1.10.0", - "cheerio": "^1.0.0-rc.12", + "cheerio": "1.0.0-rc.12", "drizzle-kit": "^0.31.4", "drizzle-orm": "^0.44.4", "elysia": "1.3.5", @@ -42,12 +42,10 @@ "@babel/preset-env": "^7.25.4", "@babel/preset-typescript": "^7.24.7", "@rollup/plugin-typescript": "^12.1.4", - "@types/bun": "^1.1.1", "@types/jest": "^29.5.14", "@types/node": "^24.0.14", "@types/pg": "^8.15.5", "babel-jest": "^29.7.0", - "bun-types": "^1.1.7", "dotenv": "^17.2.0", "dotenv-cli": "^8.0.0", "jest": "^29.7.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..65d8336 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,5861 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@elysiajs/cors': + specifier: 1.3.3 + version: 1.3.3(elysia@1.3.5(exact-mirror@0.2.2(@sinclair/typebox@0.34.41))(file-type@21.0.0)(typescript@5.9.2)) + '@elysiajs/node': + specifier: 1.3.0 + version: 1.3.0(elysia@1.3.5(exact-mirror@0.2.2(@sinclair/typebox@0.34.41))(file-type@21.0.0)(typescript@5.9.2))(hono@4.9.8) + '@types/express': + specifier: ^5.0.3 + version: 5.0.3 + axios: + specifier: ^1.10.0 + version: 1.12.2 + cheerio: + specifier: 1.0.0-rc.12 + version: 1.0.0-rc.12 + drizzle-kit: + specifier: ^0.31.4 + version: 0.31.4 + drizzle-orm: + specifier: ^0.44.4 + version: 0.44.5(@types/pg@8.15.5)(bun-types@1.2.22(@types/react@19.1.13))(pg@8.16.3) + elysia: + specifier: 1.3.5 + version: 1.3.5(exact-mirror@0.2.2(@sinclair/typebox@0.34.41))(file-type@21.0.0)(typescript@5.9.2) + express: + specifier: ^5.1.0 + version: 5.1.0 + pg: + specifier: ^8.16.3 + version: 8.16.3 + typescript: + specifier: ^5.8.3 + version: 5.9.2 + zod: + specifier: ^3.25.67 + version: 3.25.76 + devDependencies: + '@babel/core': + specifier: ^7.25.2 + version: 7.28.4 + '@babel/preset-env': + specifier: ^7.25.4 + version: 7.28.3(@babel/core@7.28.4) + '@babel/preset-typescript': + specifier: ^7.24.7 + version: 7.27.1(@babel/core@7.28.4) + '@rollup/plugin-typescript': + specifier: ^12.1.4 + version: 12.1.4(rollup@4.52.0)(tslib@2.8.1)(typescript@5.9.2) + '@types/jest': + specifier: ^29.5.14 + version: 29.5.14 + '@types/node': + specifier: ^24.0.14 + version: 24.5.2 + '@types/pg': + specifier: ^8.15.5 + version: 8.15.5 + babel-jest: + specifier: ^29.7.0 + version: 29.7.0(@babel/core@7.28.4) + dotenv: + specifier: ^17.2.0 + version: 17.2.2 + dotenv-cli: + specifier: ^8.0.0 + version: 8.0.0 + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@24.5.2) + npm-check-updates: + specifier: ^18.0.1 + version: 18.3.0 + rollup: + specifier: ^4.45.1 + version: 4.52.0 + ts-jest: + specifier: ^29.2.5 + version: 29.4.4(@babel/core@7.28.4)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.4))(esbuild@0.25.10)(jest-util@29.7.0)(jest@29.7.0(@types/node@24.5.2))(typescript@5.9.2) + tslib: + specifier: ^2.8.1 + version: 2.8.1 + tsx: + specifier: ^4.20.3 + version: 4.20.5 + +packages: + + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.28.4': + resolution: {integrity: sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.28.4': + resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.28.3': + resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-annotate-as-pure@7.27.3': + resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-create-class-features-plugin@7.28.3': + resolution: {integrity: sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-create-regexp-features-plugin@7.27.1': + resolution: {integrity: sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-define-polyfill-provider@0.6.5': + resolution: {integrity: sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-member-expression-to-functions@7.27.1': + resolution: {integrity: sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.3': + resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-optimise-call-expression@7.27.1': + resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-plugin-utils@7.27.1': + resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-remap-async-to-generator@7.27.1': + resolution: {integrity: sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-replace-supers@7.27.1': + resolution: {integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-wrap-function@7.28.3': + resolution: {integrity: sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.28.4': + resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.4': + resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1': + resolution: {integrity: sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1': + resolution: {integrity: sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1': + resolution: {integrity: sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1': + resolution: {integrity: sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.13.0 + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.3': + resolution: {integrity: sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2': + resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-async-generators@7.8.4': + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-bigint@7.8.3': + resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-class-properties@7.12.13': + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-class-static-block@7.14.5': + resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-assertions@7.27.1': + resolution: {integrity: sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-attributes@7.27.1': + resolution: {integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-meta@7.10.4': + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-json-strings@7.8.3': + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-jsx@7.27.1': + resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4': + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-numeric-separator@7.10.4': + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-object-rest-spread@7.8.3': + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3': + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-optional-chaining@7.8.3': + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-private-property-in-object@7.14.5': + resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-top-level-await@7.14.5': + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.27.1': + resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6': + resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-arrow-functions@7.27.1': + resolution: {integrity: sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-generator-functions@7.28.0': + resolution: {integrity: sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-to-generator@7.27.1': + resolution: {integrity: sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoped-functions@7.27.1': + resolution: {integrity: sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoping@7.28.4': + resolution: {integrity: sha512-1yxmvN0MJHOhPVmAsmoW5liWwoILobu/d/ShymZmj867bAdxGbehIrew1DuLpw2Ukv+qDSSPQdYW1dLNE7t11A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-properties@7.27.1': + resolution: {integrity: sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-static-block@7.28.3': + resolution: {integrity: sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.12.0 + + '@babel/plugin-transform-classes@7.28.4': + resolution: {integrity: sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-computed-properties@7.27.1': + resolution: {integrity: sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-destructuring@7.28.0': + resolution: {integrity: sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-dotall-regex@7.27.1': + resolution: {integrity: sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-keys@7.27.1': + resolution: {integrity: sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1': + resolution: {integrity: sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-dynamic-import@7.27.1': + resolution: {integrity: sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-explicit-resource-management@7.28.0': + resolution: {integrity: sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-exponentiation-operator@7.27.1': + resolution: {integrity: sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-export-namespace-from@7.27.1': + resolution: {integrity: sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-for-of@7.27.1': + resolution: {integrity: sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-function-name@7.27.1': + resolution: {integrity: sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-json-strings@7.27.1': + resolution: {integrity: sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-literals@7.27.1': + resolution: {integrity: sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-logical-assignment-operators@7.27.1': + resolution: {integrity: sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-member-expression-literals@7.27.1': + resolution: {integrity: sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-amd@7.27.1': + resolution: {integrity: sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-commonjs@7.27.1': + resolution: {integrity: sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-systemjs@7.27.1': + resolution: {integrity: sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-umd@7.27.1': + resolution: {integrity: sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-named-capturing-groups-regex@7.27.1': + resolution: {integrity: sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-new-target@7.27.1': + resolution: {integrity: sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-nullish-coalescing-operator@7.27.1': + resolution: {integrity: sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-numeric-separator@7.27.1': + resolution: {integrity: sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-rest-spread@7.28.4': + resolution: {integrity: sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-super@7.27.1': + resolution: {integrity: sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-catch-binding@7.27.1': + resolution: {integrity: sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-chaining@7.27.1': + resolution: {integrity: sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-parameters@7.27.7': + resolution: {integrity: sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-methods@7.27.1': + resolution: {integrity: sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-property-in-object@7.27.1': + resolution: {integrity: sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-property-literals@7.27.1': + resolution: {integrity: sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regenerator@7.28.4': + resolution: {integrity: sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regexp-modifiers@7.27.1': + resolution: {integrity: sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-reserved-words@7.27.1': + resolution: {integrity: sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-shorthand-properties@7.27.1': + resolution: {integrity: sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-spread@7.27.1': + resolution: {integrity: sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-sticky-regex@7.27.1': + resolution: {integrity: sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-template-literals@7.27.1': + resolution: {integrity: sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typeof-symbol@7.27.1': + resolution: {integrity: sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typescript@7.28.0': + resolution: {integrity: sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-escapes@7.27.1': + resolution: {integrity: sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-property-regex@7.27.1': + resolution: {integrity: sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-regex@7.27.1': + resolution: {integrity: sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-sets-regex@7.27.1': + resolution: {integrity: sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/preset-env@7.28.3': + resolution: {integrity: sha512-ROiDcM+GbYVPYBOeCR6uBXKkQpBExLl8k9HO1ygXEyds39j+vCCsjmj7S8GOniZQlEs81QlkdJZe76IpLSiqpg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-modules@0.1.6-no-external-plugins': + resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} + peerDependencies: + '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 + + '@babel/preset-typescript@7.27.1': + resolution: {integrity: sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.28.4': + resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.28.4': + resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} + engines: {node: '>=6.9.0'} + + '@bcoe/v8-coverage@0.2.3': + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + + '@borewit/text-codec@0.1.1': + resolution: {integrity: sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==} + + '@drizzle-team/brocli@0.10.2': + resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==} + + '@elysiajs/cors@1.3.3': + resolution: {integrity: sha512-mYIU6PyMM6xIJuj7d27Vt0/wuzVKIEnFPjcvlkyd7t/m9xspAG37cwNjFxVOnyvY43oOd2I/oW2DB85utXpA2Q==} + peerDependencies: + elysia: '>= 1.3.0' + + '@elysiajs/node@1.3.0': + resolution: {integrity: sha512-AA9rnL/FOBklxtJjFpUDBVZLuiKddDaV1KgnKawHzj5VgN99SzE+V0h1MOhp+8jlo2KQQJO/3aD3upyGgrfohQ==} + peerDependencies: + elysia: '>= 1.3.3' + + '@esbuild-kit/core-utils@3.3.2': + resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==} + deprecated: 'Merged into tsx: https://tsx.is' + + '@esbuild-kit/esm-loader@2.6.5': + resolution: {integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==} + deprecated: 'Merged into tsx: https://tsx.is' + + '@esbuild/aix-ppc64@0.25.10': + resolution: {integrity: sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.18.20': + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.25.10': + resolution: {integrity: sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.18.20': + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-arm@0.25.10': + resolution: {integrity: sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.18.20': + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/android-x64@0.25.10': + resolution: {integrity: sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.18.20': + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-arm64@0.25.10': + resolution: {integrity: sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.18.20': + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.10': + resolution: {integrity: sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.18.20': + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-arm64@0.25.10': + resolution: {integrity: sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.18.20': + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.10': + resolution: {integrity: sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.18.20': + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm64@0.25.10': + resolution: {integrity: sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.18.20': + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.25.10': + resolution: {integrity: sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.18.20': + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-ia32@0.25.10': + resolution: {integrity: sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.18.20': + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-loong64@0.25.10': + resolution: {integrity: sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.18.20': + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-mips64el@0.25.10': + resolution: {integrity: sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.18.20': + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-ppc64@0.25.10': + resolution: {integrity: sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.18.20': + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.10': + resolution: {integrity: sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.18.20': + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-s390x@0.25.10': + resolution: {integrity: sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.18.20': + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/linux-x64@0.25.10': + resolution: {integrity: sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.10': + resolution: {integrity: sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.18.20': + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.10': + resolution: {integrity: sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.10': + resolution: {integrity: sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.18.20': + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.10': + resolution: {integrity: sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.10': + resolution: {integrity: sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.18.20': + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.25.10': + resolution: {integrity: sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.18.20': + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-arm64@0.25.10': + resolution: {integrity: sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.18.20': + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.25.10': + resolution: {integrity: sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.18.20': + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.25.10': + resolution: {integrity: sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@hono/node-server@1.19.3': + resolution: {integrity: sha512-Fjyxfux0rMPXMSob79OmddfpK5ArJa2xLkLCV+zamHkbeXQtSNKOi0keiBKyHZ/hCRKjigjmKGp4AJnDFq8PUw==} + engines: {node: '>=18.14.1'} + peerDependencies: + hono: ^4 + + '@istanbuljs/load-nyc-config@1.1.0': + resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} + engines: {node: '>=8'} + + '@istanbuljs/schema@0.1.3': + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} + + '@jest/console@29.7.0': + resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/core@29.7.0': + resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/environment@29.7.0': + resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/expect-utils@29.7.0': + resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/expect@29.7.0': + resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/fake-timers@29.7.0': + resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/globals@29.7.0': + resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/reporters@29.7.0': + resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/schemas@29.6.3': + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/source-map@29.6.3': + resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/test-result@29.7.0': + resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/test-sequencer@29.7.0': + resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/transform@29.7.0': + resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/types@29.6.3': + resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@rollup/plugin-typescript@12.1.4': + resolution: {integrity: sha512-s5Hx+EtN60LMlDBvl5f04bEiFZmAepk27Q+mr85L/00zPDn1jtzlTV6FWn81MaIwqfWzKxmOJrBWHU6vtQyedQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.14.0||^3.0.0||^4.0.0 + tslib: '*' + typescript: '>=3.7.0' + peerDependenciesMeta: + rollup: + optional: true + tslib: + optional: true + + '@rollup/pluginutils@5.3.0': + resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/rollup-android-arm-eabi@4.52.0': + resolution: {integrity: sha512-VxDYCDqOaR7NXzAtvRx7G1u54d2kEHopb28YH/pKzY6y0qmogP3gG7CSiWsq9WvDFxOQMpNEyjVAHZFXfH3o/A==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.52.0': + resolution: {integrity: sha512-pqDirm8koABIKvzL59YI9W9DWbRlTX7RWhN+auR8HXJxo89m4mjqbah7nJZjeKNTNYopqL+yGg+0mhCpf3xZtQ==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.52.0': + resolution: {integrity: sha512-YCdWlY/8ltN6H78HnMsRHYlPiKvqKagBP1r+D7SSylxX+HnsgXGCmLiV3Y4nSyY9hW8qr8U9LDUx/Lo7M6MfmQ==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.52.0': + resolution: {integrity: sha512-z4nw6y1j+OOSGzuVbSWdIp1IUks9qNw4dc7z7lWuWDKojY38VMWBlEN7F9jk5UXOkUcp97vA1N213DF+Lz8BRg==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.52.0': + resolution: {integrity: sha512-Q/dv9Yvyr5rKlK8WQJZVrp5g2SOYeZUs9u/t2f9cQ2E0gJjYB/BWoedXfUT0EcDJefi2zzVfhcOj8drWCzTviw==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.52.0': + resolution: {integrity: sha512-kdBsLs4Uile/fbjZVvCRcKB4q64R+1mUq0Yd7oU1CMm1Av336ajIFqNFovByipciuUQjBCPMxwJhCgfG2re3rg==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.52.0': + resolution: {integrity: sha512-aL6hRwu0k7MTUESgkg7QHY6CoqPgr6gdQXRJI1/VbFlUMwsSzPGSR7sG5d+MCbYnJmJwThc2ol3nixj1fvI/zQ==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.52.0': + resolution: {integrity: sha512-BTs0M5s1EJejgIBJhCeiFo7GZZ2IXWkFGcyZhxX4+8usnIo5Mti57108vjXFIQmmJaRyDwmV59Tw64Ap1dkwMw==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.52.0': + resolution: {integrity: sha512-uj672IVOU9m08DBGvoPKPi/J8jlVgjh12C9GmjjBxCTQc3XtVmRkRKyeHSmIKQpvJ7fIm1EJieBUcnGSzDVFyw==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.52.0': + resolution: {integrity: sha512-/+IVbeDMDCtB/HP/wiWsSzduD10SEGzIZX2945KSgZRNi4TSkjHqRJtNTVtVb8IRwhJ65ssI56krlLik+zFWkw==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.52.0': + resolution: {integrity: sha512-U1vVzvSWtSMWKKrGoROPBXMh3Vwn93TA9V35PldokHGqiUbF6erSzox/5qrSMKp6SzakvyjcPiVF8yB1xKr9Pg==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.52.0': + resolution: {integrity: sha512-X/4WfuBAdQRH8cK3DYl8zC00XEE6aM472W+QCycpQJeLWVnHfkv7RyBFVaTqNUMsTgIX8ihMjCvFF9OUgeABzw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.52.0': + resolution: {integrity: sha512-xIRYc58HfWDBZoLmWfWXg2Sq8VCa2iJ32B7mqfWnkx5mekekl0tMe7FHpY8I72RXEcUkaWawRvl3qA55og+cwQ==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.52.0': + resolution: {integrity: sha512-mbsoUey05WJIOz8U1WzNdf+6UMYGwE3fZZnQqsM22FZ3wh1N887HT6jAOjXs6CNEK3Ntu2OBsyQDXfIjouI4dw==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.52.0': + resolution: {integrity: sha512-qP6aP970bucEi5KKKR4AuPFd8aTx9EF6BvutvYxmZuWLJHmnq4LvBfp0U+yFDMGwJ+AIJEH5sIP+SNypauMWzg==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.52.0': + resolution: {integrity: sha512-nmSVN+F2i1yKZ7rJNKO3G7ZzmxJgoQBQZ/6c4MuS553Grmr7WqR7LLDcYG53Z2m9409z3JLt4sCOhLdbKQ3HmA==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.52.0': + resolution: {integrity: sha512-2d0qRo33G6TfQVjaMR71P+yJVGODrt5V6+T0BDYH4EMfGgdC/2HWDVjSSFw888GSzAZUwuska3+zxNUCDco6rQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openharmony-arm64@4.52.0': + resolution: {integrity: sha512-A1JalX4MOaFAAyGgpO7XP5khquv/7xKzLIyLmhNrbiCxWpMlnsTYr8dnsWM7sEeotNmxvSOEL7F65j0HXFcFsw==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.52.0': + resolution: {integrity: sha512-YQugafP/rH0eOOHGjmNgDURrpYHrIX0yuojOI8bwCyXwxC9ZdTd3vYkmddPX0oHONLXu9Rb1dDmT0VNpjkzGGw==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.52.0': + resolution: {integrity: sha512-zYdUYhi3Qe2fndujBqL5FjAFzvNeLxtIqfzNEVKD1I7C37/chv1VxhscWSQHTNfjPCrBFQMnynwA3kpZpZ8w4A==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.52.0': + resolution: {integrity: sha512-fGk03kQylNaCOQ96HDMeT7E2n91EqvCDd3RwvT5k+xNdFCeMGnj5b5hEgTGrQuyidqSsD3zJDQ21QIaxXqTBJw==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.52.0': + resolution: {integrity: sha512-6iKDCVSIUQ8jPMoIV0OytRKniaYyy5EbY/RRydmLW8ZR3cEBhxbWl5ro0rkUNe0ef6sScvhbY79HrjRm8i3vDQ==} + cpu: [x64] + os: [win32] + + '@sinclair/typebox@0.27.8': + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + + '@sinclair/typebox@0.34.41': + resolution: {integrity: sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==} + + '@sinonjs/commons@3.0.1': + resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} + + '@sinonjs/fake-timers@10.3.0': + resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + + '@tokenizer/inflate@0.2.7': + resolution: {integrity: sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==} + engines: {node: '>=18'} + + '@tokenizer/token@0.3.0': + resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + + '@types/body-parser@1.19.6': + resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} + + '@types/connect@3.4.38': + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/express-serve-static-core@5.0.7': + resolution: {integrity: sha512-R+33OsgWw7rOhD1emjU7dzCDHucJrgJXMA5PYCzJxVil0dsyx5iBEPHqpPfiKNJQb7lZ1vxwoLR4Z87bBUpeGQ==} + + '@types/express@5.0.3': + resolution: {integrity: sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==} + + '@types/graceful-fs@4.1.9': + resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} + + '@types/http-errors@2.0.5': + resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} + + '@types/istanbul-lib-coverage@2.0.6': + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} + + '@types/istanbul-lib-report@3.0.3': + resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} + + '@types/istanbul-reports@3.0.4': + resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} + + '@types/jest@29.5.14': + resolution: {integrity: sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==} + + '@types/mime@1.3.5': + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + + '@types/node@24.5.2': + resolution: {integrity: sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ==} + + '@types/pg@8.15.5': + resolution: {integrity: sha512-LF7lF6zWEKxuT3/OR8wAZGzkg4ENGXFNyiV/JeOt9z5B+0ZVwbql9McqX5c/WStFq1GaGso7H1AzP/qSzmlCKQ==} + + '@types/qs@6.14.0': + resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} + + '@types/range-parser@1.2.7': + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + + '@types/react@19.1.13': + resolution: {integrity: sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ==} + + '@types/send@0.17.5': + resolution: {integrity: sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==} + + '@types/serve-static@1.15.8': + resolution: {integrity: sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==} + + '@types/stack-utils@2.0.3': + resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} + + '@types/yargs-parser@21.0.3': + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} + + '@types/yargs@17.0.33': + resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} + + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + axios@1.12.2: + resolution: {integrity: sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==} + + babel-jest@29.7.0: + resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.8.0 + + babel-plugin-istanbul@6.1.1: + resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} + engines: {node: '>=8'} + + babel-plugin-jest-hoist@29.6.3: + resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + babel-plugin-polyfill-corejs2@0.4.14: + resolution: {integrity: sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-corejs3@0.13.0: + resolution: {integrity: sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-regenerator@0.6.5: + resolution: {integrity: sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-preset-current-node-syntax@1.2.0: + resolution: {integrity: sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==} + peerDependencies: + '@babel/core': ^7.0.0 || ^8.0.0-0 + + babel-preset-jest@29.6.3: + resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.0.0 + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + baseline-browser-mapping@2.8.6: + resolution: {integrity: sha512-wrH5NNqren/QMtKUEEJf7z86YjfqW/2uw3IL3/xpqZUC95SSVIFXYQeeGjL6FT/X68IROu6RMehZQS5foy2BXw==} + hasBin: true + + body-parser@2.2.0: + resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} + engines: {node: '>=18'} + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.26.2: + resolution: {integrity: sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + bs-logger@0.2.6: + resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} + engines: {node: '>= 6'} + + bser@2.1.1: + resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + bun-types@1.2.22: + resolution: {integrity: sha512-hwaAu8tct/Zn6Zft4U9BsZcXkYomzpHJX28ofvx7k0Zz2HNz54n1n+tDgxoWFGB4PcFvJXJQloPhaV2eP3Q6EA==} + peerDependencies: + '@types/react': ^19 + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + caniuse-lite@1.0.30001743: + resolution: {integrity: sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw==} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + char-regex@1.0.2: + resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} + engines: {node: '>=10'} + + cheerio-select@2.1.0: + resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} + + cheerio@1.0.0-rc.12: + resolution: {integrity: sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==} + engines: {node: '>= 6'} + + ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} + + cjs-module-lexer@1.4.3: + resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + co@4.6.0: + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + + collect-v8-coverage@1.0.2: + resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + content-disposition@1.0.0: + resolution: {integrity: sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==} + engines: {node: '>= 0.6'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + cookie@1.0.2: + resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} + engines: {node: '>=18'} + + core-js-compat@3.45.1: + resolution: {integrity: sha512-tqTt5T4PzsMIZ430XGviK4vzYSoeNJ6CXODi6c/voxOT6IZqBht5/EKaSNnYiEjjRYxjVz7DQIsOsY0XNi8PIA==} + + create-jest@29.7.0: + resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + css-select@5.2.2: + resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} + + css-what@6.2.2: + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} + engines: {node: '>= 6'} + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + dedent@1.7.0: + resolution: {integrity: sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==} + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + detect-newline@3.1.0: + resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} + engines: {node: '>=8'} + + diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + + dotenv-cli@8.0.0: + resolution: {integrity: sha512-aLqYbK7xKOiTMIRf1lDPbI+Y+Ip/wo5k3eyp6ePysVaSqbyxjyK3dK35BTxG+rmd7djf5q2UPs4noPNH+cj0Qw==} + hasBin: true + + dotenv-expand@10.0.0: + resolution: {integrity: sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==} + engines: {node: '>=12'} + + dotenv@16.6.1: + resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} + engines: {node: '>=12'} + + dotenv@17.2.2: + resolution: {integrity: sha512-Sf2LSQP+bOlhKWWyhFsn0UsfdK/kCWRv1iuA2gXAwt3dyNabr6QSj00I2V10pidqz69soatm9ZwZvpQMTIOd5Q==} + engines: {node: '>=12'} + + drizzle-kit@0.31.4: + resolution: {integrity: sha512-tCPWVZWZqWVx2XUsVpJRnH9Mx0ClVOf5YUHerZ5so1OKSlqww4zy1R5ksEdGRcO3tM3zj0PYN6V48TbQCL1RfA==} + hasBin: true + + drizzle-orm@0.44.5: + resolution: {integrity: sha512-jBe37K7d8ZSKptdKfakQFdeljtu3P2Cbo7tJoJSVZADzIKOBo9IAJPOmMsH2bZl90bZgh8FQlD8BjxXA/zuBkQ==} + peerDependencies: + '@aws-sdk/client-rds-data': '>=3' + '@cloudflare/workers-types': '>=4' + '@electric-sql/pglite': '>=0.2.0' + '@libsql/client': '>=0.10.0' + '@libsql/client-wasm': '>=0.10.0' + '@neondatabase/serverless': '>=0.10.0' + '@op-engineering/op-sqlite': '>=2' + '@opentelemetry/api': ^1.4.1 + '@planetscale/database': '>=1.13' + '@prisma/client': '*' + '@tidbcloud/serverless': '*' + '@types/better-sqlite3': '*' + '@types/pg': '*' + '@types/sql.js': '*' + '@upstash/redis': '>=1.34.7' + '@vercel/postgres': '>=0.8.0' + '@xata.io/client': '*' + better-sqlite3: '>=7' + bun-types: '*' + expo-sqlite: '>=14.0.0' + gel: '>=2' + knex: '*' + kysely: '*' + mysql2: '>=2' + pg: '>=8' + postgres: '>=3' + prisma: '*' + sql.js: '>=1' + sqlite3: '>=5' + peerDependenciesMeta: + '@aws-sdk/client-rds-data': + optional: true + '@cloudflare/workers-types': + optional: true + '@electric-sql/pglite': + optional: true + '@libsql/client': + optional: true + '@libsql/client-wasm': + optional: true + '@neondatabase/serverless': + optional: true + '@op-engineering/op-sqlite': + optional: true + '@opentelemetry/api': + optional: true + '@planetscale/database': + optional: true + '@prisma/client': + optional: true + '@tidbcloud/serverless': + optional: true + '@types/better-sqlite3': + optional: true + '@types/pg': + optional: true + '@types/sql.js': + optional: true + '@upstash/redis': + optional: true + '@vercel/postgres': + optional: true + '@xata.io/client': + optional: true + better-sqlite3: + optional: true + bun-types: + optional: true + expo-sqlite: + optional: true + gel: + optional: true + knex: + optional: true + kysely: + optional: true + mysql2: + optional: true + pg: + optional: true + postgres: + optional: true + prisma: + optional: true + sql.js: + optional: true + sqlite3: + optional: true + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + electron-to-chromium@1.5.222: + resolution: {integrity: sha512-gA7psSwSwQRE60CEoLz6JBCQPIxNeuzB2nL8vE03GK/OHxlvykbLyeiumQy1iH5C2f3YbRAZpGCMT12a/9ih9w==} + + elysia@1.3.5: + resolution: {integrity: sha512-XVIKXlKFwUT7Sta8GY+wO5reD9I0rqAEtaz1Z71UgJb61csYt8Q3W9al8rtL5RgumuRR8e3DNdzlUN9GkC4KDw==} + peerDependencies: + exact-mirror: '>= 0.0.9' + file-type: '>= 20.0.0' + typescript: '>= 5.0.0' + + emittery@0.13.1: + resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} + engines: {node: '>=12'} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + esbuild-register@3.6.0: + resolution: {integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==} + peerDependencies: + esbuild: '>=0.12 <1' + + esbuild@0.18.20: + resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + engines: {node: '>=12'} + hasBin: true + + esbuild@0.25.10: + resolution: {integrity: sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@2.0.0: + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + exact-mirror@0.2.2: + resolution: {integrity: sha512-CrGe+4QzHZlnrXZVlo/WbUZ4qQZq8C0uATQVGVgXIrNXgHDBBNFD1VRfssRA2C9t3RYvh3MadZSdg2Wy7HBoQA==} + peerDependencies: + '@sinclair/typebox': ^0.34.15 + peerDependenciesMeta: + '@sinclair/typebox': + optional: true + + execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + + exit@0.1.2: + resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} + engines: {node: '>= 0.8.0'} + + expect@29.7.0: + resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + express@5.1.0: + resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==} + engines: {node: '>= 18'} + + fast-decode-uri-component@1.0.1: + resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fb-watchman@2.0.2: + resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + + fflate@0.8.2: + resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + + file-type@21.0.0: + resolution: {integrity: sha512-ek5xNX2YBYlXhiUXui3D/BXa3LdqPmoLJ7rqEx2bKJ7EAUEfmXgW0Das7Dc6Nr9MvqaOnIqiPV0mZk/r/UpNAg==} + engines: {node: '>=20'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + finalhandler@2.1.0: + resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} + engines: {node: '>= 0.8'} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + follow-redirects@1.15.11: + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + form-data@4.0.4: + resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} + engines: {node: '>= 6'} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-package-type@0.1.0: + resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} + engines: {node: '>=8.0.0'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + + get-tsconfig@4.10.1: + resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + handlebars@4.7.8: + resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} + engines: {node: '>=0.4.7'} + hasBin: true + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hono@4.9.8: + resolution: {integrity: sha512-JW8Bb4RFWD9iOKxg5PbUarBYGM99IcxFl2FPBo2gSJO11jjUDqlP1Bmfyqt8Z/dGhIQ63PMA9LdcLefXyIasyg==} + engines: {node: '>=16.9.0'} + + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + + htmlparser2@8.0.2: + resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} + + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + + human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.7.0: + resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==} + engines: {node: '>=0.10.0'} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + import-local@3.2.0: + resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} + engines: {node: '>=8'} + hasBin: true + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-generator-fn@2.1.0: + resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} + engines: {node: '>=6'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-instrument@5.2.1: + resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} + engines: {node: '>=8'} + + istanbul-lib-instrument@6.0.3: + resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} + engines: {node: '>=10'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-lib-source-maps@4.0.1: + resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} + engines: {node: '>=10'} + + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + engines: {node: '>=8'} + + jest-changed-files@29.7.0: + resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-circus@29.7.0: + resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-cli@29.7.0: + resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + jest-config@29.7.0: + resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@types/node': '*' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + ts-node: + optional: true + + jest-diff@29.7.0: + resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-docblock@29.7.0: + resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-each@29.7.0: + resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-environment-node@29.7.0: + resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-get-type@29.6.3: + resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-haste-map@29.7.0: + resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-leak-detector@29.7.0: + resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-matcher-utils@29.7.0: + resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-message-util@29.7.0: + resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-mock@29.7.0: + resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-pnp-resolver@1.2.3: + resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} + engines: {node: '>=6'} + peerDependencies: + jest-resolve: '*' + peerDependenciesMeta: + jest-resolve: + optional: true + + jest-regex-util@29.6.3: + resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-resolve-dependencies@29.7.0: + resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-resolve@29.7.0: + resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-runner@29.7.0: + resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-runtime@29.7.0: + resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-snapshot@29.7.0: + resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-util@29.7.0: + resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-validate@29.7.0: + resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-watcher@29.7.0: + resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-worker@29.7.0: + resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest@29.7.0: + resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + + jsesc@3.0.2: + resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==} + engines: {node: '>=6'} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + + leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + + lodash.memoize@4.1.2: + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + + make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + + makeerror@1.0.12: + resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + media-typer@1.1.0: + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} + + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime-types@3.0.1: + resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} + engines: {node: '>= 0.6'} + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + + node-int64@0.4.0: + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + + node-releases@2.0.21: + resolution: {integrity: sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + npm-check-updates@18.3.0: + resolution: {integrity: sha512-Wcm90Af5JuzxwPTtdLl0OH2O1TCeqPTYBch1M3bePmfqylRMiFXXh+uglE4sfMjwdTjw7aIReMwudXeqoYvh2Q==} + engines: {node: ^18.18.0 || >=20.0.0, npm: '>=8.12.1'} + hasBin: true + + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + openapi-types@12.1.3: + resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + parse5-htmlparser2-tree-adapter@7.1.0: + resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} + + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-to-regexp@8.3.0: + resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} + + pg-cloudflare@1.2.7: + resolution: {integrity: sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==} + + pg-connection-string@2.9.1: + resolution: {integrity: sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==} + + pg-int8@1.0.1: + resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} + engines: {node: '>=4.0.0'} + + pg-pool@3.10.1: + resolution: {integrity: sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==} + peerDependencies: + pg: '>=8.0' + + pg-protocol@1.10.3: + resolution: {integrity: sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==} + + pg-types@2.2.0: + resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} + engines: {node: '>=4'} + + pg@8.16.3: + resolution: {integrity: sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==} + engines: {node: '>= 16.0.0'} + peerDependencies: + pg-native: '>=3.0.1' + peerDependenciesMeta: + pg-native: + optional: true + + pgpass@1.0.5: + resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} + + pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + + postgres-array@2.0.0: + resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} + engines: {node: '>=4'} + + postgres-bytea@1.0.0: + resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} + engines: {node: '>=0.10.0'} + + postgres-date@1.0.7: + resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} + engines: {node: '>=0.10.0'} + + postgres-interval@1.2.0: + resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} + engines: {node: '>=0.10.0'} + + pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + pure-rand@6.1.0: + resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + + qs@6.14.0: + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + engines: {node: '>=0.6'} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@3.0.1: + resolution: {integrity: sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==} + engines: {node: '>= 0.10'} + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + regenerate-unicode-properties@10.2.2: + resolution: {integrity: sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==} + engines: {node: '>=4'} + + regenerate@1.4.2: + resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} + + regexpu-core@6.3.1: + resolution: {integrity: sha512-DzcswPr252wEr7Qz8AyAVbfyBDKLoYp6eRA1We2Fa9qirRFSdtkP5sHr3yglDKy2BbA0fd2T+j/CUSKes3FeVQ==} + engines: {node: '>=4'} + + regjsgen@0.8.0: + resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} + + regjsparser@0.12.0: + resolution: {integrity: sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==} + hasBin: true + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + resolve-cwd@3.0.0: + resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} + engines: {node: '>=8'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve.exports@2.0.3: + resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} + engines: {node: '>=10'} + + resolve@1.22.10: + resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + engines: {node: '>= 0.4'} + hasBin: true + + rollup@4.52.0: + resolution: {integrity: sha512-+IuescNkTJQgX7AkIDtITipZdIGcWF0pnVvZTWStiazUmcGA2ag8dfg0urest2XlXUi9kuhfQ+qmdc5Stc3z7g==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + engines: {node: '>=10'} + hasBin: true + + send@1.2.0: + resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} + engines: {node: '>= 18'} + + serve-static@2.2.0: + resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==} + engines: {node: '>= 18'} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + source-map-support@0.5.13: + resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + stack-utils@2.0.6: + resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} + engines: {node: '>=10'} + + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + string-length@4.0.2: + resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} + engines: {node: '>=10'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-bom@4.0.0: + resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} + engines: {node: '>=8'} + + strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + strtok3@10.3.4: + resolution: {integrity: sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==} + engines: {node: '>=18'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + test-exclude@6.0.0: + resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} + engines: {node: '>=8'} + + tmpl@1.0.5: + resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + token-types@6.1.1: + resolution: {integrity: sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ==} + engines: {node: '>=14.16'} + + ts-jest@29.4.4: + resolution: {integrity: sha512-ccVcRABct5ZELCT5U0+DZwkXMCcOCLi2doHRrKy1nK/s7J7bch6TzJMsrY09WxgUUIP/ITfmcDS8D2yl63rnXw==} + engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@babel/core': '>=7.0.0-beta.0 <8' + '@jest/transform': ^29.0.0 || ^30.0.0 + '@jest/types': ^29.0.0 || ^30.0.0 + babel-jest: ^29.0.0 || ^30.0.0 + esbuild: '*' + jest: ^29.0.0 || ^30.0.0 + jest-util: ^29.0.0 || ^30.0.0 + typescript: '>=4.3 <6' + peerDependenciesMeta: + '@babel/core': + optional: true + '@jest/transform': + optional: true + '@jest/types': + optional: true + babel-jest: + optional: true + esbuild: + optional: true + jest-util: + optional: true + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tsx@4.20.5: + resolution: {integrity: sha512-+wKjMNU9w/EaQayHXb7WA7ZaHY6hN8WgfvHNQ3t1PnU91/7O8TcTnIhCDYTZwnt8JsO9IBqZ30Ln1r7pPF52Aw==} + engines: {node: '>=18.0.0'} + hasBin: true + + type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + + type-is@2.0.1: + resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} + engines: {node: '>= 0.6'} + + typescript@5.9.2: + resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==} + engines: {node: '>=14.17'} + hasBin: true + + uglify-js@3.19.3: + resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} + engines: {node: '>=0.8.0'} + hasBin: true + + uint8array-extras@1.5.0: + resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==} + engines: {node: '>=18'} + + undici-types@7.12.0: + resolution: {integrity: sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==} + + unicode-canonical-property-names-ecmascript@2.0.1: + resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} + engines: {node: '>=4'} + + unicode-match-property-ecmascript@2.0.0: + resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} + engines: {node: '>=4'} + + unicode-match-property-value-ecmascript@2.2.1: + resolution: {integrity: sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==} + engines: {node: '>=4'} + + unicode-property-aliases-ecmascript@2.2.0: + resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==} + engines: {node: '>=4'} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + update-browserslist-db@1.1.3: + resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + v8-to-istanbul@9.3.0: + resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} + engines: {node: '>=10.12.0'} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + walker@1.0.8: + resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + wordwrap@1.0.0: + resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + write-file-atomic@4.0.2: + resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + +snapshots: + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.27.1 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.28.4': {} + + '@babel/core@7.28.4': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.3 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.4 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.28.3': + dependencies: + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-annotate-as-pure@7.27.3': + dependencies: + '@babel/types': 7.28.4 + + '@babel/helper-compilation-targets@7.27.2': + dependencies: + '@babel/compat-data': 7.28.4 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.26.2 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.28.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-member-expression-to-functions': 7.27.1 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.4) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/traverse': 7.28.4 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-create-regexp-features-plugin@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-annotate-as-pure': 7.27.3 + regexpu-core: 6.3.1 + semver: 6.3.1 + + '@babel/helper-define-polyfill-provider@0.6.5(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + debug: 4.4.3 + lodash.debounce: 4.0.8 + resolve: 1.22.10 + transitivePeerDependencies: + - supports-color + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-member-expression-to-functions@7.27.1': + dependencies: + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.27.1': + dependencies: + '@babel/types': 7.28.4 + + '@babel/helper-plugin-utils@7.27.1': {} + + '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-wrap-function': 7.28.3 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-member-expression-to-functions': 7.27.1 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + dependencies: + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.27.1': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helper-wrap-function@7.28.3': + dependencies: + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helpers@7.28.4': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.4 + + '@babel/parser@7.28.4': + dependencies: + '@babel/types': 7.28.4 + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.28.4) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-import-assertions@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-async-generator-functions@7.28.0(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.4) + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-async-to-generator@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.4) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-block-scoping@7.28.4(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-class-properties@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-class-static-block@7.28.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-classes@7.28.4(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-globals': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.4) + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-computed-properties@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/template': 7.27.2 + + '@babel/plugin-transform-destructuring@7.28.0(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-dotall-regex@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-explicit-resource-management@7.28.0(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-destructuring': 7.28.0(@babel/core@7.28.4) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-exponentiation-operator@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-json-strings@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-literals@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-logical-assignment-operators@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-systemjs@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-named-capturing-groups-regex@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-nullish-coalescing-operator@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-numeric-separator@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-object-rest-spread@7.28.4(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-destructuring': 7.28.0(@babel/core@7.28.4) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.4) + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.4) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-optional-catch-binding@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-optional-chaining@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-private-methods@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-private-property-in-object@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-regenerator@7.28.4(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-regexp-modifiers@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-spread@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-typescript@7.28.0(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.4) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-unicode-property-regex@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-unicode-sets-regex@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/preset-env@7.28.3(@babel/core@7.28.4)': + dependencies: + '@babel/compat-data': 7.28.4 + '@babel/core': 7.28.4 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.28.3(@babel/core@7.28.4) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.28.4) + '@babel/plugin-syntax-import-assertions': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.28.4) + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-async-generator-functions': 7.28.0(@babel/core@7.28.4) + '@babel/plugin-transform-async-to-generator': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-block-scoping': 7.28.4(@babel/core@7.28.4) + '@babel/plugin-transform-class-properties': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-class-static-block': 7.28.3(@babel/core@7.28.4) + '@babel/plugin-transform-classes': 7.28.4(@babel/core@7.28.4) + '@babel/plugin-transform-computed-properties': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-destructuring': 7.28.0(@babel/core@7.28.4) + '@babel/plugin-transform-dotall-regex': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-explicit-resource-management': 7.28.0(@babel/core@7.28.4) + '@babel/plugin-transform-exponentiation-operator': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-json-strings': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-logical-assignment-operators': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-modules-systemjs': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-named-capturing-groups-regex': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-nullish-coalescing-operator': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-numeric-separator': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-object-rest-spread': 7.28.4(@babel/core@7.28.4) + '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-optional-catch-binding': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.4) + '@babel/plugin-transform-private-methods': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-private-property-in-object': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-regenerator': 7.28.4(@babel/core@7.28.4) + '@babel/plugin-transform-regexp-modifiers': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-spread': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-unicode-property-regex': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-unicode-sets-regex': 7.27.1(@babel/core@7.28.4) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.28.4) + babel-plugin-polyfill-corejs2: 0.4.14(@babel/core@7.28.4) + babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.28.4) + babel-plugin-polyfill-regenerator: 0.6.5(@babel/core@7.28.4) + core-js-compat: 3.45.1 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/types': 7.28.4 + esutils: 2.0.3 + + '@babel/preset-typescript@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-typescript': 7.28.0(@babel/core@7.28.4) + transitivePeerDependencies: + - supports-color + + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + + '@babel/traverse@7.28.4': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.3 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.4 + '@babel/template': 7.27.2 + '@babel/types': 7.28.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.28.4': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + + '@bcoe/v8-coverage@0.2.3': {} + + '@borewit/text-codec@0.1.1': {} + + '@drizzle-team/brocli@0.10.2': {} + + '@elysiajs/cors@1.3.3(elysia@1.3.5(exact-mirror@0.2.2(@sinclair/typebox@0.34.41))(file-type@21.0.0)(typescript@5.9.2))': + dependencies: + elysia: 1.3.5(exact-mirror@0.2.2(@sinclair/typebox@0.34.41))(file-type@21.0.0)(typescript@5.9.2) + + '@elysiajs/node@1.3.0(elysia@1.3.5(exact-mirror@0.2.2(@sinclair/typebox@0.34.41))(file-type@21.0.0)(typescript@5.9.2))(hono@4.9.8)': + dependencies: + '@hono/node-server': 1.19.3(hono@4.9.8) + elysia: 1.3.5(exact-mirror@0.2.2(@sinclair/typebox@0.34.41))(file-type@21.0.0)(typescript@5.9.2) + transitivePeerDependencies: + - hono + + '@esbuild-kit/core-utils@3.3.2': + dependencies: + esbuild: 0.18.20 + source-map-support: 0.5.21 + + '@esbuild-kit/esm-loader@2.6.5': + dependencies: + '@esbuild-kit/core-utils': 3.3.2 + get-tsconfig: 4.10.1 + + '@esbuild/aix-ppc64@0.25.10': + optional: true + + '@esbuild/android-arm64@0.18.20': + optional: true + + '@esbuild/android-arm64@0.25.10': + optional: true + + '@esbuild/android-arm@0.18.20': + optional: true + + '@esbuild/android-arm@0.25.10': + optional: true + + '@esbuild/android-x64@0.18.20': + optional: true + + '@esbuild/android-x64@0.25.10': + optional: true + + '@esbuild/darwin-arm64@0.18.20': + optional: true + + '@esbuild/darwin-arm64@0.25.10': + optional: true + + '@esbuild/darwin-x64@0.18.20': + optional: true + + '@esbuild/darwin-x64@0.25.10': + optional: true + + '@esbuild/freebsd-arm64@0.18.20': + optional: true + + '@esbuild/freebsd-arm64@0.25.10': + optional: true + + '@esbuild/freebsd-x64@0.18.20': + optional: true + + '@esbuild/freebsd-x64@0.25.10': + optional: true + + '@esbuild/linux-arm64@0.18.20': + optional: true + + '@esbuild/linux-arm64@0.25.10': + optional: true + + '@esbuild/linux-arm@0.18.20': + optional: true + + '@esbuild/linux-arm@0.25.10': + optional: true + + '@esbuild/linux-ia32@0.18.20': + optional: true + + '@esbuild/linux-ia32@0.25.10': + optional: true + + '@esbuild/linux-loong64@0.18.20': + optional: true + + '@esbuild/linux-loong64@0.25.10': + optional: true + + '@esbuild/linux-mips64el@0.18.20': + optional: true + + '@esbuild/linux-mips64el@0.25.10': + optional: true + + '@esbuild/linux-ppc64@0.18.20': + optional: true + + '@esbuild/linux-ppc64@0.25.10': + optional: true + + '@esbuild/linux-riscv64@0.18.20': + optional: true + + '@esbuild/linux-riscv64@0.25.10': + optional: true + + '@esbuild/linux-s390x@0.18.20': + optional: true + + '@esbuild/linux-s390x@0.25.10': + optional: true + + '@esbuild/linux-x64@0.18.20': + optional: true + + '@esbuild/linux-x64@0.25.10': + optional: true + + '@esbuild/netbsd-arm64@0.25.10': + optional: true + + '@esbuild/netbsd-x64@0.18.20': + optional: true + + '@esbuild/netbsd-x64@0.25.10': + optional: true + + '@esbuild/openbsd-arm64@0.25.10': + optional: true + + '@esbuild/openbsd-x64@0.18.20': + optional: true + + '@esbuild/openbsd-x64@0.25.10': + optional: true + + '@esbuild/openharmony-arm64@0.25.10': + optional: true + + '@esbuild/sunos-x64@0.18.20': + optional: true + + '@esbuild/sunos-x64@0.25.10': + optional: true + + '@esbuild/win32-arm64@0.18.20': + optional: true + + '@esbuild/win32-arm64@0.25.10': + optional: true + + '@esbuild/win32-ia32@0.18.20': + optional: true + + '@esbuild/win32-ia32@0.25.10': + optional: true + + '@esbuild/win32-x64@0.18.20': + optional: true + + '@esbuild/win32-x64@0.25.10': + optional: true + + '@hono/node-server@1.19.3(hono@4.9.8)': + dependencies: + hono: 4.9.8 + + '@istanbuljs/load-nyc-config@1.1.0': + dependencies: + camelcase: 5.3.1 + find-up: 4.1.0 + get-package-type: 0.1.0 + js-yaml: 3.14.1 + resolve-from: 5.0.0 + + '@istanbuljs/schema@0.1.3': {} + + '@jest/console@29.7.0': + dependencies: + '@jest/types': 29.6.3 + '@types/node': 24.5.2 + chalk: 4.1.2 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + + '@jest/core@29.7.0': + dependencies: + '@jest/console': 29.7.0 + '@jest/reporters': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 24.5.2 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.7.0 + jest-config: 29.7.0(@types/node@24.5.2) + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-resolve-dependencies: 29.7.0 + jest-runner: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + jest-watcher: 29.7.0 + micromatch: 4.0.8 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + - ts-node + + '@jest/environment@29.7.0': + dependencies: + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 24.5.2 + jest-mock: 29.7.0 + + '@jest/expect-utils@29.7.0': + dependencies: + jest-get-type: 29.6.3 + + '@jest/expect@29.7.0': + dependencies: + expect: 29.7.0 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + + '@jest/fake-timers@29.7.0': + dependencies: + '@jest/types': 29.6.3 + '@sinonjs/fake-timers': 10.3.0 + '@types/node': 24.5.2 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-util: 29.7.0 + + '@jest/globals@29.7.0': + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/types': 29.6.3 + jest-mock: 29.7.0 + transitivePeerDependencies: + - supports-color + + '@jest/reporters@29.7.0': + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.31 + '@types/node': 24.5.2 + chalk: 4.1.2 + collect-v8-coverage: 1.0.2 + exit: 0.1.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 6.0.3 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.2.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + jest-worker: 29.7.0 + slash: 3.0.0 + string-length: 4.0.2 + strip-ansi: 6.0.1 + v8-to-istanbul: 9.3.0 + transitivePeerDependencies: + - supports-color + + '@jest/schemas@29.6.3': + dependencies: + '@sinclair/typebox': 0.27.8 + + '@jest/source-map@29.6.3': + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + callsites: 3.1.0 + graceful-fs: 4.2.11 + + '@jest/test-result@29.7.0': + dependencies: + '@jest/console': 29.7.0 + '@jest/types': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + collect-v8-coverage: 1.0.2 + + '@jest/test-sequencer@29.7.0': + dependencies: + '@jest/test-result': 29.7.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + slash: 3.0.0 + + '@jest/transform@29.7.0': + dependencies: + '@babel/core': 7.28.4 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.31 + babel-plugin-istanbul: 6.1.1 + chalk: 4.1.2 + convert-source-map: 2.0.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + micromatch: 4.0.8 + pirates: 4.0.7 + slash: 3.0.0 + write-file-atomic: 4.0.2 + transitivePeerDependencies: + - supports-color + + '@jest/types@29.6.3': + dependencies: + '@jest/schemas': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 24.5.2 + '@types/yargs': 17.0.33 + chalk: 4.1.2 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@rollup/plugin-typescript@12.1.4(rollup@4.52.0)(tslib@2.8.1)(typescript@5.9.2)': + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@4.52.0) + resolve: 1.22.10 + typescript: 5.9.2 + optionalDependencies: + rollup: 4.52.0 + tslib: 2.8.1 + + '@rollup/pluginutils@5.3.0(rollup@4.52.0)': + dependencies: + '@types/estree': 1.0.8 + estree-walker: 2.0.2 + picomatch: 4.0.3 + optionalDependencies: + rollup: 4.52.0 + + '@rollup/rollup-android-arm-eabi@4.52.0': + optional: true + + '@rollup/rollup-android-arm64@4.52.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.52.0': + optional: true + + '@rollup/rollup-darwin-x64@4.52.0': + optional: true + + '@rollup/rollup-freebsd-arm64@4.52.0': + optional: true + + '@rollup/rollup-freebsd-x64@4.52.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.52.0': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.52.0': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.52.0': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.52.0': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.52.0': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.52.0': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.52.0': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.52.0': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.52.0': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.52.0': + optional: true + + '@rollup/rollup-linux-x64-musl@4.52.0': + optional: true + + '@rollup/rollup-openharmony-arm64@4.52.0': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.52.0': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.52.0': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.52.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.52.0': + optional: true + + '@sinclair/typebox@0.27.8': {} + + '@sinclair/typebox@0.34.41': + optional: true + + '@sinonjs/commons@3.0.1': + dependencies: + type-detect: 4.0.8 + + '@sinonjs/fake-timers@10.3.0': + dependencies: + '@sinonjs/commons': 3.0.1 + + '@tokenizer/inflate@0.2.7': + dependencies: + debug: 4.4.3 + fflate: 0.8.2 + token-types: 6.1.1 + transitivePeerDependencies: + - supports-color + + '@tokenizer/token@0.3.0': {} + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.28.4 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.28.4 + + '@types/body-parser@1.19.6': + dependencies: + '@types/connect': 3.4.38 + '@types/node': 24.5.2 + + '@types/connect@3.4.38': + dependencies: + '@types/node': 24.5.2 + + '@types/estree@1.0.8': {} + + '@types/express-serve-static-core@5.0.7': + dependencies: + '@types/node': 24.5.2 + '@types/qs': 6.14.0 + '@types/range-parser': 1.2.7 + '@types/send': 0.17.5 + + '@types/express@5.0.3': + dependencies: + '@types/body-parser': 1.19.6 + '@types/express-serve-static-core': 5.0.7 + '@types/serve-static': 1.15.8 + + '@types/graceful-fs@4.1.9': + dependencies: + '@types/node': 24.5.2 + + '@types/http-errors@2.0.5': {} + + '@types/istanbul-lib-coverage@2.0.6': {} + + '@types/istanbul-lib-report@3.0.3': + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + + '@types/istanbul-reports@3.0.4': + dependencies: + '@types/istanbul-lib-report': 3.0.3 + + '@types/jest@29.5.14': + dependencies: + expect: 29.7.0 + pretty-format: 29.7.0 + + '@types/mime@1.3.5': {} + + '@types/node@24.5.2': + dependencies: + undici-types: 7.12.0 + + '@types/pg@8.15.5': + dependencies: + '@types/node': 24.5.2 + pg-protocol: 1.10.3 + pg-types: 2.2.0 + + '@types/qs@6.14.0': {} + + '@types/range-parser@1.2.7': {} + + '@types/react@19.1.13': + dependencies: + csstype: 3.1.3 + optional: true + + '@types/send@0.17.5': + dependencies: + '@types/mime': 1.3.5 + '@types/node': 24.5.2 + + '@types/serve-static@1.15.8': + dependencies: + '@types/http-errors': 2.0.5 + '@types/node': 24.5.2 + '@types/send': 0.17.5 + + '@types/stack-utils@2.0.3': {} + + '@types/yargs-parser@21.0.3': {} + + '@types/yargs@17.0.33': + dependencies: + '@types/yargs-parser': 21.0.3 + + accepts@2.0.0: + dependencies: + mime-types: 3.0.1 + negotiator: 1.0.0 + + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@5.2.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + asynckit@0.4.0: {} + + axios@1.12.2: + dependencies: + follow-redirects: 1.15.11 + form-data: 4.0.4 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + babel-jest@29.7.0(@babel/core@7.28.4): + dependencies: + '@babel/core': 7.28.4 + '@jest/transform': 29.7.0 + '@types/babel__core': 7.20.5 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 29.6.3(@babel/core@7.28.4) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-istanbul@6.1.1: + dependencies: + '@babel/helper-plugin-utils': 7.27.1 + '@istanbuljs/load-nyc-config': 1.1.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-instrument: 5.2.1 + test-exclude: 6.0.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-jest-hoist@29.6.3: + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.4 + '@types/babel__core': 7.20.5 + '@types/babel__traverse': 7.28.0 + + babel-plugin-polyfill-corejs2@0.4.14(@babel/core@7.28.4): + dependencies: + '@babel/compat-data': 7.28.4 + '@babel/core': 7.28.4 + '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.4) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs3@0.13.0(@babel/core@7.28.4): + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.4) + core-js-compat: 3.45.1 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-regenerator@0.6.5(@babel/core@7.28.4): + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.4) + transitivePeerDependencies: + - supports-color + + babel-preset-current-node-syntax@1.2.0(@babel/core@7.28.4): + dependencies: + '@babel/core': 7.28.4 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.28.4) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.28.4) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.28.4) + '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.4) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.28.4) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.28.4) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.28.4) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.28.4) + + babel-preset-jest@29.6.3(@babel/core@7.28.4): + dependencies: + '@babel/core': 7.28.4 + babel-plugin-jest-hoist: 29.6.3 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.4) + + balanced-match@1.0.2: {} + + baseline-browser-mapping@2.8.6: {} + + body-parser@2.2.0: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 4.4.3 + http-errors: 2.0.0 + iconv-lite: 0.6.3 + on-finished: 2.4.1 + qs: 6.14.0 + raw-body: 3.0.1 + type-is: 2.0.1 + transitivePeerDependencies: + - supports-color + + boolbase@1.0.0: {} + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.26.2: + dependencies: + baseline-browser-mapping: 2.8.6 + caniuse-lite: 1.0.30001743 + electron-to-chromium: 1.5.222 + node-releases: 2.0.21 + update-browserslist-db: 1.1.3(browserslist@4.26.2) + + bs-logger@0.2.6: + dependencies: + fast-json-stable-stringify: 2.1.0 + + bser@2.1.1: + dependencies: + node-int64: 0.4.0 + + buffer-from@1.1.2: {} + + bun-types@1.2.22(@types/react@19.1.13): + dependencies: + '@types/node': 24.5.2 + '@types/react': 19.1.13 + optional: true + + bytes@3.1.2: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + camelcase@5.3.1: {} + + camelcase@6.3.0: {} + + caniuse-lite@1.0.30001743: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + char-regex@1.0.2: {} + + cheerio-select@2.1.0: + dependencies: + boolbase: 1.0.0 + css-select: 5.2.2 + css-what: 6.2.2 + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + + cheerio@1.0.0-rc.12: + dependencies: + cheerio-select: 2.1.0 + dom-serializer: 2.0.0 + domhandler: 5.0.3 + domutils: 3.2.2 + htmlparser2: 8.0.2 + parse5: 7.3.0 + parse5-htmlparser2-tree-adapter: 7.1.0 + + ci-info@3.9.0: {} + + cjs-module-lexer@1.4.3: {} + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + co@4.6.0: {} + + collect-v8-coverage@1.0.2: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + concat-map@0.0.1: {} + + content-disposition@1.0.0: + dependencies: + safe-buffer: 5.2.1 + + content-type@1.0.5: {} + + convert-source-map@2.0.0: {} + + cookie-signature@1.2.2: {} + + cookie@0.7.2: {} + + cookie@1.0.2: {} + + core-js-compat@3.45.1: + dependencies: + browserslist: 4.26.2 + + create-jest@29.7.0(@types/node@24.5.2): + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@24.5.2) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css-select@5.2.2: + dependencies: + boolbase: 1.0.0 + css-what: 6.2.2 + domhandler: 5.0.3 + domutils: 3.2.2 + nth-check: 2.1.1 + + css-what@6.2.2: {} + + csstype@3.1.3: + optional: true + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + dedent@1.7.0: {} + + deepmerge@4.3.1: {} + + delayed-stream@1.0.0: {} + + depd@2.0.0: {} + + detect-newline@3.1.0: {} + + diff-sequences@29.6.3: {} + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domelementtype@2.3.0: {} + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@3.2.2: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + dotenv-cli@8.0.0: + dependencies: + cross-spawn: 7.0.6 + dotenv: 16.6.1 + dotenv-expand: 10.0.0 + minimist: 1.2.8 + + dotenv-expand@10.0.0: {} + + dotenv@16.6.1: {} + + dotenv@17.2.2: {} + + drizzle-kit@0.31.4: + dependencies: + '@drizzle-team/brocli': 0.10.2 + '@esbuild-kit/esm-loader': 2.6.5 + esbuild: 0.25.10 + esbuild-register: 3.6.0(esbuild@0.25.10) + transitivePeerDependencies: + - supports-color + + drizzle-orm@0.44.5(@types/pg@8.15.5)(bun-types@1.2.22(@types/react@19.1.13))(pg@8.16.3): + optionalDependencies: + '@types/pg': 8.15.5 + bun-types: 1.2.22(@types/react@19.1.13) + pg: 8.16.3 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + ee-first@1.1.1: {} + + electron-to-chromium@1.5.222: {} + + elysia@1.3.5(exact-mirror@0.2.2(@sinclair/typebox@0.34.41))(file-type@21.0.0)(typescript@5.9.2): + dependencies: + cookie: 1.0.2 + exact-mirror: 0.2.2(@sinclair/typebox@0.34.41) + fast-decode-uri-component: 1.0.1 + file-type: 21.0.0 + typescript: 5.9.2 + optionalDependencies: + '@sinclair/typebox': 0.34.41 + openapi-types: 12.1.3 + + emittery@0.13.1: {} + + emoji-regex@8.0.0: {} + + encodeurl@2.0.0: {} + + entities@4.5.0: {} + + entities@6.0.1: {} + + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + esbuild-register@3.6.0(esbuild@0.25.10): + dependencies: + debug: 4.4.3 + esbuild: 0.25.10 + transitivePeerDependencies: + - supports-color + + esbuild@0.18.20: + optionalDependencies: + '@esbuild/android-arm': 0.18.20 + '@esbuild/android-arm64': 0.18.20 + '@esbuild/android-x64': 0.18.20 + '@esbuild/darwin-arm64': 0.18.20 + '@esbuild/darwin-x64': 0.18.20 + '@esbuild/freebsd-arm64': 0.18.20 + '@esbuild/freebsd-x64': 0.18.20 + '@esbuild/linux-arm': 0.18.20 + '@esbuild/linux-arm64': 0.18.20 + '@esbuild/linux-ia32': 0.18.20 + '@esbuild/linux-loong64': 0.18.20 + '@esbuild/linux-mips64el': 0.18.20 + '@esbuild/linux-ppc64': 0.18.20 + '@esbuild/linux-riscv64': 0.18.20 + '@esbuild/linux-s390x': 0.18.20 + '@esbuild/linux-x64': 0.18.20 + '@esbuild/netbsd-x64': 0.18.20 + '@esbuild/openbsd-x64': 0.18.20 + '@esbuild/sunos-x64': 0.18.20 + '@esbuild/win32-arm64': 0.18.20 + '@esbuild/win32-ia32': 0.18.20 + '@esbuild/win32-x64': 0.18.20 + + esbuild@0.25.10: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.10 + '@esbuild/android-arm': 0.25.10 + '@esbuild/android-arm64': 0.25.10 + '@esbuild/android-x64': 0.25.10 + '@esbuild/darwin-arm64': 0.25.10 + '@esbuild/darwin-x64': 0.25.10 + '@esbuild/freebsd-arm64': 0.25.10 + '@esbuild/freebsd-x64': 0.25.10 + '@esbuild/linux-arm': 0.25.10 + '@esbuild/linux-arm64': 0.25.10 + '@esbuild/linux-ia32': 0.25.10 + '@esbuild/linux-loong64': 0.25.10 + '@esbuild/linux-mips64el': 0.25.10 + '@esbuild/linux-ppc64': 0.25.10 + '@esbuild/linux-riscv64': 0.25.10 + '@esbuild/linux-s390x': 0.25.10 + '@esbuild/linux-x64': 0.25.10 + '@esbuild/netbsd-arm64': 0.25.10 + '@esbuild/netbsd-x64': 0.25.10 + '@esbuild/openbsd-arm64': 0.25.10 + '@esbuild/openbsd-x64': 0.25.10 + '@esbuild/openharmony-arm64': 0.25.10 + '@esbuild/sunos-x64': 0.25.10 + '@esbuild/win32-arm64': 0.25.10 + '@esbuild/win32-ia32': 0.25.10 + '@esbuild/win32-x64': 0.25.10 + + escalade@3.2.0: {} + + escape-html@1.0.3: {} + + escape-string-regexp@2.0.0: {} + + esprima@4.0.1: {} + + estree-walker@2.0.2: {} + + esutils@2.0.3: {} + + etag@1.8.1: {} + + exact-mirror@0.2.2(@sinclair/typebox@0.34.41): + optionalDependencies: + '@sinclair/typebox': 0.34.41 + + execa@5.1.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + + exit@0.1.2: {} + + expect@29.7.0: + dependencies: + '@jest/expect-utils': 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + + express@5.1.0: + dependencies: + accepts: 2.0.0 + body-parser: 2.2.0 + content-disposition: 1.0.0 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.0 + fresh: 2.0.0 + http-errors: 2.0.0 + merge-descriptors: 2.0.0 + mime-types: 3.0.1 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.14.0 + range-parser: 1.2.1 + router: 2.2.0 + send: 1.2.0 + serve-static: 2.2.0 + statuses: 2.0.2 + type-is: 2.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + fast-decode-uri-component@1.0.1: {} + + fast-json-stable-stringify@2.1.0: {} + + fb-watchman@2.0.2: + dependencies: + bser: 2.1.1 + + fflate@0.8.2: {} + + file-type@21.0.0: + dependencies: + '@tokenizer/inflate': 0.2.7 + strtok3: 10.3.4 + token-types: 6.1.1 + uint8array-extras: 1.5.0 + transitivePeerDependencies: + - supports-color + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + finalhandler@2.1.0: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + + follow-redirects@1.15.11: {} + + form-data@4.0.4: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + + forwarded@0.2.0: {} + + fresh@2.0.0: {} + + fs.realpath@1.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-package-type@0.1.0: {} + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-stream@6.0.1: {} + + get-tsconfig@4.10.1: + dependencies: + resolve-pkg-maps: 1.0.0 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + handlebars@4.7.8: + dependencies: + minimist: 1.2.8 + neo-async: 2.6.2 + source-map: 0.6.1 + wordwrap: 1.0.0 + optionalDependencies: + uglify-js: 3.19.3 + + has-flag@4.0.0: {} + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + hono@4.9.8: {} + + html-escaper@2.0.2: {} + + htmlparser2@8.0.2: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + entities: 4.5.0 + + http-errors@2.0.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + + human-signals@2.1.0: {} + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + iconv-lite@0.7.0: + dependencies: + safer-buffer: 2.1.2 + + ieee754@1.2.1: {} + + import-local@3.2.0: + dependencies: + pkg-dir: 4.2.0 + resolve-cwd: 3.0.0 + + imurmurhash@0.1.4: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + ipaddr.js@1.9.1: {} + + is-arrayish@0.2.1: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-fullwidth-code-point@3.0.0: {} + + is-generator-fn@2.1.0: {} + + is-number@7.0.0: {} + + is-promise@4.0.0: {} + + is-stream@2.0.1: {} + + isexe@2.0.0: {} + + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-instrument@5.2.1: + dependencies: + '@babel/core': 7.28.4 + '@babel/parser': 7.28.4 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + istanbul-lib-instrument@6.0.3: + dependencies: + '@babel/core': 7.28.4 + '@babel/parser': 7.28.4 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 7.7.2 + transitivePeerDependencies: + - supports-color + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-lib-source-maps@4.0.1: + dependencies: + debug: 4.4.3 + istanbul-lib-coverage: 3.2.2 + source-map: 0.6.1 + transitivePeerDependencies: + - supports-color + + istanbul-reports@3.2.0: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + + jest-changed-files@29.7.0: + dependencies: + execa: 5.1.1 + jest-util: 29.7.0 + p-limit: 3.1.0 + + jest-circus@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 24.5.2 + chalk: 4.1.2 + co: 4.6.0 + dedent: 1.7.0 + is-generator-fn: 2.1.0 + jest-each: 29.7.0 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + p-limit: 3.1.0 + pretty-format: 29.7.0 + pure-rand: 6.1.0 + slash: 3.0.0 + stack-utils: 2.0.6 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-cli@29.7.0(@types/node@24.5.2): + dependencies: + '@jest/core': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@24.5.2) + exit: 0.1.2 + import-local: 3.2.0 + jest-config: 29.7.0(@types/node@24.5.2) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + jest-config@29.7.0(@types/node@24.5.2): + dependencies: + '@babel/core': 7.28.4 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.28.4) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 24.5.2 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-diff@29.7.0: + dependencies: + chalk: 4.1.2 + diff-sequences: 29.6.3 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-docblock@29.7.0: + dependencies: + detect-newline: 3.1.0 + + jest-each@29.7.0: + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + jest-get-type: 29.6.3 + jest-util: 29.7.0 + pretty-format: 29.7.0 + + jest-environment-node@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 24.5.2 + jest-mock: 29.7.0 + jest-util: 29.7.0 + + jest-get-type@29.6.3: {} + + jest-haste-map@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/graceful-fs': 4.1.9 + '@types/node': 24.5.2 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + jest-worker: 29.7.0 + micromatch: 4.0.8 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 + + jest-leak-detector@29.7.0: + dependencies: + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-matcher-utils@29.7.0: + dependencies: + chalk: 4.1.2 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-message-util@29.7.0: + dependencies: + '@babel/code-frame': 7.27.1 + '@jest/types': 29.6.3 + '@types/stack-utils': 2.0.3 + chalk: 4.1.2 + graceful-fs: 4.2.11 + micromatch: 4.0.8 + pretty-format: 29.7.0 + slash: 3.0.0 + stack-utils: 2.0.6 + + jest-mock@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/node': 24.5.2 + jest-util: 29.7.0 + + jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): + optionalDependencies: + jest-resolve: 29.7.0 + + jest-regex-util@29.6.3: {} + + jest-resolve-dependencies@29.7.0: + dependencies: + jest-regex-util: 29.6.3 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + + jest-resolve@29.7.0: + dependencies: + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) + jest-util: 29.7.0 + jest-validate: 29.7.0 + resolve: 1.22.10 + resolve.exports: 2.0.3 + slash: 3.0.0 + + jest-runner@29.7.0: + dependencies: + '@jest/console': 29.7.0 + '@jest/environment': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 24.5.2 + chalk: 4.1.2 + emittery: 0.13.1 + graceful-fs: 4.2.11 + jest-docblock: 29.7.0 + jest-environment-node: 29.7.0 + jest-haste-map: 29.7.0 + jest-leak-detector: 29.7.0 + jest-message-util: 29.7.0 + jest-resolve: 29.7.0 + jest-runtime: 29.7.0 + jest-util: 29.7.0 + jest-watcher: 29.7.0 + jest-worker: 29.7.0 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: + - supports-color + + jest-runtime@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/globals': 29.7.0 + '@jest/source-map': 29.6.3 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 24.5.2 + chalk: 4.1.2 + cjs-module-lexer: 1.4.3 + collect-v8-coverage: 1.0.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + + jest-snapshot@29.7.0: + dependencies: + '@babel/core': 7.28.4 + '@babel/generator': 7.28.3 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.4) + '@babel/types': 7.28.4 + '@jest/expect-utils': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.4) + chalk: 4.1.2 + expect: 29.7.0 + graceful-fs: 4.2.11 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + natural-compare: 1.4.0 + pretty-format: 29.7.0 + semver: 7.7.2 + transitivePeerDependencies: + - supports-color + + jest-util@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/node': 24.5.2 + chalk: 4.1.2 + ci-info: 3.9.0 + graceful-fs: 4.2.11 + picomatch: 2.3.1 + + jest-validate@29.7.0: + dependencies: + '@jest/types': 29.6.3 + camelcase: 6.3.0 + chalk: 4.1.2 + jest-get-type: 29.6.3 + leven: 3.1.0 + pretty-format: 29.7.0 + + jest-watcher@29.7.0: + dependencies: + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 24.5.2 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.13.1 + jest-util: 29.7.0 + string-length: 4.0.2 + + jest-worker@29.7.0: + dependencies: + '@types/node': 24.5.2 + jest-util: 29.7.0 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + jest@29.7.0(@types/node@24.5.2): + dependencies: + '@jest/core': 29.7.0 + '@jest/types': 29.6.3 + import-local: 3.2.0 + jest-cli: 29.7.0(@types/node@24.5.2) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + js-tokens@4.0.0: {} + + js-yaml@3.14.1: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + + jsesc@3.0.2: {} + + jsesc@3.1.0: {} + + json-parse-even-better-errors@2.3.1: {} + + json5@2.2.3: {} + + kleur@3.0.3: {} + + leven@3.1.0: {} + + lines-and-columns@1.2.4: {} + + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + + lodash.debounce@4.0.8: {} + + lodash.memoize@4.1.2: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + make-dir@4.0.0: + dependencies: + semver: 7.7.2 + + make-error@1.3.6: {} + + makeerror@1.0.12: + dependencies: + tmpl: 1.0.5 + + math-intrinsics@1.1.0: {} + + media-typer@1.1.0: {} + + merge-descriptors@2.0.0: {} + + merge-stream@2.0.0: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.52.0: {} + + mime-db@1.54.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime-types@3.0.1: + dependencies: + mime-db: 1.54.0 + + mimic-fn@2.1.0: {} + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 + + minimist@1.2.8: {} + + ms@2.1.3: {} + + natural-compare@1.4.0: {} + + negotiator@1.0.0: {} + + neo-async@2.6.2: {} + + node-int64@0.4.0: {} + + node-releases@2.0.21: {} + + normalize-path@3.0.0: {} + + npm-check-updates@18.3.0: {} + + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + object-inspect@1.13.4: {} + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + openapi-types@12.1.3: + optional: true + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-try@2.2.0: {} + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.27.1 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + parse5-htmlparser2-tree-adapter@7.1.0: + dependencies: + domhandler: 5.0.3 + parse5: 7.3.0 + + parse5@7.3.0: + dependencies: + entities: 6.0.1 + + parseurl@1.3.3: {} + + path-exists@4.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-to-regexp@8.3.0: {} + + pg-cloudflare@1.2.7: + optional: true + + pg-connection-string@2.9.1: {} + + pg-int8@1.0.1: {} + + pg-pool@3.10.1(pg@8.16.3): + dependencies: + pg: 8.16.3 + + pg-protocol@1.10.3: {} + + pg-types@2.2.0: + dependencies: + pg-int8: 1.0.1 + postgres-array: 2.0.0 + postgres-bytea: 1.0.0 + postgres-date: 1.0.7 + postgres-interval: 1.2.0 + + pg@8.16.3: + dependencies: + pg-connection-string: 2.9.1 + pg-pool: 3.10.1(pg@8.16.3) + pg-protocol: 1.10.3 + pg-types: 2.2.0 + pgpass: 1.0.5 + optionalDependencies: + pg-cloudflare: 1.2.7 + + pgpass@1.0.5: + dependencies: + split2: 4.2.0 + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.3: {} + + pirates@4.0.7: {} + + pkg-dir@4.2.0: + dependencies: + find-up: 4.1.0 + + postgres-array@2.0.0: {} + + postgres-bytea@1.0.0: {} + + postgres-date@1.0.7: {} + + postgres-interval@1.2.0: + dependencies: + xtend: 4.0.2 + + pretty-format@29.7.0: + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.3.1 + + prompts@2.4.2: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + proxy-from-env@1.1.0: {} + + pure-rand@6.1.0: {} + + qs@6.14.0: + dependencies: + side-channel: 1.1.0 + + range-parser@1.2.1: {} + + raw-body@3.0.1: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.7.0 + unpipe: 1.0.0 + + react-is@18.3.1: {} + + regenerate-unicode-properties@10.2.2: + dependencies: + regenerate: 1.4.2 + + regenerate@1.4.2: {} + + regexpu-core@6.3.1: + dependencies: + regenerate: 1.4.2 + regenerate-unicode-properties: 10.2.2 + regjsgen: 0.8.0 + regjsparser: 0.12.0 + unicode-match-property-ecmascript: 2.0.0 + unicode-match-property-value-ecmascript: 2.2.1 + + regjsgen@0.8.0: {} + + regjsparser@0.12.0: + dependencies: + jsesc: 3.0.2 + + require-directory@2.1.1: {} + + resolve-cwd@3.0.0: + dependencies: + resolve-from: 5.0.0 + + resolve-from@5.0.0: {} + + resolve-pkg-maps@1.0.0: {} + + resolve.exports@2.0.3: {} + + resolve@1.22.10: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + rollup@4.52.0: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.52.0 + '@rollup/rollup-android-arm64': 4.52.0 + '@rollup/rollup-darwin-arm64': 4.52.0 + '@rollup/rollup-darwin-x64': 4.52.0 + '@rollup/rollup-freebsd-arm64': 4.52.0 + '@rollup/rollup-freebsd-x64': 4.52.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.52.0 + '@rollup/rollup-linux-arm-musleabihf': 4.52.0 + '@rollup/rollup-linux-arm64-gnu': 4.52.0 + '@rollup/rollup-linux-arm64-musl': 4.52.0 + '@rollup/rollup-linux-loong64-gnu': 4.52.0 + '@rollup/rollup-linux-ppc64-gnu': 4.52.0 + '@rollup/rollup-linux-riscv64-gnu': 4.52.0 + '@rollup/rollup-linux-riscv64-musl': 4.52.0 + '@rollup/rollup-linux-s390x-gnu': 4.52.0 + '@rollup/rollup-linux-x64-gnu': 4.52.0 + '@rollup/rollup-linux-x64-musl': 4.52.0 + '@rollup/rollup-openharmony-arm64': 4.52.0 + '@rollup/rollup-win32-arm64-msvc': 4.52.0 + '@rollup/rollup-win32-ia32-msvc': 4.52.0 + '@rollup/rollup-win32-x64-gnu': 4.52.0 + '@rollup/rollup-win32-x64-msvc': 4.52.0 + fsevents: 2.3.3 + + router@2.2.0: + dependencies: + debug: 4.4.3 + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.3.0 + transitivePeerDependencies: + - supports-color + + safe-buffer@5.2.1: {} + + safer-buffer@2.1.2: {} + + semver@6.3.1: {} + + semver@7.7.2: {} + + send@1.2.0: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.0 + mime-types: 3.0.1 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + serve-static@2.2.0: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.0 + transitivePeerDependencies: + - supports-color + + setprototypeof@1.2.0: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + signal-exit@3.0.7: {} + + sisteransi@1.0.5: {} + + slash@3.0.0: {} + + source-map-support@0.5.13: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.6.1: {} + + split2@4.2.0: {} + + sprintf-js@1.0.3: {} + + stack-utils@2.0.6: + dependencies: + escape-string-regexp: 2.0.0 + + statuses@2.0.1: {} + + statuses@2.0.2: {} + + string-length@4.0.2: + dependencies: + char-regex: 1.0.2 + strip-ansi: 6.0.1 + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-bom@4.0.0: {} + + strip-final-newline@2.0.0: {} + + strip-json-comments@3.1.1: {} + + strtok3@10.3.4: + dependencies: + '@tokenizer/token': 0.3.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + test-exclude@6.0.0: + dependencies: + '@istanbuljs/schema': 0.1.3 + glob: 7.2.3 + minimatch: 3.1.2 + + tmpl@1.0.5: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + toidentifier@1.0.1: {} + + token-types@6.1.1: + dependencies: + '@borewit/text-codec': 0.1.1 + '@tokenizer/token': 0.3.0 + ieee754: 1.2.1 + + ts-jest@29.4.4(@babel/core@7.28.4)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.4))(esbuild@0.25.10)(jest-util@29.7.0)(jest@29.7.0(@types/node@24.5.2))(typescript@5.9.2): + dependencies: + bs-logger: 0.2.6 + fast-json-stable-stringify: 2.1.0 + handlebars: 4.7.8 + jest: 29.7.0(@types/node@24.5.2) + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.7.2 + type-fest: 4.41.0 + typescript: 5.9.2 + yargs-parser: 21.1.1 + optionalDependencies: + '@babel/core': 7.28.4 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.28.4) + esbuild: 0.25.10 + jest-util: 29.7.0 + + tslib@2.8.1: {} + + tsx@4.20.5: + dependencies: + esbuild: 0.25.10 + get-tsconfig: 4.10.1 + optionalDependencies: + fsevents: 2.3.3 + + type-detect@4.0.8: {} + + type-fest@0.21.3: {} + + type-fest@4.41.0: {} + + type-is@2.0.1: + dependencies: + content-type: 1.0.5 + media-typer: 1.1.0 + mime-types: 3.0.1 + + typescript@5.9.2: {} + + uglify-js@3.19.3: + optional: true + + uint8array-extras@1.5.0: {} + + undici-types@7.12.0: {} + + unicode-canonical-property-names-ecmascript@2.0.1: {} + + unicode-match-property-ecmascript@2.0.0: + dependencies: + unicode-canonical-property-names-ecmascript: 2.0.1 + unicode-property-aliases-ecmascript: 2.2.0 + + unicode-match-property-value-ecmascript@2.2.1: {} + + unicode-property-aliases-ecmascript@2.2.0: {} + + unpipe@1.0.0: {} + + update-browserslist-db@1.1.3(browserslist@4.26.2): + dependencies: + browserslist: 4.26.2 + escalade: 3.2.0 + picocolors: 1.1.1 + + v8-to-istanbul@9.3.0: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + '@types/istanbul-lib-coverage': 2.0.6 + convert-source-map: 2.0.0 + + vary@1.1.2: {} + + walker@1.0.8: + dependencies: + makeerror: 1.0.12 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + wordwrap@1.0.0: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrappy@1.0.2: {} + + write-file-atomic@4.0.2: + dependencies: + imurmurhash: 0.1.4 + signal-exit: 3.0.7 + + xtend@4.0.2: {} + + y18n@5.0.8: {} + + yallist@3.1.1: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yocto-queue@0.1.0: {} + + zod@3.25.76: {} diff --git a/run-bun.sh b/run-bun.sh deleted file mode 100644 index b210528..0000000 --- a/run-bun.sh +++ /dev/null @@ -1,2 +0,0 @@ -echo "running db migrations..." -bun db:migrate && bun run start \ No newline at end of file diff --git a/src/server.ts b/src/server.ts index fcdde02..5e598ec 100644 --- a/src/server.ts +++ b/src/server.ts @@ -113,7 +113,7 @@ app.get( ); app.get("/api/emails", getEmails); -app.get("/api/changes", getOverrides); +app.get("/api/changes", async () => await getOverrides()); app.post( "/api/sendSlackMessage", diff --git a/tests/integration.test.ts b/tests/integration.test.ts index 304f6ff..65f9c30 100644 --- a/tests/integration.test.ts +++ b/tests/integration.test.ts @@ -1,5 +1,4 @@ import DiningParser from "../src/parser/diningParser"; - import { expectedLocationData } from "./expectedData"; import { mockAxiosGETMethodWithFilePaths } from "./mockAxios"; import { @@ -15,7 +14,7 @@ import { } from "./mockTimings"; jest.mock("axios"); - +test("ok", () => {}); test("the whole thing, including locationOverwrites", async () => { mockAxiosGETMethodWithFilePaths({ conceptListFilePath: "html/listconcepts.html", diff --git a/tsconfig.json b/tsconfig.json index be1839e..d440c50 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,7 @@ "lib": ["ESNext", "DOM", "DOM.Iterable"], "module": "es6", "skipLibCheck": true, - "types": ["bun-types", "jest", "node"], + "types": ["jest", "node"], /* Bundler mode */ "rootDir": ".", From 563a80ca908ed4554a8d0e9246930aa492dabbf6 Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Sat, 20 Sep 2025 16:11:02 -0400 Subject: [PATCH 22/65] fix: dependabot --- .github/dependabot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 3020a73..3db4ff3 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,7 +1,7 @@ version: 2 updates: # Enable version updates for npm - - package-ecosystem: "pnpm" + - package-ecosystem: "npm" # Look for `package.json` and `lock` files in the `root` directory directory: "/" # Check the npm registry for updates every day (weekdays) From 53f106018efeae3130f169a8806e37366da2b999 Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Sat, 20 Sep 2025 16:20:10 -0400 Subject: [PATCH 23/65] fix: env test file --- .env.test | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.env.test b/.env.test index 61ba3cb..bbec267 100644 --- a/.env.test +++ b/.env.test @@ -1,5 +1,6 @@ # placeholder variables used for running tests AXIOS_RETRY_INTERVAL_MS=0 IN_TEST_MODE=true -SLACK_WEBHOOK_URL=/ +SLACK_BACKEND_WEBHOOK_URL=/ +SLACK_FRONTEND_WEBHOOK_URL=/ DATABASE_URL=/ \ No newline at end of file From 3f90658af19f3df82ec12c8e519ba14bd1f60e45 Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Sat, 20 Sep 2025 16:24:51 -0400 Subject: [PATCH 24/65] chore: rename dockerfile --- Dockerfile.pnpm => Dockerfile | 0 docker-compose.yml | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename Dockerfile.pnpm => Dockerfile (100%) diff --git a/Dockerfile.pnpm b/Dockerfile similarity index 100% rename from Dockerfile.pnpm rename to Dockerfile diff --git a/docker-compose.yml b/docker-compose.yml index c5a8658..3b992b0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -20,7 +20,7 @@ services: server: build: - dockerfile: Dockerfile.pnpm + dockerfile: Dockerfile container_name: dining-api-server ports: - "5010:5010" From 3588c5b9d8885a6e750f4cbe1c27f4083bba35a2 Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Sat, 20 Sep 2025 16:38:12 -0400 Subject: [PATCH 25/65] fix: remove env variable reference --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 9fd7b5d..a2b6c84 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM node:24-slim AS base ENV PNPM_HOME="/pnpm" -ENV PATH="$PNPM_HOME:$PATH" +# ENV PATH="$PNPM_HOME:$PATH" RUN corepack enable COPY . /app WORKDIR /app From 58f284fd42af524e822ae6bb0b61ebdc6623a445 Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Sat, 20 Sep 2025 16:43:26 -0400 Subject: [PATCH 26/65] fix: prefix cache id with project id --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index a2b6c84..e66d602 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,10 +6,10 @@ COPY . /app WORKDIR /app FROM base AS prod-deps -RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile +RUN --mount=type=cache,id=085523d9-1efb-4dee-b76a-bd2303df0757-pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile FROM base AS build -RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile +RUN --mount=type=cache,id=085523d9-1efb-4dee-b76a-bd2303df0757-pnpm,target=/pnpm/store pnpm install --frozen-lockfile RUN pnpm run build FROM base From cb71bbfc21455e6e45e6a86f6fa79a393a5c3788 Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Sat, 20 Sep 2025 16:45:41 -0400 Subject: [PATCH 27/65] fix: prefix cache id with project id --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index e66d602..be2cf89 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,10 +6,10 @@ COPY . /app WORKDIR /app FROM base AS prod-deps -RUN --mount=type=cache,id=085523d9-1efb-4dee-b76a-bd2303df0757-pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile +RUN --mount=type=cache,id=s/085523d9-1efb-4dee-b76a-bd2303df0757-/pnpm/store,target=/pnpm/store pnpm install --prod --frozen-lockfile FROM base AS build -RUN --mount=type=cache,id=085523d9-1efb-4dee-b76a-bd2303df0757-pnpm,target=/pnpm/store pnpm install --frozen-lockfile +RUN --mount=type=cache,id=s/085523d9-1efb-4dee-b76a-bd2303df0757-/pnpm/store,target=/pnpm/store pnpm install --frozen-lockfile RUN pnpm run build FROM base From baa893cdae152c4fde58f646e9c2127d8d1bfa53 Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Sat, 20 Sep 2025 16:46:50 -0400 Subject: [PATCH 28/65] fix: prefix cache id with project id --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index be2cf89..bac0ba7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,10 +6,10 @@ COPY . /app WORKDIR /app FROM base AS prod-deps -RUN --mount=type=cache,id=s/085523d9-1efb-4dee-b76a-bd2303df0757-/pnpm/store,target=/pnpm/store pnpm install --prod --frozen-lockfile +RUN --mount=type=cache,id=s/8ced5fd2-dd3a-4a6d-9429-f4bcf2553871-/pnpm/store,target=/pnpm/store pnpm install --prod --frozen-lockfile FROM base AS build -RUN --mount=type=cache,id=s/085523d9-1efb-4dee-b76a-bd2303df0757-/pnpm/store,target=/pnpm/store pnpm install --frozen-lockfile +RUN --mount=type=cache,id=s/8ced5fd2-dd3a-4a6d-9429-f4bcf2553871-/pnpm/store,target=/pnpm/store pnpm install --frozen-lockfile RUN pnpm run build FROM base From f3cfadf635e79bba5012170b9f1d4e9495d0b34e Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Sun, 21 Sep 2025 17:10:42 -0400 Subject: [PATCH 29/65] chore: empty commit From 4821b33cac4018cdb948e8ef344bd34840cd9ff5 Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Sat, 4 Oct 2025 11:45:39 -0400 Subject: [PATCH 30/65] chore: fix cheerio element typechecking --- package.json | 1 + pnpm-lock.yaml | 9 +++++++++ src/containers/locationBuilder.ts | 3 +-- src/containers/timeBuilder.ts | 3 +-- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 9f47e02..6e0c489 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "drizzle-orm": "^0.44.4", "elysia": "1.3.5", "express": "^5.1.0", + "luxon": "^3.7.2", "pg": "^8.16.3", "zod": "^3.25.67" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 65d8336..3370f2e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,6 +35,9 @@ importers: express: specifier: ^5.1.0 version: 5.1.0 + luxon: + specifier: ^3.7.2 + version: 3.7.2 pg: specifier: ^8.16.3 version: 8.16.3 @@ -2239,6 +2242,10 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + luxon@3.7.2: + resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==} + engines: {node: '>=12'} + make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} @@ -5288,6 +5295,8 @@ snapshots: dependencies: yallist: 3.1.1 + luxon@3.7.2: {} + make-dir@4.0.0: dependencies: semver: 7.7.2 diff --git a/src/containers/locationBuilder.ts b/src/containers/locationBuilder.ts index 9b42a60..54e8a7d 100644 --- a/src/containers/locationBuilder.ts +++ b/src/containers/locationBuilder.ts @@ -1,6 +1,5 @@ import { getHTMLResponse } from "utils/requestUtils"; -import { load } from "cheerio"; -import type { Element } from "domhandler"; +import { Element, load } from "cheerio"; import { getAllTimeSlotsFromSchedule } from "./timeBuilder"; import { ICoordinate, ILocation, ISpecial, ITimeRange } from "../types"; import { ITimeOverwrites } from "overwrites/timeOverwrites"; diff --git a/src/containers/timeBuilder.ts b/src/containers/timeBuilder.ts index 6dc42da..35acbb4 100644 --- a/src/containers/timeBuilder.ts +++ b/src/containers/timeBuilder.ts @@ -1,5 +1,4 @@ -import { load } from "cheerio"; -import type { Element } from "domhandler"; +import { Element, load } from "cheerio"; import { sortAndMergeTimeRanges } from "../utils/timeUtils"; import { IParsedTimeRange } from "./time/parsedTime"; From 078a5ebc52b8b73cc1ecc3fdd8d3e6f4b0fdcd8c Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Sat, 4 Oct 2025 11:46:17 -0400 Subject: [PATCH 31/65] chore: add luxon types --- package.json | 1 + pnpm-lock.yaml | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/package.json b/package.json index 6e0c489..0cd79dc 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "@babel/preset-typescript": "^7.24.7", "@rollup/plugin-typescript": "^12.1.4", "@types/jest": "^29.5.14", + "@types/luxon": "^3.7.1", "@types/node": "^24.0.14", "@types/pg": "^8.15.5", "babel-jest": "^29.7.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3370f2e..21a24e6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -63,6 +63,9 @@ importers: '@types/jest': specifier: ^29.5.14 version: 29.5.14 + '@types/luxon': + specifier: ^3.7.1 + version: 3.7.1 '@types/node': specifier: ^24.0.14 version: 24.5.2 @@ -1294,6 +1297,9 @@ packages: '@types/jest@29.5.14': resolution: {integrity: sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==} + '@types/luxon@3.7.1': + resolution: {integrity: sha512-H3iskjFIAn5SlJU7OuxUmTEpebK6TKB8rxZShDslBMZJ5u9S//KM1sbdAisiSrqwLQncVjnpi2OK2J51h+4lsg==} + '@types/mime@1.3.5': resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} @@ -4147,6 +4153,8 @@ snapshots: expect: 29.7.0 pretty-format: 29.7.0 + '@types/luxon@3.7.1': {} + '@types/mime@1.3.5': {} '@types/node@24.5.2': From b39a02cc83085f1271ae0a944290569948006b9a Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Sat, 8 Nov 2025 16:30:06 -0500 Subject: [PATCH 32/65] feat: complete database integration with overwriting any field (excluding specials) --- src/containers/locationBuilder.ts | 29 +++-- src/containers/time/parsedTime.ts | 10 +- src/containers/time/parsedTimeForDate.ts | 4 +- src/containers/timeBuilder.ts | 72 ++++++------- src/db/emails.ts | 17 +++ src/db/getLocations.ts | 129 +++++++++++++++++++++++ src/db/{query.ts => overrides.ts} | 63 ++++++----- src/db/schema.ts | 62 ++++++++++- src/db/updateLocation.ts | 74 +++++++++++++ src/deprecationNotice.ts | 49 +++++++++ src/parser/diningParser.ts | 24 +---- src/server.ts | 113 ++++++++++---------- src/types.ts | 29 ++--- src/utils/diff.ts | 2 +- src/utils/locationMerger.ts | 2 +- src/utils/timeUtils.ts | 71 +++---------- tests/locationMerger.test.ts | 6 +- tsconfig.json | 3 +- 18 files changed, 521 insertions(+), 238 deletions(-) create mode 100644 src/db/emails.ts create mode 100644 src/db/getLocations.ts rename src/db/{query.ts => overrides.ts} (56%) create mode 100644 src/db/updateLocation.ts create mode 100644 src/deprecationNotice.ts diff --git a/src/containers/locationBuilder.ts b/src/containers/locationBuilder.ts index 54e8a7d..9d7332d 100644 --- a/src/containers/locationBuilder.ts +++ b/src/containers/locationBuilder.ts @@ -1,7 +1,13 @@ import { getHTMLResponse } from "utils/requestUtils"; import { Element, load } from "cheerio"; import { getAllTimeSlotsFromSchedule } from "./timeBuilder"; -import { ICoordinate, ILocation, ISpecial, ITimeRange } from "../types"; +import { + ICoordinate, + IDate, + ILocation, + ISpecial, + IFullTimeRange, +} from "../types"; import { ITimeOverwrites } from "overwrites/timeOverwrites"; /** @@ -20,7 +26,8 @@ export default class LocationBuilder { private menu?: string; private coordinates?: ICoordinate; private acceptsOnlineOrders?: boolean; - private times?: ITimeRange[]; + private times?: IFullTimeRange[]; + private earliestDayFound?: IDate; private specials?: ISpecial[]; private soups?: ISpecial[]; @@ -47,7 +54,7 @@ export default class LocationBuilder { this.specials = specialList[this.conceptId]; } } - setTimes(times: ITimeRange[]) { + setTimes(times: IFullTimeRange[]) { if (this.conceptId && times !== undefined) { this.times = times; } @@ -61,7 +68,7 @@ export default class LocationBuilder { return { lat: parseFloat(latitude), lng: parseFloat(longitude) }; } - async populateDetailedInfo(timeSlotOverwrites: ITimeOverwrites = {}) { + async populateDetailedInfo() { const conceptURL = this.getConceptLink(); if (!conceptURL) return; @@ -80,11 +87,12 @@ export default class LocationBuilder { } const nextSevenDays = $("ul.schedule").find("li").toArray(); - this.times = getAllTimeSlotsFromSchedule( + const { times, earliestDay } = getAllTimeSlotsFromSchedule( nextSevenDays, - serverDate.year, - timeSlotOverwrites + serverDate.year ); + this.times = times; + this.earliestDayFound = earliestDay; } getConceptLink() { if (this.conceptId === undefined) return undefined; @@ -103,16 +111,14 @@ export default class LocationBuilder { this.url === undefined || this.location === undefined || this.conceptId === undefined || - this.name === undefined + this.name === undefined || + this.earliestDayFound === undefined ) { throw Error( "Didn't finish configuring location before building metadata!" ); // All fetches were good - yet we have missing data. This is a problem. } - if (this.conceptId === 179) { - this.times = []; // capital grains quick override - } return { conceptId: this.conceptId, @@ -125,6 +131,7 @@ export default class LocationBuilder { coordinates: this.coordinates, acceptsOnlineOrders: this.acceptsOnlineOrders, times: this.times, + earliestDayFound: this.earliestDayFound, todaysSpecials: this.specials, todaysSoups: this.soups, }; diff --git a/src/containers/time/parsedTime.ts b/src/containers/time/parsedTime.ts index b368be8..0ed05c7 100644 --- a/src/containers/time/parsedTime.ts +++ b/src/containers/time/parsedTime.ts @@ -23,15 +23,15 @@ export default class ParsedTime extends ParsedTimeBase { if (tokens.length !== 3) { throw new Error(`Invalid time ${timeStr}`); } - const hour = parseInt(tokens[0]); + const hour = parseInt(tokens[0]!); if (!Number.isInteger(hour) || hour > 12 || hour < 1) { throw new Error(`Invalid time ${timeStr}`); } - const minute = parseInt(tokens[1]); + const minute = parseInt(tokens[1]!); if (!Number.isInteger(minute) || minute > 59 || minute < 0) { throw new Error(`Invalid time ${timeStr}`); } - if (!["am", "pm"].includes(tokens[2])) { + if (!["am", "pm"].includes(tokens[2]!)) { throw new Error(`Invalid time ${timeStr}`); } if (tokens[2] === "am") { @@ -52,8 +52,8 @@ export default class ParsedTime extends ParsedTimeBase { if (tokens.length !== 2) { throw new Error(`Invalid time range: ${this.input}`); } - const start = this.parseTime(tokens[0]); - const end = this.parseTime(tokens[1]); + const start = this.parseTime(tokens[0]!); + const end = this.parseTime(tokens[1]!); this.value = { start, end, diff --git a/src/containers/time/parsedTimeForDate.ts b/src/containers/time/parsedTimeForDate.ts index 7f8af4e..8c37280 100644 --- a/src/containers/time/parsedTimeForDate.ts +++ b/src/containers/time/parsedTimeForDate.ts @@ -19,8 +19,8 @@ export default class ParsedTimeForDate extends ParsedTimeBase { if (tokens.length < 2) { throw new Error(`Invalid date: ${this.input}`); } - const month = convertMonthStringToEnum(tokens[0]); - const date = parseInt(tokens[1]); + const month = convertMonthStringToEnum(tokens[0]!); + const date = parseInt(tokens[1]!); if (!Number.isInteger(date)) { throw new Error(`Invalid date: ${this.input}`); diff --git a/src/containers/timeBuilder.ts b/src/containers/timeBuilder.ts index 35acbb4..fc1408e 100644 --- a/src/containers/timeBuilder.ts +++ b/src/containers/timeBuilder.ts @@ -1,14 +1,11 @@ import { Element, load } from "cheerio"; -import { sortAndMergeTimeRanges } from "../utils/timeUtils"; import { IParsedTimeRange } from "./time/parsedTime"; import { convertMonthStringToEnum } from "./time/parsedTimeForDate"; -import { ITimeRange, TimeInfoType } from "types"; +import { IDate, IFullTimeRange, TimeInfoType } from "types"; import { parseToken } from "utils/parseTimeToken"; import { notifySlack } from "utils/slack"; import { DateTime } from "luxon"; -import { ITimeOverwrites } from "overwrites/timeOverwrites"; -import ParsedTimeForDayOfWeek from "./time/parsedTimeForDay"; /** * @@ -18,11 +15,11 @@ import ParsedTimeForDayOfWeek from "./time/parsedTimeForDay"; */ export function getAllTimeSlotsFromSchedule( schedule: Element[], - currentYear: number, - scheduleOverwrites: ITimeOverwrites + currentYear: number ) { - const allTimeSlots: ITimeRange[] = []; + const allTimeSlots: IFullTimeRange[] = []; let prevMonth = -1; + let firstDay: IDate | undefined; for (const rowHTML of schedule) { const $ = load(rowHTML); try { @@ -30,7 +27,7 @@ export function getAllTimeSlotsFromSchedule( .replaceAll("\n", "") .replace(/\s\s+/g, " ") .split(/,(.*)/); // splits only on first comma - const [dayOfWeek, month, day] = date.trim().split(" "); + const [dayOfWeek, month, day] = date?.trim().split(" ") ?? []; if ( dayOfWeek === undefined || month === undefined || @@ -41,8 +38,7 @@ export function getAllTimeSlotsFromSchedule( continue; const parsedMonth = convertMonthStringToEnum(month); const parsedDay = parseInt(day); - const parsedDayOfWeek = new ParsedTimeForDayOfWeek(dayOfWeek).parse() - .value; + if (parsedMonth < prevMonth) { // new year currentYear++; @@ -58,32 +54,42 @@ export function getAllTimeSlotsFromSchedule( ); continue; } - const parsedTimeSlots = parseTimeSlots( - scheduleOverwrites[rowFullDateString] ?? - timeSlotsForThatDay.split(/[,;]/).map((slot) => slot.trim()) - ); + const parsedTimeSlots = parseTimeSlots(timeSlotsForThatDay); const parsedTimeSlotsWithDateInfo = augmentAndEditTimeRangesWithDateInfo( parsedTimeSlots, - parsedDayOfWeek // we could use rowDate.weekday % 7, but that would break a good number of tests that did not rely on serverDate to get the weekday... + rowDate.day, + rowDate.month, + rowDate.year ); + allTimeSlots.push(...parsedTimeSlotsWithDateInfo); + if (firstDay === undefined) + firstDay = { + year: rowDate.year, + month: rowDate.month, + day: rowDate.day, + }; } catch (e) { notifySlack( ` Failed to parse row ${$.text()} ${e} ${(e as any).stack}` ); } } - return sortAndMergeTimeRanges(allTimeSlots); + if (firstDay === undefined) + throw new Error(`None of the rows in ${schedule} were parsable!`); + return { times: allTimeSlots, earliestDay: firstDay }; } /** * * @param timeString The actual time slots (ex. '10:30 AM - 8:00 PM' in the string 'Tuesday September 09, 10:30 AM - 8:00 PM') * @returns */ -function parseTimeSlots(timeSlotStrings: string[]) { +export function parseTimeSlots(timeSlotStrings: string) { const timeRanges: IParsedTimeRange[] = []; - for (const token of timeSlotStrings) { + for (const token of timeSlotStrings + .split(/[,;]/) + .map((slot) => slot.trim())) { try { const { type: timeInfoType, value } = parseToken(token); switch (timeInfoType) { @@ -98,9 +104,7 @@ function parseTimeSlots(timeSlotStrings: string[]) { } } catch (err: any) { notifySlack( - ` Failed to parse token \`${token}\` from time slot \`${timeSlotStrings.join( - "|" - )}\`\n${err.stack}` + ` Failed to parse token \`${token}\` from time slot \`${timeSlotStrings}\`\n${err.stack}` ); continue; } @@ -114,28 +118,22 @@ function parseTimeSlots(timeSlotStrings: string[]) { * @param day (0-6, with Sunday being 0) * @returns */ -function augmentAndEditTimeRangesWithDateInfo( +export function augmentAndEditTimeRangesWithDateInfo( timeRanges: IParsedTimeRange[], - day: number + day: number, + month: number, + year: number ) { - const allRanges: ITimeRange[] = []; + const allRanges: IFullTimeRange[] = []; for (const range of timeRanges) { rollBack12AmEndTime(range); // not sure why this was added, but it doesn't hurt I guess (I suppose the only case this actively helps is if the time string is 12:00 AM - 12:00 AM) - const shouldSpillToNextDay = - range.start.hour * 60 + range.start.minute > - range.end.hour * 60 + range.end.minute; allRanges.push({ - start: { - day: day, - hour: range.start.hour, - minute: range.start.minute, - }, - end: { - day: shouldSpillToNextDay ? (day + 1) % 7 : day, - hour: range.end.hour, - minute: range.end.minute, - }, + year: year, + month: month, + day: day, + startMinutesFromMidnight: range.start.hour * 60 + range.start.minute, + endMinutesFromMidnight: range.end.hour * 60 + range.end.minute, }); } return allRanges; diff --git a/src/db/emails.ts b/src/db/emails.ts new file mode 100644 index 0000000..7cfe169 --- /dev/null +++ b/src/db/emails.ts @@ -0,0 +1,17 @@ +import { db } from "./db"; +import { emailTable } from "./schema"; + +export async function getEmails(): Promise<{ name: string; email: string }[]> { + const result = await db + .select({ + name: emailTable.name, + email: emailTable.email, + }) + .from(emailTable); + + // Remove 'mailto:' if present + return result.map((row) => ({ + name: row.name, + email: row.email.replace(/^mailto:/, ""), + })); +} diff --git a/src/db/getLocations.ts b/src/db/getLocations.ts new file mode 100644 index 0000000..c87e639 --- /dev/null +++ b/src/db/getLocations.ts @@ -0,0 +1,129 @@ +import { db } from "./db"; +import { + conceptIdToInternalIdTable, + locationDataTable, + overwritesTable, + timesTable, +} from "./schema"; +import { eq, gte } from "drizzle-orm"; +import { getTimeOverrides } from "./overrides"; +import { DateTime } from "luxon"; +import { mergeTimeRanges, pad } from "utils/timeUtils"; +type RequiredProperty = { [P in keyof T]: NonNullable }; +interface ITimeRangeInternal { + date: string; + startMinutesSinceMidnight: number; + endMinutesSinceMidnight: number; +} +function mergeTimeIntervals(timeRanges: ITimeRangeInternal[]) { + const timeStampedIntervals = timeRanges.map((rng) => { + const date = DateTime.fromISO(rng.date, { zone: "America/New_York" }); + return { + start: date.toMillis() + rng.startMinutesSinceMidnight * 1000 * 60, + end: + date.toMillis() + + rng.endMinutesSinceMidnight * 1000 * 60 + + (rng.endMinutesSinceMidnight < rng.startMinutesSinceMidnight + ? 24 * 60 * 60 * 1000 + : 0), // this specific slot wraps around - so we'll add a day to the end + }; + }); + return mergeTimeRanges(timeStampedIntervals, 60 * 1000); // merge gaps of at most 1 min +} +export async function getAllLocations() { + const timeSearchCutoff = DateTime.now().minus({ days: 1 }); // 1 days worth of data before today + const timeSearchCutoffStr = `${timeSearchCutoff.year}-${pad( + timeSearchCutoff.month + )}-${pad(timeSearchCutoff.day)}`; + const locationData = await db + .select() + .from(locationDataTable) + .leftJoin( + conceptIdToInternalIdTable, + eq(locationDataTable.id, conceptIdToInternalIdTable.internalId) + ) + .leftJoin(timesTable, eq(locationDataTable.id, timesTable.locationId)) + .where(gte(timesTable.date, timeSearchCutoffStr)); + + const locationIdToData = locationData.reduce< + Record< + string, + | Omit & { + id: number; + } & { + times: ITimeRangeInternal[]; + } + > + >((acc, { location_data, location_times, concept_id_to_internal_id }) => { + if (!acc[location_data.id]) { + acc[location_data.id] = { + ...location_data, + id: parseInt(concept_id_to_internal_id?.externalId ?? "-1"), + times: [], + }; + } + if (location_times !== null) { + acc[location_data.id]!.times.push({ + startMinutesSinceMidnight: location_times.startTime, + endMinutesSinceMidnight: location_times.endTime, + date: location_times.date, + }); + } + return acc; + }, {}); + + // get general location override data + const overrides = (await db.select().from(overwritesTable)).reduce< + Record> + >( + (acc, overwrite) => ({ + ...acc, + [overwrite.locationId]: Object.fromEntries( + Object.entries(overwrite).filter(([_, v]) => v !== null) + ) as RequiredProperty, + }), + {} + ); + const generallyOverriddenLocations = Object.fromEntries( + Object.entries(locationIdToData).map(([id, data]) => [ + id, + { ...data, ...overrides[id] }, + ]) + ); // extra locationId property here, whatever + + // get time override data, apply each override + const timeOverrides = await getTimeOverrides(timeSearchCutoffStr); + for (const [id, timeOverrideMap] of Object.entries(timeOverrides)) { + if (generallyOverriddenLocations[id] === undefined) continue; + let locationTimes = generallyOverriddenLocations[id].times; + for (const [overrideDate, timeRanges] of Object.entries(timeOverrideMap)) { + locationTimes = locationTimes.filter( + (time) => time.date !== overrideDate + ); + + locationTimes.push( + ...timeRanges.map((rng) => ({ + date: overrideDate, + startMinutesSinceMidnight: rng.start.hour * 60 + rng.start.minute, + endMinutesSinceMidnight: rng.end.hour * 60 + rng.end.minute, + })) + ); + } + generallyOverriddenLocations[id].times = locationTimes; + } + // merge all time intervals into usable format + const locationsWithMergedTimes = Object.entries( + generallyOverriddenLocations + ).map(([id, data]) => { + return { + ...data, + times: mergeTimeIntervals(data.times).map( + (time) => + `${new Date(time.start).toLocaleString()}-${new Date( + time.end + ).toLocaleString()}` + ), + }; + }); + return locationsWithMergedTimes; +} diff --git a/src/db/query.ts b/src/db/overrides.ts similarity index 56% rename from src/db/query.ts rename to src/db/overrides.ts index 0690c8e..04da531 100644 --- a/src/db/query.ts +++ b/src/db/overrides.ts @@ -1,28 +1,20 @@ -import { emailTable, overwritesTable } from "./schema"; -import { ILocation } from "types"; +import { overwritesTable, timeOverwritesTable } from "./schema"; +import { IFullTimeRange, ILocation } from "types"; import { db } from "./db"; import { notifySlack } from "utils/slack"; - -export async function getEmails(): Promise<{ name: string; email: string }[]> { - const result = await db - .select({ - name: emailTable.name, - email: emailTable.email, - }) - .from(emailTable); - - // Remove 'mailto:' if present - return result.map((row) => ({ - name: row.name, - email: row.email.replace(/^mailto:/, ""), - })); -} +import { gte } from "drizzle-orm"; +import { + augmentAndEditTimeRangesWithDateInfo, + parseTimeSlots, +} from "containers/timeBuilder"; +import { DateTime } from "luxon"; +import { IParsedTimeRange } from "containers/time/parsedTime"; /** * * @returns object that maps ids to overrides, where each value in the override map is guaranteed to be non-null. (important!!) */ -export async function getOverrides() { +export async function getGeneralOverrides() { const overrides = await db .select() .from(overwritesTable) @@ -47,7 +39,6 @@ export async function getOverrides() { ...(curLocation.shortDescription !== null && { shortDescription: curLocation.shortDescription, }), - ...(curLocation.times !== null && { times: curLocation.times }), ...(curLocation.url !== null && { url: curLocation.url }), ...(curLocation.coordinateLat !== null && curLocation.coordinateLng !== null && { @@ -59,14 +50,34 @@ export async function getOverrides() { }; return { ...accumulator, - [curLocation.conceptId]: { - ...Object.fromEntries( - Object.entries(reformattedObjWithNonNullFields).filter( - ([_, v]) => v !== null - ) - ), - }, + [curLocation.locationId]: reformattedObjWithNonNullFields, }; }, {}); return idToOverrideMap; } +/** + * + * @param earliestDate should be in SQL form YYYY-MM-DD + */ +export async function getTimeOverrides(earliestDate: string) { + const timeOverrides = await db + .select() + .from(timeOverwritesTable) + .where(gte(timeOverwritesTable.date, earliestDate)) + .catch((e) => { + notifySlack(` Failed to fetch time overwrites with error ${e}`); + return []; + }); + const idToTimeOverrides = timeOverrides.reduce<{ + [locationId in string]: { [date in string]: IParsedTimeRange[] }; + }>((acc, override) => { + return { + ...acc, + [override.locationId]: { + ...acc[override.locationId], + [override.date]: parseTimeSlots(override.timeString), + }, + }; + }, {}); + return idToTimeOverrides; +} diff --git a/src/db/schema.ts b/src/db/schema.ts index d53a385..ac62ccc 100644 --- a/src/db/schema.ts +++ b/src/db/schema.ts @@ -5,20 +5,62 @@ import { boolean, jsonb, decimal, + date, + primaryKey, + index, } from "drizzle-orm/pg-core"; -import { ITimeRange } from "../types"; +import { IFullTimeRange } from "../types"; export const emailTable = pgTable("emails", { id: integer().primaryKey().generatedAlwaysAsIdentity(), name: text("name").notNull(), email: text("email").notNull(), }); - +export const conceptIdToInternalIdTable = pgTable("concept_id_to_internal_id", { + // keeping both as text for the most flexibility + internalId: text("internal_id") + .notNull() + .references(() => locationDataTable.id, { onDelete: "cascade" }), + externalId: text("external_id").notNull().unique().primaryKey(), +}); +export const locationDataTable = pgTable("location_data", { + id: text("id").notNull().primaryKey(), + name: text("name"), + shortDescription: text("short_description"), + description: text("description").notNull(), + url: text("url").notNull(), + menu: text("menu"), + /** The human-readable version of the location */ + location: text("location").notNull(), + coordinateLat: decimal("coordinate_lat", { mode: "number", scale: 30 }), + coordinateLng: decimal("coordinate_lng", { mode: "number", scale: 30 }), + acceptsOnlineOrders: boolean().notNull(), +}); +export const timesTable = pgTable( + "location_times", + { + id: integer().notNull().generatedAlwaysAsIdentity().primaryKey(), + locationId: text("location_id") + .references(() => locationDataTable.id, { + onDelete: "cascade", + }) + .notNull(), + date: date("date").notNull(), + startTime: integer("start_time").notNull(), + endTime: integer("end_time").notNull(), + }, + (table) => [index("date_lookup").on(table.locationId, table.date)] +); /** * Includes everything in ILocation except for soups and specials */ export const overwritesTable = pgTable("overwrites_table", { - conceptId: integer("concept_id").notNull().primaryKey(), + locationId: text("location_id") + .notNull() + .primaryKey() + .references(() => locationDataTable.id, { + onDelete: "cascade", + }), name: text("name"), description: text("description"), shortDescription: text("short_description"), @@ -28,5 +70,17 @@ export const overwritesTable = pgTable("overwrites_table", { coordinateLat: decimal({ mode: "number", scale: 30 }), coordinateLng: decimal({ mode: "number", scale: 30 }), acceptsOnlineOrders: boolean("accepts_online_orders"), - times: jsonb("times").$type(), }); +export const timeOverwritesTable = pgTable( + "time_overwrites_table", + { + locationId: text("location_id") + .notNull() + .references(() => locationDataTable.id, { + onDelete: "cascade", + }), + date: date("date").notNull(), + timeString: text("time_string").notNull(), + }, + (table) => [primaryKey({ columns: [table.locationId, table.date] })] +); diff --git a/src/db/updateLocation.ts b/src/db/updateLocation.ts new file mode 100644 index 0000000..9bb81f9 --- /dev/null +++ b/src/db/updateLocation.ts @@ -0,0 +1,74 @@ +import { ILocation } from "types"; +import { db } from "./db"; +import { + conceptIdToInternalIdTable, + locationDataTable, + timesTable, +} from "./schema"; +import { and, eq, gte, sql } from "drizzle-orm"; +import { pad } from "utils/timeUtils"; +async function getInternalId(externalId: string) { + let [idMapping] = await db + .select() + .from(conceptIdToInternalIdTable) + .where(eq(conceptIdToInternalIdTable.externalId, externalId)); + + return idMapping?.internalId ?? crypto.randomUUID(); +} + +export async function addLocationDataToDb(location: ILocation) { + const internalId = await getInternalId(location.conceptId.toString()); + const locationDbEntry: typeof locationDataTable.$inferInsert = { + id: internalId, + name: location.name, + shortDescription: location.shortDescription, + description: location.description, + url: location.url, + menu: location.menu, + location: location.location, + coordinateLat: location.coordinates?.lat, + coordinateLng: location.coordinates?.lng, + acceptsOnlineOrders: location.acceptsOnlineOrders, + }; + + await db + .insert(locationDataTable) + .values(locationDbEntry) + .onConflictDoUpdate({ target: locationDataTable.id, set: locationDbEntry }); + + // remove rows from whenever the scrape started from (aka remove entries corresponding to the last 7 days) + await db + .delete(timesTable) + .where( + and( + eq(timesTable.locationId, internalId), + and( + gte( + timesTable.date, + `${location.earliestDayFound.year}-${pad( + location.earliestDayFound.month + )}-${pad(location.earliestDayFound.day)})` + ) + ) + ) + ); + if (location.times.length) { + await db.insert(timesTable).values( + location.times.map((time) => ({ + locationId: internalId, + date: `${time.year}-${pad(time.month)}-${pad(time.day)}`, + startTime: time.startMinutesFromMidnight, + endTime: time.endMinutesFromMidnight, + })) + ); + } + + // in case this entry isn't there + await db + .insert(conceptIdToInternalIdTable) + .values({ + internalId: internalId, + externalId: location.conceptId.toString(), + }) + .onConflictDoNothing({ target: conceptIdToInternalIdTable.externalId }); +} diff --git a/src/deprecationNotice.ts b/src/deprecationNotice.ts new file mode 100644 index 0000000..fdd0f4e --- /dev/null +++ b/src/deprecationNotice.ts @@ -0,0 +1,49 @@ +export const deprecatedNotice: ILocationOld[] = [ + { + acceptsOnlineOrders: false, + conceptId: -1111, + description: "Please use the new /api/v2/locations endpoint", + name: "This api format has been deprecated", + location: "Now", + times: [], + url: "https://cmueats.com", + }, +]; + +interface ILocationOld { + conceptId: number; + name: string; + shortDescription?: string; + description: string; + url: string; + menu?: string; + location: string; + coordinates?: ICoordinate; + acceptsOnlineOrders: boolean; + times: ITimeRange[]; + todaysSpecials?: ISpecial[]; + todaysSoups?: ISpecial[]; +} +interface ISpecial { + title: string; + description: string; +} + +interface ITimeSlot { + day: number; + hour: number; + minute: number; +} + +interface ITimeRange { + start: ITimeSlot; + end: ITimeSlot; +} +interface ICoordinate { + lat: number; + lng: number; +} + +interface ILocationCoordinateOverwrites { + [conceptId: string]: ICoordinate; +} diff --git a/src/parser/diningParser.ts b/src/parser/diningParser.ts index 11e4f34..783dcbd 100644 --- a/src/parser/diningParser.ts +++ b/src/parser/diningParser.ts @@ -2,8 +2,7 @@ import { getHTMLResponse } from "../utils/requestUtils"; import { load } from "cheerio"; import LocationBuilder from "../containers/locationBuilder"; import { retrieveSpecials } from "../containers/specials/specialsBuilder"; -import { ILocation, ILocationCoordinateOverwrites, ISpecial } from "types"; -import { IAllTimeOverwrites } from "overwrites/timeOverwrites"; +import { ILocation, ISpecial } from "types"; /** * Retrieves the HTML from the CMU Dining website and parses the information @@ -16,21 +15,6 @@ export default class DiningParser { "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Specials"; static readonly DINING_SOUPS_URL = "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Soups"; - locationCoordinateOverwrites: ILocationCoordinateOverwrites = {}; - timeSlotOverwrites: IAllTimeOverwrites = {}; - - constructor(overwrites?: { - locationCoordinateOverwrites?: ILocationCoordinateOverwrites; - timeSlotOverwrites?: IAllTimeOverwrites; - }) { - if (overwrites?.locationCoordinateOverwrites !== undefined) { - this.locationCoordinateOverwrites = - overwrites.locationCoordinateOverwrites; - } - if (overwrites?.timeSlotOverwrites !== undefined) { - this.timeSlotOverwrites = overwrites.timeSlotOverwrites; - } - } async process(): Promise { const locationBuilders = @@ -39,11 +23,7 @@ export default class DiningParser { const [specials, soups] = await this.fetchSpecials(); for (const builder of locationBuilders) { - await builder.populateDetailedInfo( - builder.getConceptId() !== undefined - ? this.timeSlotOverwrites[builder.getConceptId()!] - : undefined - ); + await builder.populateDetailedInfo(); builder.setSoup(soups); builder.setSpecials(specials); } diff --git a/src/server.ts b/src/server.ts index bec7366..59533f9 100644 --- a/src/server.ts +++ b/src/server.ts @@ -5,24 +5,22 @@ import { ILocation } from "types"; import { env } from "env"; import { notifySlack } from "utils/slack"; import { node } from "@elysiajs/node"; +import ScrapeResultMerger from "utils/locationMerger"; +import { getGeneralOverrides } from "db/overrides"; +import { addLocationDataToDb } from "db/updateLocation"; +import { deprecatedNotice } from "deprecationNotice"; import { getDiffsBetweenLocationData } from "utils/diff"; -import LocationMerger from "utils/locationMerger"; -import locationCoordinateOverwrites from "overwrites/locationCoordinateOverwrites"; -import { timeSlotOverwrites } from "overwrites/timeOverwrites"; -import { getEmails, getOverrides } from "db/query"; +import { getEmails } from "db/emails"; +import { getAllLocations } from "db/getLocations"; +/** only used for Slack debug diff logging */ let cachedLocations: ILocation[] = []; -function getCachedLocations() { - return applyOverrides(cachedLocations); -} async function reload(): Promise { + return; const now = new Date(); console.log(`Reloading Dining API: ${now}`); - const parser = new DiningParser({ - locationCoordinateOverwrites, - timeSlotOverwrites, - }); - const locationMerger = new LocationMerger(); + const parser = new DiningParser(); + const locationMerger = new ScrapeResultMerger(); for (let i = 0; i < env.NUMBER_OF_SCRAPES; i++) { // Wait a bit before starting the next round of scrapes. @@ -49,20 +47,13 @@ async function reload(): Promise { await notifySlack(diff); } } + + await Promise.all( + finalLocations.map((location) => addLocationDataToDb(location)) + ); } } -async function applyOverrides(locations: ILocation[]): Promise { - const overrides = await getOverrides(); - return locations.map((location) => { - const overrideData = overrides[location.conceptId]; - if (overrideData === undefined) return location; - return { - ...location, - ...overrideData, - }; - }); -} export const app = new Elysia({ adapter: node() }); // I don't trust bun (as a runtime) enough (Eric Xu - 7/18/2025). This may change in the future, but bun is currently NOT a full drop-in replacement for node and is still rather unstable from personal experience app.onError(({ error, path, code }) => { @@ -81,44 +72,11 @@ app.use(cors()); app.get("/", () => { return "ScottyLabs Dining API"; }); - -app.get("/locations", async () => ({ locations: await getCachedLocations() })); - -app.get("/location/:name", async ({ params: { name } }) => { - const filteredLocation = (await getCachedLocations()).filter((location) => { - return location.name?.toLowerCase().includes(name.toLowerCase()); - }); - return { - locations: filteredLocation, - }; -}); - -app.get( - "/locations/time/:day/:hour/:min", - async ({ params: { day, hour, min } }) => { - const result = (await getCachedLocations()).filter((el) => { - let returning = false; - el.times.forEach((element) => { - const startMins = - element.start.day * 1440 + - element.start.hour * 60 + - element.start.minute; - const endMins = - element.end.day * 1440 + element.end.hour * 60 + element.end.minute; - const currentMins = - parseInt(day) * 1440 + parseInt(hour) * 60 + parseInt(min); - if (currentMins >= startMins && currentMins < endMins) { - returning = true; - } - }); - return returning; - }); - return { locations: result }; - } +app.get("/api/v2/locations", async () => + JSON.stringify(await getAllLocations(), null, 4) ); - app.get("/api/emails", getEmails); -app.get("/api/changes", async () => await getOverrides()); +app.get("/api/changes", async () => await getGeneralOverrides()); app.post( "/api/sendSlackMessage", @@ -150,3 +108,40 @@ reload() await notifySlack(`*Error caught*\n${er.stack}`); process.exit(1); }); + +// DEPRECATED + +app.get("/locations", async () => ({ locations: deprecatedNotice })); + +app.get("/location/:name", async ({ params: { name } }) => { + const filteredLocation = deprecatedNotice.filter((location) => { + return location.name?.toLowerCase().includes(name.toLowerCase()); + }); + return { + locations: filteredLocation, + }; +}); + +app.get( + "/locations/time/:day/:hour/:min", + async ({ params: { day, hour, min } }) => { + const result = deprecatedNotice.filter((el) => { + let returning = false; + el.times.forEach((element) => { + const startMins = + element.start.day * 1440 + + element.start.hour * 60 + + element.start.minute; + const endMins = + element.end.day * 1440 + element.end.hour * 60 + element.end.minute; + const currentMins = + parseInt(day) * 1440 + parseInt(hour) * 60 + parseInt(min); + if (currentMins >= startMins && currentMins < endMins) { + returning = true; + } + }); + return returning; + }); + return { locations: result }; + } +); diff --git a/src/types.ts b/src/types.ts index e9e77bb..9d17a6f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,3 +1,11 @@ +import { DateTime } from "luxon"; +export interface IDate { + year: number; + /** 1-12 */ + month: number; + /** 1-31 */ + day: number; +} export interface ILocation { conceptId: number; name: string; @@ -8,7 +16,9 @@ export interface ILocation { location: string; coordinates?: ICoordinate; acceptsOnlineOrders: boolean; - times: ITimeRange[]; + times: IFullTimeRange[]; + /** useful when figuring out which db entries to overwrite */ + earliestDayFound: IDate; todaysSpecials?: ISpecial[]; todaysSoups?: ISpecial[]; } @@ -17,18 +27,13 @@ export interface ISpecial { description: string; } -export interface ITimeSlot { - /** - * 0 - 6 where 0 = Sunday - */ +export interface IFullTimeRange { + year: number; + month: number; day: number; - hour: number; - minute: number; -} - -export interface ITimeRange { - start: ITimeSlot; - end: ITimeSlot; + startMinutesFromMidnight: number; + /** Can be less than start if the time slot wraps around to the next day (eg. 2 PM - 2 AM) */ + endMinutesFromMidnight: number; } export interface ICoordinate { lat: number; diff --git a/src/utils/diff.ts b/src/utils/diff.ts index 50f992e..da84420 100644 --- a/src/utils/diff.ts +++ b/src/utils/diff.ts @@ -88,7 +88,7 @@ function getArrayDiffs(prevArray: any[], newArray: any[], path: string) { if (prevFreqCnt[key] === undefined) { diffs.push( `inserted value at ${path} ${ - newFreqCnt[key] > 1 ? newFreqCnt[key] + " times" : "" + newFreqCnt[key]! > 1 ? newFreqCnt[key] + " times" : "" }: ${key}` ); } else if (newFreqCnt[key] === undefined) { diff --git a/src/utils/locationMerger.ts b/src/utils/locationMerger.ts index b450a4f..6befd2a 100644 --- a/src/utils/locationMerger.ts +++ b/src/utils/locationMerger.ts @@ -1,6 +1,6 @@ import { ILocation } from "types"; -export default class LocationMerger { +export default class ScrapeResultMerger { majorityDict: Partial< Record< number, diff --git a/src/utils/timeUtils.ts b/src/utils/timeUtils.ts index e4704c0..8fa459a 100644 --- a/src/utils/timeUtils.ts +++ b/src/utils/timeUtils.ts @@ -1,66 +1,29 @@ -import { ITimeSlot, ITimeRange } from "types"; - -export function getMinutesSinceStartOfSunday(timeSlot: ITimeSlot) { - return timeSlot.day * (24 * 60) + timeSlot.hour * 60 + timeSlot.minute; -} /** * - * @param timeSlot1 - * @param timeSlot2 - * @returns Delta in minutes of moment1 - moment2 + * @param timeRanges does not need to be sorted + * @returns */ -export function compareTimeSlots(timeSlot1: ITimeSlot, timeSlot2: ITimeSlot) { - return ( - getMinutesSinceStartOfSunday(timeSlot1) - - getMinutesSinceStartOfSunday(timeSlot2) - ); -} - -export function sortAndMergeTimeRanges(timeRanges: ITimeRange[]) { - const MINUTES_IN_A_WEEK = 60 * 24 * 7; - const unwrappedTimeRanges = [...timeRanges] - .flatMap((rng) => { - if (compareTimeSlots(rng.start, rng.end) > 0) { - // unwrap the wrapped interval - return [ - { start: { day: 0, hour: 0, minute: 0 }, end: rng.end }, - { start: rng.start, end: { day: 6, hour: 23, minute: 59 } }, - ]; - } else { - return [rng]; - } - }) - .sort((range1, range2) => compareTimeSlots(range1.start, range2.start)); - const mergedRanges: ITimeRange[] = []; - - // merge overlapping ranges - for (const timeRange of unwrappedTimeRanges) { - const lastTimeRange = mergedRanges.length +export function mergeTimeRanges( + timeRanges: { start: number; end: number }[], + slackBetweenRanges = 0 +) { + const mergedRanges: { start: number; end: number }[] = []; + timeRanges.sort((a, b) => a.start - b.start); + for (const rng of timeRanges) { + const lastMergedRange = mergedRanges.length ? mergedRanges[mergedRanges.length - 1] : undefined; if ( - lastTimeRange && - compareTimeSlots(lastTimeRange.end, timeRange.start) >= -1 // we overlap 1-minute disjoint intervals as well (ex. 2:00 PM - 2:59 PM will get merged with 3:00PM - 4:00PM as 2:00PM - 4:00PM) + lastMergedRange && + lastMergedRange.end + slackBetweenRanges >= rng.start ) { - if (compareTimeSlots(timeRange.end, lastTimeRange.end) > 0) { - lastTimeRange.end = timeRange.end; // join current range with last range - } + lastMergedRange.end = Math.max(lastMergedRange.end, rng.end); } else { - mergedRanges.push(timeRange); + mergedRanges.push(rng); } } - // merge the last day with the first day if needed - if (mergedRanges.length >= 2) { - const lastRange = mergedRanges[mergedRanges.length - 1]; - const firstRange = mergedRanges[0]; - if ( - getMinutesSinceStartOfSunday(lastRange.end) === MINUTES_IN_A_WEEK - 1 && - getMinutesSinceStartOfSunday(firstRange.start) === 0 - ) { - lastRange.end = firstRange.end; - mergedRanges.shift(); - } - } - return mergedRanges; } +export function pad(n: number) { + return n.toString().padStart(2, "0"); +} diff --git a/tests/locationMerger.test.ts b/tests/locationMerger.test.ts index 1b682f1..232019d 100644 --- a/tests/locationMerger.test.ts +++ b/tests/locationMerger.test.ts @@ -1,4 +1,4 @@ -import LocationMerger from "../src/utils/locationMerger"; +import ScrapeResultMerger from "../src/utils/locationMerger"; const locationA = { conceptId: 3, name: "Location Name", @@ -42,7 +42,7 @@ const expectArrayEquivalence = (actual: T[], expected: T[]) => { }; describe("merging", () => { test("", () => { - const merger = new LocationMerger(); + const merger = new ScrapeResultMerger(); merger.addLocation(locationA); merger.addLocation(locationAA); merger.addLocation(locationAAA); @@ -54,7 +54,7 @@ describe("merging", () => { ]); }); test("highly necessary test", () => { - const merger = new LocationMerger(); + const merger = new ScrapeResultMerger(); merger.addLocation(locationA); merger.addLocation(locationAA); merger.addLocation(locationAAA); diff --git a/tsconfig.json b/tsconfig.json index d440c50..e3cc912 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -24,7 +24,8 @@ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true // this should honestly be the default, since you're not guaranteed to get a value when you key into an object }, "include": ["src", "tests"] } From 771a107bbaa0d17c2a4fc4a9ab0be58d0bb2c113 Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Sat, 8 Nov 2025 16:34:00 -0500 Subject: [PATCH 33/65] chore: remove time overwrites file --- src/containers/locationBuilder.ts | 1 - src/overwrites/timeOverwrites.ts | 39 ------------------------------- 2 files changed, 40 deletions(-) delete mode 100644 src/overwrites/timeOverwrites.ts diff --git a/src/containers/locationBuilder.ts b/src/containers/locationBuilder.ts index 9d7332d..3da0ef2 100644 --- a/src/containers/locationBuilder.ts +++ b/src/containers/locationBuilder.ts @@ -8,7 +8,6 @@ import { ISpecial, IFullTimeRange, } from "../types"; -import { ITimeOverwrites } from "overwrites/timeOverwrites"; /** * For building the location data structure diff --git a/src/overwrites/timeOverwrites.ts b/src/overwrites/timeOverwrites.ts deleted file mode 100644 index 6cd8051..0000000 --- a/src/overwrites/timeOverwrites.ts +++ /dev/null @@ -1,39 +0,0 @@ -export type ITimeOverwrites = { [date: string]: string[] | undefined }; - -export type IAllTimeOverwrites = { - [conceptId: string]: ITimeOverwrites; -}; - -/** - * key is in the format of MM/DD/YYYY (omit the first digit of the month if it's 0. same thing for day. (ex: Miku day is represented as 3/9/25)) - * NOTE: There is no coalescing between the existing time string for that day and the overwritten time string. - * If you choose to overwrite that day, you must do so completely. - */ -export const timeSlotOverwrites: IAllTimeOverwrites = { - // capital grains override - // won't be open until 9/13 - 179: { - "8/22/2025": ["CLOSED"], - "8/23/2025": ["CLOSED"], - "8/24/2025": ["CLOSED"], - "8/25/2025": ["CLOSED"], - "8/26/2025": ["CLOSED"], - "8/27/2025": ["CLOSED"], - "8/28/2025": ["CLOSED"], - "8/29/2025": ["CLOSED"], - "8/30/2025": ["CLOSED"], - "8/31/2025": ["CLOSED"], - "9/1/2025": ["CLOSED"], - "9/2/2025": ["CLOSED"], - "9/3/2025": ["CLOSED"], - "9/4/2025": ["CLOSED"], - "9/5/2025": ["CLOSED"], - "9/6/2025": ["CLOSED"], - "9/7/2025": ["CLOSED"], - "9/8/2025": ["CLOSED"], - "9/9/2025": ["CLOSED"], - "9/10/2025": ["CLOSED"], - "9/11/2025": ["CLOSED"], - "9/12/2025": ["CLOSED"], - }, -}; From ca24b72059150dcdabefef7ccd6b2850ef7f9327 Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Sat, 8 Nov 2025 17:53:53 -0500 Subject: [PATCH 34/65] tests: fix a good number of tests --- src/db/getLocations.ts | 13 +- tests/integration.test.ts | 551 +++++++++++--------------------------- tests/mockTimings.ts | 81 +++--- 3 files changed, 198 insertions(+), 447 deletions(-) diff --git a/src/db/getLocations.ts b/src/db/getLocations.ts index c87e639..15c20a0 100644 --- a/src/db/getLocations.ts +++ b/src/db/getLocations.ts @@ -117,12 +117,13 @@ export async function getAllLocations() { ).map(([id, data]) => { return { ...data, - times: mergeTimeIntervals(data.times).map( - (time) => - `${new Date(time.start).toLocaleString()}-${new Date( - time.end - ).toLocaleString()}` - ), + times: mergeTimeIntervals(data.times), + // .map( + // (time) => + // `${new Date(time.start).toLocaleString()}-${new Date( + // time.end + // ).toLocaleString()}` + // ), }; }); return locationsWithMergedTimes; diff --git a/tests/integration.test.ts b/tests/integration.test.ts index e6c1974..b3e095a 100644 --- a/tests/integration.test.ts +++ b/tests/integration.test.ts @@ -23,7 +23,7 @@ test("the whole thing, including locationOverwrites", async () => { specialsFilePath: "html/specials.html", soupsFilePath: "html/soups.html", getConceptFilePath: (conceptId: string) => - ["92", "110", "113", "175", "108", "168"].includes(conceptId) // note that location 168 does not have a location overwrite and thus uses the one provided on the page + ["92", "110", "113", "175", "108", "168"].includes(conceptId) ? `html/concepts/${conceptId}.html` : "html/blank.html", serverDate: DateTime.fromObject({ @@ -32,138 +32,7 @@ test("the whole thing, including locationOverwrites", async () => { day: 5, }) as DateTime, }); - const parser = new DiningParser({ - locationCoordinateOverwrites: { - "136": { - lat: 40.44497677044086, - lng: -79.94526274161514, - }, - "134": { - lat: 40.44496677039454, - lng: -79.94543510284873, - }, - "127": { - lat: 40.44296, - lng: -79.941815, - }, - "175": { - lat: 40.44492728615915, - lng: -79.94544766519252, - }, - "185": { - lat: 40.44494190940949, - lng: -79.94547335554091, - }, - "154": { - lat: 40.44493626367144, - lng: -79.94534102575214, - }, - "173": { - lat: 40.44490020783157, - lng: -79.94540910407714, - }, - "192": { - lat: 40.44494958989201, - lng: -79.94541940780638, - }, - "155": { - lat: 40.44269873724883, - lng: -79.94664155857618, - }, - "82": { - lat: 40.44260716039899, - lng: -79.93995513782087, - }, - "98": { - lat: 40.44245836757329, - lng: -79.94002835619484, - }, - "178": { - lat: 40.44265640089745, - lng: -79.94021168805847, - }, - "114": { - lat: 40.44253705946464, - lng: -79.9400539411368, - }, - "95": { - lat: 40.44110394047497, - lng: -79.94341681666283, - }, - "103": { - lat: 40.44294406631636, - lng: -79.9421613966706, - }, - "91": { - lat: 40.44306965961788, - lng: -79.94207835253889, - }, - "174": { - lat: 40.44336219896886, - lng: -79.94196740444092, - }, - "113": { - lat: 40.44392022644891, - lng: -79.94220130436851, - }, - "108": { - lat: 40.44313373573427, - lng: -79.94252833667237, - }, - "138": { - lat: 40.44319390927541, - lng: -79.9420529542194, - }, - "184": { - lat: 40.4432203633545, - lng: -79.94203823851873, - }, - "193": { - lat: 40.44326107687846, - lng: -79.94202626834755, - }, - "88": { - lat: 40.44322866937478, - lng: -79.94252749706692, - }, - "148": { - lat: 40.44618769931711, - lng: -79.95093385349946, - }, - "110": { - lat: 40.44326419955131, - lng: -79.94551330831828, - }, - "84": { - lat: 40.44161664158368, - lng: -79.94285872206078, - }, - "188": { - lat: 40.44538605085632, - lng: -79.9430188095375, - }, - "190": { - lat: 40.44541493684331, - lng: -79.94298366401661, - }, - "115": { - lat: 40.44347617300122, - lng: -79.94480928001676, - }, - "92": { - lat: 40.44144439971656, - lng: -79.94195512801193, - }, - "94": { - lat: 40.44259053115122, - lng: -79.94597744494271, - }, - "180": { - lat: 40.44414285761781, - lng: -79.93888579754876, - }, - }, - }); + const parser = new DiningParser(); const parsedLocationData = await parser.process(); expect(parsedLocationData).toStrictEqual(expectedLocationData); }); @@ -232,7 +101,12 @@ describe("time edge cases", () => { [Thur]: "24 hRs", [Fri]: "24 hours", }); - await queryParserAndAssertTimingsCorrect([[Tue, 0, 0, Fri, 23, 59]]); + await queryParserAndAssertTimingsCorrect([ + [Tue, 0, 0, 23, 59], + [Wed, 0, 0, 23, 59], + [Thur, 0, 0, 23, 59], + [Fri, 0, 0, 23, 59], + ]); }); test("all day every day", async () => { setUpTimingTest({ @@ -244,7 +118,15 @@ describe("time edge cases", () => { [Sat]: "24 hours", [Sun]: "24 hours", }); - await queryParserAndAssertTimingsCorrect([[Sun, 0, 0, Sat, 23, 59]]); // this is the reason why we leave the other return type that represents open every day as this. (backwards compatibility, mostly) + await queryParserAndAssertTimingsCorrect([ + [Mon, 0, 0, 23, 59], + [Tue, 0, 0, 23, 59], + [Wed, 0, 0, 23, 59], + [Thur, 0, 0, 23, 59], + [Fri, 0, 0, 23, 59], + [Sat, 0, 0, 23, 59], + [Sun, 0, 0, 23, 59], + ]); }); test("all day every day but slightly different", async () => { setUpTimingTest({ @@ -256,7 +138,16 @@ describe("time edge cases", () => { [Sat]: "12:00 AM - 2:59 AM, 3:00 AM - 11:59 PM", [Sun]: "24 hours", }); - await queryParserAndAssertTimingsCorrect([[Sun, 0, 0, Sat, 23, 59]]); + await queryParserAndAssertTimingsCorrect([ + [Mon, 0, 0, 23, 59], + [Tue, 0, 0, 23, 59], + [Wed, 0, 0, 23, 59], + [Thur, 0, 0, 23, 59], + [Fri, 0, 0, 23, 59], + [Sat, 0, 0, 2, 59], + [Sat, 3, 0, 23, 59], + [Sun, 0, 0, 23, 59], + ]); }); test("empty string", async () => { setUpTimingTest({ @@ -268,7 +159,10 @@ describe("time edge cases", () => { [Sat]: "", [Sun]: "24 hours", }); - await queryParserAndAssertTimingsCorrect([[Sun, 0, 0, Mon, 23, 59]]); + await queryParserAndAssertTimingsCorrect([ + [Mon, 0, 0, 23, 59], + [Sun, 0, 0, 23, 59], + ]); }); test("loop-back time coalescing (wrapping on saturday, but it overlaps with sunday)", async () => { setUpTimingTest({ @@ -280,7 +174,10 @@ describe("time edge cases", () => { [Sat]: "7:00 AM - 2:00 AM", [Sun]: "1:00 AM - 5:00 PM", // sunday is represented as 0 }); - await queryParserAndAssertTimingsCorrect([[Sat, 7, 0, Sun, 17, 0]]); + await queryParserAndAssertTimingsCorrect([ + [Sat, 7, 0, 2, 0], + [Sun, 1, 0, 17, 0], + ]); }); test("loop-back time coalescing (wrapping on saturday, but it overlaps with multiple ranges on sunday)", async () => { setUpTimingTest({ @@ -290,9 +187,13 @@ describe("time edge cases", () => { [Thur]: "", [Fri]: "", [Sat]: "7:00 AM - 2:00 AM", - [Sun]: "12:00AM - 12:35 AM, 1:00 AM - 5:00 PM", // sunday is represented as 0 + [Sun]: "12:00 AM - 12:35 AM, 1:00 AM - 5:00 PM", // sunday is represented as 0 }); - await queryParserAndAssertTimingsCorrect([[Sat, 7, 0, Sun, 17, 0]]); + await queryParserAndAssertTimingsCorrect([ + [Sat, 7, 0, 2, 0], + [Sun, 0, 0, 0, 35], + [Sun, 1, 0, 17, 0], + ]); }); test("open all week, gone wrong", async () => { setUpTimingTest({ @@ -304,7 +205,16 @@ describe("time edge cases", () => { [Fri]: "OPEN 24 HOURS", [Sat]: "12:00 AM - 10:00 AM, 9:00 AM - 2:00 AM", }); - await queryParserAndAssertTimingsCorrect([[Sun, 0, 0, Sat, 23, 59]]); // this should be the default return value if it's open all week + await queryParserAndAssertTimingsCorrect([ + [Mon, 0, 0, 23, 59], + [Tue, 0, 0, 23, 59], + [Wed, 0, 0, 23, 59], + [Thur, 0, 0, 23, 59], + [Fri, 0, 0, 23, 59], + [Sat, 0, 0, 10, 0], + [Sat, 9, 0, 2, 0], + [Sun, 0, 0, 23, 59], + ]); // this should be the default return value if it's open all week }); test("open all week, gone wrong", async () => { setUpTimingTest({ @@ -316,7 +226,17 @@ describe("time edge cases", () => { [Fri]: "OPEN 24 HOURS", [Sat]: "12:00 AM - 10:00 AM, 9:00 AM - 2:00 AM", }); - await queryParserAndAssertTimingsCorrect([[Sun, 0, 0, Sat, 23, 59]]); // this should be the default return value if it's open all week + await queryParserAndAssertTimingsCorrect([ + [Mon, 0, 0, 23, 59], + [Tue, 0, 0, 23, 59], + [Wed, 0, 0, 23, 59], + [Thur, 0, 0, 23, 59], + [Fri, 0, 0, 23, 59], + [Sat, 0, 0, 10, 0], + [Sat, 9, 0, 2, 0], + [Sun, 0, 0, 0, 5], + [Sun, 0, 10, 23, 59], + ]); // this should be the default return value if it's open all week }); test("wrapping on thursday, but it overlaps with friday", async () => { setUpTimingTest({ @@ -328,7 +248,10 @@ describe("time edge cases", () => { [Sat]: "", [Sun]: "", }); - await queryParserAndAssertTimingsCorrect([[Thur, 7, 0, Fri, 17, 0]]); + await queryParserAndAssertTimingsCorrect([ + [Thur, 7, 0, 2, 0], + [Fri, 1, 0, 17, 0], + ]); }); test("some combination of wrap-around", async () => { setUpTimingTest({ @@ -341,9 +264,10 @@ describe("time edge cases", () => { [Sun]: "open 24 hours", }); await queryParserAndAssertTimingsCorrect([ - [Sun, 0, 0, Sun, 23, 59], - [Wed, 0, 0, Wed, 23, 59], - [Thur, 7, 0, Fri, 17, 0], + [Wed, 0, 0, 23, 59], + [Thur, 7, 0, 2, 0], + [Fri, 1, 0, 17, 0], + [Sun, 0, 0, 23, 59], ]); }); test("open nearly all week, but Dining Services has truly lost it", async () => { @@ -356,7 +280,20 @@ describe("time edge cases", () => { [Fri]: "OPEN 24 HOURS, mooo", [Sat]: "12:00 AM - 10:00 AM, 9:00 AM - 7:05 AM", }); - await queryParserAndAssertTimingsCorrect([[Sun, 9, 0, Sun, 7, 5]]); + await queryParserAndAssertTimingsCorrect([ + [Sun, 0, 0, 0, 5], + [Sun, 9, 0, 23, 59], + [Mon, 0, 0, 3, 5], + [Mon, 3, 0, 2, 0], + [Tue, 1, 0, 21, 0], + [Tue, 21, 1, 23, 59], + [Tue, 0, 0, 15, 0], + [Wed, 0, 0, 23, 59], + [Thur, 0, 0, 23, 59], + [Fri, 0, 0, 23, 59], + [Sat, 0, 0, 10, 0], + [Sat, 9, 0, 7, 5], + ]); }); // tests literally everything test("open nearly all week, but Dining Services has truly lost it", async () => { setUpTimingTest({ @@ -369,8 +306,18 @@ describe("time edge cases", () => { [Sat]: "12:00 AM - 10:00 AM, 9:00 AM - 12:02 AM", }); await queryParserAndAssertTimingsCorrect([ - [Sun, 0, 5, Sun, 0, 10], - [Sun, 9, 0, Sun, 0, 2], + [Sun, 0, 5, 0, 10], + [Sun, 9, 0, 23, 59], + [Mon, 0, 0, 3, 5], + [Mon, 3, 0, 2, 0], + [Tue, 1, 0, 21, 0], + [Tue, 21, 1, 23, 59], + [Tue, 0, 0, 15, 0], + [Wed, 0, 0, 23, 59], + [Thur, 0, 0, 23, 59], + [Fri, 0, 0, 23, 59], + [Sat, 0, 0, 10, 0], + [Sat, 9, 0, 0, 2], ]); }); // tests literally everything test("degenerate open times", async () => { @@ -384,8 +331,8 @@ describe("time edge cases", () => { [Sun]: "", }); await queryParserAndAssertTimingsCorrect([ - [Thur, 2, 0, Thur, 2, 0], - [Fri, 1, 0, Fri, 1, 0], + [Thur, 2, 0, 2, 0], + [Fri, 1, 0, 1, 0], ]); }); test("single time", async () => { @@ -399,20 +346,23 @@ describe("time edge cases", () => { [Sat]: "2:00 PM - 10:00 PM", }); await queryParserAndAssertTimingsCorrect([ - [Sun, 15, 12, Sun, 23, 30], - [Mon, 9, 0, Mon, 17, 0], - [Tue, 10, 0, Tue, 18, 0], - [Wed, 11, 0, Wed, 19, 0], - [Thur, 12, 0, Thur, 20, 0], - [Fri, 13, 0, Fri, 21, 0], - [Sat, 14, 0, Sat, 22, 0], + [Sun, 15, 12, 23, 30], + [Mon, 9, 0, 17, 0], + [Tue, 10, 0, 18, 0], + [Wed, 11, 0, 19, 0], + [Thur, 12, 0, 20, 0], + [Fri, 13, 0, 21, 0], + [Sat, 14, 0, 22, 0], ]); }); test("duplicated time string", async () => { setUpTimingTest({ [Sun]: "9:00 AM - 4:00 PM, 9:00 AM - 4:00 PM", }); - await queryParserAndAssertTimingsCorrect([[Sun, 9, 0, Sun, 16, 0]]); + await queryParserAndAssertTimingsCorrect([ + [Sun, 9, 0, 16, 0], + [Sun, 9, 0, 16, 0], + ]); // we keep all duplicates as-is now }); test("gap between time strings", async () => { @@ -424,13 +374,14 @@ describe("time edge cases", () => { }); await queryParserAndAssertTimingsCorrect([ - [Sun, 11, 0, Sun, 14, 0], - [Sun, 15, 0, Sun, 15, 1], - [Sun, 16, 0, Sun, 21, 0], - [Thur, 7, 0, Thur, 22, 0], - [Fri, 11, 0, Fri, 16, 0], - [Sat, 11, 0, Sat, 14, 0], - [Sat, 16, 0, Sat, 21, 0], + [Sun, 11, 0, 14, 0], + [Sun, 15, 0, 15, 1], + [Sun, 16, 0, 21, 0], + [Thur, 7, 0, 22, 0], + [Fri, 11, 0, 16, 0], + [Fri, 11, 0, 16, 0], + [Sat, 11, 0, 14, 0], + [Sat, 16, 0, 21, 0], ]); }); test("12AM (tests the 12:00 AM -> 11:59 PM shift)", async () => { @@ -441,10 +392,10 @@ describe("time edge cases", () => { [Thur]: "6:00 PM - 12:00 AM", }); await queryParserAndAssertTimingsCorrect([ - [Mon, 0, 0, Mon, 23, 59], - [Tue, 2, 0, Tue, 23, 59], - [Wed, 11, 0, Wed, 23, 59], - [Thur, 18, 0, Thur, 23, 59], + [Mon, 0, 0, 23, 59], + [Tue, 2, 0, 23, 59], + [Wed, 11, 0, 23, 59], + [Thur, 18, 0, 23, 59], ]); }); test("same and different opening and closing times", async () => { @@ -455,10 +406,14 @@ describe("time edge cases", () => { [Sat]: "9:00 AM - 4:00 PM, 8:00 AM - 4:00 PM", }); await queryParserAndAssertTimingsCorrect([ - [Wed, 8, 0, Wed, 16, 0], - [Thur, 8, 0, Thur, 16, 0], - [Fri, 8, 0, Fri, 16, 0], - [Sat, 8, 0, Sat, 16, 0], + [Wed, 8, 0, 16, 0], + [Wed, 8, 0, 14, 0], + [Thur, 8, 0, 16, 0], + [Thur, 8, 0, 14, 0], + [Fri, 8, 0, 16, 0], + [Fri, 9, 0, 16, 0], + [Sat, 9, 0, 16, 0], + [Sat, 8, 0, 16, 0], ]); }); @@ -471,12 +426,17 @@ describe("time edge cases", () => { [Fri]: "7:00 AM - 4:00 PM, 6:00 AM - 2:00 PM, 7:00 PM - 12:00 AM", }); await queryParserAndAssertTimingsCorrect([ - [Mon, 8, 0, Mon, 21, 0], - [Tue, 8, 0, Tue, 21, 0], - [Wed, 8, 0, Wed, 21, 0], - [Thur, 8, 0, Thur, 21, 0], - [Fri, 6, 0, Fri, 16, 0], - [Fri, 19, 0, Fri, 23, 59], + [Mon, 8, 0, 16, 0], + [Mon, 14, 0, 21, 0], + [Tue, 14, 0, 21, 0], + [Tue, 8, 0, 16, 0], + [Wed, 8, 0, 21, 0], + [Wed, 14, 0, 16, 0], + [Thur, 14, 0, 16, 0], + [Thur, 8, 0, 21, 0], + [Fri, 7, 0, 16, 0], + [Fri, 6, 0, 14, 0], + [Fri, 19, 0, 23, 59], ]); }); test("partial all day", async () => { @@ -485,7 +445,11 @@ describe("time edge cases", () => { [Thur]: "open 24 hours", [Fri]: "open 24 hours", }); - await queryParserAndAssertTimingsCorrect([[Wed, 0, 0, Fri, 23, 59]]); + await queryParserAndAssertTimingsCorrect([ + [Wed, 0, 0, 23, 59], + [Thur, 0, 0, 23, 59], + [Fri, 0, 0, 23, 59], + ]); }); test("partial all day, over the weekend", async () => { setUpTimingTest({ @@ -493,7 +457,11 @@ describe("time edge cases", () => { [Sun]: "open 24 hours", [Mon]: "open 24 hours", }); - await queryParserAndAssertTimingsCorrect([[Sat, 0, 0, Mon, 23, 59]]); + await queryParserAndAssertTimingsCorrect([ + [Sat, 0, 0, 23, 59], + [Sun, 0, 0, 23, 59], + [Mon, 0, 0, 23, 59], + ]); }); test("partial all day, over the weekend", async () => { setUpTimingTest({ @@ -501,13 +469,17 @@ describe("time edge cases", () => { [Sun]: "open 24 hours", [Mon]: "open 24 hours", }); - await queryParserAndAssertTimingsCorrect([[Sat, 7, 0, Mon, 23, 59]]); + await queryParserAndAssertTimingsCorrect([ + [Sat, 7, 0, 0, 1], + [Sun, 0, 0, 23, 59], + [Mon, 0, 0, 23, 59], + ]); }); test("another one", async () => { setUpTimingTest({ [Sat]: "7:00 AM - 12:01 AM", }); - await queryParserAndAssertTimingsCorrect([[Sat, 7, 0, Sun, 0, 1]]); + await queryParserAndAssertTimingsCorrect([[Sat, 7, 0, 0, 1]]); }); test("unparseable token", async () => { setUpTimingTest({ @@ -519,212 +491,9 @@ describe("time edge cases", () => { setUpTimingTest({ [Mon]: "OPEN 24 HOURS, 2:00 AM - 3:00 AM", }); - await queryParserAndAssertTimingsCorrect([[Mon, 0, 0, Mon, 23, 59]]); - }); -}); - -describe("test time overwrites", () => { - test("no overwrites, more-so testing setUpArbitraryTest functionality", async () => { - setUpArbitraryTest( - [ - ["Friday", "August 22", "7:00 AM - 9:00 PM"], - ["Saturday", "August 23", "7:00 AM - 9:00 PM"], - ["Sunday", "August 24", "7:00 AM - 9:00 PM"], - ["Monday", "August 25", "7:00 AM - 9:00 PM"], - ["Tuesday", "August 26", "7:00 AM - 9:00 PM"], - ["Wednesday", "August 27", "7:00 AM - 9:00 PM"], - ["Thursday", "August 28", "7:00 AM - 9:00 PM"], - ], - DateTime.fromObject({ year: 2025, month: 8, day: 22 }) - ); - const diningParser = new DiningParser(); - await queryParserAndAssertTimingsCorrect( - [ - [Sun, 7, 0, Sun, 21, 0], - [Mon, 7, 0, Mon, 21, 0], - [Tue, 7, 0, Tue, 21, 0], - [Wed, 7, 0, Wed, 21, 0], - [Thur, 7, 0, Thur, 21, 0], - [Fri, 7, 0, Fri, 21, 0], - [Sat, 7, 0, Sat, 21, 0], - ], - diningParser - ); - }); - test("basic", async () => { - setUpArbitraryTest( - [ - ["Friday", "August 22", "7:00 AM - 9:00 PM"], - ["Saturday", "August 23", "7:00 AM - 9:00 PM"], - ["Sunday", "August 24", "7:00 AM - 9:00 PM"], - ["Monday", "August 25", "7:00 AM - 9:00 PM"], - ["Tuesday", "August 26", "7:00 AM - 9:00 PM"], - ["Wednesday", "August 27", "7:00 AM - 9:00 PM"], - ["Thursday", "August 28", "7:00 AM - 9:00 PM"], - ], - DateTime.fromObject({ year: 2025, month: 8, day: 22 }) - ); - const diningParser = new DiningParser({ - timeSlotOverwrites: { - 113: { - "8/23/2025": ["CLOSED"], - }, - }, - }); - await queryParserAndAssertTimingsCorrect( - [ - [Sun, 7, 0, Sun, 21, 0], - [Mon, 7, 0, Mon, 21, 0], - [Tue, 7, 0, Tue, 21, 0], - [Wed, 7, 0, Wed, 21, 0], - [Thur, 7, 0, Thur, 21, 0], - [Fri, 7, 0, Fri, 21, 0], - ], - diningParser - ); - }); - test("basic", async () => { - setUpArbitraryTest( - [ - ["Friday", "August 22", "7:00 AM - 9:00 PM"], - ["Saturday", "August 23", "7:00 AM - 9:00 PM"], - ["Sunday", "August 24", "7:00 AM - 9:00 PM"], - ["Monday", "August 25", "7:00 AM - 9:00 PM"], - ["Tuesday", "August 26", "7:00 AM - 9:00 PM"], - ["Wednesday", "August 27", "7:00 AM - 9:00 PM"], - ["Thursday", "August 28", "7:00 AM - 9:00 PM"], - ], - DateTime.fromObject({ year: 2025, month: 8, day: 22 }) - ); - const diningParser = new DiningParser({ - timeSlotOverwrites: { - 113: { - "8/23/2025": ["2:00 AM - 3:00 AM"], - }, - }, - }); - await queryParserAndAssertTimingsCorrect( - [ - [Sun, 7, 0, Sun, 21, 0], - [Mon, 7, 0, Mon, 21, 0], - [Tue, 7, 0, Tue, 21, 0], - [Wed, 7, 0, Wed, 21, 0], - [Thur, 7, 0, Thur, 21, 0], - [Fri, 7, 0, Fri, 21, 0], - [Sat, 2, 0, Sat, 3, 0], - ], - diningParser - ); - }); - test("overwrite everything", async () => { - setUpArbitraryTest( - [ - ["Friday", "August 22", "7:00 AM - 9:00 PM"], - ["Saturday", "August 23", "7:00 AM - 9:00 PM"], - ["Sunday", "August 24", "7:00 AM - 9:00 PM"], - ["Monday", "August 25", "7:00 AM - 9:00 PM"], - ["Tuesday", "August 26", "7:00 AM - 9:00 PM"], - ["Wednesday", "August 27", "7:00 AM - 9:00 PM"], - ["Thursday", "August 28", "7:00 AM - 9:00 PM"], - ], - DateTime.fromObject({ year: 2025, month: 8, day: 22 }) - ); - const diningParser = new DiningParser({ - timeSlotOverwrites: { - 113: { - "8/22/2025": ["2:00 AM - 3:00 AM"], - "8/23/2025": ["2:00 AM - 3:00 AM"], - "8/24/2025": ["2:00 AM - 3:00 AM"], - "8/25/2025": ["2:00 AM - 3:00 AM"], - "8/26/2025": ["2:00 AM - 3:00 AM"], - "8/27/2025": ["2:00 AM - 3:00 AM"], - "8/28/2025": ["2:00 AM - 3:00 AM"], - }, - }, - }); - await queryParserAndAssertTimingsCorrect( - [ - [Sun, 2, 0, Sun, 3, 0], - [Mon, 2, 0, Mon, 3, 0], - [Tue, 2, 0, Tue, 3, 0], - [Wed, 2, 0, Wed, 3, 0], - [Thur, 2, 0, Thur, 3, 0], - [Fri, 2, 0, Fri, 3, 0], - [Sat, 2, 0, Sat, 3, 0], - ], - diningParser - ); - }); - test("overwrite with coalescing", async () => { - setUpArbitraryTest( - [ - ["Friday", "August 22", "7:00 AM - 9:00 PM"], - ["Saturday", "August 23", "7:00 AM - 9:00 PM"], - ["Sunday", "August 24", "7:00 AM - 9:00 PM"], - ["Monday", "August 25", "7:00 AM - 9:00 PM"], - ["Tuesday", "August 26", "7:00 AM - 9:00 PM"], - ["Wednesday", "August 27", "7:00 AM - 9:00 PM"], - ["Thursday", "August 28", "7:00 AM - 9:00 PM"], - ], - DateTime.fromObject({ year: 2025, month: 8, day: 22 }) - ); - const diningParser = new DiningParser({ - timeSlotOverwrites: { - 113: { - "8/23/2025": ["11:00 AM - 10:00 AM"], - }, - }, - }); - await queryParserAndAssertTimingsCorrect( - [ - [Mon, 7, 0, Mon, 21, 0], - [Tue, 7, 0, Tue, 21, 0], - [Wed, 7, 0, Wed, 21, 0], - [Thur, 7, 0, Thur, 21, 0], - [Fri, 7, 0, Fri, 21, 0], - [Sat, 11, 0, Sun, 21, 0], - ], - diningParser - ); - }); - test("overwrite everything, into the New Years", async () => { - setUpArbitraryTest( - [ - ["Tuesday", "December 30", "7:00 AM - 9:00 PM"], - ["Wednesday", "December 31", "7:00 AM - 9:00 PM"], - ["Thursday", "January 1", "7:00 AM - 9:00 PM"], - ["Friday", "January 2", "7:00 AM - 9:00 PM"], - ["Saturday", "January 3", "7:00 AM - 9:00 PM"], - ["Sunday", "January 4", "7:00 AM - 9:00 PM"], - ["Monday", "January 5", "7:00 AM - 9:00 PM"], - ], - DateTime.fromObject({ year: 2025, month: 12, day: 30 }) - ); - const diningParser = new DiningParser({ - timeSlotOverwrites: { - 113: { - "8/23/2025": ["2:00 AM - 10:00 AM"], - "12/30/2025": ["2:00 AM - 10:00 AM"], - "12/31/2025": ["2:00 AM - 10:00 AM"], - "1/1/2026": ["2:00 AM - 10:00 AM"], // typing this date just gave me an existential crisis. Thanks a lot. - "1/2/2026": ["2:00 AM - 10:00 AM"], - "1/3/2026": ["2:00 AM - 10:00 AM"], - "1/4/2026": ["2:00 AM - 10:00 AM"], - "1/5/2026": ["2:00 AM - 10:00 AM"], - }, - }, - }); - await queryParserAndAssertTimingsCorrect( - [ - [Sun, 2, 0, Sun, 10, 0], - [Mon, 2, 0, Mon, 10, 0], - [Tue, 2, 0, Tue, 10, 0], - [Wed, 2, 0, Wed, 10, 0], - [Thur, 2, 0, Thur, 10, 0], - [Fri, 2, 0, Fri, 10, 0], - [Sat, 2, 0, Sat, 10, 0], - ], - diningParser - ); + await queryParserAndAssertTimingsCorrect([ + [Mon, 0, 0, 23, 59], + [Mon, 2, 0, 3, 0], + ]); }); }); diff --git a/tests/mockTimings.ts b/tests/mockTimings.ts index fcdeca7..4fb5627 100644 --- a/tests/mockTimings.ts +++ b/tests/mockTimings.ts @@ -2,24 +2,22 @@ import { DateTime } from "luxon"; import DiningParser from "../src/parser/diningParser"; import { mockAxiosGETMethod } from "./mockAxios"; import { getFileContent } from "./utils"; +import { IFullTimeRange } from "types"; -enum DayOfTheWeek { - SUNDAY = "SUNDAY", - MONDAY = "MONDAY", - TUESDAY = "TUESDAY", - WEDNESDAY = "WEDNESDAY", - THURSDAY = "THURSDAY", - FRIDAY = "FRIDAY", - SATURDAY = "SATURDAY", -} -export const Mon = DayOfTheWeek.MONDAY, - Tue = DayOfTheWeek.TUESDAY, - Wed = DayOfTheWeek.WEDNESDAY, - Thur = DayOfTheWeek.THURSDAY, - Fri = DayOfTheWeek.FRIDAY, - Sat = DayOfTheWeek.SATURDAY, - Sun = DayOfTheWeek.SUNDAY; +export const Mon = 1, + Tue = 2, + Wed = 3, + Thur = 4, + Fri = 5, + Sat = 6, + Sun = 0; +function _sort(a: IFullTimeRange, b: IFullTimeRange) { + if (a.day !== b.day) return a.day - b.day; + if (a.startMinutesFromMidnight !== b.startMinutesFromMidnight) + return a.startMinutesFromMidnight - b.startMinutesFromMidnight; + return a.endMinutesFromMidnight - b.endMinutesFromMidnight; +} /** * * @param times [startDay,startHour,startMinute,endDay,endHour,endMinute][] (order matters for input! Sunday comes first) @@ -27,46 +25,29 @@ export const Mon = DayOfTheWeek.MONDAY, * The only reason start and end are bundled into one array is because prettier will autoformat it to two lines otherwise */ export async function queryParserAndAssertTimingsCorrect( - times: [DayOfTheWeek, number, number, DayOfTheWeek, number, number][], + times: [number, number, number, number, number][], parser?: DiningParser ) { const result = await (parser ?? new DiningParser()).process(); + const rootDay = DateTime.fromObject({ year: 2024, month: 9, day: 9 }); expect(result.length).toBe(1); - expect(result[0].times).toStrictEqual( - times.map((time) => { - return { - start: { - day: mapDayOfWeekToAPIReturnValue(time[0]), - hour: time[1], - minute: time[2], - }, - end: { - day: mapDayOfWeekToAPIReturnValue(time[3]), - hour: time[4], - minute: time[5], - }, - }; - }) + expect(result[0]!.times.sort(_sort)).toStrictEqual( + times + .map((time) => { + const day = rootDay.plus({ days: (time[0] - Mon + 7) % 7 }); + return { + year: day.year, + month: day.month, + day: day.day, + startMinutesFromMidnight: time[1] * 60 + time[2], + endMinutesFromMidnight: time[3] * 60 + time[4], + }; + }) + .sort(_sort) ); } -function mapDayOfWeekToAPIReturnValue(day: DayOfTheWeek) { - return ( - { - [Sun]: 0, - [Mon]: 1, - [Tue]: 2, - [Wed]: 3, - [Thur]: 4, - [Fri]: 5, - [Sat]: 6, - } satisfies Record - )[day]; -} - -export function setUpTimingTest( - timeRows: Partial> -) { +export function setUpTimingTest(timeRows: Record) { const fillInHTMLWithTimes = (html: string) => { html = html .replace("[MONDAY]", timeRows[Mon] ?? "CLOSED") @@ -118,7 +99,7 @@ export function setUpArbitraryTest( for (let i = 0; i < 7; i++) { for (let j = 0; j < 3; j++) { const square = `[${String.fromCharCode(A_CHAR_CODE + i)}${j + 1}]`; - html = html.replace(square, data[i][j]); + html = html.replace(square, data[i]![j]!); } } return html; From c8d772241c55afa1df0409f20a0c8d0a832cf7ef Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Sun, 9 Nov 2025 11:02:27 -0500 Subject: [PATCH 35/65] style: add empty openapi endpoint and pretty-print all json --- package.json | 1 + pnpm-lock.yaml | 12 +++++++++++ src/db/getLocations.ts | 9 +++++++-- src/db/overrides.ts | 45 ------------------------------------------ src/server.ts | 17 ++++++++++------ 5 files changed, 31 insertions(+), 53 deletions(-) diff --git a/package.json b/package.json index 0cd79dc..c063492 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "dependencies": { "@elysiajs/cors": "1.3.3", "@elysiajs/node": "1.3.0", + "@elysiajs/openapi": "^1.4.11", "@types/express": "^5.0.3", "axios": "^1.10.0", "cheerio": "1.0.0-rc.12", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 21a24e6..3f7e07e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ importers: '@elysiajs/node': specifier: 1.3.0 version: 1.3.0(elysia@1.3.5(exact-mirror@0.2.2(@sinclair/typebox@0.34.41))(file-type@21.0.0)(typescript@5.9.2))(hono@4.9.8) + '@elysiajs/openapi': + specifier: ^1.4.11 + version: 1.4.11(elysia@1.3.5(exact-mirror@0.2.2(@sinclair/typebox@0.34.41))(file-type@21.0.0)(typescript@5.9.2)) '@types/express': specifier: ^5.0.3 version: 5.0.3 @@ -709,6 +712,11 @@ packages: peerDependencies: elysia: '>= 1.3.3' + '@elysiajs/openapi@1.4.11': + resolution: {integrity: sha512-d75bMxYJpN6qSDi/z9L1S7SLk1S/8Px+cTb3W2lrYzU8uQ5E0kXdy1oOMJEfTyVsz3OA19NP9KNxE7ztSbLBLg==} + peerDependencies: + elysia: '>= 1.4.0' + '@esbuild-kit/core-utils@3.3.2': resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==} deprecated: 'Merged into tsx: https://tsx.is' @@ -3632,6 +3640,10 @@ snapshots: transitivePeerDependencies: - hono + '@elysiajs/openapi@1.4.11(elysia@1.3.5(exact-mirror@0.2.2(@sinclair/typebox@0.34.41))(file-type@21.0.0)(typescript@5.9.2))': + dependencies: + elysia: 1.3.5(exact-mirror@0.2.2(@sinclair/typebox@0.34.41))(file-type@21.0.0)(typescript@5.9.2) + '@esbuild-kit/core-utils@3.3.2': dependencies: esbuild: 0.18.20 diff --git a/src/db/getLocations.ts b/src/db/getLocations.ts index 15c20a0..da5ffd0 100644 --- a/src/db/getLocations.ts +++ b/src/db/getLocations.ts @@ -74,12 +74,17 @@ export async function getAllLocations() { // get general location override data const overrides = (await db.select().from(overwritesTable)).reduce< - Record> + Record< + string, + Omit, "locationId"> // exclude locationId field, since that's our internal id + > >( (acc, overwrite) => ({ ...acc, [overwrite.locationId]: Object.fromEntries( - Object.entries(overwrite).filter(([_, v]) => v !== null) + Object.entries(overwrite).filter(([key, v]) => { + return key !== "locationId" && v !== null; + }) ) as RequiredProperty, }), {} diff --git a/src/db/overrides.ts b/src/db/overrides.ts index 04da531..00b0dbf 100644 --- a/src/db/overrides.ts +++ b/src/db/overrides.ts @@ -10,51 +10,6 @@ import { import { DateTime } from "luxon"; import { IParsedTimeRange } from "containers/time/parsedTime"; -/** - * - * @returns object that maps ids to overrides, where each value in the override map is guaranteed to be non-null. (important!!) - */ -export async function getGeneralOverrides() { - const overrides = await db - .select() - .from(overwritesTable) - .catch((e) => { - notifySlack(` Failed to fetch overwrites with error ${e}`); - return []; - }); - - const idToOverrideMap = overrides.reduce<{ - [conceptId in string]?: Partial; - }>((accumulator, curLocation) => { - const reformattedObjWithNonNullFields: Partial = { - ...(curLocation.acceptsOnlineOrders !== null && { - acceptsOnlineOrders: curLocation.acceptsOnlineOrders, - }), - ...(curLocation.description !== null && { - description: curLocation.description, - }), - ...(curLocation.location !== null && { location: curLocation.location }), - ...(curLocation.menu !== null && { menu: curLocation.menu }), - ...(curLocation.name !== null && { name: curLocation.name }), - ...(curLocation.shortDescription !== null && { - shortDescription: curLocation.shortDescription, - }), - ...(curLocation.url !== null && { url: curLocation.url }), - ...(curLocation.coordinateLat !== null && - curLocation.coordinateLng !== null && { - coordinates: { - lat: curLocation.coordinateLat, - lng: curLocation.coordinateLng, - }, - }), - }; - return { - ...accumulator, - [curLocation.locationId]: reformattedObjWithNonNullFields, - }; - }, {}); - return idToOverrideMap; -} /** * * @param earliestDate should be in SQL form YYYY-MM-DD diff --git a/src/server.ts b/src/server.ts index 59533f9..4d79d22 100644 --- a/src/server.ts +++ b/src/server.ts @@ -6,12 +6,12 @@ import { env } from "env"; import { notifySlack } from "utils/slack"; import { node } from "@elysiajs/node"; import ScrapeResultMerger from "utils/locationMerger"; -import { getGeneralOverrides } from "db/overrides"; import { addLocationDataToDb } from "db/updateLocation"; import { deprecatedNotice } from "deprecationNotice"; import { getDiffsBetweenLocationData } from "utils/diff"; import { getEmails } from "db/emails"; import { getAllLocations } from "db/getLocations"; +import { openapi } from "@elysiajs/openapi"; /** only used for Slack debug diff logging */ let cachedLocations: ILocation[] = []; @@ -54,7 +54,7 @@ async function reload(): Promise { } } -export const app = new Elysia({ adapter: node() }); // I don't trust bun (as a runtime) enough (Eric Xu - 7/18/2025). This may change in the future, but bun is currently NOT a full drop-in replacement for node and is still rather unstable from personal experience +export const app = new Elysia({ adapter: node() }).use(openapi()); // I don't trust bun (as a runtime) enough (Eric Xu - 7/18/2025). This may change in the future, but bun is currently NOT a full drop-in replacement for node and is still rather unstable from personal experience app.onError(({ error, path, code }) => { if (code === "NOT_FOUND") { @@ -68,15 +68,20 @@ app.onError(({ error, path, code }) => { } }); app.use(cors()); +app.onAfterHandle(({ response }) => { + if (typeof response === "object") { + // pretty print this + return new Response(JSON.stringify(response, null, 4), { + headers: { "Content-Type": "application/json; charset=utf-8" }, + }); // we can actually set proper content-type headers this way + } +}); app.get("/", () => { return "ScottyLabs Dining API"; }); -app.get("/api/v2/locations", async () => - JSON.stringify(await getAllLocations(), null, 4) -); +app.get("/api/v2/locations", getAllLocations); app.get("/api/emails", getEmails); -app.get("/api/changes", async () => await getGeneralOverrides()); app.post( "/api/sendSlackMessage", From 7f290e22a2f53505a30eb850b50754fc7ab7c5c4 Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Sun, 9 Nov 2025 12:35:10 -0500 Subject: [PATCH 36/65] test: fix all tests --- src/containers/locationBuilder.ts | 5 +- src/containers/time/parsedTime.ts | 1 + src/containers/timeBuilder.ts | 2 - src/db/dbQueryUtils.ts | 161 ++++++++ src/db/emails.ts | 17 - src/db/getLocations.ts | 173 +++------ src/db/overrides.ts | 38 -- src/db/schema.ts | 15 + src/db/updateLocation.ts | 60 ++- src/server.ts | 2 +- src/types.ts | 4 +- src/utils/timeUtils.ts | 19 + tests/expectedData.ts | 598 ++++++++++++++++-------------- 13 files changed, 617 insertions(+), 478 deletions(-) create mode 100644 src/db/dbQueryUtils.ts delete mode 100644 src/db/emails.ts delete mode 100644 src/db/overrides.ts diff --git a/src/containers/locationBuilder.ts b/src/containers/locationBuilder.ts index 3da0ef2..ee47214 100644 --- a/src/containers/locationBuilder.ts +++ b/src/containers/locationBuilder.ts @@ -110,8 +110,7 @@ export default class LocationBuilder { this.url === undefined || this.location === undefined || this.conceptId === undefined || - this.name === undefined || - this.earliestDayFound === undefined + this.name === undefined ) { throw Error( "Didn't finish configuring location before building metadata!" @@ -130,7 +129,7 @@ export default class LocationBuilder { coordinates: this.coordinates, acceptsOnlineOrders: this.acceptsOnlineOrders, times: this.times, - earliestDayFound: this.earliestDayFound, + earliestDayToOverride: this.earliestDayFound, todaysSpecials: this.specials, todaysSoups: this.soups, }; diff --git a/src/containers/time/parsedTime.ts b/src/containers/time/parsedTime.ts index 0ed05c7..d7389de 100644 --- a/src/containers/time/parsedTime.ts +++ b/src/containers/time/parsedTime.ts @@ -5,6 +5,7 @@ interface Time { minute: number; } +/** what you get after parsing a time string like `2:00 AM - 3:00 AM, 4:00 PM - 2:00 AM` (do note that end can be < start) */ export interface IParsedTimeRange { start: Time; end: Time; diff --git a/src/containers/timeBuilder.ts b/src/containers/timeBuilder.ts index fc1408e..57489ff 100644 --- a/src/containers/timeBuilder.ts +++ b/src/containers/timeBuilder.ts @@ -75,8 +75,6 @@ export function getAllTimeSlotsFromSchedule( ); } } - if (firstDay === undefined) - throw new Error(`None of the rows in ${schedule} were parsable!`); return { times: allTimeSlots, earliestDay: firstDay }; } /** diff --git a/src/db/dbQueryUtils.ts b/src/db/dbQueryUtils.ts new file mode 100644 index 0000000..8d6c728 --- /dev/null +++ b/src/db/dbQueryUtils.ts @@ -0,0 +1,161 @@ +import { + conceptIdToInternalIdTable, + emailTable, + locationDataTable, + overwritesTable, + specialsTable, + timeOverwritesTable, + timesTable, +} from "./schema"; + +import { db } from "./db"; +import { notifySlack } from "utils/slack"; +import { eq, gte } from "drizzle-orm"; +import { + augmentAndEditTimeRangesWithDateInfo, + parseTimeSlots, +} from "containers/timeBuilder"; +import { IParsedTimeRange } from "containers/time/parsedTime"; + +/** More-so the database representation of a time range */ +export interface ITimeRangeInternal { + date: string; + startMinutesSinceMidnight: number; + endMinutesSinceMidnight: number; +} +export async function getSpecials(todayAsSQLString: string) { + const data = await db + .select() + .from(specialsTable) + .where(eq(specialsTable.date, todayAsSQLString)); + return data.reduce< + Record< + string, + { + specials?: { name: string; description: string }[]; + soups?: { name: string; description: string }[]; + } + > + >((acc, special) => { + if (acc[special.locationId] === undefined) acc[special.locationId] = {}; + if (special.type === "special") { + acc[special.locationId]!.specials = [ + ...(acc[special.locationId]!.specials ?? []), + { + name: special.name, + description: special.description, + }, + ]; + } else { + acc[special.locationId]!.soups = [ + ...(acc[special.locationId]!.soups ?? []), + { + name: special.name, + description: special.description, + }, + ]; + } + return acc; + }, {}); +} +/** Fetches non-overridden location data + open times */ +export async function getLocationIdToDataMap(timeSearchCutoffStr: string) { + const locationData = await db + .select() + .from(locationDataTable) + .leftJoin( + conceptIdToInternalIdTable, + eq(locationDataTable.id, conceptIdToInternalIdTable.internalId) + ) + .leftJoin(timesTable, eq(locationDataTable.id, timesTable.locationId)) + .where(gte(timesTable.date, timeSearchCutoffStr)); + + return locationData.reduce< + Record< + string, + | Omit & { + id: number; + } & { + times: ITimeRangeInternal[]; + } + > + >((acc, { location_data, location_times, concept_id_to_internal_id }) => { + if (!acc[location_data.id]) { + acc[location_data.id] = { + ...location_data, + id: parseInt(concept_id_to_internal_id?.externalId ?? "-1"), + times: [], + }; + } + if (location_times !== null) { + acc[location_data.id]!.times.push({ + startMinutesSinceMidnight: location_times.startTime, + endMinutesSinceMidnight: location_times.endTime, + date: location_times.date, + }); + } + return acc; + }, {}); +} +type RequiredProperty = { [P in keyof T]: NonNullable }; + +export async function getGeneralOverrides() { + return (await db.select().from(overwritesTable)).reduce< + Record< + string, + Omit, "locationId"> // exclude locationId field, since that's our internal id + > + >( + (acc, overwrite) => ({ + ...acc, + [overwrite.locationId]: Object.fromEntries( + Object.entries(overwrite).filter(([key, v]) => { + return key !== "locationId" && v !== null; + }) + ) as RequiredProperty, + }), + {} + ); +} + +/** + * + * @param earliestDate should be in SQL form YYYY-MM-DD + */ +export async function getTimeOverrides(earliestDate: string) { + const timeOverrides = await db + .select() + .from(timeOverwritesTable) + .where(gte(timeOverwritesTable.date, earliestDate)) + .catch((e) => { + notifySlack(` Failed to fetch time overwrites with error ${e}`); + return []; + }); + const idToTimeOverrides = timeOverrides.reduce<{ + [locationId in string]: { [date in string]: IParsedTimeRange[] }; + }>((acc, override) => { + return { + ...acc, + [override.locationId]: { + ...acc[override.locationId], + [override.date]: parseTimeSlots(override.timeString), + }, + }; + }, {}); + return idToTimeOverrides; +} + +export async function getEmails(): Promise<{ name: string; email: string }[]> { + const result = await db + .select({ + name: emailTable.name, + email: emailTable.email, + }) + .from(emailTable); + + // Remove 'mailto:' if present + return result.map((row) => ({ + name: row.name, + email: row.email.replace(/^mailto:/, ""), + })); +} diff --git a/src/db/emails.ts b/src/db/emails.ts deleted file mode 100644 index 7cfe169..0000000 --- a/src/db/emails.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { db } from "./db"; -import { emailTable } from "./schema"; - -export async function getEmails(): Promise<{ name: string; email: string }[]> { - const result = await db - .select({ - name: emailTable.name, - email: emailTable.email, - }) - .from(emailTable); - - // Remove 'mailto:' if present - return result.map((row) => ({ - name: row.name, - email: row.email.replace(/^mailto:/, ""), - })); -} diff --git a/src/db/getLocations.ts b/src/db/getLocations.ts index da5ffd0..557ab36 100644 --- a/src/db/getLocations.ts +++ b/src/db/getLocations.ts @@ -1,135 +1,66 @@ -import { db } from "./db"; import { - conceptIdToInternalIdTable, - locationDataTable, - overwritesTable, - timesTable, -} from "./schema"; -import { eq, gte } from "drizzle-orm"; -import { getTimeOverrides } from "./overrides"; + getGeneralOverrides, + getLocationIdToDataMap, + getSpecials, + getTimeOverrides, + ITimeRangeInternal, +} from "./dbQueryUtils"; import { DateTime } from "luxon"; -import { mergeTimeRanges, pad } from "utils/timeUtils"; -type RequiredProperty = { [P in keyof T]: NonNullable }; -interface ITimeRangeInternal { - date: string; - startMinutesSinceMidnight: number; - endMinutesSinceMidnight: number; -} -function mergeTimeIntervals(timeRanges: ITimeRangeInternal[]) { - const timeStampedIntervals = timeRanges.map((rng) => { - const date = DateTime.fromISO(rng.date, { zone: "America/New_York" }); - return { - start: date.toMillis() + rng.startMinutesSinceMidnight * 1000 * 60, - end: - date.toMillis() + - rng.endMinutesSinceMidnight * 1000 * 60 + - (rng.endMinutesSinceMidnight < rng.startMinutesSinceMidnight - ? 24 * 60 * 60 * 1000 - : 0), // this specific slot wraps around - so we'll add a day to the end - }; - }); - return mergeTimeRanges(timeStampedIntervals, 60 * 1000); // merge gaps of at most 1 min -} +import { pad, remapAndMergeTimeIntervals } from "utils/timeUtils"; +import { IParsedTimeRange } from "containers/time/parsedTime"; + export async function getAllLocations() { - const timeSearchCutoff = DateTime.now().minus({ days: 1 }); // 1 days worth of data before today + const today = DateTime.now(); + const timeSearchCutoff = today.minus({ days: 1 }); // 1 days worth of data before today const timeSearchCutoffStr = `${timeSearchCutoff.year}-${pad( timeSearchCutoff.month )}-${pad(timeSearchCutoff.day)}`; - const locationData = await db - .select() - .from(locationDataTable) - .leftJoin( - conceptIdToInternalIdTable, - eq(locationDataTable.id, conceptIdToInternalIdTable.internalId) - ) - .leftJoin(timesTable, eq(locationDataTable.id, timesTable.locationId)) - .where(gte(timesTable.date, timeSearchCutoffStr)); - const locationIdToData = locationData.reduce< - Record< - string, - | Omit & { - id: number; - } & { - times: ITimeRangeInternal[]; - } - > - >((acc, { location_data, location_times, concept_id_to_internal_id }) => { - if (!acc[location_data.id]) { - acc[location_data.id] = { - ...location_data, - id: parseInt(concept_id_to_internal_id?.externalId ?? "-1"), - times: [], + const locationIdToData = await getLocationIdToDataMap(timeSearchCutoffStr); + const specials = await getSpecials( + `${today.year}/${pad(today.month)}/${pad(today.day)}` + ); + const generalOverrides = await getGeneralOverrides(); + const timeOverrides = await getTimeOverrides(timeSearchCutoffStr); + + // apply overrides, merge all time intervals, and add specials + const finalLocationData = Object.entries(locationIdToData).map( + ([id, data]) => { + return { + ...data, + ...generalOverrides[id], // this line only works because the override table has the same columns as the normal table + times: remapAndMergeTimeIntervals( + applyTimeOverrides(data.times, timeOverrides[id]) + ), + // .map( + // (time) => + // `${new Date(time.start).toLocaleString()}-${new Date( + // time.end + // ).toLocaleString()}` + // ), + todaysSoups: specials[id]?.soups, + todaysSpecials: specials[id]?.specials, }; } - if (location_times !== null) { - acc[location_data.id]!.times.push({ - startMinutesSinceMidnight: location_times.startTime, - endMinutesSinceMidnight: location_times.endTime, - date: location_times.date, - }); - } - return acc; - }, {}); - - // get general location override data - const overrides = (await db.select().from(overwritesTable)).reduce< - Record< - string, - Omit, "locationId"> // exclude locationId field, since that's our internal id - > - >( - (acc, overwrite) => ({ - ...acc, - [overwrite.locationId]: Object.fromEntries( - Object.entries(overwrite).filter(([key, v]) => { - return key !== "locationId" && v !== null; - }) - ) as RequiredProperty, - }), - {} ); - const generallyOverriddenLocations = Object.fromEntries( - Object.entries(locationIdToData).map(([id, data]) => [ - id, - { ...data, ...overrides[id] }, - ]) - ); // extra locationId property here, whatever + return finalLocationData; +} - // get time override data, apply each override - const timeOverrides = await getTimeOverrides(timeSearchCutoffStr); - for (const [id, timeOverrideMap] of Object.entries(timeOverrides)) { - if (generallyOverriddenLocations[id] === undefined) continue; - let locationTimes = generallyOverriddenLocations[id].times; - for (const [overrideDate, timeRanges] of Object.entries(timeOverrideMap)) { - locationTimes = locationTimes.filter( - (time) => time.date !== overrideDate - ); +function applyTimeOverrides( + originalTimes: ITimeRangeInternal[], + overrideTimes?: { [date in string]: IParsedTimeRange[] } +) { + if (overrideTimes === undefined) return originalTimes; + for (const [overrideDate, timeRanges] of Object.entries(overrideTimes)) { + originalTimes = originalTimes.filter((time) => time.date !== overrideDate); - locationTimes.push( - ...timeRanges.map((rng) => ({ - date: overrideDate, - startMinutesSinceMidnight: rng.start.hour * 60 + rng.start.minute, - endMinutesSinceMidnight: rng.end.hour * 60 + rng.end.minute, - })) - ); - } - generallyOverriddenLocations[id].times = locationTimes; + originalTimes.push( + ...timeRanges.map((rng) => ({ + date: overrideDate, + startMinutesSinceMidnight: rng.start.hour * 60 + rng.start.minute, + endMinutesSinceMidnight: rng.end.hour * 60 + rng.end.minute, + })) + ); } - // merge all time intervals into usable format - const locationsWithMergedTimes = Object.entries( - generallyOverriddenLocations - ).map(([id, data]) => { - return { - ...data, - times: mergeTimeIntervals(data.times), - // .map( - // (time) => - // `${new Date(time.start).toLocaleString()}-${new Date( - // time.end - // ).toLocaleString()}` - // ), - }; - }); - return locationsWithMergedTimes; + return originalTimes; } diff --git a/src/db/overrides.ts b/src/db/overrides.ts deleted file mode 100644 index 00b0dbf..0000000 --- a/src/db/overrides.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { overwritesTable, timeOverwritesTable } from "./schema"; -import { IFullTimeRange, ILocation } from "types"; -import { db } from "./db"; -import { notifySlack } from "utils/slack"; -import { gte } from "drizzle-orm"; -import { - augmentAndEditTimeRangesWithDateInfo, - parseTimeSlots, -} from "containers/timeBuilder"; -import { DateTime } from "luxon"; -import { IParsedTimeRange } from "containers/time/parsedTime"; - -/** - * - * @param earliestDate should be in SQL form YYYY-MM-DD - */ -export async function getTimeOverrides(earliestDate: string) { - const timeOverrides = await db - .select() - .from(timeOverwritesTable) - .where(gte(timeOverwritesTable.date, earliestDate)) - .catch((e) => { - notifySlack(` Failed to fetch time overwrites with error ${e}`); - return []; - }); - const idToTimeOverrides = timeOverrides.reduce<{ - [locationId in string]: { [date in string]: IParsedTimeRange[] }; - }>((acc, override) => { - return { - ...acc, - [override.locationId]: { - ...acc[override.locationId], - [override.date]: parseTimeSlots(override.timeString), - }, - }; - }, {}); - return idToTimeOverrides; -} diff --git a/src/db/schema.ts b/src/db/schema.ts index ac62ccc..81e953b 100644 --- a/src/db/schema.ts +++ b/src/db/schema.ts @@ -8,6 +8,7 @@ import { date, primaryKey, index, + pgEnum, } from "drizzle-orm/pg-core"; import { IFullTimeRange } from "../types"; @@ -84,3 +85,17 @@ export const timeOverwritesTable = pgTable( }, (table) => [primaryKey({ columns: [table.locationId, table.date] })] ); +export const specialType = pgEnum("specialType", ["special", "soup"]); + +export const specialsTable = pgTable("specials", { + id: integer().notNull().generatedAlwaysAsIdentity().primaryKey(), + locationId: text("location_id") + .references(() => locationDataTable.id, { + onDelete: "cascade", + }) + .notNull(), + name: text().notNull(), + description: text().notNull(), + date: date().notNull(), + type: specialType().notNull(), +}); diff --git a/src/db/updateLocation.ts b/src/db/updateLocation.ts index 9bb81f9..3c47696 100644 --- a/src/db/updateLocation.ts +++ b/src/db/updateLocation.ts @@ -3,6 +3,7 @@ import { db } from "./db"; import { conceptIdToInternalIdTable, locationDataTable, + specialsTable, timesTable, } from "./schema"; import { and, eq, gte, sql } from "drizzle-orm"; @@ -18,6 +19,7 @@ async function getInternalId(externalId: string) { export async function addLocationDataToDb(location: ILocation) { const internalId = await getInternalId(location.conceptId.toString()); + const locationDbEntry: typeof locationDataTable.$inferInsert = { id: internalId, name: location.name, @@ -36,22 +38,50 @@ export async function addLocationDataToDb(location: ILocation) { .values(locationDbEntry) .onConflictDoUpdate({ target: locationDataTable.id, set: locationDbEntry }); - // remove rows from whenever the scrape started from (aka remove entries corresponding to the last 7 days) - await db - .delete(timesTable) - .where( - and( - eq(timesTable.locationId, internalId), + if (location.earliestDayToOverride !== undefined) { + const earliestDaySQLString = `${location.earliestDayToOverride.year}-${pad( + location.earliestDayToOverride.month + )}-${pad(location.earliestDayToOverride.day)})`; + // add specials + await db + .delete(locationDataTable) + .where( and( - gte( - timesTable.date, - `${location.earliestDayFound.year}-${pad( - location.earliestDayFound.month - )}-${pad(location.earliestDayFound.day)})` - ) + eq(specialsTable.locationId, internalId), + eq(specialsTable.date, earliestDaySQLString) ) - ) - ); + ); + const specials = [ + ...(location.todaysSpecials?.map((sp) => ({ + ...sp, + type: "special" as const, + })) ?? []), + ...(location.todaysSoups?.map((sp) => ({ + ...sp, + type: "soup" as const, + })) ?? []), + ]; + if (specials.length) + await db.insert(specialsTable).values( + specials.map((special) => ({ + date: earliestDaySQLString, + locationId: internalId, + name: special.title, + description: special.description, + type: special.type, + })) + ); + + // remove rows from whenever the scrape started from (aka remove entries corresponding to the last 7 days) + await db + .delete(timesTable) + .where( + and( + eq(timesTable.locationId, internalId), + and(gte(timesTable.date, earliestDaySQLString)) + ) + ); + } if (location.times.length) { await db.insert(timesTable).values( location.times.map((time) => ({ @@ -63,7 +93,7 @@ export async function addLocationDataToDb(location: ILocation) { ); } - // in case this entry isn't there + // in case the conceptId->internalId mapping entry isn't there await db .insert(conceptIdToInternalIdTable) .values({ diff --git a/src/server.ts b/src/server.ts index 4d79d22..0c9ab00 100644 --- a/src/server.ts +++ b/src/server.ts @@ -9,7 +9,7 @@ import ScrapeResultMerger from "utils/locationMerger"; import { addLocationDataToDb } from "db/updateLocation"; import { deprecatedNotice } from "deprecationNotice"; import { getDiffsBetweenLocationData } from "utils/diff"; -import { getEmails } from "db/emails"; +import { getEmails } from "db/dbQueryUtils"; import { getAllLocations } from "db/getLocations"; import { openapi } from "@elysiajs/openapi"; diff --git a/src/types.ts b/src/types.ts index 9d17a6f..002ddfe 100644 --- a/src/types.ts +++ b/src/types.ts @@ -17,8 +17,8 @@ export interface ILocation { coordinates?: ICoordinate; acceptsOnlineOrders: boolean; times: IFullTimeRange[]; - /** useful when figuring out which db entries to overwrite */ - earliestDayFound: IDate; + /** useful when figuring out which db time entries to overwrite. Can be undefined if no time data was properly scraped */ + earliestDayToOverride?: IDate; todaysSpecials?: ISpecial[]; todaysSoups?: ISpecial[]; } diff --git a/src/utils/timeUtils.ts b/src/utils/timeUtils.ts index 8fa459a..a48d747 100644 --- a/src/utils/timeUtils.ts +++ b/src/utils/timeUtils.ts @@ -1,3 +1,6 @@ +import { ITimeRangeInternal } from "db/dbQueryUtils"; +import { DateTime } from "luxon"; + /** * * @param timeRanges does not need to be sorted @@ -27,3 +30,19 @@ export function mergeTimeRanges( export function pad(n: number) { return n.toString().padStart(2, "0"); } +/** Wraps time intervals to the next day if end < start, and then merges everything */ +export function remapAndMergeTimeIntervals(timeRanges: ITimeRangeInternal[]) { + const timeStampedIntervals = timeRanges.map((rng) => { + const date = DateTime.fromISO(rng.date, { zone: "America/New_York" }); + return { + start: date.toMillis() + rng.startMinutesSinceMidnight * 1000 * 60, + end: + date.toMillis() + + rng.endMinutesSinceMidnight * 1000 * 60 + + (rng.endMinutesSinceMidnight < rng.startMinutesSinceMidnight + ? 24 * 60 * 60 * 1000 + : 0), // this specific slot wraps around - so we'll add a day to the end + }; + }); + return mergeTimeRanges(timeStampedIntervals, 60 * 1000); // merge gaps of at most 1 min +} \ No newline at end of file diff --git a/tests/expectedData.ts b/tests/expectedData.ts index 5a9d02f..7281765 100644 --- a/tests/expectedData.ts +++ b/tests/expectedData.ts @@ -9,38 +9,67 @@ export const expectedLocationData = [ url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/113", location: "Cohon Center, Second floor", menu: "https://web.archive.org/web/20230806004812/https://apps.studentaffairs.cmu.edu/dining/dashboard_images/Production/menus/113/abp-menu6.pdf", - coordinates: { lat: 40.44, lng: -79.94 }, + coordinates: { + lat: 40.44, + lng: -79.94, + }, acceptsOnlineOrders: true, times: [ { - start: { day: 0, hour: 16, minute: 30 }, - end: { day: 0, hour: 20, minute: 0 }, + year: 2024, + month: 8, + day: 5, + startMinutesFromMidnight: 990, + endMinutesFromMidnight: 1200, }, { - start: { day: 1, hour: 8, minute: 0 }, - end: { day: 1, hour: 20, minute: 0 }, + year: 2024, + month: 8, + day: 6, + startMinutesFromMidnight: 990, + endMinutesFromMidnight: 1200, }, { - start: { day: 2, hour: 8, minute: 0 }, - end: { day: 2, hour: 20, minute: 0 }, + year: 2024, + month: 8, + day: 7, + startMinutesFromMidnight: 480, + endMinutesFromMidnight: 1200, }, { - start: { day: 3, hour: 8, minute: 0 }, - end: { day: 3, hour: 20, minute: 0 }, + year: 2024, + month: 8, + day: 8, + startMinutesFromMidnight: 480, + endMinutesFromMidnight: 1200, }, { - start: { day: 4, hour: 8, minute: 0 }, - end: { day: 4, hour: 20, minute: 0 }, + year: 2024, + month: 8, + day: 9, + startMinutesFromMidnight: 480, + endMinutesFromMidnight: 1200, }, { - start: { day: 5, hour: 8, minute: 0 }, - end: { day: 5, hour: 23, minute: 59 }, + year: 2024, + month: 8, + day: 10, + startMinutesFromMidnight: 480, + endMinutesFromMidnight: 1200, }, { - start: { day: 6, hour: 16, minute: 30 }, - end: { day: 6, hour: 20, minute: 0 }, + year: 2024, + month: 8, + day: 11, + startMinutesFromMidnight: 480, + endMinutesFromMidnight: 1439, }, ], + earliestDayToOverride: { + year: 2024, + month: 8, + day: 5, + }, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -55,6 +84,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], + earliestDayToOverride: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -70,6 +100,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], + earliestDayToOverride: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -84,6 +115,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], + earliestDayToOverride: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -98,6 +130,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], + earliestDayToOverride: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -113,6 +146,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], + earliestDayToOverride: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -128,6 +162,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], + earliestDayToOverride: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -141,30 +176,53 @@ export const expectedLocationData = [ url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/92", location: "Posner Hall, 1st Floor", menu: "https://web.archive.org/web/20240721001349/https://apps.studentaffairs.cmu.edu/dining/dashboard_images/Production/menus/92/menu-exchange-2024-25-v2.pdf", - coordinates: { lat: 40.441354, lng: -79.942125 }, + coordinates: { + lat: 40.441354, + lng: -79.942125, + }, acceptsOnlineOrders: false, times: [ { - start: { day: 1, hour: 8, minute: 0 }, - end: { day: 1, hour: 15, minute: 0 }, + year: 2024, + month: 7, + day: 22, + startMinutesFromMidnight: 480, + endMinutesFromMidnight: 900, }, { - start: { day: 2, hour: 8, minute: 0 }, - end: { day: 2, hour: 15, minute: 0 }, + year: 2024, + month: 7, + day: 23, + startMinutesFromMidnight: 480, + endMinutesFromMidnight: 900, }, { - start: { day: 3, hour: 8, minute: 0 }, - end: { day: 3, hour: 15, minute: 0 }, + year: 2024, + month: 7, + day: 24, + startMinutesFromMidnight: 480, + endMinutesFromMidnight: 900, }, { - start: { day: 4, hour: 8, minute: 0 }, - end: { day: 4, hour: 15, minute: 0 }, + year: 2024, + month: 7, + day: 25, + startMinutesFromMidnight: 480, + endMinutesFromMidnight: 900, }, { - start: { day: 5, hour: 8, minute: 0 }, - end: { day: 5, hour: 15, minute: 0 }, + year: 2024, + month: 7, + day: 26, + startMinutesFromMidnight: 480, + endMinutesFromMidnight: 900, }, ], + earliestDayToOverride: { + year: 2024, + month: 7, + day: 20, + }, todaysSpecials: [ { title: "Brunch at The EXCHANGE Every Saturday", @@ -194,6 +252,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], + earliestDayToOverride: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -209,6 +268,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], + earliestDayToOverride: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -224,6 +284,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], + earliestDayToOverride: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -239,6 +300,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], + earliestDayToOverride: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -251,34 +313,60 @@ export const expectedLocationData = [ url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/110", location: "Newell-Simon Atrium", menu: "https://web.archive.org/web/20240901003526/https://apps.studentaffairs.cmu.edu/dining/dashboard_images/Production/menus/110/Fall Menus 2024 (25).pdf", - coordinates: { lat: 40.4433922, lng: -79.9455957 }, + coordinates: { + lat: 40.4433922, + lng: -79.9455957, + }, acceptsOnlineOrders: true, times: [ { - start: { day: 0, hour: 12, minute: 0 }, - end: { day: 0, hour: 20, minute: 0 }, + year: 2024, + month: 9, + day: 1, + startMinutesFromMidnight: 720, + endMinutesFromMidnight: 1200, }, { - start: { day: 1, hour: 10, minute: 30 }, - end: { day: 1, hour: 20, minute: 0 }, + year: 2024, + month: 9, + day: 2, + startMinutesFromMidnight: 630, + endMinutesFromMidnight: 1200, }, { - start: { day: 2, hour: 10, minute: 30 }, - end: { day: 2, hour: 20, minute: 0 }, + year: 2024, + month: 9, + day: 3, + startMinutesFromMidnight: 630, + endMinutesFromMidnight: 1200, }, { - start: { day: 3, hour: 10, minute: 30 }, - end: { day: 3, hour: 20, minute: 0 }, + year: 2024, + month: 9, + day: 4, + startMinutesFromMidnight: 630, + endMinutesFromMidnight: 1200, }, { - start: { day: 4, hour: 10, minute: 30 }, - end: { day: 4, hour: 20, minute: 0 }, + year: 2024, + month: 9, + day: 5, + startMinutesFromMidnight: 630, + endMinutesFromMidnight: 1200, }, { - start: { day: 5, hour: 10, minute: 30 }, - end: { day: 5, hour: 20, minute: 0 }, + year: 2024, + month: 9, + day: 6, + startMinutesFromMidnight: 630, + endMinutesFromMidnight: 1200, }, ], + earliestDayToOverride: { + year: 2024, + month: 8, + day: 31, + }, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -294,6 +382,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], + earliestDayToOverride: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -309,6 +398,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], + earliestDayToOverride: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -324,6 +414,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], + earliestDayToOverride: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -339,6 +430,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], + earliestDayToOverride: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -354,6 +446,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], + earliestDayToOverride: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -369,6 +462,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], + earliestDayToOverride: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -381,238 +475,151 @@ export const expectedLocationData = [ url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/108", location: "Cohon Center, Second floor", menu: undefined, - coordinates: { lat: 40.4429602, lng: -79.9418151 }, + coordinates: { + lat: 40.4429602, + lng: -79.9418151, + }, acceptsOnlineOrders: false, times: [ { - start: { - day: 0, - hour: 10, - minute: 30, - }, - end: { - day: 0, - hour: 14, - minute: 30, - }, - }, - { - start: { - day: 0, - hour: 16, - minute: 30, - }, - end: { - day: 0, - hour: 20, - minute: 30, - }, - }, - { - start: { - day: 1, - hour: 7, - minute: 30, - }, - end: { - day: 1, - hour: 10, - minute: 0, - }, - }, - { - start: { - day: 1, - hour: 11, - minute: 0, - }, - end: { - day: 1, - hour: 14, - minute: 0, - }, - }, - { - start: { - day: 1, - hour: 16, - minute: 30, - }, - end: { - day: 1, - hour: 20, - minute: 30, - }, - }, - { - start: { - day: 2, - hour: 7, - minute: 30, - }, - end: { - day: 2, - hour: 10, - minute: 0, - }, - }, - { - start: { - day: 2, - hour: 11, - minute: 0, - }, - end: { - day: 2, - hour: 14, - minute: 0, - }, - }, - { - start: { - day: 2, - hour: 16, - minute: 30, - }, - end: { - day: 2, - hour: 20, - minute: 30, - }, - }, - { - start: { - day: 3, - hour: 7, - minute: 30, - }, - end: { - day: 3, - hour: 10, - minute: 0, - }, - }, - { - start: { - day: 3, - hour: 11, - minute: 0, - }, - end: { - day: 3, - hour: 14, - minute: 0, - }, - }, - { - start: { - day: 3, - hour: 16, - minute: 30, - }, - end: { - day: 3, - hour: 20, - minute: 30, - }, - }, - { - start: { - day: 4, - hour: 7, - minute: 30, - }, - end: { - day: 4, - hour: 10, - minute: 0, - }, - }, - { - start: { - day: 4, - hour: 11, - minute: 0, - }, - end: { - day: 4, - hour: 14, - minute: 0, - }, - }, - { - start: { - day: 4, - hour: 16, - minute: 30, - }, - end: { - day: 4, - hour: 20, - minute: 30, - }, - }, - { - start: { - day: 5, - hour: 7, - minute: 30, - }, - end: { - day: 5, - hour: 10, - minute: 0, - }, - }, - { - start: { - day: 5, - hour: 11, - minute: 0, - }, - end: { - day: 5, - hour: 14, - minute: 0, - }, - }, - { - start: { - day: 5, - hour: 16, - minute: 30, - }, - end: { - day: 5, - hour: 20, - minute: 30, - }, - }, - { - start: { - day: 6, - hour: 10, - minute: 30, - }, - end: { - day: 6, - hour: 14, - minute: 30, - }, - }, - { - start: { - day: 6, - hour: 16, - minute: 30, - }, - end: { - day: 6, - hour: 20, - minute: 30, - }, + year: 2024, + month: 9, + day: 20, + startMinutesFromMidnight: 450, + endMinutesFromMidnight: 600, + }, + { + year: 2024, + month: 9, + day: 20, + startMinutesFromMidnight: 660, + endMinutesFromMidnight: 840, + }, + { + year: 2024, + month: 9, + day: 20, + startMinutesFromMidnight: 990, + endMinutesFromMidnight: 1230, + }, + { + year: 2024, + month: 9, + day: 21, + startMinutesFromMidnight: 630, + endMinutesFromMidnight: 870, + }, + { + year: 2024, + month: 9, + day: 21, + startMinutesFromMidnight: 990, + endMinutesFromMidnight: 1230, + }, + { + year: 2024, + month: 9, + day: 22, + startMinutesFromMidnight: 630, + endMinutesFromMidnight: 870, + }, + { + year: 2024, + month: 9, + day: 22, + startMinutesFromMidnight: 990, + endMinutesFromMidnight: 1230, + }, + { + year: 2024, + month: 9, + day: 23, + startMinutesFromMidnight: 450, + endMinutesFromMidnight: 600, + }, + { + year: 2024, + month: 9, + day: 23, + startMinutesFromMidnight: 660, + endMinutesFromMidnight: 840, + }, + { + year: 2024, + month: 9, + day: 23, + startMinutesFromMidnight: 990, + endMinutesFromMidnight: 1230, + }, + { + year: 2024, + month: 9, + day: 24, + startMinutesFromMidnight: 450, + endMinutesFromMidnight: 600, + }, + { + year: 2024, + month: 9, + day: 24, + startMinutesFromMidnight: 660, + endMinutesFromMidnight: 840, + }, + { + year: 2024, + month: 9, + day: 24, + startMinutesFromMidnight: 990, + endMinutesFromMidnight: 1230, + }, + { + year: 2024, + month: 9, + day: 25, + startMinutesFromMidnight: 450, + endMinutesFromMidnight: 600, + }, + { + year: 2024, + month: 9, + day: 25, + startMinutesFromMidnight: 660, + endMinutesFromMidnight: 840, + }, + { + year: 2024, + month: 9, + day: 25, + startMinutesFromMidnight: 990, + endMinutesFromMidnight: 1230, + }, + { + year: 2024, + month: 9, + day: 26, + startMinutesFromMidnight: 450, + endMinutesFromMidnight: 600, + }, + { + year: 2024, + month: 9, + day: 26, + startMinutesFromMidnight: 660, + endMinutesFromMidnight: 840, + }, + { + year: 2024, + month: 9, + day: 26, + startMinutesFromMidnight: 990, + endMinutesFromMidnight: 1230, }, ], + earliestDayToOverride: { + year: 2024, + month: 9, + day: 20, + }, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -628,6 +635,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], + earliestDayToOverride: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -643,6 +651,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], + earliestDayToOverride: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -658,6 +667,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], + earliestDayToOverride: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -673,6 +683,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], + earliestDayToOverride: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -687,6 +698,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], + earliestDayToOverride: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -696,34 +708,56 @@ export const expectedLocationData = [ shortDescription: "Campus food truck", description: "The Exchange offers custom deli sandwiches, soups, hot entrées, fresh baked goods, fruit, yogurt parfaits, snack and energy bars, and other grab-and-go items. The designated coffee bar includes hot brewed La Prima coffee, specialty and organic teas, cold beverages, and bottled juices.", + url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/168", location: "Posner Hall, 1st Floor", menu: "https://web.archive.org/web/20240721001349/https://apps.studentaffairs.cmu.edu/dining/dashboard_images/Production/menus/92/menu-exchange-2024-25-v2.pdf", - - url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/168", - coordinates: { lat: 401.441354, lng: -791.942125 }, + coordinates: { + lat: 401.441354, + lng: -791.942125, + }, acceptsOnlineOrders: false, times: [ { - start: { day: 1, hour: 8, minute: 0 }, - end: { day: 1, hour: 15, minute: 0 }, + year: 2024, + month: 7, + day: 22, + startMinutesFromMidnight: 480, + endMinutesFromMidnight: 900, }, { - start: { day: 2, hour: 8, minute: 0 }, - end: { day: 2, hour: 15, minute: 0 }, + year: 2024, + month: 7, + day: 23, + startMinutesFromMidnight: 480, + endMinutesFromMidnight: 900, }, { - start: { day: 3, hour: 8, minute: 0 }, - end: { day: 3, hour: 15, minute: 0 }, + year: 2024, + month: 7, + day: 24, + startMinutesFromMidnight: 480, + endMinutesFromMidnight: 900, }, { - start: { day: 4, hour: 8, minute: 0 }, - end: { day: 4, hour: 15, minute: 0 }, + year: 2024, + month: 7, + day: 25, + startMinutesFromMidnight: 480, + endMinutesFromMidnight: 900, }, { - start: { day: 5, hour: 8, minute: 0 }, - end: { day: 5, hour: 15, minute: 0 }, + year: 2024, + month: 7, + day: 26, + startMinutesFromMidnight: 480, + endMinutesFromMidnight: 900, }, ], + earliestDayToOverride: { + year: 2024, + month: 7, + day: 20, + }, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -738,6 +772,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], + earliestDayToOverride: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -753,6 +788,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], + earliestDayToOverride: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -768,6 +804,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], + earliestDayToOverride: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -782,6 +819,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], + earliestDayToOverride: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -797,6 +835,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], + earliestDayToOverride: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -812,6 +851,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], + earliestDayToOverride: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, From 10d12ddde7f605ebeef07910ced77c041ec81138 Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Sun, 9 Nov 2025 12:45:26 -0500 Subject: [PATCH 37/65] fix: typecheck --- package.json | 1 + src/containers/locationBuilder.ts | 26 ++++++++++++------------ src/db/dbQueryUtils.ts | 5 +---- src/db/schema.ts | 2 -- src/db/updateLocation.ts | 2 +- src/deprecationNotice.ts | 4 ---- src/types.ts | 13 ++++++------ tests/integration.test.ts | 1 - tests/locationMerger.test.ts | 33 +++++++++++++++++++++++++++---- tests/mockAxios.ts | 10 +++++----- tsconfig.json | 3 ++- 11 files changed, 58 insertions(+), 42 deletions(-) diff --git a/package.json b/package.json index c063492..fc7db68 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "build": "rollup -c", "start": "NODE_ENV=production node dist/server.js", "test": "dotenv -e .env.test -- jest --coverage", + "typecheck": "tsc", "run-prod": "VOL_SRC=postgres_data docker-compose up --build", "db:start": "VOL_SRC=postgres_dev_data docker-compose up -d postgres --build", "db:push": "drizzle-kit push", diff --git a/src/containers/locationBuilder.ts b/src/containers/locationBuilder.ts index ee47214..27b4aa3 100644 --- a/src/containers/locationBuilder.ts +++ b/src/containers/locationBuilder.ts @@ -16,19 +16,19 @@ export default class LocationBuilder { static readonly CONCEPT_BASE_LINK = "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/"; - private conceptId?: number; - private name?: string; - private shortDescription?: string; - private description?: string; - private url?: string; - private location?: string; - private menu?: string; - private coordinates?: ICoordinate; - private acceptsOnlineOrders?: boolean; - private times?: IFullTimeRange[]; - private earliestDayFound?: IDate; - private specials?: ISpecial[]; - private soups?: ISpecial[]; + private conceptId: number | undefined; + private name: string | undefined; + private shortDescription: string | undefined; + private description: string | undefined; + private url: string | undefined; + private location: string | undefined; + private menu: string | undefined; + private coordinates: ICoordinate | undefined; + private acceptsOnlineOrders: boolean | undefined; + private times: IFullTimeRange[] | undefined; + private earliestDayFound: IDate | undefined; + private specials: ISpecial[] | undefined; + private soups: ISpecial[] | undefined; constructor(card: Element) { const link = load(card)("h3.name.detailsLink"); diff --git a/src/db/dbQueryUtils.ts b/src/db/dbQueryUtils.ts index 8d6c728..8c41a0c 100644 --- a/src/db/dbQueryUtils.ts +++ b/src/db/dbQueryUtils.ts @@ -11,10 +11,7 @@ import { import { db } from "./db"; import { notifySlack } from "utils/slack"; import { eq, gte } from "drizzle-orm"; -import { - augmentAndEditTimeRangesWithDateInfo, - parseTimeSlots, -} from "containers/timeBuilder"; +import { parseTimeSlots } from "containers/timeBuilder"; import { IParsedTimeRange } from "containers/time/parsedTime"; /** More-so the database representation of a time range */ diff --git a/src/db/schema.ts b/src/db/schema.ts index 81e953b..566dcf8 100644 --- a/src/db/schema.ts +++ b/src/db/schema.ts @@ -3,14 +3,12 @@ import { text, integer, boolean, - jsonb, decimal, date, primaryKey, index, pgEnum, } from "drizzle-orm/pg-core"; -import { IFullTimeRange } from "../types"; export const emailTable = pgTable("emails", { id: integer().primaryKey().generatedAlwaysAsIdentity(), diff --git a/src/db/updateLocation.ts b/src/db/updateLocation.ts index 3c47696..f4eca0f 100644 --- a/src/db/updateLocation.ts +++ b/src/db/updateLocation.ts @@ -6,7 +6,7 @@ import { specialsTable, timesTable, } from "./schema"; -import { and, eq, gte, sql } from "drizzle-orm"; +import { and, eq, gte } from "drizzle-orm"; import { pad } from "utils/timeUtils"; async function getInternalId(externalId: string) { let [idMapping] = await db diff --git a/src/deprecationNotice.ts b/src/deprecationNotice.ts index fdd0f4e..ba1ae2b 100644 --- a/src/deprecationNotice.ts +++ b/src/deprecationNotice.ts @@ -43,7 +43,3 @@ interface ICoordinate { lat: number; lng: number; } - -interface ILocationCoordinateOverwrites { - [conceptId: string]: ICoordinate; -} diff --git a/src/types.ts b/src/types.ts index 002ddfe..f2b4ed2 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,4 +1,3 @@ -import { DateTime } from "luxon"; export interface IDate { year: number; /** 1-12 */ @@ -9,18 +8,18 @@ export interface IDate { export interface ILocation { conceptId: number; name: string; - shortDescription?: string; + shortDescription: string | undefined; description: string; url: string; - menu?: string; + menu: string | undefined; location: string; - coordinates?: ICoordinate; + coordinates: ICoordinate | undefined; acceptsOnlineOrders: boolean; times: IFullTimeRange[]; /** useful when figuring out which db time entries to overwrite. Can be undefined if no time data was properly scraped */ - earliestDayToOverride?: IDate; - todaysSpecials?: ISpecial[]; - todaysSoups?: ISpecial[]; + earliestDayToOverride: IDate | undefined; + todaysSpecials: ISpecial[] | undefined; + todaysSoups: ISpecial[] | undefined; } export interface ISpecial { title: string; diff --git a/tests/integration.test.ts b/tests/integration.test.ts index b3e095a..eafc36f 100644 --- a/tests/integration.test.ts +++ b/tests/integration.test.ts @@ -11,7 +11,6 @@ import { Fri, Sat, Sun, - setUpArbitraryTest, } from "./mockTimings"; import { DateTime } from "luxon"; diff --git a/tests/locationMerger.test.ts b/tests/locationMerger.test.ts index 232019d..89a73f7 100644 --- a/tests/locationMerger.test.ts +++ b/tests/locationMerger.test.ts @@ -1,5 +1,6 @@ +import { ILocation } from "types"; import ScrapeResultMerger from "../src/utils/locationMerger"; -const locationA = { +const locationA: ILocation = { conceptId: 3, name: "Location Name", description: "Location Description", @@ -7,8 +8,14 @@ const locationA = { location: "Location Address", times: [], acceptsOnlineOrders: true, + shortDescription: undefined, + coordinates: undefined, + earliestDayToOverride: undefined, + menu: undefined, + todaysSoups: undefined, + todaysSpecials: undefined, }; -const locationAA = { +const locationAA: ILocation = { conceptId: 3, name: "Location Name", description: "Location Description", @@ -16,8 +23,14 @@ const locationAA = { location: "Location Address Changed", times: [], acceptsOnlineOrders: true, + shortDescription: undefined, + coordinates: undefined, + earliestDayToOverride: undefined, + menu: undefined, + todaysSoups: undefined, + todaysSpecials: undefined, }; -const locationAAA = { +const locationAAA: ILocation = { conceptId: 3, name: "Location Name", description: "Location Description", @@ -25,8 +38,14 @@ const locationAAA = { location: "Location Address Changed", times: [], acceptsOnlineOrders: false, + shortDescription: undefined, + coordinates: undefined, + earliestDayToOverride: undefined, + menu: undefined, + todaysSoups: undefined, + todaysSpecials: undefined, }; -const locationB = { +const locationB: ILocation = { conceptId: 2, name: "Location Name", description: "Location Description", @@ -34,6 +53,12 @@ const locationB = { location: "Location Address", times: [], acceptsOnlineOrders: true, + shortDescription: undefined, + coordinates: undefined, + earliestDayToOverride: undefined, + menu: undefined, + todaysSoups: undefined, + todaysSpecials: undefined, }; // https://stackoverflow.com/questions/40135684/is-there-an-array-equality-match-function-that-ignores-element-position-in-jest const expectArrayEquivalence = (actual: T[], expected: T[]) => { diff --git a/tests/mockAxios.ts b/tests/mockAxios.ts index fcd1ef2..158d37a 100644 --- a/tests/mockAxios.ts +++ b/tests/mockAxios.ts @@ -22,10 +22,10 @@ export function mockAxiosGETMethod({ conceptHTML, serverDate, }: { - conceptListHTML?: string; - specialsHTML?: string; - soupsHTML?: string; - conceptHTML?: (id: string) => string | undefined; + conceptListHTML?: string | undefined; + specialsHTML?: string | undefined; + soupsHTML?: string | undefined; + conceptHTML?: ((id: string) => string | undefined) | undefined; serverDate: DateTime; }) { (axios.get as jest.Mock).mockImplementation(async (url: string) => { @@ -41,7 +41,7 @@ export function mockAxiosGETMethod({ if (url === SPECIALS_URL && specialsHTML !== undefined) return specialsHTML; if (url === SOUPS_URL && soupsHTML !== undefined) return soupsHTML; if (url.startsWith(LOCATION_URL_PREFIX) && conceptHTML !== undefined) - return conceptHTML(last(url.split("/"))); + return conceptHTML(last(url.split("/"))!); throw new Error(`url ${url} not found!`); }; } diff --git a/tsconfig.json b/tsconfig.json index e3cc912..e9322f0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -25,7 +25,8 @@ "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true, - "noUncheckedIndexedAccess": true // this should honestly be the default, since you're not guaranteed to get a value when you key into an object + "noUncheckedIndexedAccess": true, // this should honestly be the default, since you're not guaranteed to get a value when you key into an object + "exactOptionalPropertyTypes": true // | undefined needs to be explicit }, "include": ["src", "tests"] } From eac9ca6dd0f97591bd40ae541bac28d03b06ea5c Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Sun, 9 Nov 2025 19:27:52 -0500 Subject: [PATCH 38/65] chore: move from jest to vitest --- Dockerfile | 1 + jest.config.js | 8 - package.json | 10 +- pnpm-lock.yaml | 1448 ++++++++++++++++++++++++++++++++++++- tests/database.test.ts | 25 + tests/diffLog.test.ts | 4 +- tests/integration.test.ts | 2 +- tests/mockAxios.ts | 3 +- tests/postgresClient.ts | 14 + tsconfig.json | 2 +- vitest.config.ts | 9 + 11 files changed, 1481 insertions(+), 45 deletions(-) delete mode 100644 jest.config.js create mode 100644 tests/database.test.ts create mode 100644 tests/postgresClient.ts create mode 100644 vitest.config.ts diff --git a/Dockerfile b/Dockerfile index bac0ba7..d964c25 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,3 +1,4 @@ +# this is the file that railway will run FROM node:24-slim AS base ENV PNPM_HOME="/pnpm" # ENV PATH="$PNPM_HOME:$PATH" diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index 0abdfd9..0000000 --- a/jest.config.js +++ /dev/null @@ -1,8 +0,0 @@ -/** @type {import('ts-jest').JestConfigWithTsJest} **/ -export default { - testEnvironment: "node", - transform: { - "^.+.tsx?$": ["ts-jest", { diagnostics: { warnOnly: true } }], - }, - moduleDirectories: ['node_modules', 'src'] -}; \ No newline at end of file diff --git a/package.json b/package.json index fc7db68..b4132fa 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "dev": "dotenv -- tsx watch src/server.ts", "build": "rollup -c", "start": "NODE_ENV=production node dist/server.js", - "test": "dotenv -e .env.test -- jest --coverage", + "test": "dotenv -e .env.test -- vitest", "typecheck": "tsc", "run-prod": "VOL_SRC=postgres_data docker-compose up --build", "db:start": "VOL_SRC=postgres_dev_data docker-compose up -d postgres --build", @@ -34,10 +34,11 @@ "cheerio": "1.0.0-rc.12", "drizzle-kit": "^0.31.4", "drizzle-orm": "^0.44.4", - "elysia": "1.3.5", + "elysia": "1.4.15", "express": "^5.1.0", "luxon": "^3.7.2", "pg": "^8.16.3", + "vite-tsconfig-paths": "^5.1.4", "zod": "^3.25.67" }, "devDependencies": { @@ -45,6 +46,7 @@ "@babel/preset-env": "^7.25.4", "@babel/preset-typescript": "^7.24.7", "@rollup/plugin-typescript": "^12.1.4", + "@testcontainers/postgresql": "^11.8.0", "@types/jest": "^29.5.14", "@types/luxon": "^3.7.1", "@types/node": "^24.0.14", @@ -55,9 +57,11 @@ "jest": "^29.7.0", "npm-check-updates": "^18.0.1", "rollup": "^4.45.1", + "testcontainers": "^11.8.0", "ts-jest": "^29.2.5", "tslib": "^2.8.1", - "tsx": "^4.20.3" + "tsx": "^4.20.3", + "vitest": "^4.0.8" }, "peerDependencies": { "typescript": "^5.8.3" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3f7e07e..c725a50 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,13 +10,13 @@ importers: dependencies: '@elysiajs/cors': specifier: 1.3.3 - version: 1.3.3(elysia@1.3.5(exact-mirror@0.2.2(@sinclair/typebox@0.34.41))(file-type@21.0.0)(typescript@5.9.2)) + version: 1.3.3(elysia@1.4.15(@sinclair/typebox@0.34.41)(exact-mirror@0.2.2(@sinclair/typebox@0.34.41))(file-type@21.0.0)(openapi-types@12.1.3)(typescript@5.9.2)) '@elysiajs/node': specifier: 1.3.0 - version: 1.3.0(elysia@1.3.5(exact-mirror@0.2.2(@sinclair/typebox@0.34.41))(file-type@21.0.0)(typescript@5.9.2))(hono@4.9.8) + version: 1.3.0(elysia@1.4.15(@sinclair/typebox@0.34.41)(exact-mirror@0.2.2(@sinclair/typebox@0.34.41))(file-type@21.0.0)(openapi-types@12.1.3)(typescript@5.9.2))(hono@4.9.8) '@elysiajs/openapi': specifier: ^1.4.11 - version: 1.4.11(elysia@1.3.5(exact-mirror@0.2.2(@sinclair/typebox@0.34.41))(file-type@21.0.0)(typescript@5.9.2)) + version: 1.4.11(elysia@1.4.15(@sinclair/typebox@0.34.41)(exact-mirror@0.2.2(@sinclair/typebox@0.34.41))(file-type@21.0.0)(openapi-types@12.1.3)(typescript@5.9.2)) '@types/express': specifier: ^5.0.3 version: 5.0.3 @@ -33,8 +33,8 @@ importers: specifier: ^0.44.4 version: 0.44.5(@types/pg@8.15.5)(bun-types@1.2.22(@types/react@19.1.13))(pg@8.16.3) elysia: - specifier: 1.3.5 - version: 1.3.5(exact-mirror@0.2.2(@sinclair/typebox@0.34.41))(file-type@21.0.0)(typescript@5.9.2) + specifier: 1.4.15 + version: 1.4.15(@sinclair/typebox@0.34.41)(exact-mirror@0.2.2(@sinclair/typebox@0.34.41))(file-type@21.0.0)(openapi-types@12.1.3)(typescript@5.9.2) express: specifier: ^5.1.0 version: 5.1.0 @@ -47,6 +47,9 @@ importers: typescript: specifier: ^5.8.3 version: 5.9.2 + vite-tsconfig-paths: + specifier: ^5.1.4 + version: 5.1.4(typescript@5.9.2)(vite@7.2.2(@types/node@24.5.2)(tsx@4.20.5)(yaml@2.8.1)) zod: specifier: ^3.25.67 version: 3.25.76 @@ -63,6 +66,9 @@ importers: '@rollup/plugin-typescript': specifier: ^12.1.4 version: 12.1.4(rollup@4.52.0)(tslib@2.8.1)(typescript@5.9.2) + '@testcontainers/postgresql': + specifier: ^11.8.0 + version: 11.8.0 '@types/jest': specifier: ^29.5.14 version: 29.5.14 @@ -93,6 +99,9 @@ importers: rollup: specifier: ^4.45.1 version: 4.52.0 + testcontainers: + specifier: ^11.8.0 + version: 11.8.0 ts-jest: specifier: ^29.2.5 version: 29.4.4(@babel/core@7.28.4)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.4))(esbuild@0.25.10)(jest-util@29.7.0)(jest@29.7.0(@types/node@24.5.2))(typescript@5.9.2) @@ -102,6 +111,9 @@ importers: tsx: specifier: ^4.20.3 version: 4.20.5 + vitest: + specifier: ^4.0.8 + version: 4.0.8(@types/node@24.5.2)(tsx@4.20.5)(yaml@2.8.1) packages: @@ -693,6 +705,9 @@ packages: resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} engines: {node: '>=6.9.0'} + '@balena/dockerignore@1.0.2': + resolution: {integrity: sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==} + '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} @@ -1013,12 +1028,30 @@ packages: cpu: [x64] os: [win32] + '@grpc/grpc-js@1.14.1': + resolution: {integrity: sha512-sPxgEWtPUR3EnRJCEtbGZG2iX8LQDUls2wUS3o27jg07KqJFMq6YDeWvMo1wfpmy3rqRdS0rivpLwhqQtEyCuQ==} + engines: {node: '>=12.10.0'} + + '@grpc/proto-loader@0.7.15': + resolution: {integrity: sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==} + engines: {node: '>=6'} + hasBin: true + + '@grpc/proto-loader@0.8.0': + resolution: {integrity: sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==} + engines: {node: '>=6'} + hasBin: true + '@hono/node-server@1.19.3': resolution: {integrity: sha512-Fjyxfux0rMPXMSob79OmddfpK5ArJa2xLkLCV+zamHkbeXQtSNKOi0keiBKyHZ/hCRKjigjmKGp4AJnDFq8PUw==} engines: {node: '>=18.14.1'} peerDependencies: hono: ^4 + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + '@istanbuljs/load-nyc-config@1.1.0': resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} engines: {node: '>=8'} @@ -1109,6 +1142,43 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@js-sdsl/ordered-map@4.4.2': + resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@protobufjs/aspromise@1.1.2': + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + + '@protobufjs/base64@1.1.2': + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + + '@protobufjs/codegen@2.0.4': + resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + + '@protobufjs/eventemitter@1.1.0': + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + + '@protobufjs/fetch@1.1.0': + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + + '@protobufjs/float@1.0.2': + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + + '@protobufjs/inquire@1.1.0': + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} + + '@protobufjs/path@1.1.2': + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + + '@protobufjs/pool@1.1.0': + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + + '@protobufjs/utf8@1.1.0': + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + '@rollup/plugin-typescript@12.1.4': resolution: {integrity: sha512-s5Hx+EtN60LMlDBvl5f04bEiFZmAepk27Q+mr85L/00zPDn1jtzlTV6FWn81MaIwqfWzKxmOJrBWHU6vtQyedQ==} engines: {node: '>=14.0.0'} @@ -1253,6 +1323,12 @@ packages: '@sinonjs/fake-timers@10.3.0': resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + '@standard-schema/spec@1.0.0': + resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + + '@testcontainers/postgresql@11.8.0': + resolution: {integrity: sha512-JTMBEoLbi1eYvbsQTqbQlPVED07EB4xhB1tAkmvmirmLsjT0IH6YPKZdZD5y91KKR/FDvSbhPsYmDtByGyGqwg==} + '@tokenizer/inflate@0.2.7': resolution: {integrity: sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==} engines: {node: '>=18'} @@ -1275,9 +1351,21 @@ packages: '@types/body-parser@1.19.6': resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/docker-modem@3.0.6': + resolution: {integrity: sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg==} + + '@types/dockerode@3.3.45': + resolution: {integrity: sha512-iYpZF+xr5QLpIICejLdUF2r5gh8IXY1Gw3WLmt41dUbS3Vn/3hVgL+6lJBVbmrhYBWfbWPPstdr6+A0s95DTWA==} + '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} @@ -1311,6 +1399,12 @@ packages: '@types/mime@1.3.5': resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + '@types/node@18.19.130': + resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==} + + '@types/node@24.10.0': + resolution: {integrity: sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==} + '@types/node@24.5.2': resolution: {integrity: sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ==} @@ -1332,6 +1426,15 @@ packages: '@types/serve-static@1.15.8': resolution: {integrity: sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==} + '@types/ssh2-streams@0.1.13': + resolution: {integrity: sha512-faHyY3brO9oLEA0QlcO8N2wT7R0+1sHWZvQ+y3rMLwdY1ZyS1z0W3t65j9PqT4HmQ6ALzNe7RZlNuCNE0wBSWA==} + + '@types/ssh2@0.5.52': + resolution: {integrity: sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg==} + + '@types/ssh2@1.15.5': + resolution: {integrity: sha512-N1ASjp/nXH3ovBHddRJpli4ozpk6UdDYIX4RJWFa9L1YKnzdhTlVmiGHm4DZnj/jLbqZpes4aeR30EFGQtvhQQ==} + '@types/stack-utils@2.0.3': resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} @@ -1341,6 +1444,39 @@ packages: '@types/yargs@17.0.33': resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} + '@vitest/expect@4.0.8': + resolution: {integrity: sha512-Rv0eabdP/xjAHQGr8cjBm+NnLHNoL268lMDK85w2aAGLFoVKLd8QGnVon5lLtkXQCoYaNL0wg04EGnyKkkKhPA==} + + '@vitest/mocker@4.0.8': + resolution: {integrity: sha512-9FRM3MZCedXH3+pIh+ME5Up2NBBHDq0wqwhOKkN4VnvCiKbVxddqH9mSGPZeawjd12pCOGnl+lo/ZGHt0/dQSg==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@4.0.8': + resolution: {integrity: sha512-qRrjdRkINi9DaZHAimV+8ia9Gq6LeGz2CgIEmMLz3sBDYV53EsnLZbJMR1q84z1HZCMsf7s0orDgZn7ScXsZKg==} + + '@vitest/runner@4.0.8': + resolution: {integrity: sha512-mdY8Sf1gsM8hKJUQfiPT3pn1n8RF4QBcJYFslgWh41JTfrK1cbqY8whpGCFzBl45LN028g0njLCYm0d7XxSaQQ==} + + '@vitest/snapshot@4.0.8': + resolution: {integrity: sha512-Nar9OTU03KGiubrIOFhcfHg8FYaRaNT+bh5VUlNz8stFhCZPNrJvmZkhsr1jtaYvuefYFwK2Hwrq026u4uPWCw==} + + '@vitest/spy@4.0.8': + resolution: {integrity: sha512-nvGVqUunyCgZH7kmo+Ord4WgZ7lN0sOULYXUOYuHr55dvg9YvMz3izfB189Pgp28w0vWFbEEfNc/c3VTrqrXeA==} + + '@vitest/utils@4.0.8': + resolution: {integrity: sha512-pdk2phO5NDvEFfUTxcTP8RFYjVj/kfLSPIN5ebP2Mu9kcIMeAQTbknqcFEyBcC4z2pJlJI9aS5UQjcYfhmKAow==} + + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + accepts@2.0.0: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} engines: {node: '>= 0.6'} @@ -1353,6 +1489,10 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -1361,19 +1501,52 @@ packages: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + archiver-utils@5.0.2: + resolution: {integrity: sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==} + engines: {node: '>= 14'} + + archiver@7.0.1: + resolution: {integrity: sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==} + engines: {node: '>= 14'} + argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + asn1@0.2.6: + resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + async-lock@1.4.1: + resolution: {integrity: sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==} + + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} axios@1.12.2: resolution: {integrity: sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==} + b4a@1.7.3: + resolution: {integrity: sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==} + peerDependencies: + react-native-b4a: '*' + peerDependenciesMeta: + react-native-b4a: + optional: true + babel-jest@29.7.0: resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1417,10 +1590,57 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + bare-events@2.8.2: + resolution: {integrity: sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==} + peerDependencies: + bare-abort-controller: '*' + peerDependenciesMeta: + bare-abort-controller: + optional: true + + bare-fs@4.5.0: + resolution: {integrity: sha512-GljgCjeupKZJNetTqxKaQArLK10vpmK28or0+RwWjEl5Rk+/xG3wkpmkv+WrcBm3q1BwHKlnhXzR8O37kcvkXQ==} + engines: {bare: '>=1.16.0'} + peerDependencies: + bare-buffer: '*' + peerDependenciesMeta: + bare-buffer: + optional: true + + bare-os@3.6.2: + resolution: {integrity: sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A==} + engines: {bare: '>=1.14.0'} + + bare-path@3.0.0: + resolution: {integrity: sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==} + + bare-stream@2.7.0: + resolution: {integrity: sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==} + peerDependencies: + bare-buffer: '*' + bare-events: '*' + peerDependenciesMeta: + bare-buffer: + optional: true + bare-events: + optional: true + + bare-url@2.3.2: + resolution: {integrity: sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + baseline-browser-mapping@2.8.6: resolution: {integrity: sha512-wrH5NNqren/QMtKUEEJf7z86YjfqW/2uw3IL3/xpqZUC95SSVIFXYQeeGjL6FT/X68IROu6RMehZQS5foy2BXw==} hasBin: true + bcrypt-pbkdf@1.0.2: + resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} + + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + body-parser@2.2.0: resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} engines: {node: '>=18'} @@ -1431,6 +1651,9 @@ packages: brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} @@ -1447,14 +1670,32 @@ packages: bser@2.1.1: resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} + buffer-crc32@1.0.0: + resolution: {integrity: sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==} + engines: {node: '>=8.0.0'} + buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + + buildcheck@0.0.6: + resolution: {integrity: sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==} + engines: {node: '>=10.0.0'} + bun-types@1.2.22: resolution: {integrity: sha512-hwaAu8tct/Zn6Zft4U9BsZcXkYomzpHJX28ofvx7k0Zz2HNz54n1n+tDgxoWFGB4PcFvJXJQloPhaV2eP3Q6EA==} peerDependencies: '@types/react': ^19 + byline@5.0.0: + resolution: {integrity: sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==} + engines: {node: '>=0.10.0'} + bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} @@ -1482,6 +1723,10 @@ packages: caniuse-lite@1.0.30001743: resolution: {integrity: sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw==} + chai@6.2.0: + resolution: {integrity: sha512-aUTnJc/JipRzJrNADXVvpVqi6CO0dn3nx4EVPxijri+fj3LUUDyZQOgVeW54Ob3Y1Xh9Iz8f+CgaCl8v0mn9bA==} + engines: {node: '>=18'} + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -1497,6 +1742,9 @@ packages: resolution: {integrity: sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==} engines: {node: '>= 6'} + chownr@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + ci-info@3.9.0: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} @@ -1526,6 +1774,10 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} + compress-commons@6.0.2: + resolution: {integrity: sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==} + engines: {node: '>= 14'} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -1555,6 +1807,22 @@ packages: core-js-compat@3.45.1: resolution: {integrity: sha512-tqTt5T4PzsMIZ430XGviK4vzYSoeNJ6CXODi6c/voxOT6IZqBht5/EKaSNnYiEjjRYxjVz7DQIsOsY0XNi8PIA==} + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + cpu-features@0.0.10: + resolution: {integrity: sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==} + engines: {node: '>=10.0.0'} + + crc-32@1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} + hasBin: true + + crc32-stream@6.0.0: + resolution: {integrity: sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==} + engines: {node: '>= 14'} + create-jest@29.7.0: resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1611,6 +1879,18 @@ packages: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + docker-compose@1.3.0: + resolution: {integrity: sha512-7Gevk/5eGD50+eMD+XDnFnOrruFkL0kSd7jEG4cjmqweDSUhB7i0g8is/nBdVpl+Bx338SqIB2GLKm32M+Vs6g==} + engines: {node: '>= 6.0.0'} + + docker-modem@5.0.6: + resolution: {integrity: sha512-ens7BiayssQz/uAxGzH8zGXCtiV24rRWXdjNha5V4zSOcxmAZsfGVm/PPFbwQdqEkDnhG+SyR9E3zSHUbOKXBQ==} + engines: {node: '>= 8.0'} + + dockerode@4.0.9: + resolution: {integrity: sha512-iND4mcOWhPaCNh54WmK/KoSb35AFqPAUWFMffTQcp52uQt36b5uNwEJTSXntJZBbeGad72Crbi/hvDIv6us/6Q==} + engines: {node: '>= 8.0'} + dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} @@ -1740,18 +2020,29 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} electron-to-chromium@1.5.222: resolution: {integrity: sha512-gA7psSwSwQRE60CEoLz6JBCQPIxNeuzB2nL8vE03GK/OHxlvykbLyeiumQy1iH5C2f3YbRAZpGCMT12a/9ih9w==} - elysia@1.3.5: - resolution: {integrity: sha512-XVIKXlKFwUT7Sta8GY+wO5reD9I0rqAEtaz1Z71UgJb61csYt8Q3W9al8rtL5RgumuRR8e3DNdzlUN9GkC4KDw==} + elysia@1.4.15: + resolution: {integrity: sha512-RaDqqZdLuC4UJetfVRQ4Z5aVpGgEtQ+pZnsbI4ZzEaf3l/MzuHcqSVoL/Fue3d6qE4RV9HMB2rAZaHyPIxkyzg==} peerDependencies: + '@sinclair/typebox': '>= 0.34.0 < 1' + '@types/bun': '>= 1.2.0' exact-mirror: '>= 0.0.9' file-type: '>= 20.0.0' + openapi-types: '>= 12.0.0' typescript: '>= 5.0.0' + peerDependenciesMeta: + '@types/bun': + optional: true + typescript: + optional: true emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} @@ -1760,10 +2051,16 @@ packages: emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + encodeurl@2.0.0: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -1783,6 +2080,9 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} @@ -1825,6 +2125,9 @@ packages: estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -1833,6 +2136,17 @@ packages: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + + events-universal@1.0.1: + resolution: {integrity: sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + exact-mirror@0.2.2: resolution: {integrity: sha512-CrGe+4QzHZlnrXZVlo/WbUZ4qQZq8C0uATQVGVgXIrNXgHDBBNFD1VRfssRA2C9t3RYvh3MadZSdg2Wy7HBoQA==} peerDependencies: @@ -1849,6 +2163,10 @@ packages: resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} engines: {node: '>= 0.8.0'} + expect-type@1.2.2: + resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} + engines: {node: '>=12.0.0'} + expect@29.7.0: resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1860,12 +2178,24 @@ packages: fast-decode-uri-component@1.0.1: resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} + fast-fifo@1.3.2: + resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} fb-watchman@2.0.2: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + fflate@0.8.2: resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} @@ -1894,6 +2224,10 @@ packages: debug: optional: true + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + form-data@4.0.4: resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} engines: {node: '>= 6'} @@ -1906,6 +2240,9 @@ packages: resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} engines: {node: '>= 0.8'} + fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -1933,6 +2270,10 @@ packages: resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} engines: {node: '>=8.0.0'} + get-port@7.1.0: + resolution: {integrity: sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==} + engines: {node: '>=16'} + get-proto@1.0.1: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} @@ -1944,10 +2285,17 @@ packages: get-tsconfig@4.10.1: resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Glob versions prior to v9 are no longer supported + globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -2051,6 +2399,9 @@ packages: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -2078,6 +2429,9 @@ packages: resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} engines: {node: '>=8'} + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + jest-changed-files@29.7.0: resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -2236,6 +2590,10 @@ packages: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} + lazystream@1.0.1: + resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} + engines: {node: '>= 0.6.3'} + leven@3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} @@ -2247,12 +2605,24 @@ packages: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} + lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} lodash.memoize@4.1.2: resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + long@5.3.2: + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -2260,6 +2630,9 @@ packages: resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==} engines: {node: '>=12'} + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} @@ -2278,6 +2651,9 @@ packages: resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} engines: {node: '>= 0.8'} + memoirist@0.4.0: + resolution: {integrity: sha512-zxTgA0mSYELa66DimuNQDvyLq36AwDlTuVRbnQtB+VuTcKWm5Qc4z3WkSpgsFWHNhexqkIooqpv4hdcqrX5Nmg==} + merge-descriptors@2.0.0: resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} engines: {node: '>=18'} @@ -2312,12 +2688,40 @@ packages: minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + nan@2.23.1: + resolution: {integrity: sha512-r7bBUGKzlqk8oPBDYxt6Z0aEdF1G1rwlMcLk8LCOMbOzf0mG+JUfUzG4fIMWwHWP0iyaLWEQZJmtB7nOHEm/qw==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -2384,6 +2788,9 @@ packages: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + parse-json@5.2.0: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} @@ -2413,9 +2820,16 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + path-to-regexp@8.3.0: resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + pg-cloudflare@1.2.7: resolution: {integrity: sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==} @@ -2469,6 +2883,10 @@ packages: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + postgres-array@2.0.0: resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} engines: {node: '>=4'} @@ -2489,10 +2907,28 @@ packages: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + prompts@2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} + proper-lockfile@4.1.2: + resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} + + properties-reader@2.3.0: + resolution: {integrity: sha512-z597WicA7nDZxK12kZqHr2TcvwNU1GCfA5UwfDY/HDp3hXPoPlb5rlEx9bwGTiJnc0OqbBTkU975jDToth8Gxw==} + engines: {node: '>=14'} + + protobufjs@7.5.4: + resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==} + engines: {node: '>=12.0.0'} + proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} @@ -2500,6 +2936,9 @@ packages: proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + pump@3.0.3: + resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} + pure-rand@6.1.0: resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} @@ -2518,6 +2957,20 @@ packages: react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readable-stream@4.7.0: + resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + readdir-glob@1.1.3: + resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} + regenerate-unicode-properties@10.2.2: resolution: {integrity: sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==} engines: {node: '>=4'} @@ -2560,6 +3013,10 @@ packages: engines: {node: '>= 0.4'} hasBin: true + retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + rollup@4.52.0: resolution: {integrity: sha512-+IuescNkTJQgX7AkIDtITipZdIGcWF0pnVvZTWStiazUmcGA2ag8dfg0urest2XlXUi9kuhfQ+qmdc5Stc3z7g==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -2569,6 +3026,9 @@ packages: resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} engines: {node: '>= 18'} + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -2619,9 +3079,16 @@ packages: resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} engines: {node: '>= 0.4'} + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} @@ -2629,6 +3096,10 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + source-map-support@0.5.13: resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} @@ -2639,6 +3110,9 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + split-ca@1.0.1: + resolution: {integrity: sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==} + split2@4.2.0: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} @@ -2646,10 +3120,20 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + ssh-remote-port-forward@1.0.4: + resolution: {integrity: sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ==} + + ssh2@1.17.0: + resolution: {integrity: sha512-wPldCk3asibAjQ/kziWQQt1Wh3PgDFpC0XpwclzKcdT1vql6KeYxf5LIt4nlFkUeR8WuphYMKqUA56X4rjbfgQ==} + engines: {node: '>=10.16.0'} + stack-utils@2.0.6: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + statuses@2.0.1: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} @@ -2658,6 +3142,12 @@ packages: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + + streamx@2.23.0: + resolution: {integrity: sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==} + string-length@4.0.2: resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} engines: {node: '>=10'} @@ -2666,10 +3156,24 @@ packages: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} + strip-ansi@7.1.2: + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} + engines: {node: '>=12'} + strip-bom@4.0.0: resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} engines: {node: '>=8'} @@ -2698,10 +3202,47 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + tar-fs@2.1.4: + resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==} + + tar-fs@3.1.1: + resolution: {integrity: sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==} + + tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + + tar-stream@3.1.7: + resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} + test-exclude@6.0.0: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} + testcontainers@11.8.0: + resolution: {integrity: sha512-kY2DfuUB1NSvmpG7wCpi/aTaIJaHcX53WSAlWHsj0La7E7fPnVFOpooheczE3fH9T+OgD5OB5IeBpFitIqqu6w==} + + text-decoder@1.2.3: + resolution: {integrity: sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + tinyrainbow@3.0.3: + resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} + engines: {node: '>=14.0.0'} + + tmp@0.2.5: + resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} + engines: {node: '>=14.14'} + tmpl@1.0.5: resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} @@ -2744,6 +3285,16 @@ packages: jest-util: optional: true + tsconfck@3.1.6: + resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} + engines: {node: ^18 || >=20} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -2752,6 +3303,9 @@ packages: engines: {node: '>=18.0.0'} hasBin: true + tweetnacl@0.14.5: + resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} + type-detect@4.0.8: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} @@ -2782,9 +3336,19 @@ packages: resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==} engines: {node: '>=18'} + undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + undici-types@7.12.0: resolution: {integrity: sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==} + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + + undici@7.16.0: + resolution: {integrity: sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g==} + engines: {node: '>=20.18.1'} + unicode-canonical-property-names-ecmascript@2.0.1: resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} engines: {node: '>=4'} @@ -2811,6 +3375,13 @@ packages: peerDependencies: browserslist: '>= 4.21.0' + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + uuid@10.0.0: + resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} + hasBin: true + v8-to-istanbul@9.3.0: resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} engines: {node: '>=10.12.0'} @@ -2819,6 +3390,88 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} + vite-tsconfig-paths@5.1.4: + resolution: {integrity: sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==} + peerDependencies: + vite: '*' + peerDependenciesMeta: + vite: + optional: true + + vite@7.2.2: + resolution: {integrity: sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@4.0.8: + resolution: {integrity: sha512-urzu3NCEV0Qa0Y2PwvBtRgmNtxhj5t5ULw7cuKhIHh3OrkKTLlut0lnBOv9qe5OvbkMH2g38G7KPDCTpIytBVg==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.0.8 + '@vitest/browser-preview': 4.0.8 + '@vitest/browser-webdriverio': 4.0.8 + '@vitest/ui': 4.0.8 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + walker@1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} @@ -2827,6 +3480,11 @@ packages: engines: {node: '>= 8'} hasBin: true + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + wordwrap@1.0.0: resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} @@ -2834,6 +3492,10 @@ packages: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} @@ -2852,6 +3514,11 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yaml@2.8.1: + resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==} + engines: {node: '>= 14.6'} + hasBin: true + yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} @@ -2864,6 +3531,10 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + zip-stream@6.0.1: + resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==} + engines: {node: '>= 14'} + zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -3623,26 +4294,28 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 + '@balena/dockerignore@1.0.2': {} + '@bcoe/v8-coverage@0.2.3': {} '@borewit/text-codec@0.1.1': {} '@drizzle-team/brocli@0.10.2': {} - '@elysiajs/cors@1.3.3(elysia@1.3.5(exact-mirror@0.2.2(@sinclair/typebox@0.34.41))(file-type@21.0.0)(typescript@5.9.2))': + '@elysiajs/cors@1.3.3(elysia@1.4.15(@sinclair/typebox@0.34.41)(exact-mirror@0.2.2(@sinclair/typebox@0.34.41))(file-type@21.0.0)(openapi-types@12.1.3)(typescript@5.9.2))': dependencies: - elysia: 1.3.5(exact-mirror@0.2.2(@sinclair/typebox@0.34.41))(file-type@21.0.0)(typescript@5.9.2) + elysia: 1.4.15(@sinclair/typebox@0.34.41)(exact-mirror@0.2.2(@sinclair/typebox@0.34.41))(file-type@21.0.0)(openapi-types@12.1.3)(typescript@5.9.2) - '@elysiajs/node@1.3.0(elysia@1.3.5(exact-mirror@0.2.2(@sinclair/typebox@0.34.41))(file-type@21.0.0)(typescript@5.9.2))(hono@4.9.8)': + '@elysiajs/node@1.3.0(elysia@1.4.15(@sinclair/typebox@0.34.41)(exact-mirror@0.2.2(@sinclair/typebox@0.34.41))(file-type@21.0.0)(openapi-types@12.1.3)(typescript@5.9.2))(hono@4.9.8)': dependencies: '@hono/node-server': 1.19.3(hono@4.9.8) - elysia: 1.3.5(exact-mirror@0.2.2(@sinclair/typebox@0.34.41))(file-type@21.0.0)(typescript@5.9.2) + elysia: 1.4.15(@sinclair/typebox@0.34.41)(exact-mirror@0.2.2(@sinclair/typebox@0.34.41))(file-type@21.0.0)(openapi-types@12.1.3)(typescript@5.9.2) transitivePeerDependencies: - hono - '@elysiajs/openapi@1.4.11(elysia@1.3.5(exact-mirror@0.2.2(@sinclair/typebox@0.34.41))(file-type@21.0.0)(typescript@5.9.2))': + '@elysiajs/openapi@1.4.11(elysia@1.4.15(@sinclair/typebox@0.34.41)(exact-mirror@0.2.2(@sinclair/typebox@0.34.41))(file-type@21.0.0)(openapi-types@12.1.3)(typescript@5.9.2))': dependencies: - elysia: 1.3.5(exact-mirror@0.2.2(@sinclair/typebox@0.34.41))(file-type@21.0.0)(typescript@5.9.2) + elysia: 1.4.15(@sinclair/typebox@0.34.41)(exact-mirror@0.2.2(@sinclair/typebox@0.34.41))(file-type@21.0.0)(openapi-types@12.1.3)(typescript@5.9.2) '@esbuild-kit/core-utils@3.3.2': dependencies: @@ -3798,16 +4471,44 @@ snapshots: '@esbuild/win32-x64@0.25.10': optional: true - '@hono/node-server@1.19.3(hono@4.9.8)': + '@grpc/grpc-js@1.14.1': dependencies: - hono: 4.9.8 + '@grpc/proto-loader': 0.8.0 + '@js-sdsl/ordered-map': 4.4.2 - '@istanbuljs/load-nyc-config@1.1.0': + '@grpc/proto-loader@0.7.15': dependencies: - camelcase: 5.3.1 - find-up: 4.1.0 - get-package-type: 0.1.0 - js-yaml: 3.14.1 + lodash.camelcase: 4.3.0 + long: 5.3.2 + protobufjs: 7.5.4 + yargs: 17.7.2 + + '@grpc/proto-loader@0.8.0': + dependencies: + lodash.camelcase: 4.3.0 + long: 5.3.2 + protobufjs: 7.5.4 + yargs: 17.7.2 + + '@hono/node-server@1.19.3(hono@4.9.8)': + dependencies: + hono: 4.9.8 + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.2 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@istanbuljs/load-nyc-config@1.1.0': + dependencies: + camelcase: 5.3.1 + find-up: 4.1.0 + get-package-type: 0.1.0 + js-yaml: 3.14.1 resolve-from: 5.0.0 '@istanbuljs/schema@0.1.3': {} @@ -3993,6 +4694,34 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@js-sdsl/ordered-map@4.4.2': {} + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@protobufjs/aspromise@1.1.2': {} + + '@protobufjs/base64@1.1.2': {} + + '@protobufjs/codegen@2.0.4': {} + + '@protobufjs/eventemitter@1.1.0': {} + + '@protobufjs/fetch@1.1.0': + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 1.1.0 + + '@protobufjs/float@1.0.2': {} + + '@protobufjs/inquire@1.1.0': {} + + '@protobufjs/path@1.1.2': {} + + '@protobufjs/pool@1.1.0': {} + + '@protobufjs/utf8@1.1.0': {} + '@rollup/plugin-typescript@12.1.4(rollup@4.52.0)(tslib@2.8.1)(typescript@5.9.2)': dependencies: '@rollup/pluginutils': 5.3.0(rollup@4.52.0) @@ -4078,8 +4807,7 @@ snapshots: '@sinclair/typebox@0.27.8': {} - '@sinclair/typebox@0.34.41': - optional: true + '@sinclair/typebox@0.34.41': {} '@sinonjs/commons@3.0.1': dependencies: @@ -4089,6 +4817,17 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.1 + '@standard-schema/spec@1.0.0': {} + + '@testcontainers/postgresql@11.8.0': + dependencies: + testcontainers: 11.8.0 + transitivePeerDependencies: + - bare-abort-controller + - bare-buffer + - react-native-b4a + - supports-color + '@tokenizer/inflate@0.2.7': dependencies: debug: 4.4.3 @@ -4125,10 +4864,28 @@ snapshots: '@types/connect': 3.4.38 '@types/node': 24.5.2 + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + '@types/connect@3.4.38': dependencies: '@types/node': 24.5.2 + '@types/deep-eql@4.0.2': {} + + '@types/docker-modem@3.0.6': + dependencies: + '@types/node': 24.5.2 + '@types/ssh2': 1.15.5 + + '@types/dockerode@3.3.45': + dependencies: + '@types/docker-modem': 3.0.6 + '@types/node': 24.5.2 + '@types/ssh2': 1.15.5 + '@types/estree@1.0.8': {} '@types/express-serve-static-core@5.0.7': @@ -4169,6 +4926,15 @@ snapshots: '@types/mime@1.3.5': {} + '@types/node@18.19.130': + dependencies: + undici-types: 5.26.5 + + '@types/node@24.10.0': + dependencies: + undici-types: 7.16.0 + optional: true + '@types/node@24.5.2': dependencies: undici-types: 7.12.0 @@ -4199,6 +4965,19 @@ snapshots: '@types/node': 24.5.2 '@types/send': 0.17.5 + '@types/ssh2-streams@0.1.13': + dependencies: + '@types/node': 24.5.2 + + '@types/ssh2@0.5.52': + dependencies: + '@types/node': 24.5.2 + '@types/ssh2-streams': 0.1.13 + + '@types/ssh2@1.15.5': + dependencies: + '@types/node': 18.19.130 + '@types/stack-utils@2.0.3': {} '@types/yargs-parser@21.0.3': {} @@ -4207,6 +4986,49 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 + '@vitest/expect@4.0.8': + dependencies: + '@standard-schema/spec': 1.0.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.0.8 + '@vitest/utils': 4.0.8 + chai: 6.2.0 + tinyrainbow: 3.0.3 + + '@vitest/mocker@4.0.8(vite@7.2.2(@types/node@24.5.2)(tsx@4.20.5)(yaml@2.8.1))': + dependencies: + '@vitest/spy': 4.0.8 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.2.2(@types/node@24.5.2)(tsx@4.20.5)(yaml@2.8.1) + + '@vitest/pretty-format@4.0.8': + dependencies: + tinyrainbow: 3.0.3 + + '@vitest/runner@4.0.8': + dependencies: + '@vitest/utils': 4.0.8 + pathe: 2.0.3 + + '@vitest/snapshot@4.0.8': + dependencies: + '@vitest/pretty-format': 4.0.8 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@4.0.8': {} + + '@vitest/utils@4.0.8': + dependencies: + '@vitest/pretty-format': 4.0.8 + tinyrainbow: 3.0.3 + + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + accepts@2.0.0: dependencies: mime-types: 3.0.1 @@ -4218,21 +5040,58 @@ snapshots: ansi-regex@5.0.1: {} + ansi-regex@6.2.2: {} + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 ansi-styles@5.2.0: {} + ansi-styles@6.2.3: {} + anymatch@3.1.3: dependencies: normalize-path: 3.0.0 picomatch: 2.3.1 + archiver-utils@5.0.2: + dependencies: + glob: 10.4.5 + graceful-fs: 4.2.11 + is-stream: 2.0.1 + lazystream: 1.0.1 + lodash: 4.17.21 + normalize-path: 3.0.0 + readable-stream: 4.7.0 + + archiver@7.0.1: + dependencies: + archiver-utils: 5.0.2 + async: 3.2.6 + buffer-crc32: 1.0.0 + readable-stream: 4.7.0 + readdir-glob: 1.1.3 + tar-stream: 3.1.7 + zip-stream: 6.0.1 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + argparse@1.0.10: dependencies: sprintf-js: 1.0.3 + asn1@0.2.6: + dependencies: + safer-buffer: 2.1.2 + + assertion-error@2.0.1: {} + + async-lock@1.4.1: {} + + async@3.2.6: {} + asynckit@0.4.0: {} axios@1.12.2: @@ -4243,6 +5102,8 @@ snapshots: transitivePeerDependencies: - debug + b4a@1.7.3: {} + babel-jest@29.7.0(@babel/core@7.28.4): dependencies: '@babel/core': 7.28.4 @@ -4324,8 +5185,57 @@ snapshots: balanced-match@1.0.2: {} + bare-events@2.8.2: {} + + bare-fs@4.5.0: + dependencies: + bare-events: 2.8.2 + bare-path: 3.0.0 + bare-stream: 2.7.0(bare-events@2.8.2) + bare-url: 2.3.2 + fast-fifo: 1.3.2 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + optional: true + + bare-os@3.6.2: + optional: true + + bare-path@3.0.0: + dependencies: + bare-os: 3.6.2 + optional: true + + bare-stream@2.7.0(bare-events@2.8.2): + dependencies: + streamx: 2.23.0 + optionalDependencies: + bare-events: 2.8.2 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + optional: true + + bare-url@2.3.2: + dependencies: + bare-path: 3.0.0 + optional: true + + base64-js@1.5.1: {} + baseline-browser-mapping@2.8.6: {} + bcrypt-pbkdf@1.0.2: + dependencies: + tweetnacl: 0.14.5 + + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + body-parser@2.2.0: dependencies: bytes: 3.1.2 @@ -4347,6 +5257,10 @@ snapshots: balanced-match: 1.0.2 concat-map: 0.0.1 + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + braces@3.0.3: dependencies: fill-range: 7.1.1 @@ -4367,14 +5281,31 @@ snapshots: dependencies: node-int64: 0.4.0 + buffer-crc32@1.0.0: {} + buffer-from@1.1.2: {} + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + buildcheck@0.0.6: + optional: true + bun-types@1.2.22(@types/react@19.1.13): dependencies: - '@types/node': 24.5.2 + '@types/node': 24.10.0 '@types/react': 19.1.13 optional: true + byline@5.0.0: {} + bytes@3.1.2: {} call-bind-apply-helpers@1.0.2: @@ -4395,6 +5326,8 @@ snapshots: caniuse-lite@1.0.30001743: {} + chai@6.2.0: {} + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 @@ -4421,6 +5354,8 @@ snapshots: parse5: 7.3.0 parse5-htmlparser2-tree-adapter: 7.1.0 + chownr@1.1.4: {} + ci-info@3.9.0: {} cjs-module-lexer@1.4.3: {} @@ -4445,6 +5380,14 @@ snapshots: dependencies: delayed-stream: 1.0.0 + compress-commons@6.0.2: + dependencies: + crc-32: 1.2.2 + crc32-stream: 6.0.0 + is-stream: 2.0.1 + normalize-path: 3.0.0 + readable-stream: 4.7.0 + concat-map@0.0.1: {} content-disposition@1.0.0: @@ -4465,6 +5408,21 @@ snapshots: dependencies: browserslist: 4.26.2 + core-util-is@1.0.3: {} + + cpu-features@0.0.10: + dependencies: + buildcheck: 0.0.6 + nan: 2.23.1 + optional: true + + crc-32@1.2.2: {} + + crc32-stream@6.0.0: + dependencies: + crc-32: 1.2.2 + readable-stream: 4.7.0 + create-jest@29.7.0(@types/node@24.5.2): dependencies: '@jest/types': 29.6.3 @@ -4515,6 +5473,31 @@ snapshots: diff-sequences@29.6.3: {} + docker-compose@1.3.0: + dependencies: + yaml: 2.8.1 + + docker-modem@5.0.6: + dependencies: + debug: 4.4.3 + readable-stream: 3.6.2 + split-ca: 1.0.1 + ssh2: 1.17.0 + transitivePeerDependencies: + - supports-color + + dockerode@4.0.9: + dependencies: + '@balena/dockerignore': 1.0.2 + '@grpc/grpc-js': 1.14.1 + '@grpc/proto-loader': 0.7.15 + docker-modem: 5.0.6 + protobufjs: 7.5.4 + tar-fs: 2.1.4 + uuid: 10.0.0 + transitivePeerDependencies: + - supports-color + dom-serializer@2.0.0: dependencies: domelementtype: 2.3.0 @@ -4567,27 +5550,36 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 + eastasianwidth@0.2.0: {} + ee-first@1.1.1: {} electron-to-chromium@1.5.222: {} - elysia@1.3.5(exact-mirror@0.2.2(@sinclair/typebox@0.34.41))(file-type@21.0.0)(typescript@5.9.2): + elysia@1.4.15(@sinclair/typebox@0.34.41)(exact-mirror@0.2.2(@sinclair/typebox@0.34.41))(file-type@21.0.0)(openapi-types@12.1.3)(typescript@5.9.2): dependencies: + '@sinclair/typebox': 0.34.41 cookie: 1.0.2 exact-mirror: 0.2.2(@sinclair/typebox@0.34.41) fast-decode-uri-component: 1.0.1 file-type: 21.0.0 - typescript: 5.9.2 - optionalDependencies: - '@sinclair/typebox': 0.34.41 + memoirist: 0.4.0 openapi-types: 12.1.3 + optionalDependencies: + typescript: 5.9.2 emittery@0.13.1: {} emoji-regex@8.0.0: {} + emoji-regex@9.2.2: {} + encodeurl@2.0.0: {} + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + entities@4.5.0: {} entities@6.0.1: {} @@ -4600,6 +5592,8 @@ snapshots: es-errors@1.3.0: {} + es-module-lexer@1.7.0: {} + es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 @@ -4682,10 +5676,24 @@ snapshots: estree-walker@2.0.2: {} + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + esutils@2.0.3: {} etag@1.8.1: {} + event-target-shim@5.0.1: {} + + events-universal@1.0.1: + dependencies: + bare-events: 2.8.2 + transitivePeerDependencies: + - bare-abort-controller + + events@3.3.0: {} + exact-mirror@0.2.2(@sinclair/typebox@0.34.41): optionalDependencies: '@sinclair/typebox': 0.34.41 @@ -4704,6 +5712,8 @@ snapshots: exit@0.1.2: {} + expect-type@1.2.2: {} + expect@29.7.0: dependencies: '@jest/expect-utils': 29.7.0 @@ -4746,12 +5756,18 @@ snapshots: fast-decode-uri-component@1.0.1: {} + fast-fifo@1.3.2: {} + fast-json-stable-stringify@2.1.0: {} fb-watchman@2.0.2: dependencies: bser: 2.1.1 + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + fflate@0.8.2: {} file-type@21.0.0: @@ -4785,6 +5801,11 @@ snapshots: follow-redirects@1.15.11: {} + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + form-data@4.0.4: dependencies: asynckit: 0.4.0 @@ -4797,6 +5818,8 @@ snapshots: fresh@2.0.0: {} + fs-constants@1.0.0: {} + fs.realpath@1.0.0: {} fsevents@2.3.3: @@ -4823,6 +5846,8 @@ snapshots: get-package-type@0.1.0: {} + get-port@7.1.0: {} + get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 @@ -4834,6 +5859,15 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 + glob@10.4.5: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + glob@7.2.3: dependencies: fs.realpath: 1.0.0 @@ -4843,6 +5877,8 @@ snapshots: once: 1.4.0 path-is-absolute: 1.0.1 + globrex@0.1.2: {} + gopd@1.2.0: {} graceful-fs@4.2.11: {} @@ -4931,6 +5967,8 @@ snapshots: is-stream@2.0.1: {} + isarray@1.0.0: {} + isexe@2.0.0: {} istanbul-lib-coverage@3.2.2: {} @@ -4974,6 +6012,12 @@ snapshots: html-escaper: 2.0.2 istanbul-lib-report: 3.0.1 + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + jest-changed-files@29.7.0: dependencies: execa: 5.1.1 @@ -5299,6 +6343,10 @@ snapshots: kleur@3.0.3: {} + lazystream@1.0.1: + dependencies: + readable-stream: 2.3.8 + leven@3.1.0: {} lines-and-columns@1.2.4: {} @@ -5307,16 +6355,28 @@ snapshots: dependencies: p-locate: 4.1.0 + lodash.camelcase@4.3.0: {} + lodash.debounce@4.0.8: {} lodash.memoize@4.1.2: {} + lodash@4.17.21: {} + + long@5.3.2: {} + + lru-cache@10.4.3: {} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 luxon@3.7.2: {} + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + make-dir@4.0.0: dependencies: semver: 7.7.2 @@ -5331,6 +6391,8 @@ snapshots: media-typer@1.1.0: {} + memoirist@0.4.0: {} + merge-descriptors@2.0.0: {} merge-stream@2.0.0: {} @@ -5358,10 +6420,29 @@ snapshots: dependencies: brace-expansion: 1.1.12 + minimatch@5.1.6: + dependencies: + brace-expansion: 2.0.2 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + minimist@1.2.8: {} + minipass@7.1.2: {} + + mkdirp-classic@0.5.3: {} + + mkdirp@1.0.4: {} + ms@2.1.3: {} + nan@2.23.1: + optional: true + + nanoid@3.3.11: {} + natural-compare@1.4.0: {} negotiator@1.0.0: {} @@ -5398,8 +6479,7 @@ snapshots: dependencies: mimic-fn: 2.1.0 - openapi-types@12.1.3: - optional: true + openapi-types@12.1.3: {} p-limit@2.3.0: dependencies: @@ -5415,6 +6495,8 @@ snapshots: p-try@2.2.0: {} + package-json-from-dist@1.0.1: {} + parse-json@5.2.0: dependencies: '@babel/code-frame': 7.27.1 @@ -5441,8 +6523,15 @@ snapshots: path-parse@1.0.7: {} + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + path-to-regexp@8.3.0: {} + pathe@2.0.3: {} + pg-cloudflare@1.2.7: optional: true @@ -5490,6 +6579,12 @@ snapshots: dependencies: find-up: 4.1.0 + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + postgres-array@2.0.0: {} postgres-bytea@1.0.0: {} @@ -5506,11 +6601,40 @@ snapshots: ansi-styles: 5.2.0 react-is: 18.3.1 + process-nextick-args@2.0.1: {} + + process@0.11.10: {} + prompts@2.4.2: dependencies: kleur: 3.0.3 sisteransi: 1.0.5 + proper-lockfile@4.1.2: + dependencies: + graceful-fs: 4.2.11 + retry: 0.12.0 + signal-exit: 3.0.7 + + properties-reader@2.3.0: + dependencies: + mkdirp: 1.0.4 + + protobufjs@7.5.4: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/node': 24.5.2 + long: 5.3.2 + proxy-addr@2.0.7: dependencies: forwarded: 0.2.0 @@ -5518,6 +6642,11 @@ snapshots: proxy-from-env@1.1.0: {} + pump@3.0.3: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + pure-rand@6.1.0: {} qs@6.14.0: @@ -5535,6 +6664,34 @@ snapshots: react-is@18.3.1: {} + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readable-stream@4.7.0: + dependencies: + abort-controller: 3.0.0 + buffer: 6.0.3 + events: 3.3.0 + process: 0.11.10 + string_decoder: 1.3.0 + + readdir-glob@1.1.3: + dependencies: + minimatch: 5.1.6 + regenerate-unicode-properties@10.2.2: dependencies: regenerate: 1.4.2 @@ -5574,6 +6731,8 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + retry@0.12.0: {} + rollup@4.52.0: dependencies: '@types/estree': 1.0.8 @@ -5612,6 +6771,8 @@ snapshots: transitivePeerDependencies: - supports-color + safe-buffer@5.1.2: {} + safe-buffer@5.2.1: {} safer-buffer@2.1.2: {} @@ -5681,12 +6842,18 @@ snapshots: side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 + siginfo@2.0.0: {} + signal-exit@3.0.7: {} + signal-exit@4.1.0: {} + sisteransi@1.0.5: {} slash@3.0.0: {} + source-map-js@1.2.1: {} + source-map-support@0.5.13: dependencies: buffer-from: 1.1.2 @@ -5699,18 +6866,46 @@ snapshots: source-map@0.6.1: {} + split-ca@1.0.1: {} + split2@4.2.0: {} sprintf-js@1.0.3: {} + ssh-remote-port-forward@1.0.4: + dependencies: + '@types/ssh2': 0.5.52 + ssh2: 1.17.0 + + ssh2@1.17.0: + dependencies: + asn1: 0.2.6 + bcrypt-pbkdf: 1.0.2 + optionalDependencies: + cpu-features: 0.0.10 + nan: 2.23.1 + stack-utils@2.0.6: dependencies: escape-string-regexp: 2.0.0 + stackback@0.0.2: {} + statuses@2.0.1: {} statuses@2.0.2: {} + std-env@3.10.0: {} + + streamx@2.23.0: + dependencies: + events-universal: 1.0.1 + fast-fifo: 1.3.2 + text-decoder: 1.2.3 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + string-length@4.0.2: dependencies: char-regex: 1.0.2 @@ -5722,10 +6917,28 @@ snapshots: is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.2 + + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 + strip-ansi@7.1.2: + dependencies: + ansi-regex: 6.2.2 + strip-bom@4.0.0: {} strip-final-newline@2.0.0: {} @@ -5746,12 +6959,90 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + tar-fs@2.1.4: + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.3 + tar-stream: 2.2.0 + + tar-fs@3.1.1: + dependencies: + pump: 3.0.3 + tar-stream: 3.1.7 + optionalDependencies: + bare-fs: 4.5.0 + bare-path: 3.0.0 + transitivePeerDependencies: + - bare-abort-controller + - bare-buffer + - react-native-b4a + + tar-stream@2.2.0: + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.5 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + + tar-stream@3.1.7: + dependencies: + b4a: 1.7.3 + fast-fifo: 1.3.2 + streamx: 2.23.0 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + test-exclude@6.0.0: dependencies: '@istanbuljs/schema': 0.1.3 glob: 7.2.3 minimatch: 3.1.2 + testcontainers@11.8.0: + dependencies: + '@balena/dockerignore': 1.0.2 + '@types/dockerode': 3.3.45 + archiver: 7.0.1 + async-lock: 1.4.1 + byline: 5.0.0 + debug: 4.4.3 + docker-compose: 1.3.0 + dockerode: 4.0.9 + get-port: 7.1.0 + proper-lockfile: 4.1.2 + properties-reader: 2.3.0 + ssh-remote-port-forward: 1.0.4 + tar-fs: 3.1.1 + tmp: 0.2.5 + undici: 7.16.0 + transitivePeerDependencies: + - bare-abort-controller + - bare-buffer + - react-native-b4a + - supports-color + + text-decoder@1.2.3: + dependencies: + b4a: 1.7.3 + transitivePeerDependencies: + - react-native-b4a + + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + tinyrainbow@3.0.3: {} + + tmp@0.2.5: {} + tmpl@1.0.5: {} to-regex-range@5.0.1: @@ -5787,6 +7078,10 @@ snapshots: esbuild: 0.25.10 jest-util: 29.7.0 + tsconfck@3.1.6(typescript@5.9.2): + optionalDependencies: + typescript: 5.9.2 + tslib@2.8.1: {} tsx@4.20.5: @@ -5796,6 +7091,8 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + tweetnacl@0.14.5: {} + type-detect@4.0.8: {} type-fest@0.21.3: {} @@ -5815,8 +7112,15 @@ snapshots: uint8array-extras@1.5.0: {} + undici-types@5.26.5: {} + undici-types@7.12.0: {} + undici-types@7.16.0: + optional: true + + undici@7.16.0: {} + unicode-canonical-property-names-ecmascript@2.0.1: {} unicode-match-property-ecmascript@2.0.0: @@ -5836,6 +7140,10 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 + util-deprecate@1.0.2: {} + + uuid@10.0.0: {} + v8-to-istanbul@9.3.0: dependencies: '@jridgewell/trace-mapping': 0.3.31 @@ -5844,6 +7152,69 @@ snapshots: vary@1.1.2: {} + vite-tsconfig-paths@5.1.4(typescript@5.9.2)(vite@7.2.2(@types/node@24.5.2)(tsx@4.20.5)(yaml@2.8.1)): + dependencies: + debug: 4.4.3 + globrex: 0.1.2 + tsconfck: 3.1.6(typescript@5.9.2) + optionalDependencies: + vite: 7.2.2(@types/node@24.5.2)(tsx@4.20.5)(yaml@2.8.1) + transitivePeerDependencies: + - supports-color + - typescript + + vite@7.2.2(@types/node@24.5.2)(tsx@4.20.5)(yaml@2.8.1): + dependencies: + esbuild: 0.25.10 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.52.0 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 24.5.2 + fsevents: 2.3.3 + tsx: 4.20.5 + yaml: 2.8.1 + + vitest@4.0.8(@types/node@24.5.2)(tsx@4.20.5)(yaml@2.8.1): + dependencies: + '@vitest/expect': 4.0.8 + '@vitest/mocker': 4.0.8(vite@7.2.2(@types/node@24.5.2)(tsx@4.20.5)(yaml@2.8.1)) + '@vitest/pretty-format': 4.0.8 + '@vitest/runner': 4.0.8 + '@vitest/snapshot': 4.0.8 + '@vitest/spy': 4.0.8 + '@vitest/utils': 4.0.8 + debug: 4.4.3 + es-module-lexer: 1.7.0 + expect-type: 1.2.2 + magic-string: 0.30.21 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.0.3 + vite: 7.2.2(@types/node@24.5.2)(tsx@4.20.5)(yaml@2.8.1) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 24.5.2 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + walker@1.0.8: dependencies: makeerror: 1.0.12 @@ -5852,6 +7223,11 @@ snapshots: dependencies: isexe: 2.0.0 + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + wordwrap@1.0.0: {} wrap-ansi@7.0.0: @@ -5860,6 +7236,12 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 5.1.2 + strip-ansi: 7.1.2 + wrappy@1.0.2: {} write-file-atomic@4.0.2: @@ -5873,6 +7255,8 @@ snapshots: yallist@3.1.1: {} + yaml@2.8.1: {} + yargs-parser@21.1.1: {} yargs@17.7.2: @@ -5887,4 +7271,10 @@ snapshots: yocto-queue@0.1.0: {} + zip-stream@6.0.1: + dependencies: + archiver-utils: 5.0.2 + compress-commons: 6.0.2 + readable-stream: 4.7.0 + zod@3.25.76: {} diff --git a/tests/database.test.ts b/tests/database.test.ts new file mode 100644 index 0000000..20660b1 --- /dev/null +++ b/tests/database.test.ts @@ -0,0 +1,25 @@ +import { Client } from "pg"; +import { StartedPostgreSqlContainer } from "@testcontainers/postgresql"; +import { initPgClient } from "./postgresClient"; + +describe.only("Redis", () => { + let container: StartedPostgreSqlContainer; + let pgClient: Client; + + beforeAll(async () => { + const init = await initPgClient(); + container = init.container; + pgClient = init.pgClient; + }); + + afterAll(async () => { + await pgClient.end(); + await container.stop(); + }); + + it("works", async () => { + console.log(container.getConnectionUri()); + const result = await pgClient.query("SELECT 1"); + expect(result.rows[0]).toEqual({ "?column?": 1 }); + }); +}); diff --git a/tests/diffLog.test.ts b/tests/diffLog.test.ts index ede0052..a65a167 100644 --- a/tests/diffLog.test.ts +++ b/tests/diffLog.test.ts @@ -1,7 +1,7 @@ import { getObjDiffs } from "../src/utils/diff"; -jest.mock("../src/utils/slack", () => { - return { notifySlack: jest.fn((str: string) => console.log(str)) }; +vi.mock("../src/utils/slack", () => { + return { notifySlack: vi.fn((str: string) => console.log(str)) }; }); describe("test diff checking", () => { diff --git a/tests/integration.test.ts b/tests/integration.test.ts index eafc36f..5ecf49b 100644 --- a/tests/integration.test.ts +++ b/tests/integration.test.ts @@ -14,7 +14,7 @@ import { } from "./mockTimings"; import { DateTime } from "luxon"; -jest.mock("axios"); +vi.mock("axios"); test("ok", () => {}); test("the whole thing, including locationOverwrites", async () => { mockAxiosGETMethodWithFilePaths({ diff --git a/tests/mockAxios.ts b/tests/mockAxios.ts index 158d37a..cedf1f2 100644 --- a/tests/mockAxios.ts +++ b/tests/mockAxios.ts @@ -1,6 +1,7 @@ import { DateTime } from "luxon"; import { getFileContent, last } from "./utils"; import axios from "axios"; +import { Mock } from "vitest"; const ALL_LOCATIONS_URL = "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/?page=listConcepts"; @@ -28,7 +29,7 @@ export function mockAxiosGETMethod({ conceptHTML?: ((id: string) => string | undefined) | undefined; serverDate: DateTime; }) { - (axios.get as jest.Mock).mockImplementation(async (url: string) => { + (axios.get as Mock).mockImplementation(async (url: string) => { return { data: getHTML(url), headers: { date: serverDate.toRFC2822() }, diff --git a/tests/postgresClient.ts b/tests/postgresClient.ts new file mode 100644 index 0000000..7a19913 --- /dev/null +++ b/tests/postgresClient.ts @@ -0,0 +1,14 @@ +import { Client } from "pg"; +import { PostgreSqlContainer } from "@testcontainers/postgresql"; +export async function initPgClient() { + const container = await new PostgreSqlContainer("postgres:17.5").start(); + const pgClient = new Client({ + host: container.getHost(), + port: container.getPort(), + database: container.getDatabase(), + user: container.getUsername(), + password: container.getPassword(), + }); + await pgClient.connect(); + return { container, pgClient }; +} diff --git a/tsconfig.json b/tsconfig.json index e9322f0..141b8e0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,7 @@ "lib": ["ESNext", "DOM", "DOM.Iterable"], "module": "es6", "skipLibCheck": true, - "types": ["jest", "node"], + "types": ["node", "vitest/globals"], /* Bundler mode */ "rootDir": ".", diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..f335e35 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vitest/config"; +import tsconfigPaths from "vite-tsconfig-paths"; + +export default defineConfig({ + test: { + globals: true, + }, + plugins: [tsconfigPaths()], +}); From 2d06aadf780eff04b565cb2ceaabcd3c163eede4 Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Sun, 9 Nov 2025 19:35:26 -0500 Subject: [PATCH 39/65] fix: tests --- tests/database.test.ts | 2 +- tests/locationMerger.test.ts | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/database.test.ts b/tests/database.test.ts index 20660b1..3401d28 100644 --- a/tests/database.test.ts +++ b/tests/database.test.ts @@ -2,7 +2,7 @@ import { Client } from "pg"; import { StartedPostgreSqlContainer } from "@testcontainers/postgresql"; import { initPgClient } from "./postgresClient"; -describe.only("Redis", () => { +describe("Redis", () => { let container: StartedPostgreSqlContainer; let pgClient: Client; diff --git a/tests/locationMerger.test.ts b/tests/locationMerger.test.ts index 89a73f7..3a65f7c 100644 --- a/tests/locationMerger.test.ts +++ b/tests/locationMerger.test.ts @@ -1,5 +1,6 @@ import { ILocation } from "types"; import ScrapeResultMerger from "../src/utils/locationMerger"; +import { DeeplyAllowMatchers } from "vitest"; const locationA: ILocation = { conceptId: 3, name: "Location Name", @@ -61,7 +62,10 @@ const locationB: ILocation = { todaysSpecials: undefined, }; // https://stackoverflow.com/questions/40135684/is-there-an-array-equality-match-function-that-ignores-element-position-in-jest -const expectArrayEquivalence = (actual: T[], expected: T[]) => { +const expectArrayEquivalence = ( + actual: DeeplyAllowMatchers[], + expected: DeeplyAllowMatchers[] +) => { expect(actual).toEqual(expect.arrayContaining(expected)); expect(expected).toEqual(expect.arrayContaining(actual)); }; From fd00731e89e50eb0289813c5121905827daa3f9b Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Mon, 10 Nov 2025 13:31:46 -0500 Subject: [PATCH 40/65] tests: add 2 mock db tests --- drizzle/0001_broad_lord_tyger.sql | 57 + drizzle/meta/0001_snapshot.json | 495 ++++++++ drizzle/meta/_journal.json | 7 + package.json | 10 +- pnpm-lock.yaml | 1922 +---------------------------- src/db/db.ts | 29 +- src/db/dbQueryUtils.ts | 12 +- src/db/updateLocation.ts | 5 +- src/server.ts | 2 + src/utils/diff.ts | 2 +- tests/database.test.ts | 73 +- tests/postgresClient.ts | 14 - 12 files changed, 660 insertions(+), 1968 deletions(-) create mode 100644 drizzle/0001_broad_lord_tyger.sql create mode 100644 drizzle/meta/0001_snapshot.json diff --git a/drizzle/0001_broad_lord_tyger.sql b/drizzle/0001_broad_lord_tyger.sql new file mode 100644 index 0000000..e0f8c4d --- /dev/null +++ b/drizzle/0001_broad_lord_tyger.sql @@ -0,0 +1,57 @@ +CREATE TYPE "public"."specialType" AS ENUM('special', 'soup');--> statement-breakpoint +CREATE TABLE "concept_id_to_internal_id" ( + "internal_id" text NOT NULL, + "external_id" text PRIMARY KEY NOT NULL, + CONSTRAINT "concept_id_to_internal_id_external_id_unique" UNIQUE("external_id") +); +--> statement-breakpoint +CREATE TABLE "location_data" ( + "id" text PRIMARY KEY NOT NULL, + "name" text, + "short_description" text, + "description" text NOT NULL, + "url" text NOT NULL, + "menu" text, + "location" text NOT NULL, + "coordinate_lat" numeric, + "coordinate_lng" numeric, + "acceptsOnlineOrders" boolean NOT NULL +); +--> statement-breakpoint +CREATE TABLE "specials" ( + "id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "specials_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1), + "location_id" text NOT NULL, + "name" text NOT NULL, + "description" text NOT NULL, + "date" date NOT NULL, + "type" "specialType" NOT NULL +); +--> statement-breakpoint +CREATE TABLE "time_overwrites_table" ( + "location_id" text NOT NULL, + "date" date NOT NULL, + "time_string" text NOT NULL, + CONSTRAINT "time_overwrites_table_location_id_date_pk" PRIMARY KEY("location_id","date") +); +--> statement-breakpoint +CREATE TABLE "location_times" ( + "id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "location_times_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1), + "location_id" text NOT NULL, + "date" date NOT NULL, + "start_time" integer NOT NULL, + "end_time" integer NOT NULL +); +--> statement-breakpoint +ALTER TABLE "overwrites_table" RENAME COLUMN "concept_id" TO "location_id";--> statement-breakpoint + +-- Manual +ALTER TABLE "overwrites_table" ALTER COLUMN "location_id" TYPE TEXT, ALTER COLUMN "location_id" SET NOT NULL; --> statement-breakpoint +-- Manual + +ALTER TABLE "concept_id_to_internal_id" ADD CONSTRAINT "concept_id_to_internal_id_internal_id_location_data_id_fk" FOREIGN KEY ("internal_id") REFERENCES "public"."location_data"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "specials" ADD CONSTRAINT "specials_location_id_location_data_id_fk" FOREIGN KEY ("location_id") REFERENCES "public"."location_data"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "time_overwrites_table" ADD CONSTRAINT "time_overwrites_table_location_id_location_data_id_fk" FOREIGN KEY ("location_id") REFERENCES "public"."location_data"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "location_times" ADD CONSTRAINT "location_times_location_id_location_data_id_fk" FOREIGN KEY ("location_id") REFERENCES "public"."location_data"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +CREATE INDEX "date_lookup" ON "location_times" USING btree ("location_id","date");--> statement-breakpoint +ALTER TABLE "overwrites_table" ADD CONSTRAINT "overwrites_table_location_id_location_data_id_fk" FOREIGN KEY ("location_id") REFERENCES "public"."location_data"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "overwrites_table" DROP COLUMN "times"; \ No newline at end of file diff --git a/drizzle/meta/0001_snapshot.json b/drizzle/meta/0001_snapshot.json new file mode 100644 index 0000000..9cbe72e --- /dev/null +++ b/drizzle/meta/0001_snapshot.json @@ -0,0 +1,495 @@ +{ + "id": "6cb70ab4-5a52-45dc-b62f-616000bbf5ec", + "prevId": "51fd9b98-60b4-4660-92b5-fa6adeb2b033", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.concept_id_to_internal_id": { + "name": "concept_id_to_internal_id", + "schema": "", + "columns": { + "internal_id": { + "name": "internal_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": true, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "concept_id_to_internal_id_internal_id_location_data_id_fk": { + "name": "concept_id_to_internal_id_internal_id_location_data_id_fk", + "tableFrom": "concept_id_to_internal_id", + "tableTo": "location_data", + "columnsFrom": [ + "internal_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "concept_id_to_internal_id_external_id_unique": { + "name": "concept_id_to_internal_id_external_id_unique", + "nullsNotDistinct": false, + "columns": [ + "external_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.emails": { + "name": "emails", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "emails_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.location_data": { + "name": "location_data", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "short_description": { + "name": "short_description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "menu": { + "name": "menu", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "coordinate_lat": { + "name": "coordinate_lat", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "coordinate_lng": { + "name": "coordinate_lng", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "acceptsOnlineOrders": { + "name": "acceptsOnlineOrders", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.overwrites_table": { + "name": "overwrites_table", + "schema": "", + "columns": { + "location_id": { + "name": "location_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "short_description": { + "name": "short_description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "menu": { + "name": "menu", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "coordinateLat": { + "name": "coordinateLat", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "coordinateLng": { + "name": "coordinateLng", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "accepts_online_orders": { + "name": "accepts_online_orders", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "overwrites_table_location_id_location_data_id_fk": { + "name": "overwrites_table_location_id_location_data_id_fk", + "tableFrom": "overwrites_table", + "tableTo": "location_data", + "columnsFrom": [ + "location_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.specials": { + "name": "specials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "specials_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "location_id": { + "name": "location_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "date": { + "name": "date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "specialType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "specials_location_id_location_data_id_fk": { + "name": "specials_location_id_location_data_id_fk", + "tableFrom": "specials", + "tableTo": "location_data", + "columnsFrom": [ + "location_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.time_overwrites_table": { + "name": "time_overwrites_table", + "schema": "", + "columns": { + "location_id": { + "name": "location_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "date": { + "name": "date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "time_string": { + "name": "time_string", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "time_overwrites_table_location_id_location_data_id_fk": { + "name": "time_overwrites_table_location_id_location_data_id_fk", + "tableFrom": "time_overwrites_table", + "tableTo": "location_data", + "columnsFrom": [ + "location_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "time_overwrites_table_location_id_date_pk": { + "name": "time_overwrites_table_location_id_date_pk", + "columns": [ + "location_id", + "date" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.location_times": { + "name": "location_times", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "location_times_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "location_id": { + "name": "location_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "date": { + "name": "date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "start_time": { + "name": "start_time", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "end_time": { + "name": "end_time", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "date_lookup": { + "name": "date_lookup", + "columns": [ + { + "expression": "location_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "location_times_location_id_location_data_id_fk": { + "name": "location_times_location_id_location_data_id_fk", + "tableFrom": "location_times", + "tableTo": "location_data", + "columnsFrom": [ + "location_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.specialType": { + "name": "specialType", + "schema": "public", + "values": [ + "special", + "soup" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 47b6d21..6d0b361 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -8,6 +8,13 @@ "when": 1757973116166, "tag": "0000_wise_firestar", "breakpoints": true + }, + { + "idx": 1, + "version": "7", + "when": 1762796324448, + "tag": "0001_broad_lord_tyger", + "breakpoints": true } ] } \ No newline at end of file diff --git a/package.json b/package.json index b4132fa..6ff99ca 100644 --- a/package.json +++ b/package.json @@ -7,14 +7,16 @@ "dev": "dotenv -- tsx watch src/server.ts", "build": "rollup -c", "start": "NODE_ENV=production node dist/server.js", - "test": "dotenv -e .env.test -- vitest", + "test": "dotenv -e .env.test -- vitest database.test.ts", + "__comment": "add DEBUG=testcontainers* to the test command to get testcontainers debug output", "typecheck": "tsc", "run-prod": "VOL_SRC=postgres_data docker-compose up --build", "db:start": "VOL_SRC=postgres_dev_data docker-compose up -d postgres --build", "db:push": "drizzle-kit push", "db:generate": "drizzle-kit generate", "db:migrate": "drizzle-kit migrate", - "db:studio": "drizzle-kit studio" + "db:studio": "drizzle-kit studio", + "db:check-migrations": "drizzle-kit check" }, "repository": { "type": "git", @@ -47,18 +49,14 @@ "@babel/preset-typescript": "^7.24.7", "@rollup/plugin-typescript": "^12.1.4", "@testcontainers/postgresql": "^11.8.0", - "@types/jest": "^29.5.14", "@types/luxon": "^3.7.1", "@types/node": "^24.0.14", "@types/pg": "^8.15.5", - "babel-jest": "^29.7.0", "dotenv": "^17.2.0", "dotenv-cli": "^8.0.0", - "jest": "^29.7.0", "npm-check-updates": "^18.0.1", "rollup": "^4.45.1", "testcontainers": "^11.8.0", - "ts-jest": "^29.2.5", "tslib": "^2.8.1", "tsx": "^4.20.3", "vitest": "^4.0.8" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c725a50..d94c5a2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -69,9 +69,6 @@ importers: '@testcontainers/postgresql': specifier: ^11.8.0 version: 11.8.0 - '@types/jest': - specifier: ^29.5.14 - version: 29.5.14 '@types/luxon': specifier: ^3.7.1 version: 3.7.1 @@ -81,18 +78,12 @@ importers: '@types/pg': specifier: ^8.15.5 version: 8.15.5 - babel-jest: - specifier: ^29.7.0 - version: 29.7.0(@babel/core@7.28.4) dotenv: specifier: ^17.2.0 version: 17.2.2 dotenv-cli: specifier: ^8.0.0 version: 8.0.0 - jest: - specifier: ^29.7.0 - version: 29.7.0(@types/node@24.5.2) npm-check-updates: specifier: ^18.0.1 version: 18.3.0 @@ -102,9 +93,6 @@ importers: testcontainers: specifier: ^11.8.0 version: 11.8.0 - ts-jest: - specifier: ^29.2.5 - version: 29.4.4(@babel/core@7.28.4)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.4))(esbuild@0.25.10)(jest-util@29.7.0)(jest@29.7.0(@types/node@24.5.2))(typescript@5.9.2) tslib: specifier: ^2.8.1 version: 2.8.1 @@ -261,27 +249,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-async-generators@7.8.4': - resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-bigint@7.8.3': - resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-class-properties@7.12.13': - resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-class-static-block@7.14.5': - resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-import-assertions@7.27.1': resolution: {integrity: sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==} engines: {node: '>=6.9.0'} @@ -294,64 +261,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-import-meta@7.10.4': - resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-json-strings@7.8.3': - resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-jsx@7.27.1': resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-logical-assignment-operators@7.10.4': - resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': - resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-numeric-separator@7.10.4': - resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-object-rest-spread@7.8.3': - resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-optional-catch-binding@7.8.3': - resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-optional-chaining@7.8.3': - resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-private-property-in-object@7.14.5': - resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-top-level-await@7.14.5': - resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-typescript@7.27.1': resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==} engines: {node: '>=6.9.0'} @@ -708,9 +623,6 @@ packages: '@balena/dockerignore@1.0.2': resolution: {integrity: sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==} - '@bcoe/v8-coverage@0.2.3': - resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - '@borewit/text-codec@0.1.1': resolution: {integrity: sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==} @@ -1052,80 +964,6 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} - '@istanbuljs/load-nyc-config@1.1.0': - resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} - engines: {node: '>=8'} - - '@istanbuljs/schema@0.1.3': - resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} - engines: {node: '>=8'} - - '@jest/console@29.7.0': - resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/core@29.7.0': - resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - - '@jest/environment@29.7.0': - resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/expect-utils@29.7.0': - resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/expect@29.7.0': - resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/fake-timers@29.7.0': - resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/globals@29.7.0': - resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/reporters@29.7.0': - resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - - '@jest/schemas@29.6.3': - resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/source-map@29.6.3': - resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/test-result@29.7.0': - resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/test-sequencer@29.7.0': - resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/transform@29.7.0': - resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/types@29.6.3': - resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -1311,18 +1149,9 @@ packages: cpu: [x64] os: [win32] - '@sinclair/typebox@0.27.8': - resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} - '@sinclair/typebox@0.34.41': resolution: {integrity: sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==} - '@sinonjs/commons@3.0.1': - resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} - - '@sinonjs/fake-timers@10.3.0': - resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} - '@standard-schema/spec@1.0.0': resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} @@ -1336,18 +1165,6 @@ packages: '@tokenizer/token@0.3.0': resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} - '@types/babel__core@7.20.5': - resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} - - '@types/babel__generator@7.27.0': - resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} - - '@types/babel__template@7.4.4': - resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} - - '@types/babel__traverse@7.28.0': - resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} - '@types/body-parser@1.19.6': resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} @@ -1375,24 +1192,9 @@ packages: '@types/express@5.0.3': resolution: {integrity: sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==} - '@types/graceful-fs@4.1.9': - resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} - '@types/http-errors@2.0.5': resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} - '@types/istanbul-lib-coverage@2.0.6': - resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} - - '@types/istanbul-lib-report@3.0.3': - resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} - - '@types/istanbul-reports@3.0.4': - resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} - - '@types/jest@29.5.14': - resolution: {integrity: sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==} - '@types/luxon@3.7.1': resolution: {integrity: sha512-H3iskjFIAn5SlJU7OuxUmTEpebK6TKB8rxZShDslBMZJ5u9S//KM1sbdAisiSrqwLQncVjnpi2OK2J51h+4lsg==} @@ -1435,15 +1237,6 @@ packages: '@types/ssh2@1.15.5': resolution: {integrity: sha512-N1ASjp/nXH3ovBHddRJpli4ozpk6UdDYIX4RJWFa9L1YKnzdhTlVmiGHm4DZnj/jLbqZpes4aeR30EFGQtvhQQ==} - '@types/stack-utils@2.0.3': - resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} - - '@types/yargs-parser@21.0.3': - resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} - - '@types/yargs@17.0.33': - resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} - '@vitest/expect@4.0.8': resolution: {integrity: sha512-Rv0eabdP/xjAHQGr8cjBm+NnLHNoL268lMDK85w2aAGLFoVKLd8QGnVon5lLtkXQCoYaNL0wg04EGnyKkkKhPA==} @@ -1481,10 +1274,6 @@ packages: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} engines: {node: '>= 0.6'} - ansi-escapes@4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} - engines: {node: '>=8'} - ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -1497,18 +1286,10 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - ansi-styles@6.2.3: resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} - anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - archiver-utils@5.0.2: resolution: {integrity: sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==} engines: {node: '>= 14'} @@ -1517,9 +1298,6 @@ packages: resolution: {integrity: sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==} engines: {node: '>= 14'} - argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} - asn1@0.2.6: resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} @@ -1547,20 +1325,6 @@ packages: react-native-b4a: optional: true - babel-jest@29.7.0: - resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@babel/core': ^7.8.0 - - babel-plugin-istanbul@6.1.1: - resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} - engines: {node: '>=8'} - - babel-plugin-jest-hoist@29.6.3: - resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - babel-plugin-polyfill-corejs2@0.4.14: resolution: {integrity: sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==} peerDependencies: @@ -1576,17 +1340,6 @@ packages: peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - babel-preset-current-node-syntax@1.2.0: - resolution: {integrity: sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==} - peerDependencies: - '@babel/core': ^7.0.0 || ^8.0.0-0 - - babel-preset-jest@29.6.3: - resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@babel/core': ^7.0.0 - balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -1648,28 +1401,14 @@ packages: boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} - brace-expansion@1.1.12: - resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} - brace-expansion@2.0.2: resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} - braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} - browserslist@4.26.2: resolution: {integrity: sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - bs-logger@0.2.6: - resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} - engines: {node: '>= 6'} - - bser@2.1.1: - resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} - buffer-crc32@1.0.0: resolution: {integrity: sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==} engines: {node: '>=8.0.0'} @@ -1708,18 +1447,6 @@ packages: resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} - callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} - - camelcase@5.3.1: - resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} - engines: {node: '>=6'} - - camelcase@6.3.0: - resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} - engines: {node: '>=10'} - caniuse-lite@1.0.30001743: resolution: {integrity: sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw==} @@ -1727,14 +1454,6 @@ packages: resolution: {integrity: sha512-aUTnJc/JipRzJrNADXVvpVqi6CO0dn3nx4EVPxijri+fj3LUUDyZQOgVeW54Ob3Y1Xh9Iz8f+CgaCl8v0mn9bA==} engines: {node: '>=18'} - chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} - - char-regex@1.0.2: - resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} - engines: {node: '>=10'} - cheerio-select@2.1.0: resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} @@ -1745,24 +1464,10 @@ packages: chownr@1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} - ci-info@3.9.0: - resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} - engines: {node: '>=8'} - - cjs-module-lexer@1.4.3: - resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} - cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} - co@4.6.0: - resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} - engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} - - collect-v8-coverage@1.0.2: - resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} - color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -1778,9 +1483,6 @@ packages: resolution: {integrity: sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==} engines: {node: '>= 14'} - concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - content-disposition@1.0.0: resolution: {integrity: sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==} engines: {node: '>= 0.6'} @@ -1823,11 +1525,6 @@ packages: resolution: {integrity: sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==} engines: {node: '>= 14'} - create-jest@29.7.0: - resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -1851,18 +1548,6 @@ packages: supports-color: optional: true - dedent@1.7.0: - resolution: {integrity: sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==} - peerDependencies: - babel-plugin-macros: ^3.1.0 - peerDependenciesMeta: - babel-plugin-macros: - optional: true - - deepmerge@4.3.1: - resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} - engines: {node: '>=0.10.0'} - delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -1871,14 +1556,6 @@ packages: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} - detect-newline@3.1.0: - resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} - engines: {node: '>=8'} - - diff-sequences@29.6.3: - resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - docker-compose@1.3.0: resolution: {integrity: sha512-7Gevk/5eGD50+eMD+XDnFnOrruFkL0kSd7jEG4cjmqweDSUhB7i0g8is/nBdVpl+Bx338SqIB2GLKm32M+Vs6g==} engines: {node: '>= 6.0.0'} @@ -2044,10 +1721,6 @@ packages: typescript: optional: true - emittery@0.13.1: - resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} - engines: {node: '>=12'} - emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -2069,9 +1742,6 @@ packages: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} - error-ex@1.3.4: - resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} - es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} @@ -2113,15 +1783,6 @@ packages: escape-html@1.0.3: resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} - escape-string-regexp@2.0.0: - resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} - engines: {node: '>=8'} - - esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} @@ -2155,22 +1816,10 @@ packages: '@sinclair/typebox': optional: true - execa@5.1.1: - resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} - engines: {node: '>=10'} - - exit@0.1.2: - resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} - engines: {node: '>= 0.8.0'} - expect-type@1.2.2: resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} engines: {node: '>=12.0.0'} - expect@29.7.0: - resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - express@5.1.0: resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==} engines: {node: '>= 18'} @@ -2181,12 +1830,6 @@ packages: fast-fifo@1.3.2: resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} - fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - - fb-watchman@2.0.2: - resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} - fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -2203,18 +1846,10 @@ packages: resolution: {integrity: sha512-ek5xNX2YBYlXhiUXui3D/BXa3LdqPmoLJ7rqEx2bKJ7EAUEfmXgW0Das7Dc6Nr9MvqaOnIqiPV0mZk/r/UpNAg==} engines: {node: '>=20'} - fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} - finalhandler@2.1.0: resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} engines: {node: '>= 0.8'} - find-up@4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} - follow-redirects@1.15.11: resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} engines: {node: '>=4.0'} @@ -2243,9 +1878,6 @@ packages: fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} - fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -2266,10 +1898,6 @@ packages: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} - get-package-type@0.1.0: - resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} - engines: {node: '>=8.0.0'} - get-port@7.1.0: resolution: {integrity: sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==} engines: {node: '>=16'} @@ -2278,10 +1906,6 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} - get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - get-tsconfig@4.10.1: resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} @@ -2289,10 +1913,6 @@ packages: resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} hasBin: true - glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported - globrex@0.1.2: resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} @@ -2303,15 +1923,6 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - handlebars@4.7.8: - resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} - engines: {node: '>=0.4.7'} - hasBin: true - - has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - has-symbols@1.1.0: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} @@ -2328,9 +1939,6 @@ packages: resolution: {integrity: sha512-JW8Bb4RFWD9iOKxg5PbUarBYGM99IcxFl2FPBo2gSJO11jjUDqlP1Bmfyqt8Z/dGhIQ63PMA9LdcLefXyIasyg==} engines: {node: '>=16.9.0'} - html-escaper@2.0.2: - resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} - htmlparser2@8.0.2: resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} @@ -2338,10 +1946,6 @@ packages: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} - human-signals@2.1.0: - resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} - engines: {node: '>=10.17.0'} - iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} @@ -2353,19 +1957,6 @@ packages: ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - import-local@3.2.0: - resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} - engines: {node: '>=8'} - hasBin: true - - imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - - inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. - inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -2373,9 +1964,6 @@ packages: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} - is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - is-core-module@2.16.1: resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} engines: {node: '>= 0.4'} @@ -2384,14 +1972,6 @@ packages: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - is-generator-fn@2.1.0: - resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} - engines: {node: '>=6'} - - is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - is-promise@4.0.0: resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} @@ -2405,169 +1985,12 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - istanbul-lib-coverage@3.2.2: - resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} - engines: {node: '>=8'} - - istanbul-lib-instrument@5.2.1: - resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} - engines: {node: '>=8'} - - istanbul-lib-instrument@6.0.3: - resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} - engines: {node: '>=10'} - - istanbul-lib-report@3.0.1: - resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} - engines: {node: '>=10'} - - istanbul-lib-source-maps@4.0.1: - resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} - engines: {node: '>=10'} - - istanbul-reports@3.2.0: - resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} - engines: {node: '>=8'} - jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - jest-changed-files@29.7.0: - resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-circus@29.7.0: - resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-cli@29.7.0: - resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - - jest-config@29.7.0: - resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@types/node': '*' - ts-node: '>=9.0.0' - peerDependenciesMeta: - '@types/node': - optional: true - ts-node: - optional: true - - jest-diff@29.7.0: - resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-docblock@29.7.0: - resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-each@29.7.0: - resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-environment-node@29.7.0: - resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-get-type@29.6.3: - resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-haste-map@29.7.0: - resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-leak-detector@29.7.0: - resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-matcher-utils@29.7.0: - resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-message-util@29.7.0: - resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-mock@29.7.0: - resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-pnp-resolver@1.2.3: - resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} - engines: {node: '>=6'} - peerDependencies: - jest-resolve: '*' - peerDependenciesMeta: - jest-resolve: - optional: true - - jest-regex-util@29.6.3: - resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-resolve-dependencies@29.7.0: - resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-resolve@29.7.0: - resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-runner@29.7.0: - resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-runtime@29.7.0: - resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-snapshot@29.7.0: - resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-util@29.7.0: - resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-validate@29.7.0: - resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-watcher@29.7.0: - resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-worker@29.7.0: - resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest@29.7.0: - resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@3.14.1: - resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} - hasBin: true - jsesc@3.0.2: resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==} engines: {node: '>=6'} @@ -2578,42 +2001,21 @@ packages: engines: {node: '>=6'} hasBin: true - json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} hasBin: true - kleur@3.0.3: - resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} - engines: {node: '>=6'} - lazystream@1.0.1: resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} engines: {node: '>= 0.6.3'} - leven@3.1.0: - resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} - engines: {node: '>=6'} - - lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - - locate-path@5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} - lodash.camelcase@4.3.0: resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} - lodash.memoize@4.1.2: - resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} - lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} @@ -2633,16 +2035,6 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - make-dir@4.0.0: - resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} - engines: {node: '>=10'} - - make-error@1.3.6: - resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - - makeerror@1.0.12: - resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} - math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} @@ -2658,13 +2050,6 @@ packages: resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} engines: {node: '>=18'} - merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - - micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} - engines: {node: '>=8.6'} - mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} @@ -2681,13 +2066,6 @@ packages: resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} engines: {node: '>= 0.6'} - mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - - minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - minimatch@5.1.6: resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} engines: {node: '>=10'} @@ -2722,19 +2100,10 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - negotiator@1.0.0: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} - neo-async@2.6.2: - resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} - - node-int64@0.4.0: - resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - node-releases@2.0.21: resolution: {integrity: sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==} @@ -2747,10 +2116,6 @@ packages: engines: {node: ^18.18.0 || >=20.0.0, npm: '>=8.12.1'} hasBin: true - npm-run-path@4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} - engines: {node: '>=8'} - nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} @@ -2765,36 +2130,12 @@ packages: once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} - openapi-types@12.1.3: resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} - p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} - - p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} - - p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} - - p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - parse-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} - parse5-htmlparser2-tree-adapter@7.1.0: resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} @@ -2805,14 +2146,6 @@ packages: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} - path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - - path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -2867,22 +2200,10 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - picomatch@4.0.3: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} - pirates@4.0.7: - resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} - engines: {node: '>= 6'} - - pkg-dir@4.2.0: - resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} - engines: {node: '>=8'} - postcss@8.5.6: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} @@ -2903,10 +2224,6 @@ packages: resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} engines: {node: '>=0.10.0'} - pretty-format@29.7.0: - resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} @@ -2914,10 +2231,6 @@ packages: resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} engines: {node: '>= 0.6.0'} - prompts@2.4.2: - resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} - engines: {node: '>= 6'} - proper-lockfile@4.1.2: resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} @@ -2939,9 +2252,6 @@ packages: pump@3.0.3: resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} - pure-rand@6.1.0: - resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} - qs@6.14.0: resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} engines: {node: '>=0.6'} @@ -2954,9 +2264,6 @@ packages: resolution: {integrity: sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==} engines: {node: '>= 0.10'} - react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - readable-stream@2.3.8: resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} @@ -2993,21 +2300,9 @@ packages: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} - resolve-cwd@3.0.0: - resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} - engines: {node: '>=8'} - - resolve-from@5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} - resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - resolve.exports@2.0.3: - resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} - engines: {node: '>=10'} - resolve@1.22.10: resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} engines: {node: '>= 0.4'} @@ -3039,11 +2334,6 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.7.2: - resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} - engines: {node: '>=10'} - hasBin: true - send@1.2.0: resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} engines: {node: '>= 18'} @@ -3089,20 +2379,10 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} - sisteransi@1.0.5: - resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - - slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} - source-map-support@0.5.13: - resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} - source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} @@ -3117,9 +2397,6 @@ packages: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} - sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - ssh-remote-port-forward@1.0.4: resolution: {integrity: sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ==} @@ -3127,10 +2404,6 @@ packages: resolution: {integrity: sha512-wPldCk3asibAjQ/kziWQQt1Wh3PgDFpC0XpwclzKcdT1vql6KeYxf5LIt4nlFkUeR8WuphYMKqUA56X4rjbfgQ==} engines: {node: '>=10.16.0'} - stack-utils@2.0.6: - resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} - engines: {node: '>=10'} - stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} @@ -3148,10 +2421,6 @@ packages: streamx@2.23.0: resolution: {integrity: sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==} - string-length@4.0.2: - resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} - engines: {node: '>=10'} - string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -3174,30 +2443,10 @@ packages: resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} engines: {node: '>=12'} - strip-bom@4.0.0: - resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} - engines: {node: '>=8'} - - strip-final-newline@2.0.0: - resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} - engines: {node: '>=6'} - - strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - strtok3@10.3.4: resolution: {integrity: sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==} engines: {node: '>=18'} - supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - - supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} - supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} @@ -3215,10 +2464,6 @@ packages: tar-stream@3.1.7: resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} - test-exclude@6.0.0: - resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} - engines: {node: '>=8'} - testcontainers@11.8.0: resolution: {integrity: sha512-kY2DfuUB1NSvmpG7wCpi/aTaIJaHcX53WSAlWHsj0La7E7fPnVFOpooheczE3fH9T+OgD5OB5IeBpFitIqqu6w==} @@ -3243,13 +2488,6 @@ packages: resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} engines: {node: '>=14.14'} - tmpl@1.0.5: - resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} - - to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - toidentifier@1.0.1: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} @@ -3258,33 +2496,6 @@ packages: resolution: {integrity: sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ==} engines: {node: '>=14.16'} - ts-jest@29.4.4: - resolution: {integrity: sha512-ccVcRABct5ZELCT5U0+DZwkXMCcOCLi2doHRrKy1nK/s7J7bch6TzJMsrY09WxgUUIP/ITfmcDS8D2yl63rnXw==} - engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@babel/core': '>=7.0.0-beta.0 <8' - '@jest/transform': ^29.0.0 || ^30.0.0 - '@jest/types': ^29.0.0 || ^30.0.0 - babel-jest: ^29.0.0 || ^30.0.0 - esbuild: '*' - jest: ^29.0.0 || ^30.0.0 - jest-util: ^29.0.0 || ^30.0.0 - typescript: '>=4.3 <6' - peerDependenciesMeta: - '@babel/core': - optional: true - '@jest/transform': - optional: true - '@jest/types': - optional: true - babel-jest: - optional: true - esbuild: - optional: true - jest-util: - optional: true - tsconfck@3.1.6: resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} engines: {node: ^18 || >=20} @@ -3306,18 +2517,6 @@ packages: tweetnacl@0.14.5: resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} - type-detect@4.0.8: - resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} - engines: {node: '>=4'} - - type-fest@0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} - engines: {node: '>=10'} - - type-fest@4.41.0: - resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} - engines: {node: '>=16'} - type-is@2.0.1: resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} engines: {node: '>= 0.6'} @@ -3327,11 +2526,6 @@ packages: engines: {node: '>=14.17'} hasBin: true - uglify-js@3.19.3: - resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} - engines: {node: '>=0.8.0'} - hasBin: true - uint8array-extras@1.5.0: resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==} engines: {node: '>=18'} @@ -3382,10 +2576,6 @@ packages: resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} hasBin: true - v8-to-istanbul@9.3.0: - resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} - engines: {node: '>=10.12.0'} - vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} @@ -3472,9 +2662,6 @@ packages: jsdom: optional: true - walker@1.0.8: - resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} - which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -3485,9 +2672,6 @@ packages: engines: {node: '>=8'} hasBin: true - wordwrap@1.0.0: - resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} - wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -3499,10 +2683,6 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - write-file-atomic@4.0.2: - resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} @@ -3527,10 +2707,6 @@ packages: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} - yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} - zip-stream@6.0.1: resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==} engines: {node: '>= 14'} @@ -3737,26 +2913,6 @@ snapshots: dependencies: '@babel/core': 7.28.4 - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-import-assertions@7.27.1(@babel/core@7.28.4)': dependencies: '@babel/core': 7.28.4 @@ -3767,61 +2923,11 @@ snapshots: '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.4)': dependencies: '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.4)': dependencies: '@babel/core': 7.28.4 @@ -4296,8 +3402,6 @@ snapshots: '@balena/dockerignore@1.0.2': {} - '@bcoe/v8-coverage@0.2.3': {} - '@borewit/text-codec@0.1.1': {} '@drizzle-team/brocli@0.10.2': {} @@ -4503,178 +3607,6 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 - '@istanbuljs/load-nyc-config@1.1.0': - dependencies: - camelcase: 5.3.1 - find-up: 4.1.0 - get-package-type: 0.1.0 - js-yaml: 3.14.1 - resolve-from: 5.0.0 - - '@istanbuljs/schema@0.1.3': {} - - '@jest/console@29.7.0': - dependencies: - '@jest/types': 29.6.3 - '@types/node': 24.5.2 - chalk: 4.1.2 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - slash: 3.0.0 - - '@jest/core@29.7.0': - dependencies: - '@jest/console': 29.7.0 - '@jest/reporters': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 24.5.2 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - ci-info: 3.9.0 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@24.5.2) - jest-haste-map: 29.7.0 - jest-message-util: 29.7.0 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-resolve-dependencies: 29.7.0 - jest-runner: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - jest-watcher: 29.7.0 - micromatch: 4.0.8 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-ansi: 6.0.1 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - ts-node - - '@jest/environment@29.7.0': - dependencies: - '@jest/fake-timers': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 24.5.2 - jest-mock: 29.7.0 - - '@jest/expect-utils@29.7.0': - dependencies: - jest-get-type: 29.6.3 - - '@jest/expect@29.7.0': - dependencies: - expect: 29.7.0 - jest-snapshot: 29.7.0 - transitivePeerDependencies: - - supports-color - - '@jest/fake-timers@29.7.0': - dependencies: - '@jest/types': 29.6.3 - '@sinonjs/fake-timers': 10.3.0 - '@types/node': 24.5.2 - jest-message-util: 29.7.0 - jest-mock: 29.7.0 - jest-util: 29.7.0 - - '@jest/globals@29.7.0': - dependencies: - '@jest/environment': 29.7.0 - '@jest/expect': 29.7.0 - '@jest/types': 29.6.3 - jest-mock: 29.7.0 - transitivePeerDependencies: - - supports-color - - '@jest/reporters@29.7.0': - dependencies: - '@bcoe/v8-coverage': 0.2.3 - '@jest/console': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.31 - '@types/node': 24.5.2 - chalk: 4.1.2 - collect-v8-coverage: 1.0.2 - exit: 0.1.2 - glob: 7.2.3 - graceful-fs: 4.2.11 - istanbul-lib-coverage: 3.2.2 - istanbul-lib-instrument: 6.0.3 - istanbul-lib-report: 3.0.1 - istanbul-lib-source-maps: 4.0.1 - istanbul-reports: 3.2.0 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - jest-worker: 29.7.0 - slash: 3.0.0 - string-length: 4.0.2 - strip-ansi: 6.0.1 - v8-to-istanbul: 9.3.0 - transitivePeerDependencies: - - supports-color - - '@jest/schemas@29.6.3': - dependencies: - '@sinclair/typebox': 0.27.8 - - '@jest/source-map@29.6.3': - dependencies: - '@jridgewell/trace-mapping': 0.3.31 - callsites: 3.1.0 - graceful-fs: 4.2.11 - - '@jest/test-result@29.7.0': - dependencies: - '@jest/console': 29.7.0 - '@jest/types': 29.6.3 - '@types/istanbul-lib-coverage': 2.0.6 - collect-v8-coverage: 1.0.2 - - '@jest/test-sequencer@29.7.0': - dependencies: - '@jest/test-result': 29.7.0 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - slash: 3.0.0 - - '@jest/transform@29.7.0': - dependencies: - '@babel/core': 7.28.4 - '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.31 - babel-plugin-istanbul: 6.1.1 - chalk: 4.1.2 - convert-source-map: 2.0.0 - fast-json-stable-stringify: 2.1.0 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - jest-regex-util: 29.6.3 - jest-util: 29.7.0 - micromatch: 4.0.8 - pirates: 4.0.7 - slash: 3.0.0 - write-file-atomic: 4.0.2 - transitivePeerDependencies: - - supports-color - - '@jest/types@29.6.3': - dependencies: - '@jest/schemas': 29.6.3 - '@types/istanbul-lib-coverage': 2.0.6 - '@types/istanbul-reports': 3.0.4 - '@types/node': 24.5.2 - '@types/yargs': 17.0.33 - chalk: 4.1.2 - '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -4805,18 +3737,8 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.52.0': optional: true - '@sinclair/typebox@0.27.8': {} - '@sinclair/typebox@0.34.41': {} - '@sinonjs/commons@3.0.1': - dependencies: - type-detect: 4.0.8 - - '@sinonjs/fake-timers@10.3.0': - dependencies: - '@sinonjs/commons': 3.0.1 - '@standard-schema/spec@1.0.0': {} '@testcontainers/postgresql@11.8.0': @@ -4838,27 +3760,6 @@ snapshots: '@tokenizer/token@0.3.0': {} - '@types/babel__core@7.20.5': - dependencies: - '@babel/parser': 7.28.4 - '@babel/types': 7.28.4 - '@types/babel__generator': 7.27.0 - '@types/babel__template': 7.4.4 - '@types/babel__traverse': 7.28.0 - - '@types/babel__generator@7.27.0': - dependencies: - '@babel/types': 7.28.4 - - '@types/babel__template@7.4.4': - dependencies: - '@babel/parser': 7.28.4 - '@babel/types': 7.28.4 - - '@types/babel__traverse@7.28.0': - dependencies: - '@babel/types': 7.28.4 - '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 @@ -4901,27 +3802,8 @@ snapshots: '@types/express-serve-static-core': 5.0.7 '@types/serve-static': 1.15.8 - '@types/graceful-fs@4.1.9': - dependencies: - '@types/node': 24.5.2 - '@types/http-errors@2.0.5': {} - '@types/istanbul-lib-coverage@2.0.6': {} - - '@types/istanbul-lib-report@3.0.3': - dependencies: - '@types/istanbul-lib-coverage': 2.0.6 - - '@types/istanbul-reports@3.0.4': - dependencies: - '@types/istanbul-lib-report': 3.0.3 - - '@types/jest@29.5.14': - dependencies: - expect: 29.7.0 - pretty-format: 29.7.0 - '@types/luxon@3.7.1': {} '@types/mime@1.3.5': {} @@ -4978,14 +3860,6 @@ snapshots: dependencies: '@types/node': 18.19.130 - '@types/stack-utils@2.0.3': {} - - '@types/yargs-parser@21.0.3': {} - - '@types/yargs@17.0.33': - dependencies: - '@types/yargs-parser': 21.0.3 - '@vitest/expect@4.0.8': dependencies: '@standard-schema/spec': 1.0.0 @@ -5034,10 +3908,6 @@ snapshots: mime-types: 3.0.1 negotiator: 1.0.0 - ansi-escapes@4.3.2: - dependencies: - type-fest: 0.21.3 - ansi-regex@5.0.1: {} ansi-regex@6.2.2: {} @@ -5046,15 +3916,8 @@ snapshots: dependencies: color-convert: 2.0.1 - ansi-styles@5.2.0: {} - ansi-styles@6.2.3: {} - anymatch@3.1.3: - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.1 - archiver-utils@5.0.2: dependencies: glob: 10.4.5 @@ -5078,10 +3941,6 @@ snapshots: - bare-abort-controller - react-native-b4a - argparse@1.0.10: - dependencies: - sprintf-js: 1.0.3 - asn1@0.2.6: dependencies: safer-buffer: 2.1.2 @@ -5104,36 +3963,6 @@ snapshots: b4a@1.7.3: {} - babel-jest@29.7.0(@babel/core@7.28.4): - dependencies: - '@babel/core': 7.28.4 - '@jest/transform': 29.7.0 - '@types/babel__core': 7.20.5 - babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.6.3(@babel/core@7.28.4) - chalk: 4.1.2 - graceful-fs: 4.2.11 - slash: 3.0.0 - transitivePeerDependencies: - - supports-color - - babel-plugin-istanbul@6.1.1: - dependencies: - '@babel/helper-plugin-utils': 7.27.1 - '@istanbuljs/load-nyc-config': 1.1.0 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-instrument: 5.2.1 - test-exclude: 6.0.0 - transitivePeerDependencies: - - supports-color - - babel-plugin-jest-hoist@29.6.3: - dependencies: - '@babel/template': 7.27.2 - '@babel/types': 7.28.4 - '@types/babel__core': 7.20.5 - '@types/babel__traverse': 7.28.0 - babel-plugin-polyfill-corejs2@0.4.14(@babel/core@7.28.4): dependencies: '@babel/compat-data': 7.28.4 @@ -5158,36 +3987,11 @@ snapshots: transitivePeerDependencies: - supports-color - babel-preset-current-node-syntax@1.2.0(@babel/core@7.28.4): - dependencies: - '@babel/core': 7.28.4 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.28.4) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.28.4) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.28.4) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.28.4) - '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.4) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.28.4) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.28.4) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.28.4) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.28.4) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.28.4) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.28.4) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.28.4) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.28.4) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.28.4) - - babel-preset-jest@29.6.3(@babel/core@7.28.4): - dependencies: - '@babel/core': 7.28.4 - babel-plugin-jest-hoist: 29.6.3 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.4) - - balanced-match@1.0.2: {} - - bare-events@2.8.2: {} - - bare-fs@4.5.0: + balanced-match@1.0.2: {} + + bare-events@2.8.2: {} + + bare-fs@4.5.0: dependencies: bare-events: 2.8.2 bare-path: 3.0.0 @@ -5252,19 +4056,10 @@ snapshots: boolbase@1.0.0: {} - brace-expansion@1.1.12: - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - brace-expansion@2.0.2: dependencies: balanced-match: 1.0.2 - braces@3.0.3: - dependencies: - fill-range: 7.1.1 - browserslist@4.26.2: dependencies: baseline-browser-mapping: 2.8.6 @@ -5273,14 +4068,6 @@ snapshots: node-releases: 2.0.21 update-browserslist-db: 1.1.3(browserslist@4.26.2) - bs-logger@0.2.6: - dependencies: - fast-json-stable-stringify: 2.1.0 - - bser@2.1.1: - dependencies: - node-int64: 0.4.0 - buffer-crc32@1.0.0: {} buffer-from@1.1.2: {} @@ -5318,23 +4105,10 @@ snapshots: call-bind-apply-helpers: 1.0.2 get-intrinsic: 1.3.0 - callsites@3.1.0: {} - - camelcase@5.3.1: {} - - camelcase@6.3.0: {} - caniuse-lite@1.0.30001743: {} chai@6.2.0: {} - chalk@4.1.2: - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - - char-regex@1.0.2: {} - cheerio-select@2.1.0: dependencies: boolbase: 1.0.0 @@ -5356,20 +4130,12 @@ snapshots: chownr@1.1.4: {} - ci-info@3.9.0: {} - - cjs-module-lexer@1.4.3: {} - cliui@8.0.1: dependencies: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - co@4.6.0: {} - - collect-v8-coverage@1.0.2: {} - color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -5388,8 +4154,6 @@ snapshots: normalize-path: 3.0.0 readable-stream: 4.7.0 - concat-map@0.0.1: {} - content-disposition@1.0.0: dependencies: safe-buffer: 5.2.1 @@ -5423,21 +4187,6 @@ snapshots: crc-32: 1.2.2 readable-stream: 4.7.0 - create-jest@29.7.0(@types/node@24.5.2): - dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@24.5.2) - jest-util: 29.7.0 - prompts: 2.4.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -5461,18 +4210,10 @@ snapshots: dependencies: ms: 2.1.3 - dedent@1.7.0: {} - - deepmerge@4.3.1: {} - delayed-stream@1.0.0: {} depd@2.0.0: {} - detect-newline@3.1.0: {} - - diff-sequences@29.6.3: {} - docker-compose@1.3.0: dependencies: yaml: 2.8.1 @@ -5568,8 +4309,6 @@ snapshots: optionalDependencies: typescript: 5.9.2 - emittery@0.13.1: {} - emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} @@ -5584,10 +4323,6 @@ snapshots: entities@6.0.1: {} - error-ex@1.3.4: - dependencies: - is-arrayish: 0.2.1 - es-define-property@1.0.1: {} es-errors@1.3.0: {} @@ -5670,10 +4405,6 @@ snapshots: escape-html@1.0.3: {} - escape-string-regexp@2.0.0: {} - - esprima@4.0.1: {} - estree-walker@2.0.2: {} estree-walker@3.0.3: @@ -5698,30 +4429,8 @@ snapshots: optionalDependencies: '@sinclair/typebox': 0.34.41 - execa@5.1.1: - dependencies: - cross-spawn: 7.0.6 - get-stream: 6.0.1 - human-signals: 2.1.0 - is-stream: 2.0.1 - merge-stream: 2.0.0 - npm-run-path: 4.0.1 - onetime: 5.1.2 - signal-exit: 3.0.7 - strip-final-newline: 2.0.0 - - exit@0.1.2: {} - expect-type@1.2.2: {} - expect@29.7.0: - dependencies: - '@jest/expect-utils': 29.7.0 - jest-get-type: 29.6.3 - jest-matcher-utils: 29.7.0 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - express@5.1.0: dependencies: accepts: 2.0.0 @@ -5758,12 +4467,6 @@ snapshots: fast-fifo@1.3.2: {} - fast-json-stable-stringify@2.1.0: {} - - fb-watchman@2.0.2: - dependencies: - bser: 2.1.1 - fdir@6.5.0(picomatch@4.0.3): optionalDependencies: picomatch: 4.0.3 @@ -5779,10 +4482,6 @@ snapshots: transitivePeerDependencies: - supports-color - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 - finalhandler@2.1.0: dependencies: debug: 4.4.3 @@ -5794,11 +4493,6 @@ snapshots: transitivePeerDependencies: - supports-color - find-up@4.1.0: - dependencies: - locate-path: 5.0.0 - path-exists: 4.0.0 - follow-redirects@1.15.11: {} foreground-child@3.3.1: @@ -5820,8 +4514,6 @@ snapshots: fs-constants@1.0.0: {} - fs.realpath@1.0.0: {} - fsevents@2.3.3: optional: true @@ -5844,8 +4536,6 @@ snapshots: hasown: 2.0.2 math-intrinsics: 1.1.0 - get-package-type@0.1.0: {} - get-port@7.1.0: {} get-proto@1.0.1: @@ -5853,8 +4543,6 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 - get-stream@6.0.1: {} - get-tsconfig@4.10.1: dependencies: resolve-pkg-maps: 1.0.0 @@ -5868,32 +4556,12 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 1.11.1 - glob@7.2.3: - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - globrex@0.1.2: {} gopd@1.2.0: {} graceful-fs@4.2.11: {} - handlebars@4.7.8: - dependencies: - minimist: 1.2.8 - neo-async: 2.6.2 - source-map: 0.6.1 - wordwrap: 1.0.0 - optionalDependencies: - uglify-js: 3.19.3 - - has-flag@4.0.0: {} - has-symbols@1.1.0: {} has-tostringtag@1.0.2: @@ -5906,8 +4574,6 @@ snapshots: hono@4.9.8: {} - html-escaper@2.0.2: {} - htmlparser2@8.0.2: dependencies: domelementtype: 2.3.0 @@ -5923,8 +4589,6 @@ snapshots: statuses: 2.0.1 toidentifier: 1.0.1 - human-signals@2.1.0: {} - iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 @@ -5935,34 +4599,16 @@ snapshots: ieee754@1.2.1: {} - import-local@3.2.0: - dependencies: - pkg-dir: 4.2.0 - resolve-cwd: 3.0.0 - - imurmurhash@0.1.4: {} - - inflight@1.0.6: - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - inherits@2.0.4: {} ipaddr.js@1.9.1: {} - is-arrayish@0.2.1: {} - is-core-module@2.16.1: dependencies: hasown: 2.0.2 is-fullwidth-code-point@3.0.0: {} - is-generator-fn@2.1.0: {} - - is-number@7.0.0: {} - is-promise@4.0.0: {} is-stream@2.0.1: {} @@ -5971,396 +4617,28 @@ snapshots: isexe@2.0.0: {} - istanbul-lib-coverage@3.2.2: {} - - istanbul-lib-instrument@5.2.1: - dependencies: - '@babel/core': 7.28.4 - '@babel/parser': 7.28.4 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-coverage: 3.2.2 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - istanbul-lib-instrument@6.0.3: - dependencies: - '@babel/core': 7.28.4 - '@babel/parser': 7.28.4 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-coverage: 3.2.2 - semver: 7.7.2 - transitivePeerDependencies: - - supports-color - - istanbul-lib-report@3.0.1: - dependencies: - istanbul-lib-coverage: 3.2.2 - make-dir: 4.0.0 - supports-color: 7.2.0 - - istanbul-lib-source-maps@4.0.1: - dependencies: - debug: 4.4.3 - istanbul-lib-coverage: 3.2.2 - source-map: 0.6.1 - transitivePeerDependencies: - - supports-color - - istanbul-reports@3.2.0: - dependencies: - html-escaper: 2.0.2 - istanbul-lib-report: 3.0.1 - jackspeak@3.4.3: dependencies: '@isaacs/cliui': 8.0.2 optionalDependencies: '@pkgjs/parseargs': 0.11.0 - jest-changed-files@29.7.0: - dependencies: - execa: 5.1.1 - jest-util: 29.7.0 - p-limit: 3.1.0 - - jest-circus@29.7.0: - dependencies: - '@jest/environment': 29.7.0 - '@jest/expect': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 24.5.2 - chalk: 4.1.2 - co: 4.6.0 - dedent: 1.7.0 - is-generator-fn: 2.1.0 - jest-each: 29.7.0 - jest-matcher-utils: 29.7.0 - jest-message-util: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - p-limit: 3.1.0 - pretty-format: 29.7.0 - pure-rand: 6.1.0 - slash: 3.0.0 - stack-utils: 2.0.6 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - jest-cli@29.7.0(@types/node@24.5.2): - dependencies: - '@jest/core': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - chalk: 4.1.2 - create-jest: 29.7.0(@types/node@24.5.2) - exit: 0.1.2 - import-local: 3.2.0 - jest-config: 29.7.0(@types/node@24.5.2) - jest-util: 29.7.0 - jest-validate: 29.7.0 - yargs: 17.7.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - - jest-config@29.7.0(@types/node@24.5.2): - dependencies: - '@babel/core': 7.28.4 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.28.4) - chalk: 4.1.2 - ci-info: 3.9.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.7.0 - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.8 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - optionalDependencies: - '@types/node': 24.5.2 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - jest-diff@29.7.0: - dependencies: - chalk: 4.1.2 - diff-sequences: 29.6.3 - jest-get-type: 29.6.3 - pretty-format: 29.7.0 - - jest-docblock@29.7.0: - dependencies: - detect-newline: 3.1.0 - - jest-each@29.7.0: - dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - jest-get-type: 29.6.3 - jest-util: 29.7.0 - pretty-format: 29.7.0 - - jest-environment-node@29.7.0: - dependencies: - '@jest/environment': 29.7.0 - '@jest/fake-timers': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 24.5.2 - jest-mock: 29.7.0 - jest-util: 29.7.0 - - jest-get-type@29.6.3: {} - - jest-haste-map@29.7.0: - dependencies: - '@jest/types': 29.6.3 - '@types/graceful-fs': 4.1.9 - '@types/node': 24.5.2 - anymatch: 3.1.3 - fb-watchman: 2.0.2 - graceful-fs: 4.2.11 - jest-regex-util: 29.6.3 - jest-util: 29.7.0 - jest-worker: 29.7.0 - micromatch: 4.0.8 - walker: 1.0.8 - optionalDependencies: - fsevents: 2.3.3 - - jest-leak-detector@29.7.0: - dependencies: - jest-get-type: 29.6.3 - pretty-format: 29.7.0 - - jest-matcher-utils@29.7.0: - dependencies: - chalk: 4.1.2 - jest-diff: 29.7.0 - jest-get-type: 29.6.3 - pretty-format: 29.7.0 - - jest-message-util@29.7.0: - dependencies: - '@babel/code-frame': 7.27.1 - '@jest/types': 29.6.3 - '@types/stack-utils': 2.0.3 - chalk: 4.1.2 - graceful-fs: 4.2.11 - micromatch: 4.0.8 - pretty-format: 29.7.0 - slash: 3.0.0 - stack-utils: 2.0.6 - - jest-mock@29.7.0: - dependencies: - '@jest/types': 29.6.3 - '@types/node': 24.5.2 - jest-util: 29.7.0 - - jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): - optionalDependencies: - jest-resolve: 29.7.0 - - jest-regex-util@29.6.3: {} - - jest-resolve-dependencies@29.7.0: - dependencies: - jest-regex-util: 29.6.3 - jest-snapshot: 29.7.0 - transitivePeerDependencies: - - supports-color - - jest-resolve@29.7.0: - dependencies: - chalk: 4.1.2 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) - jest-util: 29.7.0 - jest-validate: 29.7.0 - resolve: 1.22.10 - resolve.exports: 2.0.3 - slash: 3.0.0 - - jest-runner@29.7.0: - dependencies: - '@jest/console': 29.7.0 - '@jest/environment': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 24.5.2 - chalk: 4.1.2 - emittery: 0.13.1 - graceful-fs: 4.2.11 - jest-docblock: 29.7.0 - jest-environment-node: 29.7.0 - jest-haste-map: 29.7.0 - jest-leak-detector: 29.7.0 - jest-message-util: 29.7.0 - jest-resolve: 29.7.0 - jest-runtime: 29.7.0 - jest-util: 29.7.0 - jest-watcher: 29.7.0 - jest-worker: 29.7.0 - p-limit: 3.1.0 - source-map-support: 0.5.13 - transitivePeerDependencies: - - supports-color - - jest-runtime@29.7.0: - dependencies: - '@jest/environment': 29.7.0 - '@jest/fake-timers': 29.7.0 - '@jest/globals': 29.7.0 - '@jest/source-map': 29.6.3 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 24.5.2 - chalk: 4.1.2 - cjs-module-lexer: 1.4.3 - collect-v8-coverage: 1.0.2 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - jest-message-util: 29.7.0 - jest-mock: 29.7.0 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - slash: 3.0.0 - strip-bom: 4.0.0 - transitivePeerDependencies: - - supports-color - - jest-snapshot@29.7.0: - dependencies: - '@babel/core': 7.28.4 - '@babel/generator': 7.28.3 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.4) - '@babel/types': 7.28.4 - '@jest/expect-utils': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.4) - chalk: 4.1.2 - expect: 29.7.0 - graceful-fs: 4.2.11 - jest-diff: 29.7.0 - jest-get-type: 29.6.3 - jest-matcher-utils: 29.7.0 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - natural-compare: 1.4.0 - pretty-format: 29.7.0 - semver: 7.7.2 - transitivePeerDependencies: - - supports-color - - jest-util@29.7.0: - dependencies: - '@jest/types': 29.6.3 - '@types/node': 24.5.2 - chalk: 4.1.2 - ci-info: 3.9.0 - graceful-fs: 4.2.11 - picomatch: 2.3.1 - - jest-validate@29.7.0: - dependencies: - '@jest/types': 29.6.3 - camelcase: 6.3.0 - chalk: 4.1.2 - jest-get-type: 29.6.3 - leven: 3.1.0 - pretty-format: 29.7.0 - - jest-watcher@29.7.0: - dependencies: - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 24.5.2 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - emittery: 0.13.1 - jest-util: 29.7.0 - string-length: 4.0.2 - - jest-worker@29.7.0: - dependencies: - '@types/node': 24.5.2 - jest-util: 29.7.0 - merge-stream: 2.0.0 - supports-color: 8.1.1 - - jest@29.7.0(@types/node@24.5.2): - dependencies: - '@jest/core': 29.7.0 - '@jest/types': 29.6.3 - import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@24.5.2) - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - js-tokens@4.0.0: {} - js-yaml@3.14.1: - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 - jsesc@3.0.2: {} jsesc@3.1.0: {} - json-parse-even-better-errors@2.3.1: {} - json5@2.2.3: {} - kleur@3.0.3: {} - lazystream@1.0.1: dependencies: readable-stream: 2.3.8 - leven@3.1.0: {} - - lines-and-columns@1.2.4: {} - - locate-path@5.0.0: - dependencies: - p-locate: 4.1.0 - lodash.camelcase@4.3.0: {} lodash.debounce@4.0.8: {} - lodash.memoize@4.1.2: {} - lodash@4.17.21: {} long@5.3.2: {} @@ -6377,16 +4655,6 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 - make-dir@4.0.0: - dependencies: - semver: 7.7.2 - - make-error@1.3.6: {} - - makeerror@1.0.12: - dependencies: - tmpl: 1.0.5 - math-intrinsics@1.1.0: {} media-typer@1.1.0: {} @@ -6395,13 +4663,6 @@ snapshots: merge-descriptors@2.0.0: {} - merge-stream@2.0.0: {} - - micromatch@4.0.8: - dependencies: - braces: 3.0.3 - picomatch: 2.3.1 - mime-db@1.52.0: {} mime-db@1.54.0: {} @@ -6414,12 +4675,6 @@ snapshots: dependencies: mime-db: 1.54.0 - mimic-fn@2.1.0: {} - - minimatch@3.1.2: - dependencies: - brace-expansion: 1.1.12 - minimatch@5.1.6: dependencies: brace-expansion: 2.0.2 @@ -6443,24 +4698,14 @@ snapshots: nanoid@3.3.11: {} - natural-compare@1.4.0: {} - negotiator@1.0.0: {} - neo-async@2.6.2: {} - - node-int64@0.4.0: {} - node-releases@2.0.21: {} normalize-path@3.0.0: {} npm-check-updates@18.3.0: {} - npm-run-path@4.0.1: - dependencies: - path-key: 3.1.1 - nth-check@2.1.1: dependencies: boolbase: 1.0.0 @@ -6475,35 +4720,10 @@ snapshots: dependencies: wrappy: 1.0.2 - onetime@5.1.2: - dependencies: - mimic-fn: 2.1.0 - openapi-types@12.1.3: {} - p-limit@2.3.0: - dependencies: - p-try: 2.2.0 - - p-limit@3.1.0: - dependencies: - yocto-queue: 0.1.0 - - p-locate@4.1.0: - dependencies: - p-limit: 2.3.0 - - p-try@2.2.0: {} - package-json-from-dist@1.0.1: {} - parse-json@5.2.0: - dependencies: - '@babel/code-frame': 7.27.1 - error-ex: 1.3.4 - json-parse-even-better-errors: 2.3.1 - lines-and-columns: 1.2.4 - parse5-htmlparser2-tree-adapter@7.1.0: dependencies: domhandler: 5.0.3 @@ -6515,10 +4735,6 @@ snapshots: parseurl@1.3.3: {} - path-exists@4.0.0: {} - - path-is-absolute@1.0.1: {} - path-key@3.1.1: {} path-parse@1.0.7: {} @@ -6569,16 +4785,8 @@ snapshots: picocolors@1.1.1: {} - picomatch@2.3.1: {} - picomatch@4.0.3: {} - pirates@4.0.7: {} - - pkg-dir@4.2.0: - dependencies: - find-up: 4.1.0 - postcss@8.5.6: dependencies: nanoid: 3.3.11 @@ -6595,21 +4803,10 @@ snapshots: dependencies: xtend: 4.0.2 - pretty-format@29.7.0: - dependencies: - '@jest/schemas': 29.6.3 - ansi-styles: 5.2.0 - react-is: 18.3.1 - process-nextick-args@2.0.1: {} process@0.11.10: {} - prompts@2.4.2: - dependencies: - kleur: 3.0.3 - sisteransi: 1.0.5 - proper-lockfile@4.1.2: dependencies: graceful-fs: 4.2.11 @@ -6647,8 +4844,6 @@ snapshots: end-of-stream: 1.4.5 once: 1.4.0 - pure-rand@6.1.0: {} - qs@6.14.0: dependencies: side-channel: 1.1.0 @@ -6662,8 +4857,6 @@ snapshots: iconv-lite: 0.7.0 unpipe: 1.0.0 - react-is@18.3.1: {} - readable-stream@2.3.8: dependencies: core-util-is: 1.0.3 @@ -6715,16 +4908,8 @@ snapshots: require-directory@2.1.1: {} - resolve-cwd@3.0.0: - dependencies: - resolve-from: 5.0.0 - - resolve-from@5.0.0: {} - resolve-pkg-maps@1.0.0: {} - resolve.exports@2.0.3: {} - resolve@1.22.10: dependencies: is-core-module: 2.16.1 @@ -6779,8 +4964,6 @@ snapshots: semver@6.3.1: {} - semver@7.7.2: {} - send@1.2.0: dependencies: debug: 4.4.3 @@ -6848,17 +5031,8 @@ snapshots: signal-exit@4.1.0: {} - sisteransi@1.0.5: {} - - slash@3.0.0: {} - source-map-js@1.2.1: {} - source-map-support@0.5.13: - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 - source-map-support@0.5.21: dependencies: buffer-from: 1.1.2 @@ -6870,8 +5044,6 @@ snapshots: split2@4.2.0: {} - sprintf-js@1.0.3: {} - ssh-remote-port-forward@1.0.4: dependencies: '@types/ssh2': 0.5.52 @@ -6885,10 +5057,6 @@ snapshots: cpu-features: 0.0.10 nan: 2.23.1 - stack-utils@2.0.6: - dependencies: - escape-string-regexp: 2.0.0 - stackback@0.0.2: {} statuses@2.0.1: {} @@ -6906,11 +5074,6 @@ snapshots: - bare-abort-controller - react-native-b4a - string-length@4.0.2: - dependencies: - char-regex: 1.0.2 - strip-ansi: 6.0.1 - string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -6939,24 +5102,10 @@ snapshots: dependencies: ansi-regex: 6.2.2 - strip-bom@4.0.0: {} - - strip-final-newline@2.0.0: {} - - strip-json-comments@3.1.1: {} - strtok3@10.3.4: dependencies: '@tokenizer/token': 0.3.0 - supports-color@7.2.0: - dependencies: - has-flag: 4.0.0 - - supports-color@8.1.1: - dependencies: - has-flag: 4.0.0 - supports-preserve-symlinks-flag@1.0.0: {} tar-fs@2.1.4: @@ -6995,12 +5144,6 @@ snapshots: - bare-abort-controller - react-native-b4a - test-exclude@6.0.0: - dependencies: - '@istanbuljs/schema': 0.1.3 - glob: 7.2.3 - minimatch: 3.1.2 - testcontainers@11.8.0: dependencies: '@balena/dockerignore': 1.0.2 @@ -7043,12 +5186,6 @@ snapshots: tmp@0.2.5: {} - tmpl@1.0.5: {} - - to-regex-range@5.0.1: - dependencies: - is-number: 7.0.0 - toidentifier@1.0.1: {} token-types@6.1.1: @@ -7057,27 +5194,6 @@ snapshots: '@tokenizer/token': 0.3.0 ieee754: 1.2.1 - ts-jest@29.4.4(@babel/core@7.28.4)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.4))(esbuild@0.25.10)(jest-util@29.7.0)(jest@29.7.0(@types/node@24.5.2))(typescript@5.9.2): - dependencies: - bs-logger: 0.2.6 - fast-json-stable-stringify: 2.1.0 - handlebars: 4.7.8 - jest: 29.7.0(@types/node@24.5.2) - json5: 2.2.3 - lodash.memoize: 4.1.2 - make-error: 1.3.6 - semver: 7.7.2 - type-fest: 4.41.0 - typescript: 5.9.2 - yargs-parser: 21.1.1 - optionalDependencies: - '@babel/core': 7.28.4 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.28.4) - esbuild: 0.25.10 - jest-util: 29.7.0 - tsconfck@3.1.6(typescript@5.9.2): optionalDependencies: typescript: 5.9.2 @@ -7093,12 +5209,6 @@ snapshots: tweetnacl@0.14.5: {} - type-detect@4.0.8: {} - - type-fest@0.21.3: {} - - type-fest@4.41.0: {} - type-is@2.0.1: dependencies: content-type: 1.0.5 @@ -7107,9 +5217,6 @@ snapshots: typescript@5.9.2: {} - uglify-js@3.19.3: - optional: true - uint8array-extras@1.5.0: {} undici-types@5.26.5: {} @@ -7144,12 +5251,6 @@ snapshots: uuid@10.0.0: {} - v8-to-istanbul@9.3.0: - dependencies: - '@jridgewell/trace-mapping': 0.3.31 - '@types/istanbul-lib-coverage': 2.0.6 - convert-source-map: 2.0.0 - vary@1.1.2: {} vite-tsconfig-paths@5.1.4(typescript@5.9.2)(vite@7.2.2(@types/node@24.5.2)(tsx@4.20.5)(yaml@2.8.1)): @@ -7215,10 +5316,6 @@ snapshots: - tsx - yaml - walker@1.0.8: - dependencies: - makeerror: 1.0.12 - which@2.0.2: dependencies: isexe: 2.0.0 @@ -7228,8 +5325,6 @@ snapshots: siginfo: 2.0.0 stackback: 0.0.2 - wordwrap@1.0.0: {} - wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 @@ -7244,11 +5339,6 @@ snapshots: wrappy@1.0.2: {} - write-file-atomic@4.0.2: - dependencies: - imurmurhash: 0.1.4 - signal-exit: 3.0.7 - xtend@4.0.2: {} y18n@5.0.8: {} @@ -7269,8 +5359,6 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 - yocto-queue@0.1.0: {} - zip-stream@6.0.1: dependencies: archiver-utils: 5.0.2 diff --git a/src/db/db.ts b/src/db/db.ts index 558c7bb..783b56f 100644 --- a/src/db/db.ts +++ b/src/db/db.ts @@ -1,13 +1,22 @@ -import { drizzle } from "drizzle-orm/node-postgres"; +import { drizzle, NodePgDatabase } from "drizzle-orm/node-postgres"; import { Pool } from "pg"; -import { env } from "env"; import * as schema from "./schema"; -const pool: Pool = new Pool({ - connectionString: env.DATABASE_URL, - ssl: false, -}); - -export const db = drizzle(pool, { - schema, -}); +export let db: NodePgDatabase & { + $client: Pool; +}; +let pool: Pool; +export function initDB(connectionString: string) { + pool = new Pool({ + connectionString: connectionString, + ssl: false, + }); + db = drizzle(pool, { + schema, + }); +} +/** Only used for testing */ +export function _disconnectPoolConnection() { + if (db === undefined) return; + pool.end(); +} diff --git a/src/db/dbQueryUtils.ts b/src/db/dbQueryUtils.ts index 8c41a0c..10f810e 100644 --- a/src/db/dbQueryUtils.ts +++ b/src/db/dbQueryUtils.ts @@ -10,7 +10,7 @@ import { import { db } from "./db"; import { notifySlack } from "utils/slack"; -import { eq, gte } from "drizzle-orm"; +import { and, eq, gte } from "drizzle-orm"; import { parseTimeSlots } from "containers/timeBuilder"; import { IParsedTimeRange } from "containers/time/parsedTime"; @@ -64,9 +64,13 @@ export async function getLocationIdToDataMap(timeSearchCutoffStr: string) { conceptIdToInternalIdTable, eq(locationDataTable.id, conceptIdToInternalIdTable.internalId) ) - .leftJoin(timesTable, eq(locationDataTable.id, timesTable.locationId)) - .where(gte(timesTable.date, timeSearchCutoffStr)); - + .leftJoin( + timesTable, + and( + eq(locationDataTable.id, timesTable.locationId), + gte(timesTable.date, timeSearchCutoffStr) + ) + ); return locationData.reduce< Record< string, diff --git a/src/db/updateLocation.ts b/src/db/updateLocation.ts index f4eca0f..7136ca7 100644 --- a/src/db/updateLocation.ts +++ b/src/db/updateLocation.ts @@ -36,7 +36,10 @@ export async function addLocationDataToDb(location: ILocation) { await db .insert(locationDataTable) .values(locationDbEntry) - .onConflictDoUpdate({ target: locationDataTable.id, set: locationDbEntry }); + .onConflictDoUpdate({ + target: locationDataTable.id, + set: locationDbEntry, + }); if (location.earliestDayToOverride !== undefined) { const earliestDaySQLString = `${location.earliestDayToOverride.year}-${pad( diff --git a/src/server.ts b/src/server.ts index 0c9ab00..dc5f0b1 100644 --- a/src/server.ts +++ b/src/server.ts @@ -12,6 +12,7 @@ import { getDiffsBetweenLocationData } from "utils/diff"; import { getEmails } from "db/dbQueryUtils"; import { getAllLocations } from "db/getLocations"; import { openapi } from "@elysiajs/openapi"; +import { initDB } from "db/db"; /** only used for Slack debug diff logging */ let cachedLocations: ILocation[] = []; @@ -101,6 +102,7 @@ setInterval(() => { ); }, env.RELOAD_WAIT_INTERVAL); +initDB(env.DATABASE_URL); // Initial load and start the server reload() .then(() => { diff --git a/src/utils/diff.ts b/src/utils/diff.ts index da84420..ecbc60c 100644 --- a/src/utils/diff.ts +++ b/src/utils/diff.ts @@ -4,7 +4,7 @@ type T = { [key: string]: T } | T[] | string | number | undefined; export function getObjDiffs( prevObject: T, newObject: T, - path: string = "~" // ~uwu senpai + path: string = "~" ): string[] { let diffs: string[] = []; if (typeof prevObject === "object" && typeof newObject === "object") { diff --git a/tests/database.test.ts b/tests/database.test.ts index 3401d28..386db5c 100644 --- a/tests/database.test.ts +++ b/tests/database.test.ts @@ -1,25 +1,68 @@ -import { Client } from "pg"; -import { StartedPostgreSqlContainer } from "@testcontainers/postgresql"; -import { initPgClient } from "./postgresClient"; - +import { + PostgreSqlContainer, + StartedPostgreSqlContainer, +} from "@testcontainers/postgresql"; +import { _disconnectPoolConnection, initDB } from "db/db"; +import { addLocationDataToDb } from "db/updateLocation"; +import { getAllLocations } from "db/getLocations"; +const wait = (ms: number) => new Promise((re) => setTimeout(re, ms)); describe("Redis", () => { let container: StartedPostgreSqlContainer; - let pgClient: Client; - beforeAll(async () => { - const init = await initPgClient(); - container = init.container; - pgClient = init.pgClient; + beforeEach(async () => { + container = await new PostgreSqlContainer("postgres:17.5") + .withCopyDirectoriesToContainer([ + { + source: `${__dirname}/../drizzle`, + target: "/docker-entrypoint-initdb.d", + }, + ]) + .start(); + + initDB(container.getConnectionUri()); }); - afterAll(async () => { - await pgClient.end(); + afterEach(async () => { + _disconnectPoolConnection(); await container.stop(); }); - it("works", async () => { - console.log(container.getConnectionUri()); - const result = await pgClient.query("SELECT 1"); - expect(result.rows[0]).toEqual({ "?column?": 1 }); + it("works on basic insertion", async () => { + await addLocationDataToDb({ + name: "test", + acceptsOnlineOrders: false, + conceptId: 1, + coordinates: { lat: 1, lng: 1 }, + description: "description", + earliestDayToOverride: undefined, + times: [], + location: "location", + menu: "menu", + shortDescription: "hi", + url: "https://hi.com", + todaysSoups: undefined, + todaysSpecials: undefined, + }); + const dbResult = await getAllLocations(); + expect(dbResult).toEqual([ + { + id: 1, + name: "test", + shortDescription: "hi", + description: "description", + url: "https://hi.com", + menu: "menu", + location: "location", + coordinateLat: 1, + coordinateLng: 1, + acceptsOnlineOrders: false, + times: [], + todaysSoups: undefined, + todaysSpecials: undefined, + }, + ]); + }); + it("properly resets state on every new test", async () => { + expect(await getAllLocations()).toEqual([]); }); }); diff --git a/tests/postgresClient.ts b/tests/postgresClient.ts index 7a19913..e69de29 100644 --- a/tests/postgresClient.ts +++ b/tests/postgresClient.ts @@ -1,14 +0,0 @@ -import { Client } from "pg"; -import { PostgreSqlContainer } from "@testcontainers/postgresql"; -export async function initPgClient() { - const container = await new PostgreSqlContainer("postgres:17.5").start(); - const pgClient = new Client({ - host: container.getHost(), - port: container.getPort(), - database: container.getDatabase(), - user: container.getUsername(), - password: container.getPassword(), - }); - await pgClient.connect(); - return { container, pgClient }; -} From ac79f0779a47dae65bb5f8e610f99a079c03b4e7 Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Mon, 10 Nov 2025 13:34:13 -0500 Subject: [PATCH 41/65] fix: give more startup time for tests --- README.md | 2 +- tests/database.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 76b3892..a894fb2 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Start your local database with `pnpm db:start` and then start the server with `p ## Database schema changes (important!) -When you make changes to the database schema, be sure to run `pnpm db:push` to keep your local db in sync. +When you make changes to the database schema, be sure to run `pnpm db:push` to keep your local db in sync. (You should do this before running tests as well!) Before merging your PR, be sure to run `pnpm db:generate` to generate a migration file, which will then be automatically applied to the staging and production databases when deployed. diff --git a/tests/database.test.ts b/tests/database.test.ts index 386db5c..c5a6e8b 100644 --- a/tests/database.test.ts +++ b/tests/database.test.ts @@ -20,7 +20,7 @@ describe("Redis", () => { .start(); initDB(container.getConnectionUri()); - }); + }, 120 * 1000); // 2 minutes for startup afterEach(async () => { _disconnectPoolConnection(); From 250723448e12f1e582b43199012c5704549e4be0 Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Mon, 10 Nov 2025 13:36:06 -0500 Subject: [PATCH 42/65] test: run all tests --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6ff99ca..b1dd27b 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "dev": "dotenv -- tsx watch src/server.ts", "build": "rollup -c", "start": "NODE_ENV=production node dist/server.js", - "test": "dotenv -e .env.test -- vitest database.test.ts", + "test": "dotenv -e .env.test -- vitest", "__comment": "add DEBUG=testcontainers* to the test command to get testcontainers debug output", "typecheck": "tsc", "run-prod": "VOL_SRC=postgres_data docker-compose up --build", From cc1c3f659e3669e538204ef5a7926d2af4c9dec9 Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Wed, 12 Nov 2025 17:47:22 -0500 Subject: [PATCH 43/65] tests: test on dst times and specials --- src/containers/locationBuilder.ts | 2 +- src/db/getLocations.ts | 7 +- src/db/updateLocation.ts | 30 +- src/server.ts | 3 +- src/types.ts | 3 +- src/utils/timeUtils.ts | 15 +- tests/database.test.ts | 561 +++++++++++++++++++++++++++++- tests/expectedData.ts | 68 ++-- tests/locationMerger.test.ts | 8 +- 9 files changed, 619 insertions(+), 78 deletions(-) diff --git a/src/containers/locationBuilder.ts b/src/containers/locationBuilder.ts index 27b4aa3..e2e6f05 100644 --- a/src/containers/locationBuilder.ts +++ b/src/containers/locationBuilder.ts @@ -129,7 +129,7 @@ export default class LocationBuilder { coordinates: this.coordinates, acceptsOnlineOrders: this.acceptsOnlineOrders, times: this.times, - earliestDayToOverride: this.earliestDayFound, + today: this.earliestDayFound, todaysSpecials: this.specials, todaysSoups: this.soups, }; diff --git a/src/db/getLocations.ts b/src/db/getLocations.ts index 557ab36..caca600 100644 --- a/src/db/getLocations.ts +++ b/src/db/getLocations.ts @@ -9,8 +9,7 @@ import { DateTime } from "luxon"; import { pad, remapAndMergeTimeIntervals } from "utils/timeUtils"; import { IParsedTimeRange } from "containers/time/parsedTime"; -export async function getAllLocations() { - const today = DateTime.now(); +export async function getAllLocations(today: DateTime) { const timeSearchCutoff = today.minus({ days: 1 }); // 1 days worth of data before today const timeSearchCutoffStr = `${timeSearchCutoff.year}-${pad( timeSearchCutoff.month @@ -38,8 +37,8 @@ export async function getAllLocations() { // time.end // ).toLocaleString()}` // ), - todaysSoups: specials[id]?.soups, - todaysSpecials: specials[id]?.specials, + todaysSoups: specials[id]?.soups ?? [], + todaysSpecials: specials[id]?.specials ?? [], }; } ); diff --git a/src/db/updateLocation.ts b/src/db/updateLocation.ts index 7136ca7..a4a9bf6 100644 --- a/src/db/updateLocation.ts +++ b/src/db/updateLocation.ts @@ -41,13 +41,13 @@ export async function addLocationDataToDb(location: ILocation) { set: locationDbEntry, }); - if (location.earliestDayToOverride !== undefined) { - const earliestDaySQLString = `${location.earliestDayToOverride.year}-${pad( - location.earliestDayToOverride.month - )}-${pad(location.earliestDayToOverride.day)})`; + if (location.today !== undefined) { + const earliestDaySQLString = `${location.today.year}-${pad( + location.today.month + )}-${pad(location.today.day)})`; // add specials await db - .delete(locationDataTable) + .delete(specialsTable) .where( and( eq(specialsTable.locationId, internalId), @@ -84,16 +84,16 @@ export async function addLocationDataToDb(location: ILocation) { and(gte(timesTable.date, earliestDaySQLString)) ) ); - } - if (location.times.length) { - await db.insert(timesTable).values( - location.times.map((time) => ({ - locationId: internalId, - date: `${time.year}-${pad(time.month)}-${pad(time.day)}`, - startTime: time.startMinutesFromMidnight, - endTime: time.endMinutesFromMidnight, - })) - ); + if (location.times.length) { + await db.insert(timesTable).values( + location.times.map((time) => ({ + locationId: internalId, + date: `${time.year}-${pad(time.month)}-${pad(time.day)}`, + startTime: time.startMinutesFromMidnight, + endTime: time.endMinutesFromMidnight, + })) + ); + } } // in case the conceptId->internalId mapping entry isn't there diff --git a/src/server.ts b/src/server.ts index dc5f0b1..2abed7b 100644 --- a/src/server.ts +++ b/src/server.ts @@ -13,6 +13,7 @@ import { getEmails } from "db/dbQueryUtils"; import { getAllLocations } from "db/getLocations"; import { openapi } from "@elysiajs/openapi"; import { initDB } from "db/db"; +import { DateTime } from "luxon"; /** only used for Slack debug diff logging */ let cachedLocations: ILocation[] = []; @@ -81,7 +82,7 @@ app.onAfterHandle(({ response }) => { app.get("/", () => { return "ScottyLabs Dining API"; }); -app.get("/api/v2/locations", getAllLocations); +app.get("/api/v2/locations", async () => await getAllLocations(DateTime.now())); app.get("/api/emails", getEmails); app.post( diff --git a/src/types.ts b/src/types.ts index f2b4ed2..2b3f198 100644 --- a/src/types.ts +++ b/src/types.ts @@ -15,9 +15,10 @@ export interface ILocation { location: string; coordinates: ICoordinate | undefined; acceptsOnlineOrders: boolean; + /** Assuming these times fall after today */ times: IFullTimeRange[]; /** useful when figuring out which db time entries to overwrite. Can be undefined if no time data was properly scraped */ - earliestDayToOverride: IDate | undefined; + today: IDate | undefined; todaysSpecials: ISpecial[] | undefined; todaysSoups: ISpecial[] | undefined; } diff --git a/src/utils/timeUtils.ts b/src/utils/timeUtils.ts index a48d747..240e694 100644 --- a/src/utils/timeUtils.ts +++ b/src/utils/timeUtils.ts @@ -34,15 +34,14 @@ export function pad(n: number) { export function remapAndMergeTimeIntervals(timeRanges: ITimeRangeInternal[]) { const timeStampedIntervals = timeRanges.map((rng) => { const date = DateTime.fromISO(rng.date, { zone: "America/New_York" }); + const startDate = date.set({ minute: rng.startMinutesSinceMidnight }); + const endDate = date.set({ minute: rng.endMinutesSinceMidnight }).plus({ + days: rng.endMinutesSinceMidnight < rng.startMinutesSinceMidnight ? 1 : 0, + }); // account for wrap-around return { - start: date.toMillis() + rng.startMinutesSinceMidnight * 1000 * 60, - end: - date.toMillis() + - rng.endMinutesSinceMidnight * 1000 * 60 + - (rng.endMinutesSinceMidnight < rng.startMinutesSinceMidnight - ? 24 * 60 * 60 * 1000 - : 0), // this specific slot wraps around - so we'll add a day to the end + start: startDate.toMillis(), + end: endDate.toMillis(), }; }); return mergeTimeRanges(timeStampedIntervals, 60 * 1000); // merge gaps of at most 1 min -} \ No newline at end of file +} diff --git a/tests/database.test.ts b/tests/database.test.ts index c5a6e8b..c66c931 100644 --- a/tests/database.test.ts +++ b/tests/database.test.ts @@ -5,6 +5,7 @@ import { import { _disconnectPoolConnection, initDB } from "db/db"; import { addLocationDataToDb } from "db/updateLocation"; import { getAllLocations } from "db/getLocations"; +import { DateTime } from "luxon"; const wait = (ms: number) => new Promise((re) => setTimeout(re, ms)); describe("Redis", () => { let container: StartedPostgreSqlContainer; @@ -18,9 +19,8 @@ describe("Redis", () => { }, ]) .start(); - initDB(container.getConnectionUri()); - }, 120 * 1000); // 2 minutes for startup + }, 120 * 1000); afterEach(async () => { _disconnectPoolConnection(); @@ -32,18 +32,22 @@ describe("Redis", () => { name: "test", acceptsOnlineOrders: false, conceptId: 1, - coordinates: { lat: 1, lng: 1 }, + coordinates: { lat: 1, lng: 10 }, description: "description", - earliestDayToOverride: undefined, + today: { + year: 2025, + month: 1, + day: 1, + }, times: [], location: "location", menu: "menu", shortDescription: "hi", url: "https://hi.com", - todaysSoups: undefined, - todaysSpecials: undefined, + todaysSoups: [], + todaysSpecials: [], }); - const dbResult = await getAllLocations(); + const dbResult = await getAllLocations(DateTime.now()); expect(dbResult).toEqual([ { id: 1, @@ -54,15 +58,550 @@ describe("Redis", () => { menu: "menu", location: "location", coordinateLat: 1, - coordinateLng: 1, + coordinateLng: 10, acceptsOnlineOrders: false, times: [], - todaysSoups: undefined, - todaysSpecials: undefined, + todaysSoups: [], + todaysSpecials: [], }, ]); }); it("properly resets state on every new test", async () => { - expect(await getAllLocations()).toEqual([]); + expect(await getAllLocations(DateTime.now())).toEqual([]); + }); + it("works on insertion with times", async () => { + await addLocationDataToDb({ + name: "test", + acceptsOnlineOrders: false, + conceptId: 1, + coordinates: { lat: 1, lng: 10 }, + description: "description", + today: { + year: 2025, + month: 1, + day: 1, + }, + times: [ + { + day: 1, + month: 1, + year: 2025, + startMinutesFromMidnight: 5 * 60, + endMinutesFromMidnight: 12 * 60, + }, + { + day: 1, + month: 1, + year: 2025, + startMinutesFromMidnight: 5 * 60, + endMinutesFromMidnight: 12 * 60, + }, + { + day: 1, + month: 1, + year: 2025, + startMinutesFromMidnight: 14 * 60, + endMinutesFromMidnight: 2 * 60, + }, + { + day: 7, + month: 7, + year: 2025, + startMinutesFromMidnight: 14 * 60, + endMinutesFromMidnight: 2 * 60, + }, + ], + location: "location", + menu: "menu", + shortDescription: "hi", + url: "https://hi.com", + todaysSoups: [], + todaysSpecials: [], + }); + const dbResult = await getAllLocations( + DateTime.fromObject({ year: 2025, month: 1, day: 2 }) + ); + expect(dbResult).toEqual([ + { + id: 1, + name: "test", + shortDescription: "hi", + description: "description", + url: "https://hi.com", + menu: "menu", + location: "location", + coordinateLat: 1, + coordinateLng: 10, + acceptsOnlineOrders: false, + times: [ + { + start: 1735725600000, + end: 1735750800000, + }, + { + start: 1735758000000, + end: 1735801200000, + }, + { + start: 1751911200000, + end: 1751954400000, + }, + ], + todaysSoups: [], + todaysSpecials: [], + }, + ]); + }); + it("works on insertion with times (tests search window)", async () => { + await addLocationDataToDb({ + name: "test", + acceptsOnlineOrders: false, + conceptId: 1, + coordinates: { lat: 1, lng: 10 }, + description: "description", + today: { + year: 2025, + month: 1, + day: 1, + }, + times: [ + { + day: 1, + month: 1, + year: 2025, + startMinutesFromMidnight: 5 * 60, + endMinutesFromMidnight: 12 * 60, + }, + { + day: 1, + month: 1, + year: 2025, + startMinutesFromMidnight: 5 * 60, + endMinutesFromMidnight: 12 * 60, + }, + { + day: 1, + month: 1, + year: 2025, + startMinutesFromMidnight: 14 * 60, + endMinutesFromMidnight: 2 * 60, + }, + ], + location: "location", + menu: "menu", + shortDescription: "hi", + url: "https://hi.com", + todaysSoups: [], + todaysSpecials: [], + }); + const dbResult = await getAllLocations( + DateTime.fromObject({ year: 2025, month: 1, day: 3 }) // 2 days after latest time + ); + expect(dbResult).toEqual([ + { + id: 1, + name: "test", + shortDescription: "hi", + description: "description", + url: "https://hi.com", + menu: "menu", + location: "location", + coordinateLat: 1, + coordinateLng: 10, + acceptsOnlineOrders: false, + times: [], + todaysSoups: [], + todaysSpecials: [], + }, + ]); + }); + it("works on insertion with times (DST - start 2AM -> 3AM) (3/9/25)", async () => { + await addLocationDataToDb({ + name: "test", + acceptsOnlineOrders: false, + conceptId: 1, + coordinates: { lat: 1, lng: 10 }, + description: "description", + today: { + year: 2025, + month: 1, + day: 1, + }, + times: [ + { + day: 9, + month: 3, + year: 2025, + startMinutesFromMidnight: 5 * 60, + endMinutesFromMidnight: 12 * 60, + }, + ], + location: "location", + menu: "menu", + shortDescription: "hi", + url: "https://hi.com", + todaysSoups: [], + todaysSpecials: [], + }); + const dbResult = await getAllLocations( + DateTime.fromObject({ year: 2025, month: 1, day: 3 }) // 2 days after latest time + ); + expect(dbResult).toEqual([ + { + id: 1, + name: "test", + shortDescription: "hi", + description: "description", + url: "https://hi.com", + menu: "menu", + location: "location", + coordinateLat: 1, + coordinateLng: 10, + acceptsOnlineOrders: false, + times: [ + { + start: 1741510800000, + end: 1741536000000, + }, + ], + todaysSoups: [], + todaysSpecials: [], + }, + ]); + }); + it("works on insertion with times (DST - end 2AM -> 1AM) (3/9/25) [if a place closes at 2 AM, we assume it's the second 2 AM and not the first (why? arbitrary)]", async () => { + await addLocationDataToDb({ + name: "test", + acceptsOnlineOrders: false, + conceptId: 1, + coordinates: { lat: 1, lng: 10 }, + description: "description", + today: { + year: 2025, + month: 1, + day: 1, + }, + times: [ + { + day: 1, + month: 11, + year: 2025, + startMinutesFromMidnight: 7 * 60, + endMinutesFromMidnight: 2 * 60, + }, + ], + location: "location", + menu: "menu", + shortDescription: "hi", + url: "https://hi.com", + todaysSoups: [], + todaysSpecials: [], + }); + const dbResult = await getAllLocations( + DateTime.fromObject({ year: 2025, month: 1, day: 3 }) + ); + expect(dbResult).toEqual([ + { + id: 1, + name: "test", + shortDescription: "hi", + description: "description", + url: "https://hi.com", + menu: "menu", + location: "location", + coordinateLat: 1, + coordinateLng: 10, + acceptsOnlineOrders: false, + times: [ + { + start: 1761994800000, + end: 1762066800000, // the second 2AM + }, + ], + todaysSoups: [], + todaysSpecials: [], + }, + ]); + }); + it("works on insertion with times (DST - end 2AM -> 1AM) (3/9/25) [if a place closes at 1:30 AM, we assume it's the first 1:30 AM and not the second]", async () => { + await addLocationDataToDb({ + name: "test", + acceptsOnlineOrders: false, + conceptId: 1, + coordinates: { lat: 1, lng: 10 }, + description: "description", + today: { + year: 2025, + month: 1, + day: 1, + }, + times: [ + { + day: 1, + month: 11, + year: 2025, + startMinutesFromMidnight: 7 * 60, + endMinutesFromMidnight: 1.5 * 60, + }, + ], + location: "location", + menu: "menu", + shortDescription: "hi", + url: "https://hi.com", + todaysSoups: [], + todaysSpecials: [], + }); + const dbResult = await getAllLocations( + DateTime.fromObject({ year: 2025, month: 1, day: 3 }) + ); + expect(dbResult).toEqual([ + { + id: 1, + name: "test", + shortDescription: "hi", + description: "description", + url: "https://hi.com", + menu: "menu", + location: "location", + coordinateLat: 1, + coordinateLng: 10, + acceptsOnlineOrders: false, + times: [ + { + start: 1761994800000, + end: 1762061400000, // the first 1:30 AM + }, + ], + todaysSoups: [], + todaysSpecials: [], + }, + ]); + }); + it("works on specials", async () => { + await addLocationDataToDb({ + name: "test", + acceptsOnlineOrders: false, + conceptId: 1, + coordinates: { lat: 1, lng: 10 }, + description: "description", + today: { + year: 2025, + month: 1, + day: 1, + }, + times: [ + { + day: 1, + month: 1, + year: 2025, + startMinutesFromMidnight: 5 * 60, + endMinutesFromMidnight: 12 * 60, + }, + { + day: 1, + month: 1, + year: 2025, + startMinutesFromMidnight: 5 * 60, + endMinutesFromMidnight: 12 * 60, + }, + { + day: 1, + month: 1, + year: 2025, + startMinutesFromMidnight: 14 * 60, + endMinutesFromMidnight: 2 * 60, + }, + ], + location: "location", + menu: "menu", + shortDescription: "hi", + url: "https://hi.com", + todaysSoups: [ + { title: "soup 1", description: "desc 1" }, + { title: "soup 2", description: "desc 2" }, + ], + todaysSpecials: [ + { title: "special 1", description: "desc 1" }, + { title: "special 2", description: "desc 2" }, + ], + }); + const dbResult = await getAllLocations( + DateTime.fromObject({ year: 2025, month: 1, day: 1 }) + ); + expect(dbResult).toEqual([ + { + id: 1, + name: "test", + shortDescription: "hi", + description: "description", + url: "https://hi.com", + menu: "menu", + location: "location", + coordinateLat: 1, + coordinateLng: 10, + acceptsOnlineOrders: false, + times: [ + { + start: 1735725600000, + end: 1735750800000, + }, + { + start: 1735758000000, + end: 1735801200000, + }, + ], + todaysSoups: [ + { + description: "desc 1", + name: "soup 1", + }, + { + description: "desc 2", + name: "soup 2", + }, + ], + todaysSpecials: [ + { + description: "desc 1", + name: "special 1", + }, + { + description: "desc 2", + name: "special 2", + }, + ], + }, + ]); + }); + it("works on specials (overriding)", async () => { + await addLocationDataToDb({ + name: "test", + acceptsOnlineOrders: false, + conceptId: 1, + coordinates: { lat: 1, lng: 10 }, + description: "description", + today: { + year: 2025, + month: 1, + day: 1, + }, + times: [ + { + day: 1, + month: 1, + year: 2025, + startMinutesFromMidnight: 5 * 60, + endMinutesFromMidnight: 12 * 60, + }, + { + day: 1, + month: 1, + year: 2025, + startMinutesFromMidnight: 5 * 60, + endMinutesFromMidnight: 12 * 60, + }, + { + day: 1, + month: 1, + year: 2025, + startMinutesFromMidnight: 14 * 60, + endMinutesFromMidnight: 2 * 60, + }, + ], + location: "location", + menu: "menu", + shortDescription: "hi", + url: "https://hi.com", + todaysSoups: [ + { title: "soup 1", description: "desc 1" }, + { title: "soup 2", description: "desc 2" }, + ], + todaysSpecials: [ + { title: "special 1", description: "desc 1" }, + { title: "special 2", description: "desc 2" }, + ], + }); + await addLocationDataToDb({ + name: "test", + acceptsOnlineOrders: false, + conceptId: 1, + coordinates: { lat: 1, lng: 10 }, + description: "description", + today: { + year: 2025, + month: 1, + day: 1, + }, + times: [ + { + day: 1, + month: 1, + year: 2025, + startMinutesFromMidnight: 5 * 60, + endMinutesFromMidnight: 12 * 60, + }, + { + day: 1, + month: 1, + year: 2025, + startMinutesFromMidnight: 5 * 60, + endMinutesFromMidnight: 12 * 60, + }, + { + day: 1, + month: 1, + year: 2025, + startMinutesFromMidnight: 14 * 60, + endMinutesFromMidnight: 2 * 60, + }, + ], + location: "location", + menu: "menu", + shortDescription: "hi", + url: "https://hi.com", + todaysSoups: [], + todaysSpecials: [ + { title: "special 1", description: "desc 1" }, + { title: "special 2", description: "desc 2" }, + ], + }); + const dbResult = await getAllLocations( + DateTime.fromObject({ year: 2025, month: 1, day: 1 }) + ); + expect(dbResult).toEqual([ + { + id: 1, + name: "test", + shortDescription: "hi", + description: "description", + url: "https://hi.com", + menu: "menu", + location: "location", + coordinateLat: 1, + coordinateLng: 10, + acceptsOnlineOrders: false, + times: [ + { + start: 1735725600000, + end: 1735750800000, + }, + { + start: 1735758000000, + end: 1735801200000, + }, + ], + todaysSoups: [], + todaysSpecials: [ + { + description: "desc 1", + name: "special 1", + }, + { + description: "desc 2", + name: "special 2", + }, + ], + }, + ]); }); }); diff --git a/tests/expectedData.ts b/tests/expectedData.ts index 7281765..873db4f 100644 --- a/tests/expectedData.ts +++ b/tests/expectedData.ts @@ -1,4 +1,6 @@ -export const expectedLocationData = [ +import { ILocation } from "types"; + +export const expectedLocationData: ILocation[] = [ { conceptId: 113, name: "AU BON PAIN AT SKIBO CAFÉ", @@ -65,7 +67,7 @@ export const expectedLocationData = [ endMinutesFromMidnight: 1439, }, ], - earliestDayToOverride: { + today: { year: 2024, month: 8, day: 5, @@ -84,7 +86,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], - earliestDayToOverride: undefined, + today: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -100,7 +102,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], - earliestDayToOverride: undefined, + today: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -115,7 +117,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], - earliestDayToOverride: undefined, + today: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -130,7 +132,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], - earliestDayToOverride: undefined, + today: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -146,7 +148,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], - earliestDayToOverride: undefined, + today: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -162,7 +164,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], - earliestDayToOverride: undefined, + today: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -218,7 +220,7 @@ export const expectedLocationData = [ endMinutesFromMidnight: 900, }, ], - earliestDayToOverride: { + today: { year: 2024, month: 7, day: 20, @@ -252,7 +254,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], - earliestDayToOverride: undefined, + today: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -268,7 +270,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], - earliestDayToOverride: undefined, + today: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -284,7 +286,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], - earliestDayToOverride: undefined, + today: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -300,7 +302,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], - earliestDayToOverride: undefined, + today: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -362,7 +364,7 @@ export const expectedLocationData = [ endMinutesFromMidnight: 1200, }, ], - earliestDayToOverride: { + today: { year: 2024, month: 8, day: 31, @@ -382,7 +384,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], - earliestDayToOverride: undefined, + today: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -398,7 +400,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], - earliestDayToOverride: undefined, + today: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -414,7 +416,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], - earliestDayToOverride: undefined, + today: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -430,7 +432,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], - earliestDayToOverride: undefined, + today: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -446,7 +448,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], - earliestDayToOverride: undefined, + today: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -462,7 +464,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], - earliestDayToOverride: undefined, + today: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -615,7 +617,7 @@ export const expectedLocationData = [ endMinutesFromMidnight: 1230, }, ], - earliestDayToOverride: { + today: { year: 2024, month: 9, day: 20, @@ -635,7 +637,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], - earliestDayToOverride: undefined, + today: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -651,7 +653,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], - earliestDayToOverride: undefined, + today: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -667,7 +669,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], - earliestDayToOverride: undefined, + today: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -683,7 +685,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], - earliestDayToOverride: undefined, + today: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -698,7 +700,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], - earliestDayToOverride: undefined, + today: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -753,7 +755,7 @@ export const expectedLocationData = [ endMinutesFromMidnight: 900, }, ], - earliestDayToOverride: { + today: { year: 2024, month: 7, day: 20, @@ -772,7 +774,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], - earliestDayToOverride: undefined, + today: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -788,7 +790,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], - earliestDayToOverride: undefined, + today: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -804,7 +806,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], - earliestDayToOverride: undefined, + today: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -819,7 +821,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], - earliestDayToOverride: undefined, + today: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -835,7 +837,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], - earliestDayToOverride: undefined, + today: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -851,7 +853,7 @@ export const expectedLocationData = [ coordinates: undefined, acceptsOnlineOrders: false, times: [], - earliestDayToOverride: undefined, + today: undefined, todaysSpecials: undefined, todaysSoups: undefined, }, diff --git a/tests/locationMerger.test.ts b/tests/locationMerger.test.ts index 3a65f7c..f02d8c8 100644 --- a/tests/locationMerger.test.ts +++ b/tests/locationMerger.test.ts @@ -11,7 +11,7 @@ const locationA: ILocation = { acceptsOnlineOrders: true, shortDescription: undefined, coordinates: undefined, - earliestDayToOverride: undefined, + today: undefined, menu: undefined, todaysSoups: undefined, todaysSpecials: undefined, @@ -26,7 +26,7 @@ const locationAA: ILocation = { acceptsOnlineOrders: true, shortDescription: undefined, coordinates: undefined, - earliestDayToOverride: undefined, + today: undefined, menu: undefined, todaysSoups: undefined, todaysSpecials: undefined, @@ -41,7 +41,7 @@ const locationAAA: ILocation = { acceptsOnlineOrders: false, shortDescription: undefined, coordinates: undefined, - earliestDayToOverride: undefined, + today: undefined, menu: undefined, todaysSoups: undefined, todaysSpecials: undefined, @@ -56,7 +56,7 @@ const locationB: ILocation = { acceptsOnlineOrders: true, shortDescription: undefined, coordinates: undefined, - earliestDayToOverride: undefined, + today: undefined, menu: undefined, todaysSoups: undefined, todaysSpecials: undefined, From 1bf7b1bcd6cd553e025995988ea5bf31dc4a3084 Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Thu, 13 Nov 2025 09:49:55 -0500 Subject: [PATCH 44/65] fix: make the 'today' param required for scrape result --- src/containers/locationBuilder.ts | 17 +- src/containers/timeBuilder.ts | 1 + src/parser/diningParser.ts | 15 +- src/types.ts | 2 +- tests/database.test.ts | 98 +++--- tests/expectedData.ts | 493 ++++++++++++++++++++-------- tests/html/concepts/all-closed.html | 224 +++++++++++++ tests/html/concepts/error.html | 0 tests/integration.test.ts | 59 +++- tests/locationMerger.test.ts | 8 +- tests/mockTimings.ts | 6 +- 11 files changed, 706 insertions(+), 217 deletions(-) create mode 100644 tests/html/concepts/all-closed.html create mode 100644 tests/html/concepts/error.html diff --git a/src/containers/locationBuilder.ts b/src/containers/locationBuilder.ts index e2e6f05..a888a94 100644 --- a/src/containers/locationBuilder.ts +++ b/src/containers/locationBuilder.ts @@ -8,6 +8,7 @@ import { ISpecial, IFullTimeRange, } from "../types"; +import { notifySlack } from "utils/slack"; /** * For building the location data structure @@ -26,7 +27,7 @@ export default class LocationBuilder { private coordinates: ICoordinate | undefined; private acceptsOnlineOrders: boolean | undefined; private times: IFullTimeRange[] | undefined; - private earliestDayFound: IDate | undefined; + private today: IDate | undefined; private specials: ISpecial[] | undefined; private soups: ISpecial[] | undefined; @@ -91,7 +92,7 @@ export default class LocationBuilder { serverDate.year ); this.times = times; - this.earliestDayFound = earliestDay; + this.today = earliestDay; } getConceptLink() { if (this.conceptId === undefined) return undefined; @@ -102,7 +103,7 @@ export default class LocationBuilder { return this.conceptId; } - build(): ILocation { + build(): ILocation | undefined { if ( this.times === undefined || this.acceptsOnlineOrders === undefined || @@ -110,11 +111,13 @@ export default class LocationBuilder { this.url === undefined || this.location === undefined || this.conceptId === undefined || - this.name === undefined + this.name === undefined || + this.today === undefined ) { - throw Error( - "Didn't finish configuring location before building metadata!" + notifySlack( + ` Didn't finish configuring location for ${this.conceptId} before building metadata!` ); + return undefined; // All fetches were good - yet we have missing data. This is a problem. } @@ -129,7 +132,7 @@ export default class LocationBuilder { coordinates: this.coordinates, acceptsOnlineOrders: this.acceptsOnlineOrders, times: this.times, - today: this.earliestDayFound, + today: this.today, todaysSpecials: this.specials, todaysSoups: this.soups, }; diff --git a/src/containers/timeBuilder.ts b/src/containers/timeBuilder.ts index 57489ff..7f03ebf 100644 --- a/src/containers/timeBuilder.ts +++ b/src/containers/timeBuilder.ts @@ -75,6 +75,7 @@ export function getAllTimeSlotsFromSchedule( ); } } + if (firstDay === undefined) throw new Error("No valid rows parsed!"); return { times: allTimeSlots, earliestDay: firstDay }; } /** diff --git a/src/parser/diningParser.ts b/src/parser/diningParser.ts index 783dcbd..44c847d 100644 --- a/src/parser/diningParser.ts +++ b/src/parser/diningParser.ts @@ -3,6 +3,7 @@ import { load } from "cheerio"; import LocationBuilder from "../containers/locationBuilder"; import { retrieveSpecials } from "../containers/specials/specialsBuilder"; import { ILocation, ISpecial } from "types"; +import { notifySlack } from "utils/slack"; /** * Retrieves the HTML from the CMU Dining website and parses the information @@ -23,12 +24,22 @@ export default class DiningParser { const [specials, soups] = await this.fetchSpecials(); for (const builder of locationBuilders) { - await builder.populateDetailedInfo(); + await builder + .populateDetailedInfo() + .catch((e) => + notifySlack( + ` failed to parse page with id ${builder.getConceptId()} with error ${ + e.stack + }` + ) + ); builder.setSoup(soups); builder.setSpecials(specials); } - return locationBuilders.map((builder) => builder.build()); + return locationBuilders + .map((builder) => builder.build()) + .filter((location) => location !== undefined); // remove the unsuccessful parses } private async initializeLocationBuildersFromMainPage(): Promise< diff --git a/src/types.ts b/src/types.ts index 2b3f198..e8235e6 100644 --- a/src/types.ts +++ b/src/types.ts @@ -18,7 +18,7 @@ export interface ILocation { /** Assuming these times fall after today */ times: IFullTimeRange[]; /** useful when figuring out which db time entries to overwrite. Can be undefined if no time data was properly scraped */ - today: IDate | undefined; + today: IDate; todaysSpecials: ISpecial[] | undefined; todaysSoups: ISpecial[] | undefined; } diff --git a/tests/database.test.ts b/tests/database.test.ts index c66c931..5b8eca3 100644 --- a/tests/database.test.ts +++ b/tests/database.test.ts @@ -6,7 +6,42 @@ import { _disconnectPoolConnection, initDB } from "db/db"; import { addLocationDataToDb } from "db/updateLocation"; import { getAllLocations } from "db/getLocations"; import { DateTime } from "luxon"; +import { ILocation } from "types"; const wait = (ms: number) => new Promise((re) => setTimeout(re, ms)); +const locationIn: ILocation = { + name: "test", + acceptsOnlineOrders: false, + conceptId: 1, + coordinates: { lat: 1, lng: 10 }, + description: "description", + today: { + year: 2025, + month: 1, + day: 1, + }, + times: [], + location: "location", + menu: "menu", + shortDescription: "hi", + url: "https://hi.com", + todaysSoups: [], + todaysSpecials: [], +}; +const locationOut = { + id: 1, + name: "test", + shortDescription: "hi", + description: "description", + url: "https://hi.com", + menu: "menu", + location: "location", + coordinateLat: 1, + coordinateLng: 10, + acceptsOnlineOrders: false, + times: [], + todaysSoups: [], + todaysSpecials: [], +}; describe("Redis", () => { let container: StartedPostgreSqlContainer; @@ -28,54 +63,16 @@ describe("Redis", () => { }); it("works on basic insertion", async () => { - await addLocationDataToDb({ - name: "test", - acceptsOnlineOrders: false, - conceptId: 1, - coordinates: { lat: 1, lng: 10 }, - description: "description", - today: { - year: 2025, - month: 1, - day: 1, - }, - times: [], - location: "location", - menu: "menu", - shortDescription: "hi", - url: "https://hi.com", - todaysSoups: [], - todaysSpecials: [], - }); + await addLocationDataToDb(locationIn); const dbResult = await getAllLocations(DateTime.now()); - expect(dbResult).toEqual([ - { - id: 1, - name: "test", - shortDescription: "hi", - description: "description", - url: "https://hi.com", - menu: "menu", - location: "location", - coordinateLat: 1, - coordinateLng: 10, - acceptsOnlineOrders: false, - times: [], - todaysSoups: [], - todaysSpecials: [], - }, - ]); + expect(dbResult).toEqual([locationOut]); }); it("properly resets state on every new test", async () => { expect(await getAllLocations(DateTime.now())).toEqual([]); }); it("works on insertion with times", async () => { await addLocationDataToDb({ - name: "test", - acceptsOnlineOrders: false, - conceptId: 1, - coordinates: { lat: 1, lng: 10 }, - description: "description", + ...locationIn, today: { year: 2025, month: 1, @@ -111,28 +108,13 @@ describe("Redis", () => { endMinutesFromMidnight: 2 * 60, }, ], - location: "location", - menu: "menu", - shortDescription: "hi", - url: "https://hi.com", - todaysSoups: [], - todaysSpecials: [], }); const dbResult = await getAllLocations( DateTime.fromObject({ year: 2025, month: 1, day: 2 }) ); expect(dbResult).toEqual([ { - id: 1, - name: "test", - shortDescription: "hi", - description: "description", - url: "https://hi.com", - menu: "menu", - location: "location", - coordinateLat: 1, - coordinateLng: 10, - acceptsOnlineOrders: false, + ...locationOut, times: [ { start: 1735725600000, @@ -147,8 +129,6 @@ describe("Redis", () => { end: 1751954400000, }, ], - todaysSoups: [], - todaysSpecials: [], }, ]); }); diff --git a/tests/expectedData.ts b/tests/expectedData.ts index 873db4f..ae333a3 100644 --- a/tests/expectedData.ts +++ b/tests/expectedData.ts @@ -79,14 +79,22 @@ export const expectedLocationData: ILocation[] = [ conceptId: 184, name: "CIAO BELLA", shortDescription: "Customizable pasta plates", - description: "", + description: + "At Au Bon Pain café bakery, each signature recipe is uniquely crafted. You can enjoy delicious hot or iced coffee and teas, espresso drinks, a variety of cold beverages, soup, a customized made-to-order breakfast or lunch sandwich or salad, or you can grab a pre-made salad, sandwich, wrap, yogurt parfait, fresh fruit or snack. There is always something new to try ... healthy choices, comfort food, indulgent treats … try them all!\n \n \n Nutritional information can be found at aubonpain.com/nutrition\n .\n \n \n \n To place a catering order online, visit catering.aubonpain.com\n . To pay with a department or organization’s oracle string, please email your order to abpcmu@grcafes.com\n . For on-campus assistance, call 412-268-1054.", url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/184", - location: "", + location: "Cohon Center, Second floor", menu: undefined, - coordinates: undefined, - acceptsOnlineOrders: false, + coordinates: { + lat: 40.44401, + lng: -79.942258, + }, + acceptsOnlineOrders: true, times: [], - today: undefined, + today: { + year: 2024, + month: 11, + day: 13, + }, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -95,14 +103,22 @@ export const expectedLocationData: ILocation[] = [ name: "DE FER COFFEE & TEA AT MAGGIE MURPH CAFÉ", shortDescription: "Locally-roasted specialty coffee, tea, and scratch-made food", - description: "", + description: + "At Au Bon Pain café bakery, each signature recipe is uniquely crafted. You can enjoy delicious hot or iced coffee and teas, espresso drinks, a variety of cold beverages, soup, a customized made-to-order breakfast or lunch sandwich or salad, or you can grab a pre-made salad, sandwich, wrap, yogurt parfait, fresh fruit or snack. There is always something new to try ... healthy choices, comfort food, indulgent treats … try them all!\n \n \n Nutritional information can be found at aubonpain.com/nutrition\n .\n \n \n \n To place a catering order online, visit catering.aubonpain.com\n . To pay with a department or organization’s oracle string, please email your order to abpcmu@grcafes.com\n . For on-campus assistance, call 412-268-1054.", url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/95", - location: "", + location: "Cohon Center, Second floor", menu: undefined, - coordinates: undefined, - acceptsOnlineOrders: false, + coordinates: { + lat: 40.44401, + lng: -79.942258, + }, + acceptsOnlineOrders: true, times: [], - today: undefined, + today: { + year: 2024, + month: 11, + day: 13, + }, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -110,14 +126,22 @@ export const expectedLocationData: ILocation[] = [ conceptId: 134, name: "E.A.T. (EVENINGS AT TEPPER) - ROHR COMMONS", shortDescription: "Dinner options at Tepper are Grubhub only!", - description: "", + description: + "At Au Bon Pain café bakery, each signature recipe is uniquely crafted. You can enjoy delicious hot or iced coffee and teas, espresso drinks, a variety of cold beverages, soup, a customized made-to-order breakfast or lunch sandwich or salad, or you can grab a pre-made salad, sandwich, wrap, yogurt parfait, fresh fruit or snack. There is always something new to try ... healthy choices, comfort food, indulgent treats … try them all!\n \n \n Nutritional information can be found at aubonpain.com/nutrition\n .\n \n \n \n To place a catering order online, visit catering.aubonpain.com\n . To pay with a department or organization’s oracle string, please email your order to abpcmu@grcafes.com\n . For on-campus assistance, call 412-268-1054.", url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/134", - location: "", + location: "Cohon Center, Second floor", menu: undefined, - coordinates: undefined, - acceptsOnlineOrders: false, + coordinates: { + lat: 40.44401, + lng: -79.942258, + }, + acceptsOnlineOrders: true, times: [], - today: undefined, + today: { + year: 2024, + month: 11, + day: 13, + }, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -125,14 +149,22 @@ export const expectedLocationData: ILocation[] = [ conceptId: 178, name: "THE EDGE CAFE & MARKET", shortDescription: "Vaad-certified kosher bagels, pizza, bourekas & more!", - description: "", + description: + "At Au Bon Pain café bakery, each signature recipe is uniquely crafted. You can enjoy delicious hot or iced coffee and teas, espresso drinks, a variety of cold beverages, soup, a customized made-to-order breakfast or lunch sandwich or salad, or you can grab a pre-made salad, sandwich, wrap, yogurt parfait, fresh fruit or snack. There is always something new to try ... healthy choices, comfort food, indulgent treats … try them all!\n \n \n Nutritional information can be found at aubonpain.com/nutrition\n .\n \n \n \n To place a catering order online, visit catering.aubonpain.com\n . To pay with a department or organization’s oracle string, please email your order to abpcmu@grcafes.com\n . For on-campus assistance, call 412-268-1054.", url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/178", - location: "", + location: "Cohon Center, Second floor", menu: undefined, - coordinates: undefined, - acceptsOnlineOrders: false, + coordinates: { + lat: 40.44401, + lng: -79.942258, + }, + acceptsOnlineOrders: true, times: [], - today: undefined, + today: { + year: 2024, + month: 11, + day: 13, + }, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -141,14 +173,22 @@ export const expectedLocationData: ILocation[] = [ name: "EGG SHOPPE - GRUBHUB ONLY", shortDescription: "Breakfast available only on Grubhub for pickup in Schatz.", - description: "", + description: + "At Au Bon Pain café bakery, each signature recipe is uniquely crafted. You can enjoy delicious hot or iced coffee and teas, espresso drinks, a variety of cold beverages, soup, a customized made-to-order breakfast or lunch sandwich or salad, or you can grab a pre-made salad, sandwich, wrap, yogurt parfait, fresh fruit or snack. There is always something new to try ... healthy choices, comfort food, indulgent treats … try them all!\n \n \n Nutritional information can be found at aubonpain.com/nutrition\n .\n \n \n \n To place a catering order online, visit catering.aubonpain.com\n . To pay with a department or organization’s oracle string, please email your order to abpcmu@grcafes.com\n . For on-campus assistance, call 412-268-1054.", url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/88", - location: "", + location: "Cohon Center, Second floor", menu: undefined, - coordinates: undefined, - acceptsOnlineOrders: false, + coordinates: { + lat: 40.44401, + lng: -79.942258, + }, + acceptsOnlineOrders: true, times: [], - today: undefined, + today: { + year: 2024, + month: 11, + day: 13, + }, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -157,14 +197,22 @@ export const expectedLocationData: ILocation[] = [ name: "ENTROPY+", shortDescription: "On-campus convenience store, serving snacks, grab-and-go meals, coffee, & more", - description: "", + description: + "At Au Bon Pain café bakery, each signature recipe is uniquely crafted. You can enjoy delicious hot or iced coffee and teas, espresso drinks, a variety of cold beverages, soup, a customized made-to-order breakfast or lunch sandwich or salad, or you can grab a pre-made salad, sandwich, wrap, yogurt parfait, fresh fruit or snack. There is always something new to try ... healthy choices, comfort food, indulgent treats … try them all!\n \n \n Nutritional information can be found at aubonpain.com/nutrition\n .\n \n \n \n To place a catering order online, visit catering.aubonpain.com\n . To pay with a department or organization’s oracle string, please email your order to abpcmu@grcafes.com\n . For on-campus assistance, call 412-268-1054.", url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/103", - location: "", + location: "Cohon Center, Second floor", menu: undefined, - coordinates: undefined, - acceptsOnlineOrders: false, + coordinates: { + lat: 40.44401, + lng: -79.942258, + }, + acceptsOnlineOrders: true, times: [], - today: undefined, + today: { + year: 2024, + month: 11, + day: 13, + }, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -247,14 +295,22 @@ export const expectedLocationData: ILocation[] = [ conceptId: 126, name: "FOOD HALL AT RESNIK", shortDescription: "Summer all-you-care-to-eat: OPENS JUNE 24!", - description: "", + description: + "At Au Bon Pain café bakery, each signature recipe is uniquely crafted. You can enjoy delicious hot or iced coffee and teas, espresso drinks, a variety of cold beverages, soup, a customized made-to-order breakfast or lunch sandwich or salad, or you can grab a pre-made salad, sandwich, wrap, yogurt parfait, fresh fruit or snack. There is always something new to try ... healthy choices, comfort food, indulgent treats … try them all!\n \n \n Nutritional information can be found at aubonpain.com/nutrition\n .\n \n \n \n To place a catering order online, visit catering.aubonpain.com\n . To pay with a department or organization’s oracle string, please email your order to abpcmu@grcafes.com\n . For on-campus assistance, call 412-268-1054.", url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/126", - location: "", + location: "Cohon Center, Second floor", menu: undefined, - coordinates: undefined, - acceptsOnlineOrders: false, + coordinates: { + lat: 40.44401, + lng: -79.942258, + }, + acceptsOnlineOrders: true, times: [], - today: undefined, + today: { + year: 2024, + month: 11, + day: 13, + }, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -263,14 +319,22 @@ export const expectedLocationData: ILocation[] = [ name: "FORBES AVENUE SUBS - ROHR COMMONS", shortDescription: "Made-to-order deli-style subs and wraps. Vegan options available.", - description: "", + description: + "At Au Bon Pain café bakery, each signature recipe is uniquely crafted. You can enjoy delicious hot or iced coffee and teas, espresso drinks, a variety of cold beverages, soup, a customized made-to-order breakfast or lunch sandwich or salad, or you can grab a pre-made salad, sandwich, wrap, yogurt parfait, fresh fruit or snack. There is always something new to try ... healthy choices, comfort food, indulgent treats … try them all!\n \n \n Nutritional information can be found at aubonpain.com/nutrition\n .\n \n \n \n To place a catering order online, visit catering.aubonpain.com\n . To pay with a department or organization’s oracle string, please email your order to abpcmu@grcafes.com\n . For on-campus assistance, call 412-268-1054.", url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/173", - location: "", + location: "Cohon Center, Second floor", menu: undefined, - coordinates: undefined, - acceptsOnlineOrders: false, + coordinates: { + lat: 40.44401, + lng: -79.942258, + }, + acceptsOnlineOrders: true, times: [], - today: undefined, + today: { + year: 2024, + month: 11, + day: 13, + }, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -279,14 +343,22 @@ export const expectedLocationData: ILocation[] = [ name: "EL GALLO DE ORO", shortDescription: "Mexican cuisine, burritos and burrito bowls, tacos, quesadillas, salads", - description: "", + description: + "At Au Bon Pain café bakery, each signature recipe is uniquely crafted. You can enjoy delicious hot or iced coffee and teas, espresso drinks, a variety of cold beverages, soup, a customized made-to-order breakfast or lunch sandwich or salad, or you can grab a pre-made salad, sandwich, wrap, yogurt parfait, fresh fruit or snack. There is always something new to try ... healthy choices, comfort food, indulgent treats … try them all!\n \n \n Nutritional information can be found at aubonpain.com/nutrition\n .\n \n \n \n To place a catering order online, visit catering.aubonpain.com\n . To pay with a department or organization’s oracle string, please email your order to abpcmu@grcafes.com\n . For on-campus assistance, call 412-268-1054.", url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/91", - location: "", + location: "Cohon Center, Second floor", menu: undefined, - coordinates: undefined, - acceptsOnlineOrders: false, + coordinates: { + lat: 40.44401, + lng: -79.942258, + }, + acceptsOnlineOrders: true, times: [], - today: undefined, + today: { + year: 2024, + month: 11, + day: 13, + }, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -295,14 +367,22 @@ export const expectedLocationData: ILocation[] = [ name: "GRANO PIZZA", shortDescription: "Hand-stretched, personal-sized pizzas on a New York or focaccia-style crust", - description: "", + description: + "At Au Bon Pain café bakery, each signature recipe is uniquely crafted. You can enjoy delicious hot or iced coffee and teas, espresso drinks, a variety of cold beverages, soup, a customized made-to-order breakfast or lunch sandwich or salad, or you can grab a pre-made salad, sandwich, wrap, yogurt parfait, fresh fruit or snack. There is always something new to try ... healthy choices, comfort food, indulgent treats … try them all!\n \n \n Nutritional information can be found at aubonpain.com/nutrition\n .\n \n \n \n To place a catering order online, visit catering.aubonpain.com\n . To pay with a department or organization’s oracle string, please email your order to abpcmu@grcafes.com\n . For on-campus assistance, call 412-268-1054.", url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/139", - location: "", + location: "Cohon Center, Second floor", menu: undefined, - coordinates: undefined, - acceptsOnlineOrders: false, + coordinates: { + lat: 40.44401, + lng: -79.942258, + }, + acceptsOnlineOrders: true, times: [], - today: undefined, + today: { + year: 2024, + month: 11, + day: 13, + }, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -377,14 +457,22 @@ export const expectedLocationData: ILocation[] = [ name: "ROHR CAFÉ - LA PRIMA", shortDescription: "La Prima's second location on campus serving Italian-style coffee and food", - description: "", + description: + "At Au Bon Pain café bakery, each signature recipe is uniquely crafted. You can enjoy delicious hot or iced coffee and teas, espresso drinks, a variety of cold beverages, soup, a customized made-to-order breakfast or lunch sandwich or salad, or you can grab a pre-made salad, sandwich, wrap, yogurt parfait, fresh fruit or snack. There is always something new to try ... healthy choices, comfort food, indulgent treats … try them all!\n \n \n Nutritional information can be found at aubonpain.com/nutrition\n .\n \n \n \n To place a catering order online, visit catering.aubonpain.com\n . To pay with a department or organization’s oracle string, please email your order to abpcmu@grcafes.com\n . For on-campus assistance, call 412-268-1054.", url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/115", - location: "", + location: "Cohon Center, Second floor", menu: undefined, - coordinates: undefined, - acceptsOnlineOrders: false, + coordinates: { + lat: 40.44401, + lng: -79.942258, + }, + acceptsOnlineOrders: true, times: [], - today: undefined, + today: { + year: 2024, + month: 11, + day: 13, + }, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -393,14 +481,22 @@ export const expectedLocationData: ILocation[] = [ name: "MILLIE'S COFFEE 'N' CREAMERY - ROHR COMMONS", shortDescription: "NOW OPEN! Sustainably sourced coffee, ice cream, and vegan gelato.", - description: "", + description: + "At Au Bon Pain café bakery, each signature recipe is uniquely crafted. You can enjoy delicious hot or iced coffee and teas, espresso drinks, a variety of cold beverages, soup, a customized made-to-order breakfast or lunch sandwich or salad, or you can grab a pre-made salad, sandwich, wrap, yogurt parfait, fresh fruit or snack. There is always something new to try ... healthy choices, comfort food, indulgent treats … try them all!\n \n \n Nutritional information can be found at aubonpain.com/nutrition\n .\n \n \n \n To place a catering order online, visit catering.aubonpain.com\n . To pay with a department or organization’s oracle string, please email your order to abpcmu@grcafes.com\n . For on-campus assistance, call 412-268-1054.", url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/136", - location: "", + location: "Cohon Center, Second floor", menu: undefined, - coordinates: undefined, - acceptsOnlineOrders: false, + coordinates: { + lat: 40.44401, + lng: -79.942258, + }, + acceptsOnlineOrders: true, times: [], - today: undefined, + today: { + year: 2024, + month: 11, + day: 13, + }, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -409,14 +505,22 @@ export const expectedLocationData: ILocation[] = [ name: "NOURISH", shortDescription: "Closed until fall semester: Grab and go available in Entropy", - description: "", + description: + "At Au Bon Pain café bakery, each signature recipe is uniquely crafted. You can enjoy delicious hot or iced coffee and teas, espresso drinks, a variety of cold beverages, soup, a customized made-to-order breakfast or lunch sandwich or salad, or you can grab a pre-made salad, sandwich, wrap, yogurt parfait, fresh fruit or snack. There is always something new to try ... healthy choices, comfort food, indulgent treats … try them all!\n \n \n Nutritional information can be found at aubonpain.com/nutrition\n .\n \n \n \n To place a catering order online, visit catering.aubonpain.com\n . To pay with a department or organization’s oracle string, please email your order to abpcmu@grcafes.com\n . For on-campus assistance, call 412-268-1054.", url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/127", - location: "", + location: "Cohon Center, Second floor", menu: undefined, - coordinates: undefined, - acceptsOnlineOrders: false, + coordinates: { + lat: 40.44401, + lng: -79.942258, + }, + acceptsOnlineOrders: true, times: [], - today: undefined, + today: { + year: 2024, + month: 11, + day: 13, + }, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -425,14 +529,22 @@ export const expectedLocationData: ILocation[] = [ name: "LA PRIMA ESPRESSO", shortDescription: "Italian-style coffee, pastries, grab-and-go sandwiches, salads, and sides", - description: "", + description: + "At Au Bon Pain café bakery, each signature recipe is uniquely crafted. You can enjoy delicious hot or iced coffee and teas, espresso drinks, a variety of cold beverages, soup, a customized made-to-order breakfast or lunch sandwich or salad, or you can grab a pre-made salad, sandwich, wrap, yogurt parfait, fresh fruit or snack. There is always something new to try ... healthy choices, comfort food, indulgent treats … try them all!\n \n \n Nutritional information can be found at aubonpain.com/nutrition\n .\n \n \n \n To place a catering order online, visit catering.aubonpain.com\n . To pay with a department or organization’s oracle string, please email your order to abpcmu@grcafes.com\n . For on-campus assistance, call 412-268-1054.", url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/94", - location: "", + location: "Cohon Center, Second floor", menu: undefined, - coordinates: undefined, - acceptsOnlineOrders: false, + coordinates: { + lat: 40.44401, + lng: -79.942258, + }, + acceptsOnlineOrders: true, times: [], - today: undefined, + today: { + year: 2024, + month: 11, + day: 13, + }, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -441,14 +553,22 @@ export const expectedLocationData: ILocation[] = [ name: "REDHAWK COFFEE", shortDescription: "Local coffee roaster serving specialty coffee, tea, baked goods, and grab-and-go food.", - description: "", + description: + "At Au Bon Pain café bakery, each signature recipe is uniquely crafted. You can enjoy delicious hot or iced coffee and teas, espresso drinks, a variety of cold beverages, soup, a customized made-to-order breakfast or lunch sandwich or salad, or you can grab a pre-made salad, sandwich, wrap, yogurt parfait, fresh fruit or snack. There is always something new to try ... healthy choices, comfort food, indulgent treats … try them all!\n \n \n Nutritional information can be found at aubonpain.com/nutrition\n .\n \n \n \n To place a catering order online, visit catering.aubonpain.com\n . To pay with a department or organization’s oracle string, please email your order to abpcmu@grcafes.com\n . For on-campus assistance, call 412-268-1054.", url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/186", - location: "", + location: "Cohon Center, Second floor", menu: undefined, - coordinates: undefined, - acceptsOnlineOrders: false, + coordinates: { + lat: 40.44401, + lng: -79.942258, + }, + acceptsOnlineOrders: true, times: [], - today: undefined, + today: { + year: 2024, + month: 11, + day: 13, + }, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -457,14 +577,22 @@ export const expectedLocationData: ILocation[] = [ name: "REVOLUTION NOODLE", shortDescription: "Customizable Malatang Noodle bowls, from the owners of Hunan Express", - description: "", + description: + "At Au Bon Pain café bakery, each signature recipe is uniquely crafted. You can enjoy delicious hot or iced coffee and teas, espresso drinks, a variety of cold beverages, soup, a customized made-to-order breakfast or lunch sandwich or salad, or you can grab a pre-made salad, sandwich, wrap, yogurt parfait, fresh fruit or snack. There is always something new to try ... healthy choices, comfort food, indulgent treats … try them all!\n \n \n Nutritional information can be found at aubonpain.com/nutrition\n .\n \n \n \n To place a catering order online, visit catering.aubonpain.com\n . To pay with a department or organization’s oracle string, please email your order to abpcmu@grcafes.com\n . For on-campus assistance, call 412-268-1054.", url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/174", - location: "", + location: "Cohon Center, Second floor", menu: undefined, - coordinates: undefined, - acceptsOnlineOrders: false, + coordinates: { + lat: 40.44401, + lng: -79.942258, + }, + acceptsOnlineOrders: true, times: [], - today: undefined, + today: { + year: 2024, + month: 11, + day: 13, + }, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -630,14 +758,22 @@ export const expectedLocationData: ILocation[] = [ name: "SCOTTY'S MARKET BY SALEM'S", shortDescription: "International and conventional groceries, savory grilled meats and hot meals.", - description: "", + description: + "At Au Bon Pain café bakery, each signature recipe is uniquely crafted. You can enjoy delicious hot or iced coffee and teas, espresso drinks, a variety of cold beverages, soup, a customized made-to-order breakfast or lunch sandwich or salad, or you can grab a pre-made salad, sandwich, wrap, yogurt parfait, fresh fruit or snack. There is always something new to try ... healthy choices, comfort food, indulgent treats … try them all!\n \n \n Nutritional information can be found at aubonpain.com/nutrition\n .\n \n \n \n To place a catering order online, visit catering.aubonpain.com\n . To pay with a department or organization’s oracle string, please email your order to abpcmu@grcafes.com\n . For on-campus assistance, call 412-268-1054.", url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/180", - location: "", + location: "Cohon Center, Second floor", menu: undefined, - coordinates: undefined, - acceptsOnlineOrders: false, + coordinates: { + lat: 40.44401, + lng: -79.942258, + }, + acceptsOnlineOrders: true, times: [], - today: undefined, + today: { + year: 2024, + month: 11, + day: 13, + }, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -646,14 +782,22 @@ export const expectedLocationData: ILocation[] = [ name: "STACK'D DESSERT BAR", shortDescription: "Cool down this summer with milkshakes, ice cream sundaes and floats!", - description: "", + description: + "At Au Bon Pain café bakery, each signature recipe is uniquely crafted. You can enjoy delicious hot or iced coffee and teas, espresso drinks, a variety of cold beverages, soup, a customized made-to-order breakfast or lunch sandwich or salad, or you can grab a pre-made salad, sandwich, wrap, yogurt parfait, fresh fruit or snack. There is always something new to try ... healthy choices, comfort food, indulgent treats … try them all!\n \n \n Nutritional information can be found at aubonpain.com/nutrition\n .\n \n \n \n To place a catering order online, visit catering.aubonpain.com\n . To pay with a department or organization’s oracle string, please email your order to abpcmu@grcafes.com\n . For on-campus assistance, call 412-268-1054.", url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/190", - location: "", + location: "Cohon Center, Second floor", menu: undefined, - coordinates: undefined, - acceptsOnlineOrders: false, + coordinates: { + lat: 40.44401, + lng: -79.942258, + }, + acceptsOnlineOrders: true, times: [], - today: undefined, + today: { + year: 2024, + month: 11, + day: 13, + }, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -662,14 +806,22 @@ export const expectedLocationData: ILocation[] = [ name: "STACK'D UNDERGROUND", shortDescription: "Smashed burgers, Nashville-style chicken an gourmet grilled cheese", - description: "", + description: + "At Au Bon Pain café bakery, each signature recipe is uniquely crafted. You can enjoy delicious hot or iced coffee and teas, espresso drinks, a variety of cold beverages, soup, a customized made-to-order breakfast or lunch sandwich or salad, or you can grab a pre-made salad, sandwich, wrap, yogurt parfait, fresh fruit or snack. There is always something new to try ... healthy choices, comfort food, indulgent treats … try them all!\n \n \n Nutritional information can be found at aubonpain.com/nutrition\n .\n \n \n \n To place a catering order online, visit catering.aubonpain.com\n . To pay with a department or organization’s oracle string, please email your order to abpcmu@grcafes.com\n . For on-campus assistance, call 412-268-1054.", url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/188", - location: "", + location: "Cohon Center, Second floor", menu: undefined, - coordinates: undefined, - acceptsOnlineOrders: false, + coordinates: { + lat: 40.44401, + lng: -79.942258, + }, + acceptsOnlineOrders: true, times: [], - today: undefined, + today: { + year: 2024, + month: 11, + day: 13, + }, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -678,14 +830,22 @@ export const expectedLocationData: ILocation[] = [ name: "STEPHANIE'S - MARKET C", shortDescription: "Fresh sandwiches, wraps and salads, snacks, sweets, gourmet coffee and cold beverages", - description: "", + description: + "At Au Bon Pain café bakery, each signature recipe is uniquely crafted. You can enjoy delicious hot or iced coffee and teas, espresso drinks, a variety of cold beverages, soup, a customized made-to-order breakfast or lunch sandwich or salad, or you can grab a pre-made salad, sandwich, wrap, yogurt parfait, fresh fruit or snack. There is always something new to try ... healthy choices, comfort food, indulgent treats … try them all!\n \n \n Nutritional information can be found at aubonpain.com/nutrition\n .\n \n \n \n To place a catering order online, visit catering.aubonpain.com\n . To pay with a department or organization’s oracle string, please email your order to abpcmu@grcafes.com\n . For on-campus assistance, call 412-268-1054.", url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/148", - location: "", + location: "Cohon Center, Second floor", menu: undefined, - coordinates: undefined, - acceptsOnlineOrders: false, + coordinates: { + lat: 40.44401, + lng: -79.942258, + }, + acceptsOnlineOrders: true, times: [], - today: undefined, + today: { + year: 2024, + month: 11, + day: 13, + }, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -693,14 +853,22 @@ export const expectedLocationData: ILocation[] = [ conceptId: 82, name: "TAHINI", shortDescription: "Fresh Mediterranean, Certified Kosher Cuisine", - description: "", + description: + "At Au Bon Pain café bakery, each signature recipe is uniquely crafted. You can enjoy delicious hot or iced coffee and teas, espresso drinks, a variety of cold beverages, soup, a customized made-to-order breakfast or lunch sandwich or salad, or you can grab a pre-made salad, sandwich, wrap, yogurt parfait, fresh fruit or snack. There is always something new to try ... healthy choices, comfort food, indulgent treats … try them all!\n \n \n Nutritional information can be found at aubonpain.com/nutrition\n .\n \n \n \n To place a catering order online, visit catering.aubonpain.com\n . To pay with a department or organization’s oracle string, please email your order to abpcmu@grcafes.com\n . For on-campus assistance, call 412-268-1054.", url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/82", - location: "", + location: "Cohon Center, Second floor", menu: undefined, - coordinates: undefined, - acceptsOnlineOrders: false, + coordinates: { + lat: 40.44401, + lng: -79.942258, + }, + acceptsOnlineOrders: true, times: [], - today: undefined, + today: { + year: 2024, + month: 11, + day: 13, + }, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -767,14 +935,22 @@ export const expectedLocationData: ILocation[] = [ conceptId: 114, name: "TASTE OF INDIA", shortDescription: "Taste of India provide a vibrant tastes of India.", - description: "", + description: + "At Au Bon Pain café bakery, each signature recipe is uniquely crafted. You can enjoy delicious hot or iced coffee and teas, espresso drinks, a variety of cold beverages, soup, a customized made-to-order breakfast or lunch sandwich or salad, or you can grab a pre-made salad, sandwich, wrap, yogurt parfait, fresh fruit or snack. There is always something new to try ... healthy choices, comfort food, indulgent treats … try them all!\n \n \n Nutritional information can be found at aubonpain.com/nutrition\n .\n \n \n \n To place a catering order online, visit catering.aubonpain.com\n . To pay with a department or organization’s oracle string, please email your order to abpcmu@grcafes.com\n . For on-campus assistance, call 412-268-1054.", url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/114", - location: "", + location: "Cohon Center, Second floor", menu: undefined, - coordinates: undefined, - acceptsOnlineOrders: false, + coordinates: { + lat: 40.44401, + lng: -79.942258, + }, + acceptsOnlineOrders: true, times: [], - today: undefined, + today: { + year: 2024, + month: 11, + day: 13, + }, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -783,14 +959,22 @@ export const expectedLocationData: ILocation[] = [ name: "TEPPER TAQUERIA", shortDescription: "Mexican-style street tacos, burritos, quesadillas, bowls, and nachos", - description: "", + description: + "At Au Bon Pain café bakery, each signature recipe is uniquely crafted. You can enjoy delicious hot or iced coffee and teas, espresso drinks, a variety of cold beverages, soup, a customized made-to-order breakfast or lunch sandwich or salad, or you can grab a pre-made salad, sandwich, wrap, yogurt parfait, fresh fruit or snack. There is always something new to try ... healthy choices, comfort food, indulgent treats … try them all!\n \n \n Nutritional information can be found at aubonpain.com/nutrition\n .\n \n \n \n To place a catering order online, visit catering.aubonpain.com\n . To pay with a department or organization’s oracle string, please email your order to abpcmu@grcafes.com\n . For on-campus assistance, call 412-268-1054.", url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/185", - location: "", + location: "Cohon Center, Second floor", menu: undefined, - coordinates: undefined, - acceptsOnlineOrders: false, + coordinates: { + lat: 40.44401, + lng: -79.942258, + }, + acceptsOnlineOrders: true, times: [], - today: undefined, + today: { + year: 2024, + month: 11, + day: 13, + }, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -799,14 +983,22 @@ export const expectedLocationData: ILocation[] = [ name: "TRUE BURGER", shortDescription: "Unique, hand-crafted signature sandwiches and smash burgers", - description: "", + description: + "At Au Bon Pain café bakery, each signature recipe is uniquely crafted. You can enjoy delicious hot or iced coffee and teas, espresso drinks, a variety of cold beverages, soup, a customized made-to-order breakfast or lunch sandwich or salad, or you can grab a pre-made salad, sandwich, wrap, yogurt parfait, fresh fruit or snack. There is always something new to try ... healthy choices, comfort food, indulgent treats … try them all!\n \n \n Nutritional information can be found at aubonpain.com/nutrition\n .\n \n \n \n To place a catering order online, visit catering.aubonpain.com\n . To pay with a department or organization’s oracle string, please email your order to abpcmu@grcafes.com\n . For on-campus assistance, call 412-268-1054.", url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/138", - location: "", + location: "Cohon Center, Second floor", menu: undefined, - coordinates: undefined, - acceptsOnlineOrders: false, + coordinates: { + lat: 40.44401, + lng: -79.942258, + }, + acceptsOnlineOrders: true, times: [], - today: undefined, + today: { + year: 2024, + month: 11, + day: 13, + }, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -814,14 +1006,22 @@ export const expectedLocationData: ILocation[] = [ conceptId: 98, name: "URBAN REVOLUTION - GRUBHUB ONLY", shortDescription: "Grubhub-only, featuring fresh-carved rotisserie options", - description: "", + description: + "At Au Bon Pain café bakery, each signature recipe is uniquely crafted. You can enjoy delicious hot or iced coffee and teas, espresso drinks, a variety of cold beverages, soup, a customized made-to-order breakfast or lunch sandwich or salad, or you can grab a pre-made salad, sandwich, wrap, yogurt parfait, fresh fruit or snack. There is always something new to try ... healthy choices, comfort food, indulgent treats … try them all!\n \n \n Nutritional information can be found at aubonpain.com/nutrition\n .\n \n \n \n To place a catering order online, visit catering.aubonpain.com\n . To pay with a department or organization’s oracle string, please email your order to abpcmu@grcafes.com\n . For on-campus assistance, call 412-268-1054.", url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/98", - location: "", + location: "Cohon Center, Second floor", menu: undefined, - coordinates: undefined, - acceptsOnlineOrders: false, + coordinates: { + lat: 40.44401, + lng: -79.942258, + }, + acceptsOnlineOrders: true, times: [], - today: undefined, + today: { + year: 2024, + month: 11, + day: 13, + }, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -830,14 +1030,22 @@ export const expectedLocationData: ILocation[] = [ name: "WILD BLUE SUSHI - RUGE ATRIUM", shortDescription: "Fresh prepared sushi, hot rice bowls, bubble tea and coffee", - description: "", + description: + "At Au Bon Pain café bakery, each signature recipe is uniquely crafted. You can enjoy delicious hot or iced coffee and teas, espresso drinks, a variety of cold beverages, soup, a customized made-to-order breakfast or lunch sandwich or salad, or you can grab a pre-made salad, sandwich, wrap, yogurt parfait, fresh fruit or snack. There is always something new to try ... healthy choices, comfort food, indulgent treats … try them all!\n \n \n Nutritional information can be found at aubonpain.com/nutrition\n .\n \n \n \n To place a catering order online, visit catering.aubonpain.com\n . To pay with a department or organization’s oracle string, please email your order to abpcmu@grcafes.com\n . For on-campus assistance, call 412-268-1054.", url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/155", - location: "", + location: "Cohon Center, Second floor", menu: undefined, - coordinates: undefined, - acceptsOnlineOrders: false, + coordinates: { + lat: 40.44401, + lng: -79.942258, + }, + acceptsOnlineOrders: true, times: [], - today: undefined, + today: { + year: 2024, + month: 11, + day: 13, + }, todaysSpecials: undefined, todaysSoups: undefined, }, @@ -846,15 +1054,30 @@ export const expectedLocationData: ILocation[] = [ name: "ZEBRA LOUNGE", shortDescription: "hot and iced coffee and espresso drinks, pastries, sushi, bagels, pizza", - description: "", + description: + "At Au Bon Pain café bakery, each signature recipe is uniquely crafted. You can enjoy delicious hot or iced coffee and teas, espresso drinks, a variety of cold beverages, soup, a customized made-to-order breakfast or lunch sandwich or salad, or you can grab a pre-made salad, sandwich, wrap, yogurt parfait, fresh fruit or snack. There is always something new to try ... healthy choices, comfort food, indulgent treats … try them all!\n \n \n Nutritional information can be found at aubonpain.com/nutrition\n .\n \n \n \n To place a catering order online, visit catering.aubonpain.com\n . To pay with a department or organization’s oracle string, please email your order to abpcmu@grcafes.com\n . For on-campus assistance, call 412-268-1054.", url: "https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/84", - location: "", + location: "Cohon Center, Second floor", menu: undefined, - coordinates: undefined, - acceptsOnlineOrders: false, + coordinates: { + lat: 40.44401, + lng: -79.942258, + }, + acceptsOnlineOrders: true, times: [], - today: undefined, + today: { + year: 2024, + month: 11, + day: 13, + }, todaysSpecials: undefined, todaysSoups: undefined, }, ]; + +export const expectedLocationData2: ILocation[] = expectedLocationData.filter( + (location) => + ["92", "110", "113", "175", "108", "168"].includes( + location.conceptId.toString() + ) +); diff --git a/tests/html/concepts/all-closed.html b/tests/html/concepts/all-closed.html new file mode 100644 index 0000000..c817fb3 --- /dev/null +++ b/tests/html/concepts/all-closed.html @@ -0,0 +1,224 @@ + + + + + + + + + + + + + Concept + + + + +

+ +
+
+
+
+
+
+
+ arrow_back +
+
Back to Locations
+
+
+
+
+
+
+

AU BON PAIN AT SKIBO CAF É

+ +
+

At Au Bon Pain café bakery, each signature recipe is uniquely crafted. You can enjoy delicious hot or iced coffee and teas, espresso drinks, a variety of cold beverages, soup, a customized made-to-order breakfast or lunch sandwich or salad, or you can grab a pre-made salad, sandwich, wrap, yogurt parfait, fresh fruit or snack. There is always something new to try ... healthy choices, comfort food, indulgent treats … try them all!

+

+
+

+

+ Nutritional information can be found at aubonpain.com/nutrition + . +

+

+
+

+

+ To place a catering order online, visit catering.aubonpain.com + . To pay with a department or organization’s oracle string, please email your order to abpcmu@grcafes.com + . For on-campus assistance, call 412-268-1054. +

+
+

Hours

+
+
    +
  • +
    + Thursday + November 13, CLOSED +
    +
  • +
  • +
    + Friday + November 14, CLOSED +
    +
  • +
  • +
    + Saturday + November 15, CLOSED +
    +
  • +
  • +
    + Sunday + November 16, CLOSED +
    +
  • +
  • +
    + Monday + November 17, CLOSED +
    +
  • +
  • +
    + Tuesday + November 18, CLOSED +
    +
  • +
  • +
    + Wednesday + November 19, CLOSED +
    +
  • +
+
+
+ +
+
+
+
+
+ +
+ +
+ An error has occurred. This application may no longer respond until reloaded. + + + Reload + 🗙 +
+ + + diff --git a/tests/html/concepts/error.html b/tests/html/concepts/error.html new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration.test.ts b/tests/integration.test.ts index 5ecf49b..d30ba24 100644 --- a/tests/integration.test.ts +++ b/tests/integration.test.ts @@ -1,5 +1,5 @@ import DiningParser from "../src/parser/diningParser"; -import { expectedLocationData } from "./expectedData"; +import { expectedLocationData, expectedLocationData2 } from "./expectedData"; import { mockAxiosGETMethodWithFilePaths } from "./mockAxios"; import { setUpTimingTest, @@ -11,12 +11,13 @@ import { Fri, Sat, Sun, + setUpArbitraryTest, } from "./mockTimings"; import { DateTime } from "luxon"; vi.mock("axios"); -test("ok", () => {}); -test("the whole thing, including locationOverwrites", async () => { + +test("the whole thing", async () => { mockAxiosGETMethodWithFilePaths({ conceptListFilePath: "html/listconcepts.html", specialsFilePath: "html/specials.html", @@ -24,7 +25,7 @@ test("the whole thing, including locationOverwrites", async () => { getConceptFilePath: (conceptId: string) => ["92", "110", "113", "175", "108", "168"].includes(conceptId) ? `html/concepts/${conceptId}.html` - : "html/blank.html", + : "html/concepts/all-closed.html", serverDate: DateTime.fromObject({ year: 2024, month: 8, @@ -33,8 +34,28 @@ test("the whole thing, including locationOverwrites", async () => { }); const parser = new DiningParser(); const parsedLocationData = await parser.process(); + expect(parsedLocationData).toStrictEqual(expectedLocationData); }); +test("the whole thing, with per-page errors", async () => { + mockAxiosGETMethodWithFilePaths({ + conceptListFilePath: "html/listconcepts.html", + specialsFilePath: "html/specials.html", + soupsFilePath: "html/soups.html", + getConceptFilePath: (conceptId: string) => + ["92", "110", "113", "175", "108", "168"].includes(conceptId) + ? `html/concepts/${conceptId}.html` + : "html/concepts/error.html", + serverDate: DateTime.fromObject({ + year: 2024, + month: 8, + day: 5, + }) as DateTime, + }); + const parser = new DiningParser(); + const parsedLocationData = await parser.process(); + expect(parsedLocationData).toStrictEqual(expectedLocationData2); +}); test("specials for The Exchange", async () => { mockAxiosGETMethodWithFilePaths({ conceptListFilePath: "html/listconcepts.html", @@ -43,7 +64,7 @@ test("specials for The Exchange", async () => { getConceptFilePath: (conceptId: string) => ["92", "110", "113", "175", "108"].includes(conceptId) ? `html/concepts/${conceptId}.html` - : "html/blank.html", + : "html/concepts/error.html", serverDate: DateTime.fromObject({ year: 2024, month: 8, @@ -496,3 +517,31 @@ describe("time edge cases", () => { ]); }); }); +describe("new year parsing", () => { + test("new years", async () => { + setUpArbitraryTest( + [ + ["Tuesday", "December 30", "7:00 AM - 9:00 PM"], + ["Wednesday", "December 31", "7:00 AM - 9:00 PM"], + ["Thursday", "January 1", "7:00 AM - 9:00 PM"], + ["Friday", "January 2", "7:00 AM - 9:00 PM"], + ["Saturday", "January 3", "7:00 AM - 9:00 PM"], + ["Sunday", "January 4", "7:00 AM - 9:00 PM"], + ["Monday", "January 5", "7:00 AM - 9:00 PM"], + ], + DateTime.fromObject({ year: 2025, month: 12, day: 30 }) + ); + await queryParserAndAssertTimingsCorrect( + [ + [Sun, 7, 0, 21, 0], + [Mon, 7, 0, 21, 0], + [Tue, 7, 0, 21, 0], + [Wed, 7, 0, 21, 0], + [Thur, 7, 0, 21, 0], + [Fri, 7, 0, 21, 0], + [Sat, 7, 0, 21, 0], + ], + DateTime.fromObject({ year: 2025, month: 12, day: 30 }) + ); + }); +}); diff --git a/tests/locationMerger.test.ts b/tests/locationMerger.test.ts index f02d8c8..630799e 100644 --- a/tests/locationMerger.test.ts +++ b/tests/locationMerger.test.ts @@ -11,7 +11,7 @@ const locationA: ILocation = { acceptsOnlineOrders: true, shortDescription: undefined, coordinates: undefined, - today: undefined, + today: { day: 1, month: 1, year: 1 }, menu: undefined, todaysSoups: undefined, todaysSpecials: undefined, @@ -26,7 +26,7 @@ const locationAA: ILocation = { acceptsOnlineOrders: true, shortDescription: undefined, coordinates: undefined, - today: undefined, + today: { day: 1, month: 1, year: 1 }, menu: undefined, todaysSoups: undefined, todaysSpecials: undefined, @@ -41,7 +41,7 @@ const locationAAA: ILocation = { acceptsOnlineOrders: false, shortDescription: undefined, coordinates: undefined, - today: undefined, + today: { day: 1, month: 1, year: 1 }, menu: undefined, todaysSoups: undefined, todaysSpecials: undefined, @@ -56,7 +56,7 @@ const locationB: ILocation = { acceptsOnlineOrders: true, shortDescription: undefined, coordinates: undefined, - today: undefined, + today: { day: 1, month: 1, year: 1 }, menu: undefined, todaysSoups: undefined, todaysSpecials: undefined, diff --git a/tests/mockTimings.ts b/tests/mockTimings.ts index 4fb5627..465af2d 100644 --- a/tests/mockTimings.ts +++ b/tests/mockTimings.ts @@ -21,15 +21,13 @@ function _sort(a: IFullTimeRange, b: IFullTimeRange) { /** * * @param times [startDay,startHour,startMinute,endDay,endHour,endMinute][] (order matters for input! Sunday comes first) - * @param parser In case you want to pass in a custom parser with overwrites or something. If nothing is passed in, a default parser is assumed * The only reason start and end are bundled into one array is because prettier will autoformat it to two lines otherwise */ export async function queryParserAndAssertTimingsCorrect( times: [number, number, number, number, number][], - parser?: DiningParser + rootDay: DateTime = DateTime.fromObject({ year: 2024, month: 9, day: 9 }) // is the default for most of the tests ) { - const result = await (parser ?? new DiningParser()).process(); - const rootDay = DateTime.fromObject({ year: 2024, month: 9, day: 9 }); + const result = await new DiningParser().process(); expect(result.length).toBe(1); expect(result[0]!.times.sort(_sort)).toStrictEqual( times From c5d45c9689bd48407b0e4be9280697bad9f749f2 Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Thu, 13 Nov 2025 10:51:14 -0500 Subject: [PATCH 45/65] test: parallel tests --- package.json | 2 +- src/db/db.ts | 22 +- src/db/dbQueryUtils.ts | 281 +++++++++--------- src/db/getLocations.ts | 21 +- src/db/seed.ts | 4 +- src/db/updateLocation.ts | 110 ++++--- src/server.ts | 16 +- tests/database.test.ts | 621 +++++++++++++++++---------------------- vitest.config.ts | 1 + 9 files changed, 494 insertions(+), 584 deletions(-) diff --git a/package.json b/package.json index b1dd27b..6ff99ca 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "dev": "dotenv -- tsx watch src/server.ts", "build": "rollup -c", "start": "NODE_ENV=production node dist/server.js", - "test": "dotenv -e .env.test -- vitest", + "test": "dotenv -e .env.test -- vitest database.test.ts", "__comment": "add DEBUG=testcontainers* to the test command to get testcontainers debug output", "typecheck": "tsc", "run-prod": "VOL_SRC=postgres_data docker-compose up --build", diff --git a/src/db/db.ts b/src/db/db.ts index 783b56f..87c0c77 100644 --- a/src/db/db.ts +++ b/src/db/db.ts @@ -2,21 +2,19 @@ import { drizzle, NodePgDatabase } from "drizzle-orm/node-postgres"; import { Pool } from "pg"; import * as schema from "./schema"; -export let db: NodePgDatabase & { +export type DBType = NodePgDatabase & { $client: Pool; }; -let pool: Pool; -export function initDB(connectionString: string) { - pool = new Pool({ + +export function initDBConnection(connectionString: string) { + const pool = new Pool({ connectionString: connectionString, ssl: false, }); - db = drizzle(pool, { - schema, - }); -} -/** Only used for testing */ -export function _disconnectPoolConnection() { - if (db === undefined) return; - pool.end(); + return [ + pool, + drizzle(pool, { + schema, + }), + ] as const; } diff --git a/src/db/dbQueryUtils.ts b/src/db/dbQueryUtils.ts index 10f810e..9a3bbc3 100644 --- a/src/db/dbQueryUtils.ts +++ b/src/db/dbQueryUtils.ts @@ -8,155 +8,168 @@ import { timesTable, } from "./schema"; -import { db } from "./db"; +import { DBType } from "./db"; import { notifySlack } from "utils/slack"; import { and, eq, gte } from "drizzle-orm"; import { parseTimeSlots } from "containers/timeBuilder"; import { IParsedTimeRange } from "containers/time/parsedTime"; +type RequiredProperty = { [P in keyof T]: NonNullable }; + /** More-so the database representation of a time range */ export interface ITimeRangeInternal { date: string; startMinutesSinceMidnight: number; endMinutesSinceMidnight: number; } -export async function getSpecials(todayAsSQLString: string) { - const data = await db - .select() - .from(specialsTable) - .where(eq(specialsTable.date, todayAsSQLString)); - return data.reduce< - Record< - string, - { - specials?: { name: string; description: string }[]; - soups?: { name: string; description: string }[]; - } - > - >((acc, special) => { - if (acc[special.locationId] === undefined) acc[special.locationId] = {}; - if (special.type === "special") { - acc[special.locationId]!.specials = [ - ...(acc[special.locationId]!.specials ?? []), - { - name: special.name, - description: special.description, - }, - ]; - } else { - acc[special.locationId]!.soups = [ - ...(acc[special.locationId]!.soups ?? []), +export class QueryUtils { + db: DBType; + constructor(db: DBType) { + this.db = db; + } + + async getSpecials(todayAsSQLString: string) { + const data = await this.db + .select() + .from(specialsTable) + .where(eq(specialsTable.date, todayAsSQLString)); + return data.reduce< + Record< + string, { - name: special.name, - description: special.description, - }, - ]; - } - return acc; - }, {}); -} -/** Fetches non-overridden location data + open times */ -export async function getLocationIdToDataMap(timeSearchCutoffStr: string) { - const locationData = await db - .select() - .from(locationDataTable) - .leftJoin( - conceptIdToInternalIdTable, - eq(locationDataTable.id, conceptIdToInternalIdTable.internalId) - ) - .leftJoin( - timesTable, - and( - eq(locationDataTable.id, timesTable.locationId), - gte(timesTable.date, timeSearchCutoffStr) - ) - ); - return locationData.reduce< - Record< - string, - | Omit & { - id: number; - } & { - times: ITimeRangeInternal[]; + specials?: { name: string; description: string }[]; + soups?: { name: string; description: string }[]; } - > - >((acc, { location_data, location_times, concept_id_to_internal_id }) => { - if (!acc[location_data.id]) { - acc[location_data.id] = { - ...location_data, - id: parseInt(concept_id_to_internal_id?.externalId ?? "-1"), - times: [], - }; - } - if (location_times !== null) { - acc[location_data.id]!.times.push({ - startMinutesSinceMidnight: location_times.startTime, - endMinutesSinceMidnight: location_times.endTime, - date: location_times.date, - }); - } - return acc; - }, {}); -} -type RequiredProperty = { [P in keyof T]: NonNullable }; + > + >((acc, special) => { + if (acc[special.locationId] === undefined) acc[special.locationId] = {}; + if (special.type === "special") { + acc[special.locationId]!.specials = [ + ...(acc[special.locationId]!.specials ?? []), + { + name: special.name, + description: special.description, + }, + ]; + } else { + acc[special.locationId]!.soups = [ + ...(acc[special.locationId]!.soups ?? []), + { + name: special.name, + description: special.description, + }, + ]; + } + return acc; + }, {}); + } + /** Fetches non-overridden location data + open times */ + async getLocationIdToDataMap(timeSearchCutoffStr: string) { + const locationData = await this.db + .select() + .from(locationDataTable) + .leftJoin( + conceptIdToInternalIdTable, + eq(locationDataTable.id, conceptIdToInternalIdTable.internalId) + ) + .leftJoin( + timesTable, + and( + eq(locationDataTable.id, timesTable.locationId), + gte(timesTable.date, timeSearchCutoffStr) + ) + ); + return locationData.reduce< + Record< + string, + | Omit & { + id: number; + } & { + times: ITimeRangeInternal[]; + } + > + >((acc, { location_data, location_times, concept_id_to_internal_id }) => { + if (!acc[location_data.id]) { + acc[location_data.id] = { + ...location_data, + id: parseInt(concept_id_to_internal_id?.externalId ?? "-1"), + times: [], + }; + } + if (location_times !== null) { + acc[location_data.id]!.times.push({ + startMinutesSinceMidnight: location_times.startTime, + endMinutesSinceMidnight: location_times.endTime, + date: location_times.date, + }); + } + return acc; + }, {}); + } -export async function getGeneralOverrides() { - return (await db.select().from(overwritesTable)).reduce< - Record< - string, - Omit, "locationId"> // exclude locationId field, since that's our internal id - > - >( - (acc, overwrite) => ({ - ...acc, - [overwrite.locationId]: Object.fromEntries( - Object.entries(overwrite).filter(([key, v]) => { - return key !== "locationId" && v !== null; - }) - ) as RequiredProperty, - }), - {} - ); -} + async getGeneralOverrides() { + return (await this.db.select().from(overwritesTable)).reduce< + Record< + string, + Omit< + RequiredProperty, + "locationId" + > // exclude locationId field, since that's our internal id + > + >( + (acc, overwrite) => ({ + ...acc, + [overwrite.locationId]: Object.fromEntries( + Object.entries(overwrite).filter(([key, v]) => { + return key !== "locationId" && v !== null; + }) + ) as RequiredProperty, + }), + {} + ); + } -/** - * - * @param earliestDate should be in SQL form YYYY-MM-DD - */ -export async function getTimeOverrides(earliestDate: string) { - const timeOverrides = await db - .select() - .from(timeOverwritesTable) - .where(gte(timeOverwritesTable.date, earliestDate)) - .catch((e) => { - notifySlack(` Failed to fetch time overwrites with error ${e}`); - return []; - }); - const idToTimeOverrides = timeOverrides.reduce<{ - [locationId in string]: { [date in string]: IParsedTimeRange[] }; - }>((acc, override) => { - return { - ...acc, - [override.locationId]: { - ...acc[override.locationId], - [override.date]: parseTimeSlots(override.timeString), - }, - }; - }, {}); - return idToTimeOverrides; -} + /** + * + * @param earliestDate should be in SQL form YYYY-MM-DD + */ + async getTimeOverrides(earliestDate: string) { + const timeOverrides = await this.db + .select() + .from(timeOverwritesTable) + .where(gte(timeOverwritesTable.date, earliestDate)) + .catch((e) => { + notifySlack( + ` Failed to fetch time overwrites with error ${e}` + ); + return []; + }); + const idToTimeOverrides = timeOverrides.reduce<{ + [locationId in string]: { [date in string]: IParsedTimeRange[] }; + }>((acc, override) => { + return { + ...acc, + [override.locationId]: { + ...acc[override.locationId], + [override.date]: parseTimeSlots(override.timeString), + }, + }; + }, {}); + return idToTimeOverrides; + } -export async function getEmails(): Promise<{ name: string; email: string }[]> { - const result = await db - .select({ - name: emailTable.name, - email: emailTable.email, - }) - .from(emailTable); + async getEmails(): Promise<{ name: string; email: string }[]> { + const result = await this.db + .select({ + name: emailTable.name, + email: emailTable.email, + }) + .from(emailTable); - // Remove 'mailto:' if present - return result.map((row) => ({ - name: row.name, - email: row.email.replace(/^mailto:/, ""), - })); + // Remove 'mailto:' if present + return result.map((row) => ({ + name: row.name, + email: row.email.replace(/^mailto:/, ""), + })); + } } diff --git a/src/db/getLocations.ts b/src/db/getLocations.ts index caca600..0e4a59d 100644 --- a/src/db/getLocations.ts +++ b/src/db/getLocations.ts @@ -1,26 +1,21 @@ -import { - getGeneralOverrides, - getLocationIdToDataMap, - getSpecials, - getTimeOverrides, - ITimeRangeInternal, -} from "./dbQueryUtils"; +import { ITimeRangeInternal, QueryUtils } from "./dbQueryUtils"; import { DateTime } from "luxon"; import { pad, remapAndMergeTimeIntervals } from "utils/timeUtils"; import { IParsedTimeRange } from "containers/time/parsedTime"; +import { DBType } from "./db"; -export async function getAllLocations(today: DateTime) { +export async function getAllLocations(db: DBType, today: DateTime) { const timeSearchCutoff = today.minus({ days: 1 }); // 1 days worth of data before today const timeSearchCutoffStr = `${timeSearchCutoff.year}-${pad( timeSearchCutoff.month )}-${pad(timeSearchCutoff.day)}`; - - const locationIdToData = await getLocationIdToDataMap(timeSearchCutoffStr); - const specials = await getSpecials( + const DB = new QueryUtils(db); + const locationIdToData = await DB.getLocationIdToDataMap(timeSearchCutoffStr); + const specials = await DB.getSpecials( `${today.year}/${pad(today.month)}/${pad(today.day)}` ); - const generalOverrides = await getGeneralOverrides(); - const timeOverrides = await getTimeOverrides(timeSearchCutoffStr); + const generalOverrides = await DB.getGeneralOverrides(); + const timeOverrides = await DB.getTimeOverrides(timeSearchCutoffStr); // apply overrides, merge all time intervals, and add specials const finalLocationData = Object.entries(locationIdToData).map( diff --git a/src/db/seed.ts b/src/db/seed.ts index 4f364af..7fa3b22 100644 --- a/src/db/seed.ts +++ b/src/db/seed.ts @@ -1,7 +1,7 @@ -import { db } from "./db"; +import { DBType } from "./db"; import { emailTable } from "./schema"; -export async function populateEmails() { +export async function populateEmails(db: DBType) { await db.insert(emailTable).values([ { email: "czech@un.org", diff --git a/src/db/updateLocation.ts b/src/db/updateLocation.ts index a4a9bf6..53cc5d9 100644 --- a/src/db/updateLocation.ts +++ b/src/db/updateLocation.ts @@ -1,5 +1,5 @@ import { ILocation } from "types"; -import { db } from "./db"; +import { DBType } from "./db"; import { conceptIdToInternalIdTable, locationDataTable, @@ -8,7 +8,7 @@ import { } from "./schema"; import { and, eq, gte } from "drizzle-orm"; import { pad } from "utils/timeUtils"; -async function getInternalId(externalId: string) { +async function getInternalId(db: DBType, externalId: string) { let [idMapping] = await db .select() .from(conceptIdToInternalIdTable) @@ -17,8 +17,8 @@ async function getInternalId(externalId: string) { return idMapping?.internalId ?? crypto.randomUUID(); } -export async function addLocationDataToDb(location: ILocation) { - const internalId = await getInternalId(location.conceptId.toString()); +export async function addLocationDataToDb(db: DBType, location: ILocation) { + const internalId = await getInternalId(db, location.conceptId.toString()); const locationDbEntry: typeof locationDataTable.$inferInsert = { id: internalId, @@ -41,59 +41,57 @@ export async function addLocationDataToDb(location: ILocation) { set: locationDbEntry, }); - if (location.today !== undefined) { - const earliestDaySQLString = `${location.today.year}-${pad( - location.today.month - )}-${pad(location.today.day)})`; - // add specials - await db - .delete(specialsTable) - .where( - and( - eq(specialsTable.locationId, internalId), - eq(specialsTable.date, earliestDaySQLString) - ) - ); - const specials = [ - ...(location.todaysSpecials?.map((sp) => ({ - ...sp, - type: "special" as const, - })) ?? []), - ...(location.todaysSoups?.map((sp) => ({ - ...sp, - type: "soup" as const, - })) ?? []), - ]; - if (specials.length) - await db.insert(specialsTable).values( - specials.map((special) => ({ - date: earliestDaySQLString, - locationId: internalId, - name: special.title, - description: special.description, - type: special.type, - })) - ); + const earliestDaySQLString = `${location.today.year}-${pad( + location.today.month + )}-${pad(location.today.day)})`; + // add specials + await db + .delete(specialsTable) + .where( + and( + eq(specialsTable.locationId, internalId), + eq(specialsTable.date, earliestDaySQLString) + ) + ); + const specials = [ + ...(location.todaysSpecials?.map((sp) => ({ + ...sp, + type: "special" as const, + })) ?? []), + ...(location.todaysSoups?.map((sp) => ({ + ...sp, + type: "soup" as const, + })) ?? []), + ]; + if (specials.length) + await db.insert(specialsTable).values( + specials.map((special) => ({ + date: earliestDaySQLString, + locationId: internalId, + name: special.title, + description: special.description, + type: special.type, + })) + ); - // remove rows from whenever the scrape started from (aka remove entries corresponding to the last 7 days) - await db - .delete(timesTable) - .where( - and( - eq(timesTable.locationId, internalId), - and(gte(timesTable.date, earliestDaySQLString)) - ) - ); - if (location.times.length) { - await db.insert(timesTable).values( - location.times.map((time) => ({ - locationId: internalId, - date: `${time.year}-${pad(time.month)}-${pad(time.day)}`, - startTime: time.startMinutesFromMidnight, - endTime: time.endMinutesFromMidnight, - })) - ); - } + // remove rows from whenever the scrape started from (aka remove entries corresponding to the last 7 days) + await db + .delete(timesTable) + .where( + and( + eq(timesTable.locationId, internalId), + and(gte(timesTable.date, earliestDaySQLString)) + ) + ); + if (location.times.length) { + await db.insert(timesTable).values( + location.times.map((time) => ({ + locationId: internalId, + date: `${time.year}-${pad(time.month)}-${pad(time.day)}`, + startTime: time.startMinutesFromMidnight, + endTime: time.endMinutesFromMidnight, + })) + ); } // in case the conceptId->internalId mapping entry isn't there diff --git a/src/server.ts b/src/server.ts index 2abed7b..13190b4 100644 --- a/src/server.ts +++ b/src/server.ts @@ -9,11 +9,11 @@ import ScrapeResultMerger from "utils/locationMerger"; import { addLocationDataToDb } from "db/updateLocation"; import { deprecatedNotice } from "deprecationNotice"; import { getDiffsBetweenLocationData } from "utils/diff"; -import { getEmails } from "db/dbQueryUtils"; import { getAllLocations } from "db/getLocations"; import { openapi } from "@elysiajs/openapi"; -import { initDB } from "db/db"; +import { initDBConnection } from "db/db"; import { DateTime } from "luxon"; +import { QueryUtils } from "db/dbQueryUtils"; /** only used for Slack debug diff logging */ let cachedLocations: ILocation[] = []; @@ -51,11 +51,11 @@ async function reload(): Promise { } await Promise.all( - finalLocations.map((location) => addLocationDataToDb(location)) + finalLocations.map((location) => addLocationDataToDb(db, location)) ); } } - +const [pool, db] = initDBConnection(env.DATABASE_URL); export const app = new Elysia({ adapter: node() }).use(openapi()); // I don't trust bun (as a runtime) enough (Eric Xu - 7/18/2025). This may change in the future, but bun is currently NOT a full drop-in replacement for node and is still rather unstable from personal experience app.onError(({ error, path, code }) => { @@ -82,8 +82,11 @@ app.onAfterHandle(({ response }) => { app.get("/", () => { return "ScottyLabs Dining API"; }); -app.get("/api/v2/locations", async () => await getAllLocations(DateTime.now())); -app.get("/api/emails", getEmails); +app.get( + "/api/v2/locations", + async () => await getAllLocations(db, DateTime.now()) +); +app.get("/api/emails", async () => await new QueryUtils(db).getEmails()); app.post( "/api/sendSlackMessage", @@ -103,7 +106,6 @@ setInterval(() => { ); }, env.RELOAD_WAIT_INTERVAL); -initDB(env.DATABASE_URL); // Initial load and start the server reload() .then(() => { diff --git a/tests/database.test.ts b/tests/database.test.ts index 5b8eca3..1a250c4 100644 --- a/tests/database.test.ts +++ b/tests/database.test.ts @@ -2,14 +2,17 @@ import { PostgreSqlContainer, StartedPostgreSqlContainer, } from "@testcontainers/postgresql"; -import { _disconnectPoolConnection, initDB } from "db/db"; +import { DBType, initDBConnection } from "db/db"; import { addLocationDataToDb } from "db/updateLocation"; import { getAllLocations } from "db/getLocations"; import { DateTime } from "luxon"; import { ILocation } from "types"; +import { test as baseTest } from "vitest"; +import { Pool } from "pg"; + const wait = (ms: number) => new Promise((re) => setTimeout(re, ms)); const locationIn: ILocation = { - name: "test", + name: "dbTest", acceptsOnlineOrders: false, conceptId: 1, coordinates: { lat: 1, lng: 10 }, @@ -29,7 +32,7 @@ const locationIn: ILocation = { }; const locationOut = { id: 1, - name: "test", + name: "dbTest", shortDescription: "hi", description: "description", url: "https://hi.com", @@ -42,11 +45,15 @@ const locationOut = { todaysSoups: [], todaysSpecials: [], }; -describe("Redis", () => { - let container: StartedPostgreSqlContainer; - - beforeEach(async () => { - container = await new PostgreSqlContainer("postgres:17.5") +const dbTest = baseTest.extend<{ + db: { + db: DBType; + container: StartedPostgreSqlContainer; + pool: Pool; + }; +}>({ + db: async ({}, use) => { + const container = await new PostgreSqlContainer("postgres:17.5") .withCopyDirectoriesToContainer([ { source: `${__dirname}/../drizzle`, @@ -54,24 +61,29 @@ describe("Redis", () => { }, ]) .start(); - initDB(container.getConnectionUri()); - }, 120 * 1000); - - afterEach(async () => { - _disconnectPoolConnection(); - await container.stop(); - }); + const [pool, db] = initDBConnection(container.getConnectionUri()); + use({ container, pool, db }); + }, +}); - it("works on basic insertion", async () => { - await addLocationDataToDb(locationIn); - const dbResult = await getAllLocations(DateTime.now()); +dbTest.afterEach(({ db }) => { + db.pool.end(); + db.container.stop(); +}); +describe("DB", () => { + dbTest.concurrent("works on basic insertion", async ({ db: { db } }) => { + await addLocationDataToDb(db, locationIn); + const dbResult = await getAllLocations(db, DateTime.now()); expect(dbResult).toEqual([locationOut]); }); - it("properly resets state on every new test", async () => { - expect(await getAllLocations(DateTime.now())).toEqual([]); - }); - it("works on insertion with times", async () => { - await addLocationDataToDb({ + dbTest.concurrent( + "properly resets state on every new dbTest", + async ({ db: { db } }) => { + expect(await getAllLocations(db, DateTime.now())).toEqual([]); + } + ); + dbTest.concurrent("works on insertion with times", async ({ db: { db } }) => { + await addLocationDataToDb(db, { ...locationIn, today: { year: 2025, @@ -110,6 +122,7 @@ describe("Redis", () => { ], }); const dbResult = await getAllLocations( + db, DateTime.fromObject({ year: 2025, month: 1, day: 2 }) ); expect(dbResult).toEqual([ @@ -132,238 +145,161 @@ describe("Redis", () => { }, ]); }); - it("works on insertion with times (tests search window)", async () => { - await addLocationDataToDb({ - name: "test", - acceptsOnlineOrders: false, - conceptId: 1, - coordinates: { lat: 1, lng: 10 }, - description: "description", - today: { - year: 2025, - month: 1, - day: 1, - }, - times: [ - { - day: 1, - month: 1, + dbTest.concurrent( + "works on insertion with times (tests search window)", + async ({ db: { db } }) => { + await addLocationDataToDb(db, { + ...locationIn, + today: { year: 2025, - startMinutesFromMidnight: 5 * 60, - endMinutesFromMidnight: 12 * 60, - }, - { - day: 1, month: 1, - year: 2025, - startMinutesFromMidnight: 5 * 60, - endMinutesFromMidnight: 12 * 60, - }, - { day: 1, - month: 1, - year: 2025, - startMinutesFromMidnight: 14 * 60, - endMinutesFromMidnight: 2 * 60, }, - ], - location: "location", - menu: "menu", - shortDescription: "hi", - url: "https://hi.com", - todaysSoups: [], - todaysSpecials: [], - }); - const dbResult = await getAllLocations( - DateTime.fromObject({ year: 2025, month: 1, day: 3 }) // 2 days after latest time - ); - expect(dbResult).toEqual([ - { - id: 1, - name: "test", - shortDescription: "hi", - description: "description", - url: "https://hi.com", - menu: "menu", - location: "location", - coordinateLat: 1, - coordinateLng: 10, - acceptsOnlineOrders: false, - times: [], - todaysSoups: [], - todaysSpecials: [], - }, - ]); - }); - it("works on insertion with times (DST - start 2AM -> 3AM) (3/9/25)", async () => { - await addLocationDataToDb({ - name: "test", - acceptsOnlineOrders: false, - conceptId: 1, - coordinates: { lat: 1, lng: 10 }, - description: "description", - today: { - year: 2025, - month: 1, - day: 1, - }, - times: [ + times: [ + { + day: 1, + month: 1, + year: 2025, + startMinutesFromMidnight: 5 * 60, + endMinutesFromMidnight: 12 * 60, + }, + { + day: 1, + month: 1, + year: 2025, + startMinutesFromMidnight: 5 * 60, + endMinutesFromMidnight: 12 * 60, + }, + { + day: 1, + month: 1, + year: 2025, + startMinutesFromMidnight: 14 * 60, + endMinutesFromMidnight: 2 * 60, + }, + ], + }); + const dbResult = await getAllLocations( + db, + DateTime.fromObject({ year: 2025, month: 1, day: 3 }) // 2 days after latest time + ); + expect(dbResult).toEqual([ { - day: 9, - month: 3, - year: 2025, - startMinutesFromMidnight: 5 * 60, - endMinutesFromMidnight: 12 * 60, + ...locationOut, + times: [], }, - ], - location: "location", - menu: "menu", - shortDescription: "hi", - url: "https://hi.com", - todaysSoups: [], - todaysSpecials: [], - }); - const dbResult = await getAllLocations( - DateTime.fromObject({ year: 2025, month: 1, day: 3 }) // 2 days after latest time - ); - expect(dbResult).toEqual([ - { - id: 1, - name: "test", - shortDescription: "hi", - description: "description", - url: "https://hi.com", - menu: "menu", - location: "location", - coordinateLat: 1, - coordinateLng: 10, - acceptsOnlineOrders: false, + ]); + } + ); + dbTest.concurrent( + "works on insertion with times (DST - start 2AM -> 3AM) (3/9/25)", + async ({ db: { db } }) => { + await addLocationDataToDb(db, { + ...locationIn, times: [ { - start: 1741510800000, - end: 1741536000000, + day: 9, + month: 3, + year: 2025, + startMinutesFromMidnight: 5 * 60, + endMinutesFromMidnight: 12 * 60, }, ], - todaysSoups: [], - todaysSpecials: [], - }, - ]); - }); - it("works on insertion with times (DST - end 2AM -> 1AM) (3/9/25) [if a place closes at 2 AM, we assume it's the second 2 AM and not the first (why? arbitrary)]", async () => { - await addLocationDataToDb({ - name: "test", - acceptsOnlineOrders: false, - conceptId: 1, - coordinates: { lat: 1, lng: 10 }, - description: "description", - today: { - year: 2025, - month: 1, - day: 1, - }, - times: [ + }); + const dbResult = await getAllLocations( + db, + DateTime.fromObject({ year: 2025, month: 1, day: 3 }) // 2 days after latest time + ); + expect(dbResult).toEqual([ { - day: 1, - month: 11, + ...locationOut, + times: [ + { + start: 1741510800000, + end: 1741536000000, + }, + ], + }, + ]); + } + ); + dbTest.concurrent( + "works on insertion with times (DST - end 2AM -> 1AM) (3/9/25) [if a place closes at 2 AM, we assume it's the second 2AM (I mean, the first 2AM technically doesn't exist...)", + async ({ db: { db } }) => { + await addLocationDataToDb(db, { + ...locationIn, + today: { year: 2025, - startMinutesFromMidnight: 7 * 60, - endMinutesFromMidnight: 2 * 60, + month: 1, + day: 1, }, - ], - location: "location", - menu: "menu", - shortDescription: "hi", - url: "https://hi.com", - todaysSoups: [], - todaysSpecials: [], - }); - const dbResult = await getAllLocations( - DateTime.fromObject({ year: 2025, month: 1, day: 3 }) - ); - expect(dbResult).toEqual([ - { - id: 1, - name: "test", - shortDescription: "hi", - description: "description", - url: "https://hi.com", - menu: "menu", - location: "location", - coordinateLat: 1, - coordinateLng: 10, - acceptsOnlineOrders: false, times: [ { - start: 1761994800000, - end: 1762066800000, // the second 2AM + day: 1, + month: 11, + year: 2025, + startMinutesFromMidnight: 7 * 60, + endMinutesFromMidnight: 2 * 60, }, ], - todaysSoups: [], - todaysSpecials: [], - }, - ]); - }); - it("works on insertion with times (DST - end 2AM -> 1AM) (3/9/25) [if a place closes at 1:30 AM, we assume it's the first 1:30 AM and not the second]", async () => { - await addLocationDataToDb({ - name: "test", - acceptsOnlineOrders: false, - conceptId: 1, - coordinates: { lat: 1, lng: 10 }, - description: "description", - today: { - year: 2025, - month: 1, - day: 1, - }, - times: [ + }); + const dbResult = await getAllLocations( + db, + DateTime.fromObject({ year: 2025, month: 1, day: 3 }) + ); + expect(dbResult).toEqual([ { - day: 1, - month: 11, + ...locationOut, + times: [ + { + start: 1761994800000, + end: 1762066800000, // the second 2AM + }, + ], + }, + ]); + } + ); + dbTest.concurrent( + "works on insertion with times (DST - end 2AM -> 1AM) (3/9/25) [if a place closes at 1:30 AM, we assume dbTest's the first 1:30 AM and not the second]", + async ({ db: { db } }) => { + await addLocationDataToDb(db, { + ...locationIn, + today: { year: 2025, - startMinutesFromMidnight: 7 * 60, - endMinutesFromMidnight: 1.5 * 60, + month: 1, + day: 1, }, - ], - location: "location", - menu: "menu", - shortDescription: "hi", - url: "https://hi.com", - todaysSoups: [], - todaysSpecials: [], - }); - const dbResult = await getAllLocations( - DateTime.fromObject({ year: 2025, month: 1, day: 3 }) - ); - expect(dbResult).toEqual([ - { - id: 1, - name: "test", - shortDescription: "hi", - description: "description", - url: "https://hi.com", - menu: "menu", - location: "location", - coordinateLat: 1, - coordinateLng: 10, - acceptsOnlineOrders: false, times: [ { - start: 1761994800000, - end: 1762061400000, // the first 1:30 AM + day: 1, + month: 11, + year: 2025, + startMinutesFromMidnight: 7 * 60, + endMinutesFromMidnight: 1.5 * 60, }, ], - todaysSoups: [], - todaysSpecials: [], - }, - ]); - }); - it("works on specials", async () => { - await addLocationDataToDb({ - name: "test", - acceptsOnlineOrders: false, - conceptId: 1, - coordinates: { lat: 1, lng: 10 }, - description: "description", + }); + const dbResult = await getAllLocations( + db, + DateTime.fromObject({ year: 2025, month: 1, day: 3 }) + ); + expect(dbResult).toEqual([ + { + ...locationOut, + times: [ + { + start: 1761994800000, + end: 1762061400000, // the first 1:30 AM + }, + ], + }, + ]); + } + ); + dbTest.concurrent("works on specials", async ({ db: { db } }) => { + await addLocationDataToDb(db, { + ...locationIn, today: { year: 2025, month: 1, @@ -392,10 +328,6 @@ describe("Redis", () => { endMinutesFromMidnight: 2 * 60, }, ], - location: "location", - menu: "menu", - shortDescription: "hi", - url: "https://hi.com", todaysSoups: [ { title: "soup 1", description: "desc 1" }, { title: "soup 2", description: "desc 2" }, @@ -406,20 +338,12 @@ describe("Redis", () => { ], }); const dbResult = await getAllLocations( + db, DateTime.fromObject({ year: 2025, month: 1, day: 1 }) ); expect(dbResult).toEqual([ { - id: 1, - name: "test", - shortDescription: "hi", - description: "description", - url: "https://hi.com", - menu: "menu", - location: "location", - coordinateLat: 1, - coordinateLng: 10, - acceptsOnlineOrders: false, + ...locationOut, times: [ { start: 1735725600000, @@ -453,135 +377,114 @@ describe("Redis", () => { }, ]); }); - it("works on specials (overriding)", async () => { - await addLocationDataToDb({ - name: "test", - acceptsOnlineOrders: false, - conceptId: 1, - coordinates: { lat: 1, lng: 10 }, - description: "description", - today: { - year: 2025, - month: 1, - day: 1, - }, - times: [ - { - day: 1, - month: 1, + dbTest.concurrent( + "works on specials (overriding)", + async ({ db: { db } }) => { + await addLocationDataToDb(db, { + ...locationIn, + today: { year: 2025, - startMinutesFromMidnight: 5 * 60, - endMinutesFromMidnight: 12 * 60, - }, - { - day: 1, month: 1, - year: 2025, - startMinutesFromMidnight: 5 * 60, - endMinutesFromMidnight: 12 * 60, - }, - { day: 1, - month: 1, - year: 2025, - startMinutesFromMidnight: 14 * 60, - endMinutesFromMidnight: 2 * 60, }, - ], - location: "location", - menu: "menu", - shortDescription: "hi", - url: "https://hi.com", - todaysSoups: [ - { title: "soup 1", description: "desc 1" }, - { title: "soup 2", description: "desc 2" }, - ], - todaysSpecials: [ - { title: "special 1", description: "desc 1" }, - { title: "special 2", description: "desc 2" }, - ], - }); - await addLocationDataToDb({ - name: "test", - acceptsOnlineOrders: false, - conceptId: 1, - coordinates: { lat: 1, lng: 10 }, - description: "description", - today: { - year: 2025, - month: 1, - day: 1, - }, - times: [ - { - day: 1, - month: 1, + times: [ + { + day: 1, + month: 1, + year: 2025, + startMinutesFromMidnight: 5 * 60, + endMinutesFromMidnight: 12 * 60, + }, + { + day: 1, + month: 1, + year: 2025, + startMinutesFromMidnight: 5 * 60, + endMinutesFromMidnight: 12 * 60, + }, + { + day: 1, + month: 1, + year: 2025, + startMinutesFromMidnight: 14 * 60, + endMinutesFromMidnight: 2 * 60, + }, + ], + todaysSoups: [ + { title: "soup 1", description: "desc 1" }, + { title: "soup 2", description: "desc 2" }, + ], + todaysSpecials: [ + { title: "special 1", description: "desc 1" }, + { title: "special 2", description: "desc 2" }, + ], + }); + await addLocationDataToDb(db, { + ...locationIn, + today: { year: 2025, - startMinutesFromMidnight: 5 * 60, - endMinutesFromMidnight: 12 * 60, - }, - { - day: 1, month: 1, - year: 2025, - startMinutesFromMidnight: 5 * 60, - endMinutesFromMidnight: 12 * 60, - }, - { day: 1, - month: 1, - year: 2025, - startMinutesFromMidnight: 14 * 60, - endMinutesFromMidnight: 2 * 60, }, - ], - location: "location", - menu: "menu", - shortDescription: "hi", - url: "https://hi.com", - todaysSoups: [], - todaysSpecials: [ - { title: "special 1", description: "desc 1" }, - { title: "special 2", description: "desc 2" }, - ], - }); - const dbResult = await getAllLocations( - DateTime.fromObject({ year: 2025, month: 1, day: 1 }) - ); - expect(dbResult).toEqual([ - { - id: 1, - name: "test", - shortDescription: "hi", - description: "description", - url: "https://hi.com", - menu: "menu", - location: "location", - coordinateLat: 1, - coordinateLng: 10, - acceptsOnlineOrders: false, times: [ { - start: 1735725600000, - end: 1735750800000, + day: 1, + month: 1, + year: 2025, + startMinutesFromMidnight: 5 * 60, + endMinutesFromMidnight: 12 * 60, }, { - start: 1735758000000, - end: 1735801200000, + day: 1, + month: 1, + year: 2025, + startMinutesFromMidnight: 5 * 60, + endMinutesFromMidnight: 12 * 60, + }, + { + day: 1, + month: 1, + year: 2025, + startMinutesFromMidnight: 14 * 60, + endMinutesFromMidnight: 2 * 60, }, ], todaysSoups: [], todaysSpecials: [ - { - description: "desc 1", - name: "special 1", - }, - { - description: "desc 2", - name: "special 2", - }, + { title: "special 1", description: "desc 1" }, + { title: "special 2", description: "desc 2" }, ], - }, - ]); - }); + }); + const dbResult = await getAllLocations( + db, + DateTime.fromObject({ year: 2025, month: 1, day: 1 }) + ); + expect(dbResult).toEqual([ + { + ...locationOut, + times: [ + { + start: 1735725600000, + end: 1735750800000, + }, + { + start: 1735758000000, + end: 1735801200000, + }, + ], + todaysSoups: [], + todaysSpecials: [ + { + description: "desc 1", + name: "special 1", + }, + { + description: "desc 2", + name: "special 2", + }, + ], + }, + ]); + } + ); }); diff --git a/vitest.config.ts b/vitest.config.ts index f335e35..e4bd5cf 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -4,6 +4,7 @@ import tsconfigPaths from "vite-tsconfig-paths"; export default defineConfig({ test: { globals: true, + testTimeout: 30_000, }, plugins: [tsconfigPaths()], }); From 07ad8c491e8d458f5226be7e26ec353d991f6c47 Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Thu, 13 Nov 2025 10:54:35 -0500 Subject: [PATCH 46/65] test: run all tests --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6ff99ca..b1dd27b 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "dev": "dotenv -- tsx watch src/server.ts", "build": "rollup -c", "start": "NODE_ENV=production node dist/server.js", - "test": "dotenv -e .env.test -- vitest database.test.ts", + "test": "dotenv -e .env.test -- vitest", "__comment": "add DEBUG=testcontainers* to the test command to get testcontainers debug output", "typecheck": "tsc", "run-prod": "VOL_SRC=postgres_data docker-compose up --build", From f8d6878691f6cab7f4801f41de9379eaedfccb20 Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Thu, 13 Nov 2025 17:08:24 -0500 Subject: [PATCH 47/65] test: make tests more readable --- src/db/dbQueryUtils.ts | 11 +- src/db/getLocations.ts | 20 +- src/db/updateLocation.ts | 38 ++++ src/server.ts | 4 +- tests/database.test.ts | 418 +++++++++++++++++++-------------------- 5 files changed, 257 insertions(+), 234 deletions(-) diff --git a/src/db/dbQueryUtils.ts b/src/db/dbQueryUtils.ts index 9a3bbc3..db91230 100644 --- a/src/db/dbQueryUtils.ts +++ b/src/db/dbQueryUtils.ts @@ -68,10 +68,6 @@ export class QueryUtils { const locationData = await this.db .select() .from(locationDataTable) - .leftJoin( - conceptIdToInternalIdTable, - eq(locationDataTable.id, conceptIdToInternalIdTable.internalId) - ) .leftJoin( timesTable, and( @@ -82,17 +78,14 @@ export class QueryUtils { return locationData.reduce< Record< string, - | Omit & { - id: number; - } & { + | typeof locationDataTable.$inferSelect & { times: ITimeRangeInternal[]; } > - >((acc, { location_data, location_times, concept_id_to_internal_id }) => { + >((acc, { location_data, location_times }) => { if (!acc[location_data.id]) { acc[location_data.id] = { ...location_data, - id: parseInt(concept_id_to_internal_id?.externalId ?? "-1"), times: [], }; } diff --git a/src/db/getLocations.ts b/src/db/getLocations.ts index 0e4a59d..2331242 100644 --- a/src/db/getLocations.ts +++ b/src/db/getLocations.ts @@ -4,18 +4,22 @@ import { pad, remapAndMergeTimeIntervals } from "utils/timeUtils"; import { IParsedTimeRange } from "containers/time/parsedTime"; import { DBType } from "./db"; -export async function getAllLocations(db: DBType, today: DateTime) { +/** + * + * @param db + * @param today this parameter is necessary so we can get today's specials and the open hours for the next 7 days, rather than returning everything we've stored in the db + * @returns + */ +export async function getAllLocationsFromDB(db: DBType, today: DateTime) { const timeSearchCutoff = today.minus({ days: 1 }); // 1 days worth of data before today - const timeSearchCutoffStr = `${timeSearchCutoff.year}-${pad( - timeSearchCutoff.month - )}-${pad(timeSearchCutoff.day)}`; + const DB = new QueryUtils(db); - const locationIdToData = await DB.getLocationIdToDataMap(timeSearchCutoffStr); - const specials = await DB.getSpecials( - `${today.year}/${pad(today.month)}/${pad(today.day)}` + const locationIdToData = await DB.getLocationIdToDataMap( + timeSearchCutoff.toSQLDate() ); + const specials = await DB.getSpecials(today.toSQLDate()); const generalOverrides = await DB.getGeneralOverrides(); - const timeOverrides = await DB.getTimeOverrides(timeSearchCutoffStr); + const timeOverrides = await DB.getTimeOverrides(timeSearchCutoff.toSQLDate()); // apply overrides, merge all time intervals, and add specials const finalLocationData = Object.entries(locationIdToData).map( diff --git a/src/db/updateLocation.ts b/src/db/updateLocation.ts index 53cc5d9..9a30726 100644 --- a/src/db/updateLocation.ts +++ b/src/db/updateLocation.ts @@ -4,10 +4,12 @@ import { conceptIdToInternalIdTable, locationDataTable, specialsTable, + timeOverwritesTable, timesTable, } from "./schema"; import { and, eq, gte } from "drizzle-orm"; import { pad } from "utils/timeUtils"; +import { DateTime } from "luxon"; async function getInternalId(db: DBType, externalId: string) { let [idMapping] = await db .select() @@ -17,6 +19,12 @@ async function getInternalId(db: DBType, externalId: string) { return idMapping?.internalId ?? crypto.randomUUID(); } +/** + * + * @param db + * @param location + * @returns the internal id of the location that was added + */ export async function addLocationDataToDb(db: DBType, location: ILocation) { const internalId = await getInternalId(db, location.conceptId.toString()); @@ -102,4 +110,34 @@ export async function addLocationDataToDb(db: DBType, location: ILocation) { externalId: location.conceptId.toString(), }) .onConflictDoNothing({ target: conceptIdToInternalIdTable.externalId }); + return internalId; +} +/** + * + * @param db + * @param locationId + * @param date + * @param timeStringOverride + * @returns if successful + */ +export async function addTimeOverride( + db: DBType, + locationId: string, + date: string, + timeStringOverride: string +) { + const parsedDate = DateTime.fromFormat(date, "M/d/yy"); + if (!parsedDate.isValid) { + return false; + } + const rowToInsert: typeof timeOverwritesTable.$inferInsert = { + date: parsedDate.toSQLDate(), + locationId: locationId, + timeString: timeStringOverride, + }; + await db.insert(timeOverwritesTable).values(rowToInsert).onConflictDoUpdate({ + target: timeOverwritesTable.locationId, + set: rowToInsert, + }); + return true; } diff --git a/src/server.ts b/src/server.ts index 13190b4..0b3cbce 100644 --- a/src/server.ts +++ b/src/server.ts @@ -9,7 +9,7 @@ import ScrapeResultMerger from "utils/locationMerger"; import { addLocationDataToDb } from "db/updateLocation"; import { deprecatedNotice } from "deprecationNotice"; import { getDiffsBetweenLocationData } from "utils/diff"; -import { getAllLocations } from "db/getLocations"; +import { getAllLocationsFromDB } from "db/getLocations"; import { openapi } from "@elysiajs/openapi"; import { initDBConnection } from "db/db"; import { DateTime } from "luxon"; @@ -84,7 +84,7 @@ app.get("/", () => { }); app.get( "/api/v2/locations", - async () => await getAllLocations(db, DateTime.now()) + async () => await getAllLocationsFromDB(db, DateTime.now()) ); app.get("/api/emails", async () => await new QueryUtils(db).getEmails()); diff --git a/tests/database.test.ts b/tests/database.test.ts index 1a250c4..771d314 100644 --- a/tests/database.test.ts +++ b/tests/database.test.ts @@ -4,13 +4,15 @@ import { } from "@testcontainers/postgresql"; import { DBType, initDBConnection } from "db/db"; import { addLocationDataToDb } from "db/updateLocation"; -import { getAllLocations } from "db/getLocations"; +import { getAllLocationsFromDB } from "db/getLocations"; import { DateTime } from "luxon"; import { ILocation } from "types"; import { test as baseTest } from "vitest"; import { Pool } from "pg"; const wait = (ms: number) => new Promise((re) => setTimeout(re, ms)); + +/** Something that you can get from DiningParser */ const locationIn: ILocation = { name: "dbTest", acceptsOnlineOrders: false, @@ -30,8 +32,10 @@ const locationIn: ILocation = { todaysSoups: [], todaysSpecials: [], }; + +/** What getAllLocations() returns when `locationIn` has been added to the db */ const locationOut = { - id: 1, + id: "TODO", name: "dbTest", shortDescription: "hi", description: "description", @@ -46,13 +50,13 @@ const locationOut = { todaysSpecials: [], }; const dbTest = baseTest.extend<{ - db: { + ctx: { db: DBType; container: StartedPostgreSqlContainer; pool: Pool; }; }>({ - db: async ({}, use) => { + ctx: async ({}, use) => { const container = await new PostgreSqlContainer("postgres:17.5") .withCopyDirectoriesToContainer([ { @@ -66,89 +70,67 @@ const dbTest = baseTest.extend<{ }, }); -dbTest.afterEach(({ db }) => { - db.pool.end(); - db.container.stop(); +dbTest.afterEach(({ ctx }) => { + ctx.pool.end(); + ctx.container.stop(); }); + describe("DB", () => { - dbTest.concurrent("works on basic insertion", async ({ db: { db } }) => { - await addLocationDataToDb(db, locationIn); - const dbResult = await getAllLocations(db, DateTime.now()); - expect(dbResult).toEqual([locationOut]); + dbTest.concurrent("works on basic insertion", async ({ ctx: { db } }) => { + const id = await addLocationDataToDb(db, locationIn); + const dbResult = await getAllLocationsFromDB(db, DateTime.now()); + expect(dbResult).toEqual([{ ...locationOut, id }]); }); dbTest.concurrent( "properly resets state on every new dbTest", - async ({ db: { db } }) => { - expect(await getAllLocations(db, DateTime.now())).toEqual([]); + async ({ ctx: { db } }) => { + expect(await getAllLocationsFromDB(db, DateTime.now())).toEqual([]); } ); - dbTest.concurrent("works on insertion with times", async ({ db: { db } }) => { - await addLocationDataToDb(db, { - ...locationIn, - today: { - year: 2025, - month: 1, - day: 1, - }, - times: [ - { - day: 1, - month: 1, + dbTest.concurrent( + "works on insertion with times", + async ({ ctx: { db } }) => { + const id = await addLocationDataToDb(db, { + ...locationIn, + today: { year: 2025, - startMinutesFromMidnight: 5 * 60, - endMinutesFromMidnight: 12 * 60, - }, - { - day: 1, month: 1, - year: 2025, - startMinutesFromMidnight: 5 * 60, - endMinutesFromMidnight: 12 * 60, - }, - { day: 1, - month: 1, - year: 2025, - startMinutesFromMidnight: 14 * 60, - endMinutesFromMidnight: 2 * 60, - }, - { - day: 7, - month: 7, - year: 2025, - startMinutesFromMidnight: 14 * 60, - endMinutesFromMidnight: 2 * 60, }, - ], - }); - const dbResult = await getAllLocations( - db, - DateTime.fromObject({ year: 2025, month: 1, day: 2 }) - ); - expect(dbResult).toEqual([ - { - ...locationOut, times: [ - { - start: 1735725600000, - end: 1735750800000, - }, - { - start: 1735758000000, - end: 1735801200000, - }, - { - start: 1751911200000, - end: 1751954400000, - }, + parseTime("1/1/25", "5:00 am", "12:00 pm"), + parseTime("1/1/25", "5:00 am", "12:00 pm"), + parseTime("1/1/25", "2:00 pm", "2:00 am"), + parseTime("7/7/25", "2:00 pm", "2:00 am"), ], - }, - ]); - }); + }); + const dbResult = await getAllLocationsFromDB(db, parseDate("1/2/25")); + expect(dbResult).toEqual([ + { + ...locationOut, + id: id, + times: [ + { + start: 1735725600000, + end: 1735750800000, + }, + { + start: 1735758000000, + end: 1735801200000, + }, + { + start: 1751911200000, + end: 1751954400000, + }, + ], + }, + ]); + } + ); dbTest.concurrent( "works on insertion with times (tests search window)", - async ({ db: { db } }) => { - await addLocationDataToDb(db, { + async ({ ctx: { db } }) => { + const id = await addLocationDataToDb(db, { ...locationIn, today: { year: 2025, @@ -156,63 +138,65 @@ describe("DB", () => { day: 1, }, times: [ - { - day: 1, - month: 1, - year: 2025, - startMinutesFromMidnight: 5 * 60, - endMinutesFromMidnight: 12 * 60, - }, - { - day: 1, - month: 1, - year: 2025, - startMinutesFromMidnight: 5 * 60, - endMinutesFromMidnight: 12 * 60, - }, - { - day: 1, - month: 1, - year: 2025, - startMinutesFromMidnight: 14 * 60, - endMinutesFromMidnight: 2 * 60, - }, + parseTime("1/1/25", "5:00 am", "12:00 pm"), + parseTime("1/1/25", "5:00 am", "12:00 pm"), + parseTime("1/1/25", "2:00 pm", "2:00 am"), ], }); - const dbResult = await getAllLocations( + const dbResult = await getAllLocationsFromDB( db, - DateTime.fromObject({ year: 2025, month: 1, day: 3 }) // 2 days after latest time + parseDate("1/3/25") // 2 days after latest time ); expect(dbResult).toEqual([ { ...locationOut, + id: id, + times: [], }, ]); } ); dbTest.concurrent( - "works on insertion with times (DST - start 2AM -> 3AM) (3/9/25)", - async ({ db: { db } }) => { - await addLocationDataToDb(db, { + "works on insertion with times (tests search window)", + async ({ ctx: { db } }) => { + const id = await addLocationDataToDb(db, { ...locationIn, + today: { + year: 2025, + month: 1, + day: 1, + }, times: [ - { - day: 9, - month: 3, - year: 2025, - startMinutesFromMidnight: 5 * 60, - endMinutesFromMidnight: 12 * 60, - }, + parseTime("1/1/25", "5:00 am", "12:00 pm"), + parseTime("2/1/25", "5:00 am", "12:00 pm"), ], }); - const dbResult = await getAllLocations( + const dbResult = await getAllLocationsFromDB( db, - DateTime.fromObject({ year: 2025, month: 1, day: 3 }) // 2 days after latest time + parseDate("1/3/25") // 2 days after latest time ); expect(dbResult).toEqual([ { ...locationOut, + id: id, + times: [{ start: 1738404000000, end: 1738429200000 }], + }, + ]); + } + ); + dbTest.concurrent( + "works on insertion with times (DST - start 2AM -> 3AM) (3/9/25)", + async ({ ctx: { db } }) => { + const id = await addLocationDataToDb(db, { + ...locationIn, + times: [parseTime("3/9/25", "5:00 am", "12:00 pm")], + }); + const dbResult = await getAllLocationsFromDB(db, parseDate("1/3/25")); + expect(dbResult).toEqual([ + { + ...locationOut, + id: id, times: [ { start: 1741510800000, @@ -224,32 +208,44 @@ describe("DB", () => { } ); dbTest.concurrent( - "works on insertion with times (DST - end 2AM -> 1AM) (3/9/25) [if a place closes at 2 AM, we assume it's the second 2AM (I mean, the first 2AM technically doesn't exist...)", - async ({ db: { db } }) => { - await addLocationDataToDb(db, { + "works on insertion with times (DST - start 2AM -> 3AM) (3/9/25)", + async ({ ctx: { db } }) => { + const id = await addLocationDataToDb(db, { + ...locationIn, + times: [parseTime("3/9/25", "2:30 am", "12:00 pm")], // 2:30 technically doesn't exist on that day + }); + const dbResult = await getAllLocationsFromDB(db, parseDate("1/3/25")); + expect(dbResult).toEqual([ + { + ...locationOut, + id: id, + times: [ + { + start: 1741505400000, // 3:30 AM... this is certainly some behavior... + end: 1741536000000, + }, + ], + }, + ]); + } + ); + dbTest.concurrent( + "works on insertion with times (DST - end 2AM -> 1AM) (11/2/25) [if a place closes at 2 AM, we assume it's the second 2AM (I mean, the first 2AM technically doesn't exist...)", + async ({ ctx: { db } }) => { + const id = await addLocationDataToDb(db, { ...locationIn, today: { year: 2025, month: 1, day: 1, }, - times: [ - { - day: 1, - month: 11, - year: 2025, - startMinutesFromMidnight: 7 * 60, - endMinutesFromMidnight: 2 * 60, - }, - ], + times: [parseTime("11/1/25", "7:00 am", "2:00 am")], }); - const dbResult = await getAllLocations( - db, - DateTime.fromObject({ year: 2025, month: 1, day: 3 }) - ); + const dbResult = await getAllLocationsFromDB(db, parseDate("1/3/25")); expect(dbResult).toEqual([ { ...locationOut, + id: id, times: [ { start: 1761994800000, @@ -261,32 +257,22 @@ describe("DB", () => { } ); dbTest.concurrent( - "works on insertion with times (DST - end 2AM -> 1AM) (3/9/25) [if a place closes at 1:30 AM, we assume dbTest's the first 1:30 AM and not the second]", - async ({ db: { db } }) => { - await addLocationDataToDb(db, { + "works on insertion with times (DST - end 2AM -> 1AM) (11/2/25) [if a place closes at 1:30 AM, we assume dbTest's the first 1:30 AM and not the second]", + async ({ ctx: { db } }) => { + const id = await addLocationDataToDb(db, { ...locationIn, today: { year: 2025, month: 1, day: 1, }, - times: [ - { - day: 1, - month: 11, - year: 2025, - startMinutesFromMidnight: 7 * 60, - endMinutesFromMidnight: 1.5 * 60, - }, - ], + times: [parseTime("11/1/25", "7:00 am", "1:30 am")], }); - const dbResult = await getAllLocations( - db, - DateTime.fromObject({ year: 2025, month: 1, day: 3 }) - ); + const dbResult = await getAllLocationsFromDB(db, parseDate("1/3/25")); expect(dbResult).toEqual([ { ...locationOut, + id: id, times: [ { start: 1761994800000, @@ -297,8 +283,8 @@ describe("DB", () => { ]); } ); - dbTest.concurrent("works on specials", async ({ db: { db } }) => { - await addLocationDataToDb(db, { + dbTest.concurrent("works on specials", async ({ ctx: { db } }) => { + const id = await addLocationDataToDb(db, { ...locationIn, today: { year: 2025, @@ -306,27 +292,8 @@ describe("DB", () => { day: 1, }, times: [ - { - day: 1, - month: 1, - year: 2025, - startMinutesFromMidnight: 5 * 60, - endMinutesFromMidnight: 12 * 60, - }, - { - day: 1, - month: 1, - year: 2025, - startMinutesFromMidnight: 5 * 60, - endMinutesFromMidnight: 12 * 60, - }, - { - day: 1, - month: 1, - year: 2025, - startMinutesFromMidnight: 14 * 60, - endMinutesFromMidnight: 2 * 60, - }, + parseTime("1/1/25", "5:00 am", "12:00 pm"), + parseTime("1/1/25", "2:00 pm", "2:00am"), ], todaysSoups: [ { title: "soup 1", description: "desc 1" }, @@ -337,13 +304,12 @@ describe("DB", () => { { title: "special 2", description: "desc 2" }, ], }); - const dbResult = await getAllLocations( - db, - DateTime.fromObject({ year: 2025, month: 1, day: 1 }) - ); + const dbResult = await getAllLocationsFromDB(db, parseDate("1/1/25")); expect(dbResult).toEqual([ { ...locationOut, + id: id, + times: [ { start: 1735725600000, @@ -379,8 +345,8 @@ describe("DB", () => { }); dbTest.concurrent( "works on specials (overriding)", - async ({ db: { db } }) => { - await addLocationDataToDb(db, { + async ({ ctx: { db } }) => { + const id1 = await addLocationDataToDb(db, { ...locationIn, today: { year: 2025, @@ -388,27 +354,8 @@ describe("DB", () => { day: 1, }, times: [ - { - day: 1, - month: 1, - year: 2025, - startMinutesFromMidnight: 5 * 60, - endMinutesFromMidnight: 12 * 60, - }, - { - day: 1, - month: 1, - year: 2025, - startMinutesFromMidnight: 5 * 60, - endMinutesFromMidnight: 12 * 60, - }, - { - day: 1, - month: 1, - year: 2025, - startMinutesFromMidnight: 14 * 60, - endMinutesFromMidnight: 2 * 60, - }, + parseTime("1/1/25", "5:00 am", "12:00 pm"), + parseTime("1/1/25", "2:00 pm", "2:00 am"), ], todaysSoups: [ { title: "soup 1", description: "desc 1" }, @@ -419,7 +366,7 @@ describe("DB", () => { { title: "special 2", description: "desc 2" }, ], }); - await addLocationDataToDb(db, { + const id2 = await addLocationDataToDb(db, { ...locationIn, today: { year: 2025, @@ -427,27 +374,8 @@ describe("DB", () => { day: 1, }, times: [ - { - day: 1, - month: 1, - year: 2025, - startMinutesFromMidnight: 5 * 60, - endMinutesFromMidnight: 12 * 60, - }, - { - day: 1, - month: 1, - year: 2025, - startMinutesFromMidnight: 5 * 60, - endMinutesFromMidnight: 12 * 60, - }, - { - day: 1, - month: 1, - year: 2025, - startMinutesFromMidnight: 14 * 60, - endMinutesFromMidnight: 2 * 60, - }, + parseTime("1/1/25", "5:00 am", "12:00 pm"), + parseTime("1/1/25", "2:00 pm", "2:00 am"), ], todaysSoups: [], todaysSpecials: [ @@ -455,13 +383,12 @@ describe("DB", () => { { title: "special 2", description: "desc 2" }, ], }); - const dbResult = await getAllLocations( - db, - DateTime.fromObject({ year: 2025, month: 1, day: 1 }) - ); + expect(id1).toEqual(id2); + const dbResult = await getAllLocationsFromDB(db, parseDate("1/1/25")); expect(dbResult).toEqual([ { ...locationOut, + id: id1, times: [ { start: 1735725600000, @@ -487,4 +414,65 @@ describe("DB", () => { ]); } ); + dbTest.concurrent("works on time overwrites", async ({ ctx: { db } }) => { + const id = await addLocationDataToDb(db, { + ...locationIn, + times: [ + parseTime("1/1/25", "5:00 am", "5:00 pm"), + parseTime("1/2/25", "5:00 am", "5:00 pm"), + parseTime("1/3/25", "5:00 am", "5:00 pm"), + parseTime("1/4/25", "5:00 am", "5:00 pm"), + parseTime("1/5/25", "5:00 am", "5:00 pm"), + ], + }); + // add the overwrite thing in twice + }); + dbTest.concurrent("stub", async ({ ctx: { db } }) => {}); // just for reference }); + +/** + * + * @param date in mm/dd/yy format + */ +function parseDate(date: string) { + const dateParsed = DateTime.fromFormat(date, "M/d/yy"); + if (!dateParsed.isValid) throw new Error(`Invalid date string ${date}`); + return dateParsed; +} +/** + * + * @param time ex. 2:00 AM (2:00AM is also fine) + * @returns minutes since midnight for that time + */ +function _parseTime(time: string) { + const [_, hour, minute, ampm] = [ + ...(/(\d*)\s*:\s*(\d*)\s*(AM|PM)/i.exec(time) ?? []), + ]; + if (hour === undefined || minute === undefined || ampm === undefined) + throw new Error(`Malformed time ${time}`); + const hourNum = parseInt(hour); + const minuteNum = parseInt(minute); + if (isNaN(hourNum) || isNaN(minuteNum)) + throw new Error(`Malformed time ${time}`); + const minutesSinceMidnight = + (hourNum % 12) * 60 + + minuteNum + + (ampm.toLowerCase() === "pm" ? 12 * 60 : 0); + return minutesSinceMidnight; +} +/** + * + * @param date ex. 2/3/25 + * @param startTime ex. 2:00 AM + * @param endTime ex. 2:00 AM (can wrap to next day) + */ +function parseTime(date: string, startTime: string, endTime: string) { + const parsedDate = parseDate(date); + return { + year: parsedDate.year, + month: parsedDate.month, + day: parsedDate.day, + startMinutesFromMidnight: _parseTime(startTime), + endMinutesFromMidnight: _parseTime(endTime), + }; +} From 687ed6f319016d3e46948c1afd045e2073a05778 Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Thu, 13 Nov 2025 17:36:37 -0500 Subject: [PATCH 48/65] test: add a few more tests --- src/db/updateLocation.ts | 11 +-- src/types.ts | 2 +- tests/database.test.ts | 151 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 154 insertions(+), 10 deletions(-) diff --git a/src/db/updateLocation.ts b/src/db/updateLocation.ts index 9a30726..2fb2eb9 100644 --- a/src/db/updateLocation.ts +++ b/src/db/updateLocation.ts @@ -135,9 +135,12 @@ export async function addTimeOverride( locationId: locationId, timeString: timeStringOverride, }; - await db.insert(timeOverwritesTable).values(rowToInsert).onConflictDoUpdate({ - target: timeOverwritesTable.locationId, - set: rowToInsert, - }); + await db + .insert(timeOverwritesTable) + .values(rowToInsert) + .onConflictDoUpdate({ + target: [timeOverwritesTable.locationId, timeOverwritesTable.date], + set: rowToInsert, + }); return true; } diff --git a/src/types.ts b/src/types.ts index e8235e6..903de90 100644 --- a/src/types.ts +++ b/src/types.ts @@ -17,7 +17,7 @@ export interface ILocation { acceptsOnlineOrders: boolean; /** Assuming these times fall after today */ times: IFullTimeRange[]; - /** useful when figuring out which db time entries to overwrite. Can be undefined if no time data was properly scraped */ + /** used when figuring out which time entries to clear in the database. (we can't just look at `times` directly to figure that out, because it might as well be empty) */ today: IDate; todaysSpecials: ISpecial[] | undefined; todaysSoups: ISpecial[] | undefined; diff --git a/tests/database.test.ts b/tests/database.test.ts index 771d314..155b594 100644 --- a/tests/database.test.ts +++ b/tests/database.test.ts @@ -3,14 +3,13 @@ import { StartedPostgreSqlContainer, } from "@testcontainers/postgresql"; import { DBType, initDBConnection } from "db/db"; -import { addLocationDataToDb } from "db/updateLocation"; +import { addLocationDataToDb, addTimeOverride } from "db/updateLocation"; import { getAllLocationsFromDB } from "db/getLocations"; import { DateTime } from "luxon"; import { ILocation } from "types"; import { test as baseTest } from "vitest"; import { Pool } from "pg"; - -const wait = (ms: number) => new Promise((re) => setTimeout(re, ms)); +import { overwritesTable } from "db/schema"; /** Something that you can get from DiningParser */ const locationIn: ILocation = { @@ -35,7 +34,7 @@ const locationIn: ILocation = { /** What getAllLocations() returns when `locationIn` has been added to the db */ const locationOut = { - id: "TODO", + id: "DYNAMICALLY GENERATED, replace with real id", name: "dbTest", shortDescription: "hi", description: "description", @@ -425,7 +424,134 @@ describe("DB", () => { parseTime("1/5/25", "5:00 am", "5:00 pm"), ], }); - // add the overwrite thing in twice + const success1 = await addTimeOverride( + db, + id, + "1/1/25", + "2:00 AM - 3:00 PM" + ); + const success2 = await addTimeOverride( + db, + id, + "1/1/25", + "3:00 AM - 4:00 PM" + ); // second one should overwrite the first one + expect(success1).toBe(true); + expect(success2).toBe(true); + expect(await getAllLocationsFromDB(db, parseDate("1/1/25"))).toEqual([ + { + ...locationOut, + id: id, + times: [ + { + start: timeToUnixTimestamp("1/1/25 3:00 AM"), + end: timeToUnixTimestamp("1/1/25 4:00 PM"), + }, + { + start: timeToUnixTimestamp("1/2/25 5:00 AM"), + end: timeToUnixTimestamp("1/2/25 5:00 PM"), + }, + { + start: timeToUnixTimestamp("1/3/25 5:00 AM"), + end: timeToUnixTimestamp("1/3/25 5:00 PM"), + }, + { + start: timeToUnixTimestamp("1/4/25 5:00 AM"), + end: timeToUnixTimestamp("1/4/25 5:00 PM"), + }, + { + start: timeToUnixTimestamp("1/5/25 5:00 AM"), + end: timeToUnixTimestamp("1/5/25 5:00 PM"), + }, + ], + }, + ]); + }); + dbTest.concurrent("malformed time overwrites", async ({ ctx: { db } }) => { + const id = await addLocationDataToDb(db, { + ...locationIn, + times: [ + parseTime("1/1/25", "5:00 am", "5:00 pm"), + parseTime("1/2/25", "5:00 am", "5:00 pm"), + parseTime("1/3/25", "5:00 am", "5:00 pm"), + parseTime("1/4/25", "5:00 am", "5:00 pm"), + parseTime("1/5/25", "5:00 am", "5:00 pm"), + ], + }); + + const success = await addTimeOverride(db, id, "1/1/25", "moo"); + expect(success).toBe(true); + expect(await getAllLocationsFromDB(db, parseDate("1/1/25"))).toEqual([ + { + ...locationOut, + id: id, + times: [ + // 1/1/25 should be wiped because there is an overwrite -- it's just invalid + { + start: timeToUnixTimestamp("1/2/25 5:00 AM"), + end: timeToUnixTimestamp("1/2/25 5:00 PM"), + }, + { + start: timeToUnixTimestamp("1/3/25 5:00 AM"), + end: timeToUnixTimestamp("1/3/25 5:00 PM"), + }, + { + start: timeToUnixTimestamp("1/4/25 5:00 AM"), + end: timeToUnixTimestamp("1/4/25 5:00 PM"), + }, + { + start: timeToUnixTimestamp("1/5/25 5:00 AM"), + end: timeToUnixTimestamp("1/5/25 5:00 PM"), + }, + ], + }, + ]); + }); + dbTest.concurrent("works on general overwrites", async ({ ctx: { db } }) => { + const id = await addLocationDataToDb(db, { + ...locationIn, + acceptsOnlineOrders: false, + menu: "bleh", + }); + await db.insert(overwritesTable).values({ + locationId: id, + acceptsOnlineOrders: true, + menu: "overwritten menu", + }); + + expect(await getAllLocationsFromDB(db, parseDate("1/1/25"))).toEqual([ + { + ...locationOut, + id: id, + acceptsOnlineOrders: true, + menu: "overwritten menu", + }, + ]); + }); + dbTest.concurrent("two locations", async ({ ctx: { db } }) => { + const id1 = await addLocationDataToDb(db, { + ...locationIn, + conceptId: 1, + }); + const id2 = await addLocationDataToDb(db, { + ...locationIn, + conceptId: 2, + }); + expect(id1).not.toEqual(id2); + const locationData = await getAllLocationsFromDB(db, parseDate("1/1/25")); + expect(locationData).toHaveLength(2); + expect(locationData).toEqual( + expect.arrayContaining([ + { + ...locationOut, + id: id1, + }, + { + ...locationOut, + id: id2, + }, + ]) + ); }); dbTest.concurrent("stub", async ({ ctx: { db } }) => {}); // just for reference }); @@ -476,3 +602,18 @@ function parseTime(date: string, startTime: string, endTime: string) { endMinutesFromMidnight: _parseTime(endTime), }; } + +/** + * + * @param datetime + * @returns timestamp, when datetime is interpreted in EST + */ +function timeToUnixTimestamp(datetime: string) { + const parsedDate = DateTime.fromFormat(datetime, "M/d/yy h:mm a", { + zone: "America/New_York", + }); + if (!parsedDate.isValid) throw new Error(`Malformed date string ${datetime}`); + return parsedDate.toMillis(); +} + +const wait = (ms: number) => new Promise((re) => setTimeout(re, ms)); From e93638d2d61dca1f2b18f3a06148646c8ed85ec2 Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Thu, 13 Nov 2025 17:41:27 -0500 Subject: [PATCH 49/65] chore: rename variable --- src/db/updateLocation.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/db/updateLocation.ts b/src/db/updateLocation.ts index 2fb2eb9..3b065de 100644 --- a/src/db/updateLocation.ts +++ b/src/db/updateLocation.ts @@ -49,7 +49,7 @@ export async function addLocationDataToDb(db: DBType, location: ILocation) { set: locationDbEntry, }); - const earliestDaySQLString = `${location.today.year}-${pad( + const todayAsSQLString = `${location.today.year}-${pad( location.today.month )}-${pad(location.today.day)})`; // add specials @@ -58,7 +58,7 @@ export async function addLocationDataToDb(db: DBType, location: ILocation) { .where( and( eq(specialsTable.locationId, internalId), - eq(specialsTable.date, earliestDaySQLString) + eq(specialsTable.date, todayAsSQLString) ) ); const specials = [ @@ -74,7 +74,7 @@ export async function addLocationDataToDb(db: DBType, location: ILocation) { if (specials.length) await db.insert(specialsTable).values( specials.map((special) => ({ - date: earliestDaySQLString, + date: todayAsSQLString, locationId: internalId, name: special.title, description: special.description, @@ -88,7 +88,7 @@ export async function addLocationDataToDb(db: DBType, location: ILocation) { .where( and( eq(timesTable.locationId, internalId), - and(gte(timesTable.date, earliestDaySQLString)) + and(gte(timesTable.date, todayAsSQLString)) ) ); if (location.times.length) { From 4ecf5cb300def1667548d05192879b1a07b3e8f8 Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Thu, 13 Nov 2025 17:55:47 -0500 Subject: [PATCH 50/65] chore: bundle writes into single transaction and add coverage reporting --- .github/workflows/test.yml | 4 +- package.json | 2 + pnpm-lock.yaml | 153 +++++++++++++++++++++++++++++++++++++ src/db/updateLocation.ts | 135 ++++++++++++++++---------------- vitest.config.ts | 4 + 5 files changed, 230 insertions(+), 68 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fd7bca7..9332a98 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -23,4 +23,6 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile - name: run tests - run: pnpm test + run: pnpm test:coverage + - name: "Report Coverage" + uses: davelosert/vitest-coverage-report-action@v2 diff --git a/package.json b/package.json index b1dd27b..61c64dd 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "build": "rollup -c", "start": "NODE_ENV=production node dist/server.js", "test": "dotenv -e .env.test -- vitest", + "test:coverage": "dotenv -e .env.test -- vitest --coverage", "__comment": "add DEBUG=testcontainers* to the test command to get testcontainers debug output", "typecheck": "tsc", "run-prod": "VOL_SRC=postgres_data docker-compose up --build", @@ -52,6 +53,7 @@ "@types/luxon": "^3.7.1", "@types/node": "^24.0.14", "@types/pg": "^8.15.5", + "@vitest/coverage-v8": "4.0.8", "dotenv": "^17.2.0", "dotenv-cli": "^8.0.0", "npm-check-updates": "^18.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d94c5a2..75465ba 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -78,6 +78,9 @@ importers: '@types/pg': specifier: ^8.15.5 version: 8.15.5 + '@vitest/coverage-v8': + specifier: 4.0.8 + version: 4.0.8(vitest@4.0.8(@types/node@24.5.2)(tsx@4.20.5)(yaml@2.8.1)) dotenv: specifier: ^17.2.0 version: 17.2.2 @@ -196,6 +199,10 @@ packages: resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.27.1': resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} @@ -213,6 +220,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.28.5': + resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1': resolution: {integrity: sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==} engines: {node: '>=6.9.0'} @@ -620,9 +632,17 @@ packages: resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} engines: {node: '>=6.9.0'} + '@babel/types@7.28.5': + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} + engines: {node: '>=6.9.0'} + '@balena/dockerignore@1.0.2': resolution: {integrity: sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==} + '@bcoe/v8-coverage@1.0.2': + resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} + engines: {node: '>=18'} + '@borewit/text-codec@0.1.1': resolution: {integrity: sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==} @@ -1237,6 +1257,15 @@ packages: '@types/ssh2@1.15.5': resolution: {integrity: sha512-N1ASjp/nXH3ovBHddRJpli4ozpk6UdDYIX4RJWFa9L1YKnzdhTlVmiGHm4DZnj/jLbqZpes4aeR30EFGQtvhQQ==} + '@vitest/coverage-v8@4.0.8': + resolution: {integrity: sha512-wQgmtW6FtPNn4lWUXi8ZSYLpOIb92j3QCujxX3sQ81NTfQ/ORnE0HtK7Kqf2+7J9jeveMGyGyc4NWc5qy3rC4A==} + peerDependencies: + '@vitest/browser': 4.0.8 + vitest: 4.0.8 + peerDependenciesMeta: + '@vitest/browser': + optional: true + '@vitest/expect@4.0.8': resolution: {integrity: sha512-Rv0eabdP/xjAHQGr8cjBm+NnLHNoL268lMDK85w2aAGLFoVKLd8QGnVon5lLtkXQCoYaNL0wg04EGnyKkkKhPA==} @@ -1305,6 +1334,9 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + ast-v8-to-istanbul@0.3.8: + resolution: {integrity: sha512-szgSZqUxI5T8mLKvS7WTjF9is+MVbOeLADU73IseOcrqhxr/VAvy6wfoVE39KnKzA7JRhjF5eUagNlHwvZPlKQ==} + async-lock@1.4.1: resolution: {integrity: sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==} @@ -1923,6 +1955,10 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + has-symbols@1.1.0: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} @@ -1939,6 +1975,9 @@ packages: resolution: {integrity: sha512-JW8Bb4RFWD9iOKxg5PbUarBYGM99IcxFl2FPBo2gSJO11jjUDqlP1Bmfyqt8Z/dGhIQ63PMA9LdcLefXyIasyg==} engines: {node: '>=16.9.0'} + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + htmlparser2@8.0.2: resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} @@ -1985,12 +2024,31 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-lib-source-maps@5.0.6: + resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} + engines: {node: '>=10'} + + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + engines: {node: '>=8'} + jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + jsesc@3.0.2: resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==} engines: {node: '>=6'} @@ -2035,6 +2093,13 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + magicast@0.5.1: + resolution: {integrity: sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw==} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} @@ -2334,6 +2399,11 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + engines: {node: '>=10'} + hasBin: true + send@1.2.0: resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} engines: {node: '>= 18'} @@ -2447,6 +2517,10 @@ packages: resolution: {integrity: sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==} engines: {node: '>=18'} + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} @@ -2855,6 +2929,8 @@ snapshots: '@babel/helper-validator-identifier@7.27.1': {} + '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-validator-option@7.27.1': {} '@babel/helper-wrap-function@7.28.3': @@ -2874,6 +2950,10 @@ snapshots: dependencies: '@babel/types': 7.28.4 + '@babel/parser@7.28.5': + dependencies: + '@babel/types': 7.28.5 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1(@babel/core@7.28.4)': dependencies: '@babel/core': 7.28.4 @@ -3400,8 +3480,15 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 + '@babel/types@7.28.5': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@balena/dockerignore@1.0.2': {} + '@bcoe/v8-coverage@1.0.2': {} + '@borewit/text-codec@0.1.1': {} '@drizzle-team/brocli@0.10.2': {} @@ -3860,6 +3947,23 @@ snapshots: dependencies: '@types/node': 18.19.130 + '@vitest/coverage-v8@4.0.8(vitest@4.0.8(@types/node@24.5.2)(tsx@4.20.5)(yaml@2.8.1))': + dependencies: + '@bcoe/v8-coverage': 1.0.2 + '@vitest/utils': 4.0.8 + ast-v8-to-istanbul: 0.3.8 + debug: 4.4.3 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.6 + istanbul-reports: 3.2.0 + magicast: 0.5.1 + std-env: 3.10.0 + tinyrainbow: 3.0.3 + vitest: 4.0.8(@types/node@24.5.2)(tsx@4.20.5)(yaml@2.8.1) + transitivePeerDependencies: + - supports-color + '@vitest/expect@4.0.8': dependencies: '@standard-schema/spec': 1.0.0 @@ -3947,6 +4051,12 @@ snapshots: assertion-error@2.0.1: {} + ast-v8-to-istanbul@0.3.8: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + estree-walker: 3.0.3 + js-tokens: 9.0.1 + async-lock@1.4.1: {} async@3.2.6: {} @@ -4562,6 +4672,8 @@ snapshots: graceful-fs@4.2.11: {} + has-flag@4.0.0: {} + has-symbols@1.1.0: {} has-tostringtag@1.0.2: @@ -4574,6 +4686,8 @@ snapshots: hono@4.9.8: {} + html-escaper@2.0.2: {} + htmlparser2@8.0.2: dependencies: domelementtype: 2.3.0 @@ -4617,6 +4731,27 @@ snapshots: isexe@2.0.0: {} + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-lib-source-maps@5.0.6: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + debug: 4.4.3 + istanbul-lib-coverage: 3.2.2 + transitivePeerDependencies: + - supports-color + + istanbul-reports@3.2.0: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + jackspeak@3.4.3: dependencies: '@isaacs/cliui': 8.0.2 @@ -4625,6 +4760,8 @@ snapshots: js-tokens@4.0.0: {} + js-tokens@9.0.1: {} + jsesc@3.0.2: {} jsesc@3.1.0: {} @@ -4655,6 +4792,16 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + magicast@0.5.1: + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + source-map-js: 1.2.1 + + make-dir@4.0.0: + dependencies: + semver: 7.7.3 + math-intrinsics@1.1.0: {} media-typer@1.1.0: {} @@ -4964,6 +5111,8 @@ snapshots: semver@6.3.1: {} + semver@7.7.3: {} + send@1.2.0: dependencies: debug: 4.4.3 @@ -5106,6 +5255,10 @@ snapshots: dependencies: '@tokenizer/token': 0.3.0 + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + supports-preserve-symlinks-flag@1.0.0: {} tar-fs@2.1.4: diff --git a/src/db/updateLocation.ts b/src/db/updateLocation.ts index 3b065de..96671a6 100644 --- a/src/db/updateLocation.ts +++ b/src/db/updateLocation.ts @@ -40,76 +40,77 @@ export async function addLocationDataToDb(db: DBType, location: ILocation) { coordinateLng: location.coordinates?.lng, acceptsOnlineOrders: location.acceptsOnlineOrders, }; + await db.transaction(async (tx) => { + await tx + .insert(locationDataTable) + .values(locationDbEntry) + .onConflictDoUpdate({ + target: locationDataTable.id, + set: locationDbEntry, + }); - await db - .insert(locationDataTable) - .values(locationDbEntry) - .onConflictDoUpdate({ - target: locationDataTable.id, - set: locationDbEntry, - }); - - const todayAsSQLString = `${location.today.year}-${pad( - location.today.month - )}-${pad(location.today.day)})`; - // add specials - await db - .delete(specialsTable) - .where( - and( - eq(specialsTable.locationId, internalId), - eq(specialsTable.date, todayAsSQLString) - ) - ); - const specials = [ - ...(location.todaysSpecials?.map((sp) => ({ - ...sp, - type: "special" as const, - })) ?? []), - ...(location.todaysSoups?.map((sp) => ({ - ...sp, - type: "soup" as const, - })) ?? []), - ]; - if (specials.length) - await db.insert(specialsTable).values( - specials.map((special) => ({ - date: todayAsSQLString, - locationId: internalId, - name: special.title, - description: special.description, - type: special.type, - })) - ); + const todayAsSQLString = `${location.today.year}-${pad( + location.today.month + )}-${pad(location.today.day)})`; + // add specials + await tx + .delete(specialsTable) + .where( + and( + eq(specialsTable.locationId, internalId), + eq(specialsTable.date, todayAsSQLString) + ) + ); + const specials = [ + ...(location.todaysSpecials?.map((sp) => ({ + ...sp, + type: "special" as const, + })) ?? []), + ...(location.todaysSoups?.map((sp) => ({ + ...sp, + type: "soup" as const, + })) ?? []), + ]; + if (specials.length) + await tx.insert(specialsTable).values( + specials.map((special) => ({ + date: todayAsSQLString, + locationId: internalId, + name: special.title, + description: special.description, + type: special.type, + })) + ); - // remove rows from whenever the scrape started from (aka remove entries corresponding to the last 7 days) - await db - .delete(timesTable) - .where( - and( - eq(timesTable.locationId, internalId), - and(gte(timesTable.date, todayAsSQLString)) - ) - ); - if (location.times.length) { - await db.insert(timesTable).values( - location.times.map((time) => ({ - locationId: internalId, - date: `${time.year}-${pad(time.month)}-${pad(time.day)}`, - startTime: time.startMinutesFromMidnight, - endTime: time.endMinutesFromMidnight, - })) - ); - } + // remove rows from whenever the scrape started from (aka remove entries corresponding to the last 7 days) + await tx + .delete(timesTable) + .where( + and( + eq(timesTable.locationId, internalId), + and(gte(timesTable.date, todayAsSQLString)) + ) + ); + if (location.times.length) { + await tx.insert(timesTable).values( + location.times.map((time) => ({ + locationId: internalId, + date: `${time.year}-${pad(time.month)}-${pad(time.day)}`, + startTime: time.startMinutesFromMidnight, + endTime: time.endMinutesFromMidnight, + })) + ); + } - // in case the conceptId->internalId mapping entry isn't there - await db - .insert(conceptIdToInternalIdTable) - .values({ - internalId: internalId, - externalId: location.conceptId.toString(), - }) - .onConflictDoNothing({ target: conceptIdToInternalIdTable.externalId }); + // in case the conceptId->internalId mapping entry isn't there + await tx + .insert(conceptIdToInternalIdTable) + .values({ + internalId: internalId, + externalId: location.conceptId.toString(), + }) + .onConflictDoNothing({ target: conceptIdToInternalIdTable.externalId }); + }); return internalId; } /** diff --git a/vitest.config.ts b/vitest.config.ts index e4bd5cf..59d6604 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -5,6 +5,10 @@ export default defineConfig({ test: { globals: true, testTimeout: 30_000, + coverage: { + provider: "v8", + reporter: ["text", "json-summary", "json"], + }, }, plugins: [tsconfigPaths()], }); From 7f6d7ef965a835e42451e823d48d325b8cc9684a Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Thu, 13 Nov 2025 18:04:19 -0500 Subject: [PATCH 51/65] chore: uncomment reload function --- src/server.ts | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/server.ts b/src/server.ts index 0b3cbce..624110c 100644 --- a/src/server.ts +++ b/src/server.ts @@ -18,7 +18,6 @@ import { QueryUtils } from "db/dbQueryUtils"; /** only used for Slack debug diff logging */ let cachedLocations: ILocation[] = []; async function reload(): Promise { - return; const now = new Date(); console.log(`Reloading Dining API: ${now}`); const parser = new DiningParser(); @@ -105,19 +104,13 @@ setInterval(() => { (er) => `Error in reload process: ${notifySlack(String(er))}\n${er.stack}` ); }, env.RELOAD_WAIT_INTERVAL); +reload().catch( + (er) => `Error in reload process: ${notifySlack(String(er))}\n${er.stack}` +); -// Initial load and start the server -reload() - .then(() => { - app.listen(env.PORT, ({ hostname, port }) => { - notifySlack(`Dining API is running at ${hostname}:${port}`); - }); - }) - .catch(async (er) => { - await notifySlack(" Dining API startup failed!!"); - await notifySlack(`*Error caught*\n${er.stack}`); - process.exit(1); - }); +app.listen(env.PORT, ({ hostname, port }) => { + notifySlack(`Dining API is running at ${hostname}:${port}`); +}); // DEPRECATED From 079587df3b916d80b6202689246e788cccc472cf Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Thu, 13 Nov 2025 18:12:05 -0500 Subject: [PATCH 52/65] chore: upd readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a894fb2..06d8016 100644 --- a/README.md +++ b/README.md @@ -31,9 +31,9 @@ Start your local database with `pnpm db:start` and then start the server with `p ## Database schema changes (important!) -When you make changes to the database schema, be sure to run `pnpm db:push` to keep your local db in sync. (You should do this before running tests as well!) +When you make changes to the database schema, be sure to run `pnpm db:push` to keep your local db in sync. -Before merging your PR, be sure to run `pnpm db:generate` to generate a migration file, which will then be automatically applied to the staging and production databases when deployed. +Before merging your PR, be sure to run `pnpm db:generate` to generate a migration file, which will then be automatically applied to the staging and production databases when deployed. (You should do this before running tests as well!) To test if the migration files work, you can run `pnpm run-prod`, which will spin up a production version of the server and a postgres database mounted on a new volume. The server is created using the same Dockerfile used in our Railway deployments, so if it works locally, it (probably) works in production as well. From 4ac144bd7e49509f8f070fd667a44f3665d6905b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 13 Nov 2025 23:27:16 +0000 Subject: [PATCH 53/65] Initial plan From 76891b8b4ee06b31262f227f8d7b3553ca405627 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 13 Nov 2025 23:36:00 +0000 Subject: [PATCH 54/65] Refactor timestamps in database.test.ts to use timeToUnixTimestamp function Co-authored-by: cirex-web <57322506+cirex-web@users.noreply.github.com> --- tests/database.test.ts | 51 +++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/tests/database.test.ts b/tests/database.test.ts index 155b594..6c3edaa 100644 --- a/tests/database.test.ts +++ b/tests/database.test.ts @@ -110,16 +110,16 @@ describe("DB", () => { id: id, times: [ { - start: 1735725600000, - end: 1735750800000, + start: timeToUnixTimestamp("1/1/25 5:00 AM"), + end: timeToUnixTimestamp("1/1/25 12:00 PM"), }, { - start: 1735758000000, - end: 1735801200000, + start: timeToUnixTimestamp("1/1/25 2:00 PM"), + end: timeToUnixTimestamp("1/2/25 2:00 AM"), }, { - start: 1751911200000, - end: 1751954400000, + start: timeToUnixTimestamp("7/7/25 2:00 PM"), + end: timeToUnixTimestamp("7/8/25 2:00 AM"), }, ], }, @@ -179,7 +179,12 @@ describe("DB", () => { { ...locationOut, id: id, - times: [{ start: 1738404000000, end: 1738429200000 }], + times: [ + { + start: timeToUnixTimestamp("2/1/25 5:00 AM"), + end: timeToUnixTimestamp("2/1/25 12:00 PM"), + }, + ], }, ]); } @@ -198,8 +203,8 @@ describe("DB", () => { id: id, times: [ { - start: 1741510800000, - end: 1741536000000, + start: timeToUnixTimestamp("3/9/25 5:00 AM"), + end: timeToUnixTimestamp("3/9/25 12:00 PM"), }, ], }, @@ -220,8 +225,8 @@ describe("DB", () => { id: id, times: [ { - start: 1741505400000, // 3:30 AM... this is certainly some behavior... - end: 1741536000000, + start: timeToUnixTimestamp("3/9/25 3:30 AM"), // 3:30 AM... this is certainly some behavior... + end: timeToUnixTimestamp("3/9/25 12:00 PM"), }, ], }, @@ -247,8 +252,8 @@ describe("DB", () => { id: id, times: [ { - start: 1761994800000, - end: 1762066800000, // the second 2AM + start: timeToUnixTimestamp("11/1/25 7:00 AM"), + end: timeToUnixTimestamp("11/2/25 2:00 AM"), // the second 2AM }, ], }, @@ -274,8 +279,8 @@ describe("DB", () => { id: id, times: [ { - start: 1761994800000, - end: 1762061400000, // the first 1:30 AM + start: timeToUnixTimestamp("11/1/25 7:00 AM"), + end: 1762061400000, // the first 1:30 AM EDT (timeToUnixTimestamp would give the second 1:30 AM EST due to DST fallback) }, ], }, @@ -311,12 +316,12 @@ describe("DB", () => { times: [ { - start: 1735725600000, - end: 1735750800000, + start: timeToUnixTimestamp("1/1/25 5:00 AM"), + end: timeToUnixTimestamp("1/1/25 12:00 PM"), }, { - start: 1735758000000, - end: 1735801200000, + start: timeToUnixTimestamp("1/1/25 2:00 PM"), + end: timeToUnixTimestamp("1/2/25 2:00 AM"), }, ], todaysSoups: [ @@ -390,12 +395,12 @@ describe("DB", () => { id: id1, times: [ { - start: 1735725600000, - end: 1735750800000, + start: timeToUnixTimestamp("1/1/25 5:00 AM"), + end: timeToUnixTimestamp("1/1/25 12:00 PM"), }, { - start: 1735758000000, - end: 1735801200000, + start: timeToUnixTimestamp("1/1/25 2:00 PM"), + end: timeToUnixTimestamp("1/2/25 2:00 AM"), }, ], todaysSoups: [], From fdaa26e854cc82614126d398dec0f6b28951f153 Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Sat, 22 Nov 2025 16:37:27 -0500 Subject: [PATCH 55/65] chore: upd readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 06d8016..2b40bbf 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Now install the API's dependencies by 'cd'-ing into the root of the repository a pnpm install ``` -Start your local database with `pnpm db:start` and then start the server with `pnpm dev` and it should work, assuming you have the correct env variables. (To see the contents of the database, I recommend using DBeaver. You can also run `pnpm db:studio` to start up drizzle studio) +Start your local database with `pnpm db:start`, `pnpm db:push` (if this is your first time) and then start the server with `pnpm dev` and it should work, assuming you have the correct env variables. (To see the contents of the database, I recommend using DBeaver. You can also run `pnpm db:studio` to start up drizzle studio) ## Database schema changes (important!) From 9f7901db820c809a2f6de9b39bdbc85db34a753d Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Sat, 22 Nov 2025 17:24:07 -0500 Subject: [PATCH 56/65] chore: add column names to some columns in the schema --- drizzle/0002_fair_meteorite.sql | 3 + drizzle/meta/0002_snapshot.json | 495 ++++++++++++++++++++++++++++++++ drizzle/meta/_journal.json | 7 + src/db/schema.ts | 27 +- 4 files changed, 519 insertions(+), 13 deletions(-) create mode 100644 drizzle/0002_fair_meteorite.sql create mode 100644 drizzle/meta/0002_snapshot.json diff --git a/drizzle/0002_fair_meteorite.sql b/drizzle/0002_fair_meteorite.sql new file mode 100644 index 0000000..cb7d028 --- /dev/null +++ b/drizzle/0002_fair_meteorite.sql @@ -0,0 +1,3 @@ +ALTER TABLE "location_data" RENAME COLUMN "acceptsOnlineOrders" TO "accepts_online_orders";--> statement-breakpoint +ALTER TABLE "overwrites_table" RENAME COLUMN "coordinateLat" TO "coordinate_lat";--> statement-breakpoint +ALTER TABLE "overwrites_table" RENAME COLUMN "coordinateLng" TO "coordinate_lng"; \ No newline at end of file diff --git a/drizzle/meta/0002_snapshot.json b/drizzle/meta/0002_snapshot.json new file mode 100644 index 0000000..ea2ad8f --- /dev/null +++ b/drizzle/meta/0002_snapshot.json @@ -0,0 +1,495 @@ +{ + "id": "0cafcb54-9cfd-41e1-b46c-748f17a03c53", + "prevId": "6cb70ab4-5a52-45dc-b62f-616000bbf5ec", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.concept_id_to_internal_id": { + "name": "concept_id_to_internal_id", + "schema": "", + "columns": { + "internal_id": { + "name": "internal_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": true, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "concept_id_to_internal_id_internal_id_location_data_id_fk": { + "name": "concept_id_to_internal_id_internal_id_location_data_id_fk", + "tableFrom": "concept_id_to_internal_id", + "tableTo": "location_data", + "columnsFrom": [ + "internal_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "concept_id_to_internal_id_external_id_unique": { + "name": "concept_id_to_internal_id_external_id_unique", + "nullsNotDistinct": false, + "columns": [ + "external_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.emails": { + "name": "emails", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "emails_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.location_data": { + "name": "location_data", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "short_description": { + "name": "short_description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "menu": { + "name": "menu", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "coordinate_lat": { + "name": "coordinate_lat", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "coordinate_lng": { + "name": "coordinate_lng", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "accepts_online_orders": { + "name": "accepts_online_orders", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.overwrites_table": { + "name": "overwrites_table", + "schema": "", + "columns": { + "location_id": { + "name": "location_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "short_description": { + "name": "short_description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "menu": { + "name": "menu", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "coordinate_lat": { + "name": "coordinate_lat", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "coordinate_lng": { + "name": "coordinate_lng", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "accepts_online_orders": { + "name": "accepts_online_orders", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "overwrites_table_location_id_location_data_id_fk": { + "name": "overwrites_table_location_id_location_data_id_fk", + "tableFrom": "overwrites_table", + "tableTo": "location_data", + "columnsFrom": [ + "location_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.specials": { + "name": "specials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "specials_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "location_id": { + "name": "location_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "date": { + "name": "date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "specialType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "specials_location_id_location_data_id_fk": { + "name": "specials_location_id_location_data_id_fk", + "tableFrom": "specials", + "tableTo": "location_data", + "columnsFrom": [ + "location_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.time_overwrites_table": { + "name": "time_overwrites_table", + "schema": "", + "columns": { + "location_id": { + "name": "location_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "date": { + "name": "date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "time_string": { + "name": "time_string", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "time_overwrites_table_location_id_location_data_id_fk": { + "name": "time_overwrites_table_location_id_location_data_id_fk", + "tableFrom": "time_overwrites_table", + "tableTo": "location_data", + "columnsFrom": [ + "location_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "time_overwrites_table_location_id_date_pk": { + "name": "time_overwrites_table_location_id_date_pk", + "columns": [ + "location_id", + "date" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.location_times": { + "name": "location_times", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "location_times_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "location_id": { + "name": "location_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "date": { + "name": "date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "start_time": { + "name": "start_time", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "end_time": { + "name": "end_time", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "date_lookup": { + "name": "date_lookup", + "columns": [ + { + "expression": "location_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "location_times_location_id_location_data_id_fk": { + "name": "location_times_location_id_location_data_id_fk", + "tableFrom": "location_times", + "tableTo": "location_data", + "columnsFrom": [ + "location_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.specialType": { + "name": "specialType", + "schema": "public", + "values": [ + "special", + "soup" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 6d0b361..b3c252f 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -15,6 +15,13 @@ "when": 1762796324448, "tag": "0001_broad_lord_tyger", "breakpoints": true + }, + { + "idx": 2, + "version": "7", + "when": 1763850203721, + "tag": "0002_fair_meteorite", + "breakpoints": true } ] } \ No newline at end of file diff --git a/src/db/schema.ts b/src/db/schema.ts index 566dcf8..357463b 100644 --- a/src/db/schema.ts +++ b/src/db/schema.ts @@ -1,3 +1,5 @@ +// don't add .unique() or .notNull() to a primary key, drizzle doesn't like it when you remove it later on. + import { pgTable, text, @@ -11,7 +13,7 @@ import { } from "drizzle-orm/pg-core"; export const emailTable = pgTable("emails", { - id: integer().primaryKey().generatedAlwaysAsIdentity(), + id: integer("id").primaryKey().generatedAlwaysAsIdentity(), name: text("name").notNull(), email: text("email").notNull(), }); @@ -20,10 +22,10 @@ export const conceptIdToInternalIdTable = pgTable("concept_id_to_internal_id", { internalId: text("internal_id") .notNull() .references(() => locationDataTable.id, { onDelete: "cascade" }), - externalId: text("external_id").notNull().unique().primaryKey(), + externalId: text("external_id").unique().primaryKey(), }); export const locationDataTable = pgTable("location_data", { - id: text("id").notNull().primaryKey(), + id: text("id").primaryKey(), name: text("name"), shortDescription: text("short_description"), description: text("description").notNull(), @@ -33,12 +35,12 @@ export const locationDataTable = pgTable("location_data", { location: text("location").notNull(), coordinateLat: decimal("coordinate_lat", { mode: "number", scale: 30 }), coordinateLng: decimal("coordinate_lng", { mode: "number", scale: 30 }), - acceptsOnlineOrders: boolean().notNull(), + acceptsOnlineOrders: boolean("accepts_online_orders").notNull(), }); export const timesTable = pgTable( "location_times", { - id: integer().notNull().generatedAlwaysAsIdentity().primaryKey(), + id: integer("id").notNull().generatedAlwaysAsIdentity().primaryKey(), locationId: text("location_id") .references(() => locationDataTable.id, { onDelete: "cascade", @@ -55,7 +57,6 @@ export const timesTable = pgTable( */ export const overwritesTable = pgTable("overwrites_table", { locationId: text("location_id") - .notNull() .primaryKey() .references(() => locationDataTable.id, { onDelete: "cascade", @@ -66,8 +67,8 @@ export const overwritesTable = pgTable("overwrites_table", { url: text("url"), menu: text("menu"), location: text("location"), - coordinateLat: decimal({ mode: "number", scale: 30 }), - coordinateLng: decimal({ mode: "number", scale: 30 }), + coordinateLat: decimal("coordinate_lat", { mode: "number", scale: 30 }), + coordinateLng: decimal("coordinate_lng", { mode: "number", scale: 30 }), acceptsOnlineOrders: boolean("accepts_online_orders"), }); export const timeOverwritesTable = pgTable( @@ -86,14 +87,14 @@ export const timeOverwritesTable = pgTable( export const specialType = pgEnum("specialType", ["special", "soup"]); export const specialsTable = pgTable("specials", { - id: integer().notNull().generatedAlwaysAsIdentity().primaryKey(), + id: integer("id").notNull().generatedAlwaysAsIdentity().primaryKey(), locationId: text("location_id") .references(() => locationDataTable.id, { onDelete: "cascade", }) .notNull(), - name: text().notNull(), - description: text().notNull(), - date: date().notNull(), - type: specialType().notNull(), + name: text("name").notNull(), + description: text("description").notNull(), + date: date("date").notNull(), + type: specialType("type").notNull(), }); From 85bafd18ef1cf6800b54aee65fbbb45d3d43e794 Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Sat, 22 Nov 2025 17:26:25 -0500 Subject: [PATCH 57/65] chore: remove redundant paren --- src/db/updateLocation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/updateLocation.ts b/src/db/updateLocation.ts index 96671a6..78ed001 100644 --- a/src/db/updateLocation.ts +++ b/src/db/updateLocation.ts @@ -51,7 +51,7 @@ export async function addLocationDataToDb(db: DBType, location: ILocation) { const todayAsSQLString = `${location.today.year}-${pad( location.today.month - )}-${pad(location.today.day)})`; + )}-${pad(location.today.day)}`; // add specials await tx .delete(specialsTable) From 8be7e445122f2499883ffc9599a1715241ba795a Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Sat, 22 Nov 2025 17:47:49 -0500 Subject: [PATCH 58/65] test: add two more tests --- src/types.ts | 2 +- tests/database.test.ts | 92 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 87 insertions(+), 7 deletions(-) diff --git a/src/types.ts b/src/types.ts index 903de90..03e48de 100644 --- a/src/types.ts +++ b/src/types.ts @@ -17,7 +17,7 @@ export interface ILocation { acceptsOnlineOrders: boolean; /** Assuming these times fall after today */ times: IFullTimeRange[]; - /** used when figuring out which time entries to clear in the database. (we can't just look at `times` directly to figure that out, because it might as well be empty) */ + /** used when figuring out which time entries to clear in the database. (we can't just look at `times` directly to figure that out, because it might as well be empty) (also, technically if this data is in the future, times won't be cleared properly. we hope that isn't the case though) */ today: IDate; todaysSpecials: ISpecial[] | undefined; todaysSoups: ISpecial[] | undefined; diff --git a/tests/database.test.ts b/tests/database.test.ts index 6c3edaa..09b72fb 100644 --- a/tests/database.test.ts +++ b/tests/database.test.ts @@ -558,7 +558,91 @@ describe("DB", () => { ]) ); }); - dbTest.concurrent("stub", async ({ ctx: { db } }) => {}); // just for reference + dbTest.concurrent( + "adding times for the next day", + async ({ ctx: { db } }) => { + const id1 = await addLocationDataToDb(db, { + ...locationIn, + today: { + year: 2025, + month: 1, + day: 1, + }, + times: [ + parseTime("1/1/25", "7:00 AM", "2:00 PM"), + parseTime("1/2/25", "7:00 AM", "2:00 PM"), + parseTime("1/3/25", "7:00 AM", "2:00 PM"), + ], + }); + const id2 = await addLocationDataToDb(db, { + ...locationIn, + today: { + year: 2025, + month: 1, + day: 2, + }, + times: [parseTime("1/3/25", "7:00 AM", "7:00 PM")], + }); + expect(id1).toEqual(id2); + expect(await getAllLocationsFromDB(db, parseDate("1/1/25"))).toEqual([ + { + ...locationOut, + id: id1, + times: [ + { + start: timeToUnixTimestamp("1/1/25 7:00 AM"), + end: timeToUnixTimestamp("1/1/25 2:00 PM"), + }, + { + start: timeToUnixTimestamp("1/3/25 7:00 AM"), + end: timeToUnixTimestamp("1/3/25 7:00 PM"), + }, + ], + }, + ]); + } + ); + dbTest.concurrent("time merging", async ({ ctx: { db } }) => { + const id = await addLocationDataToDb(db, { + ...locationIn, + today: { + year: 2025, + month: 1, + day: 1, + }, + times: [ + parseTime("1/1/25", "7:00 AM", "2:00 PM"), + parseTime("1/1/25", "2:00 AM", "12:00 PM"), + parseTime("1/1/25", "2:01 PM", "9:00 PM"), + parseTime("1/1/25", "9:00 PM", "11:59 PM"), + parseTime("1/2/25", "12:00 AM", "11:59 PM"), + ], + }); + + expect(await getAllLocationsFromDB(db, parseDate("1/1/25"))).toEqual([ + { + ...locationOut, + id: id, + times: [ + { + start: timeToUnixTimestamp("1/1/25 2:00 AM"), + end: timeToUnixTimestamp("1/2/25 11:59 PM"), + }, + ], + }, + ]); + }); + dbTest.concurrent.skip("stub", async ({ ctx: { db } }) => { + const id = await addLocationDataToDb(db, { + ...locationIn, + }); + + expect(await getAllLocationsFromDB(db, parseDate("1/1/25"))).toEqual([ + { + ...locationOut, + }, + ]); + }); // just for reference }); /** @@ -614,11 +698,7 @@ function parseTime(date: string, startTime: string, endTime: string) { * @returns timestamp, when datetime is interpreted in EST */ function timeToUnixTimestamp(datetime: string) { - const parsedDate = DateTime.fromFormat(datetime, "M/d/yy h:mm a", { - zone: "America/New_York", - }); - if (!parsedDate.isValid) throw new Error(`Malformed date string ${datetime}`); - return parsedDate.toMillis(); + return +new Date(datetime); } const wait = (ms: number) => new Promise((re) => setTimeout(re, ms)); From d9b38ef8444db1298fa6f17271ef00b3befc58ac Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Sat, 22 Nov 2025 17:51:12 -0500 Subject: [PATCH 59/65] chore: add additional today param check --- tests/mockTimings.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/mockTimings.ts b/tests/mockTimings.ts index 465af2d..87cf2b2 100644 --- a/tests/mockTimings.ts +++ b/tests/mockTimings.ts @@ -43,6 +43,11 @@ export async function queryParserAndAssertTimingsCorrect( }) .sort(_sort) ); + expect(result[0]?.today).toEqual({ + year: rootDay.year, + month: rootDay.month, + day: rootDay.day, + }); } export function setUpTimingTest(timeRows: Record) { From a40b010352761b1ef51f57e84f8d119def94bc9d Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Sat, 22 Nov 2025 17:56:28 -0500 Subject: [PATCH 60/65] fix: date timezone thing for tests --- tests/database.test.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/database.test.ts b/tests/database.test.ts index 09b72fb..4c17ce3 100644 --- a/tests/database.test.ts +++ b/tests/database.test.ts @@ -698,7 +698,11 @@ function parseTime(date: string, startTime: string, endTime: string) { * @returns timestamp, when datetime is interpreted in EST */ function timeToUnixTimestamp(datetime: string) { - return +new Date(datetime); + const parsedDate = DateTime.fromFormat(datetime, "M/d/yy h:mm a", { + zone: "America/New_York", // important! enforces timezone + }); + if (!parsedDate.isValid) throw new Error(`Malformed date string ${datetime}`); + return parsedDate.toMillis(); } const wait = (ms: number) => new Promise((re) => setTimeout(re, ms)); From 1252a5737154b61befbd813abb574ca0725abdb8 Mon Sep 17 00:00:00 2001 From: aisha Date: Thu, 4 Dec 2025 13:22:17 +0300 Subject: [PATCH 61/65] Add /api/report-location endpoint --- src/server.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/server.ts b/src/server.ts index 624110c..23338cd 100644 --- a/src/server.ts +++ b/src/server.ts @@ -98,6 +98,22 @@ app.post( }), } ); +// backend for reporting locations // +app.post( + "/api/report-location", + async ({ body: { locationId, message } }) => { + const slackMsg = `Location Reported\nLocation ID: ${locationId}\nMessage: ${message}`; + await notifySlack(slackMsg, env.SLACK_BACKEND_WEBHOOK_URL); + + return { success: true }; + }, + { + body: t.Object({ + locationId: t.Number(), + message: t.String(), + }), + } +); setInterval(() => { reload().catch( From fca929a14161faec662b4728e8908a9d85063381 Mon Sep 17 00:00:00 2001 From: aisha Date: Thu, 4 Dec 2025 13:23:51 +0300 Subject: [PATCH 62/65] Add bun.lock after installing dependencies for report-location feature --- bun.lock | 1218 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1218 insertions(+) create mode 100644 bun.lock diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..e746951 --- /dev/null +++ b/bun.lock @@ -0,0 +1,1218 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "dining-api", + "dependencies": { + "@elysiajs/cors": "1.3.3", + "@elysiajs/node": "1.3.0", + "@elysiajs/openapi": "^1.4.11", + "@types/express": "^5.0.3", + "axios": "^1.10.0", + "cheerio": "1.0.0-rc.12", + "drizzle-kit": "^0.31.4", + "drizzle-orm": "^0.44.4", + "elysia": "1.4.15", + "express": "^5.1.0", + "luxon": "^3.7.2", + "pg": "^8.16.3", + "vite-tsconfig-paths": "^5.1.4", + "zod": "^3.25.67", + }, + "devDependencies": { + "@babel/core": "^7.25.2", + "@babel/preset-env": "^7.25.4", + "@babel/preset-typescript": "^7.24.7", + "@rollup/plugin-typescript": "^12.1.4", + "@testcontainers/postgresql": "^11.8.0", + "@types/luxon": "^3.7.1", + "@types/node": "^24.0.14", + "@types/pg": "^8.15.5", + "@vitest/coverage-v8": "4.0.8", + "dotenv": "^17.2.0", + "dotenv-cli": "^8.0.0", + "npm-check-updates": "^18.0.1", + "rollup": "^4.45.1", + "testcontainers": "^11.8.0", + "tslib": "^2.8.1", + "tsx": "^4.20.3", + "vitest": "^4.0.8", + }, + "peerDependencies": { + "typescript": "^5.8.3", + }, + }, + }, + "packages": { + "@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "7.27.1", "js-tokens": "4.0.0", "picocolors": "1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], + + "@babel/compat-data": ["@babel/compat-data@7.28.4", "", {}, "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw=="], + + "@babel/core": ["@babel/core@7.28.4", "", { "dependencies": { "@babel/code-frame": "7.27.1", "@babel/generator": "7.28.3", "@babel/helper-compilation-targets": "7.27.2", "@babel/helper-module-transforms": "7.28.3", "@babel/helpers": "7.28.4", "@babel/parser": "7.28.4", "@babel/template": "7.27.2", "@babel/traverse": "7.28.4", "@babel/types": "7.28.4", "@jridgewell/remapping": "2.3.5", "convert-source-map": "2.0.0", "debug": "4.4.3", "gensync": "1.0.0-beta.2", "json5": "2.2.3", "semver": "6.3.1" } }, "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA=="], + + "@babel/generator": ["@babel/generator@7.28.3", "", { "dependencies": { "@babel/parser": "7.28.4", "@babel/types": "7.28.4", "@jridgewell/gen-mapping": "0.3.13", "@jridgewell/trace-mapping": "0.3.31", "jsesc": "3.1.0" } }, "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw=="], + + "@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "7.28.4" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], + + "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "7.28.4", "@babel/helper-validator-option": "7.27.1", "browserslist": "4.26.2", "lru-cache": "5.1.1", "semver": "6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="], + + "@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.3", "", { "dependencies": { "@babel/helper-annotate-as-pure": "7.27.3", "@babel/helper-member-expression-to-functions": "7.27.1", "@babel/helper-optimise-call-expression": "7.27.1", "@babel/helper-replace-supers": "7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "7.27.1", "@babel/traverse": "7.28.4", "semver": "6.3.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg=="], + + "@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "7.27.3", "regexpu-core": "6.3.1", "semver": "6.3.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ=="], + + "@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.6.5", "", { "dependencies": { "@babel/helper-compilation-targets": "7.27.2", "@babel/helper-plugin-utils": "7.27.1", "debug": "4.4.3", "lodash.debounce": "4.0.8", "resolve": "1.22.10" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg=="], + + "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="], + + "@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.27.1", "", { "dependencies": { "@babel/traverse": "7.28.4", "@babel/types": "7.28.4" } }, "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA=="], + + "@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "7.28.4", "@babel/types": "7.28.4" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="], + + "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.3", "", { "dependencies": { "@babel/helper-module-imports": "7.27.1", "@babel/helper-validator-identifier": "7.27.1", "@babel/traverse": "7.28.4" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw=="], + + "@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "7.28.4" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="], + + "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.27.1", "", {}, "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw=="], + + "@babel/helper-remap-async-to-generator": ["@babel/helper-remap-async-to-generator@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "7.27.3", "@babel/helper-wrap-function": "7.28.3", "@babel/traverse": "7.28.4" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA=="], + + "@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "7.27.1", "@babel/helper-optimise-call-expression": "7.27.1", "@babel/traverse": "7.28.4" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="], + + "@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "7.28.4", "@babel/types": "7.28.4" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="], + + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.27.1", "", {}, "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="], + + "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="], + + "@babel/helper-wrap-function": ["@babel/helper-wrap-function@7.28.3", "", { "dependencies": { "@babel/template": "7.27.2", "@babel/traverse": "7.28.4", "@babel/types": "7.28.4" } }, "sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g=="], + + "@babel/helpers": ["@babel/helpers@7.28.4", "", { "dependencies": { "@babel/template": "7.27.2", "@babel/types": "7.28.4" } }, "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w=="], + + "@babel/parser": ["@babel/parser@7.28.4", "", { "dependencies": { "@babel/types": "7.28.4" }, "bin": "./bin/babel-parser.js" }, "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg=="], + + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": ["@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1", "@babel/traverse": "7.28.4" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA=="], + + "@babel/plugin-bugfix-safari-class-field-initializer-scope": ["@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA=="], + + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ["@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA=="], + + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ["@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "7.27.1", "@babel/plugin-transform-optional-chaining": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw=="], + + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": ["@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.3", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1", "@babel/traverse": "7.28.4" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw=="], + + "@babel/plugin-proposal-private-property-in-object": ["@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2", "", { "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w=="], + + "@babel/plugin-syntax-import-assertions": ["@babel/plugin-syntax-import-assertions@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg=="], + + "@babel/plugin-syntax-import-attributes": ["@babel/plugin-syntax-import-attributes@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww=="], + + "@babel/plugin-syntax-jsx": ["@babel/plugin-syntax-jsx@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w=="], + + "@babel/plugin-syntax-typescript": ["@babel/plugin-syntax-typescript@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ=="], + + "@babel/plugin-syntax-unicode-sets-regex": ["@babel/plugin-syntax-unicode-sets-regex@7.18.6", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "7.27.1", "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg=="], + + "@babel/plugin-transform-arrow-functions": ["@babel/plugin-transform-arrow-functions@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA=="], + + "@babel/plugin-transform-async-generator-functions": ["@babel/plugin-transform-async-generator-functions@7.28.0", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1", "@babel/helper-remap-async-to-generator": "7.27.1", "@babel/traverse": "7.28.4" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q=="], + + "@babel/plugin-transform-async-to-generator": ["@babel/plugin-transform-async-to-generator@7.27.1", "", { "dependencies": { "@babel/helper-module-imports": "7.27.1", "@babel/helper-plugin-utils": "7.27.1", "@babel/helper-remap-async-to-generator": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA=="], + + "@babel/plugin-transform-block-scoped-functions": ["@babel/plugin-transform-block-scoped-functions@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg=="], + + "@babel/plugin-transform-block-scoping": ["@babel/plugin-transform-block-scoping@7.28.4", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-1yxmvN0MJHOhPVmAsmoW5liWwoILobu/d/ShymZmj867bAdxGbehIrew1DuLpw2Ukv+qDSSPQdYW1dLNE7t11A=="], + + "@babel/plugin-transform-class-properties": ["@babel/plugin-transform-class-properties@7.27.1", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "7.28.3", "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA=="], + + "@babel/plugin-transform-class-static-block": ["@babel/plugin-transform-class-static-block@7.28.3", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "7.28.3", "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg=="], + + "@babel/plugin-transform-classes": ["@babel/plugin-transform-classes@7.28.4", "", { "dependencies": { "@babel/helper-annotate-as-pure": "7.27.3", "@babel/helper-compilation-targets": "7.27.2", "@babel/helper-globals": "7.28.0", "@babel/helper-plugin-utils": "7.27.1", "@babel/helper-replace-supers": "7.27.1", "@babel/traverse": "7.28.4" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA=="], + + "@babel/plugin-transform-computed-properties": ["@babel/plugin-transform-computed-properties@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1", "@babel/template": "7.27.2" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw=="], + + "@babel/plugin-transform-destructuring": ["@babel/plugin-transform-destructuring@7.28.0", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1", "@babel/traverse": "7.28.4" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A=="], + + "@babel/plugin-transform-dotall-regex": ["@babel/plugin-transform-dotall-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "7.27.1", "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw=="], + + "@babel/plugin-transform-duplicate-keys": ["@babel/plugin-transform-duplicate-keys@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q=="], + + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": ["@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "7.27.1", "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ=="], + + "@babel/plugin-transform-dynamic-import": ["@babel/plugin-transform-dynamic-import@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A=="], + + "@babel/plugin-transform-explicit-resource-management": ["@babel/plugin-transform-explicit-resource-management@7.28.0", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1", "@babel/plugin-transform-destructuring": "7.28.0" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ=="], + + "@babel/plugin-transform-exponentiation-operator": ["@babel/plugin-transform-exponentiation-operator@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ=="], + + "@babel/plugin-transform-export-namespace-from": ["@babel/plugin-transform-export-namespace-from@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ=="], + + "@babel/plugin-transform-for-of": ["@babel/plugin-transform-for-of@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw=="], + + "@babel/plugin-transform-function-name": ["@babel/plugin-transform-function-name@7.27.1", "", { "dependencies": { "@babel/helper-compilation-targets": "7.27.2", "@babel/helper-plugin-utils": "7.27.1", "@babel/traverse": "7.28.4" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ=="], + + "@babel/plugin-transform-json-strings": ["@babel/plugin-transform-json-strings@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q=="], + + "@babel/plugin-transform-literals": ["@babel/plugin-transform-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA=="], + + "@babel/plugin-transform-logical-assignment-operators": ["@babel/plugin-transform-logical-assignment-operators@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw=="], + + "@babel/plugin-transform-member-expression-literals": ["@babel/plugin-transform-member-expression-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ=="], + + "@babel/plugin-transform-modules-amd": ["@babel/plugin-transform-modules-amd@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "7.28.3", "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA=="], + + "@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "7.28.3", "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw=="], + + "@babel/plugin-transform-modules-systemjs": ["@babel/plugin-transform-modules-systemjs@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "7.28.3", "@babel/helper-plugin-utils": "7.27.1", "@babel/helper-validator-identifier": "7.27.1", "@babel/traverse": "7.28.4" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA=="], + + "@babel/plugin-transform-modules-umd": ["@babel/plugin-transform-modules-umd@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "7.28.3", "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w=="], + + "@babel/plugin-transform-named-capturing-groups-regex": ["@babel/plugin-transform-named-capturing-groups-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "7.27.1", "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng=="], + + "@babel/plugin-transform-new-target": ["@babel/plugin-transform-new-target@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ=="], + + "@babel/plugin-transform-nullish-coalescing-operator": ["@babel/plugin-transform-nullish-coalescing-operator@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA=="], + + "@babel/plugin-transform-numeric-separator": ["@babel/plugin-transform-numeric-separator@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw=="], + + "@babel/plugin-transform-object-rest-spread": ["@babel/plugin-transform-object-rest-spread@7.28.4", "", { "dependencies": { "@babel/helper-compilation-targets": "7.27.2", "@babel/helper-plugin-utils": "7.27.1", "@babel/plugin-transform-destructuring": "7.28.0", "@babel/plugin-transform-parameters": "7.27.7", "@babel/traverse": "7.28.4" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew=="], + + "@babel/plugin-transform-object-super": ["@babel/plugin-transform-object-super@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1", "@babel/helper-replace-supers": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng=="], + + "@babel/plugin-transform-optional-catch-binding": ["@babel/plugin-transform-optional-catch-binding@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q=="], + + "@babel/plugin-transform-optional-chaining": ["@babel/plugin-transform-optional-chaining@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg=="], + + "@babel/plugin-transform-parameters": ["@babel/plugin-transform-parameters@7.27.7", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg=="], + + "@babel/plugin-transform-private-methods": ["@babel/plugin-transform-private-methods@7.27.1", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "7.28.3", "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA=="], + + "@babel/plugin-transform-private-property-in-object": ["@babel/plugin-transform-private-property-in-object@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "7.27.3", "@babel/helper-create-class-features-plugin": "7.28.3", "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ=="], + + "@babel/plugin-transform-property-literals": ["@babel/plugin-transform-property-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ=="], + + "@babel/plugin-transform-regenerator": ["@babel/plugin-transform-regenerator@7.28.4", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA=="], + + "@babel/plugin-transform-regexp-modifiers": ["@babel/plugin-transform-regexp-modifiers@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "7.27.1", "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA=="], + + "@babel/plugin-transform-reserved-words": ["@babel/plugin-transform-reserved-words@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw=="], + + "@babel/plugin-transform-shorthand-properties": ["@babel/plugin-transform-shorthand-properties@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ=="], + + "@babel/plugin-transform-spread": ["@babel/plugin-transform-spread@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q=="], + + "@babel/plugin-transform-sticky-regex": ["@babel/plugin-transform-sticky-regex@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g=="], + + "@babel/plugin-transform-template-literals": ["@babel/plugin-transform-template-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg=="], + + "@babel/plugin-transform-typeof-symbol": ["@babel/plugin-transform-typeof-symbol@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw=="], + + "@babel/plugin-transform-typescript": ["@babel/plugin-transform-typescript@7.28.0", "", { "dependencies": { "@babel/helper-annotate-as-pure": "7.27.3", "@babel/helper-create-class-features-plugin": "7.28.3", "@babel/helper-plugin-utils": "7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "7.27.1", "@babel/plugin-syntax-typescript": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg=="], + + "@babel/plugin-transform-unicode-escapes": ["@babel/plugin-transform-unicode-escapes@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg=="], + + "@babel/plugin-transform-unicode-property-regex": ["@babel/plugin-transform-unicode-property-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "7.27.1", "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q=="], + + "@babel/plugin-transform-unicode-regex": ["@babel/plugin-transform-unicode-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "7.27.1", "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw=="], + + "@babel/plugin-transform-unicode-sets-regex": ["@babel/plugin-transform-unicode-sets-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "7.27.1", "@babel/helper-plugin-utils": "7.27.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw=="], + + "@babel/preset-env": ["@babel/preset-env@7.28.3", "", { "dependencies": { "@babel/compat-data": "7.28.4", "@babel/helper-compilation-targets": "7.27.2", "@babel/helper-plugin-utils": "7.27.1", "@babel/helper-validator-option": "7.27.1", "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "7.27.1", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "7.27.1", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "7.27.1", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "7.27.1", "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "7.28.3", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-import-assertions": "7.27.1", "@babel/plugin-syntax-import-attributes": "7.27.1", "@babel/plugin-syntax-unicode-sets-regex": "7.18.6", "@babel/plugin-transform-arrow-functions": "7.27.1", "@babel/plugin-transform-async-generator-functions": "7.28.0", "@babel/plugin-transform-async-to-generator": "7.27.1", "@babel/plugin-transform-block-scoped-functions": "7.27.1", "@babel/plugin-transform-block-scoping": "7.28.4", "@babel/plugin-transform-class-properties": "7.27.1", "@babel/plugin-transform-class-static-block": "7.28.3", "@babel/plugin-transform-classes": "7.28.4", "@babel/plugin-transform-computed-properties": "7.27.1", "@babel/plugin-transform-destructuring": "7.28.0", "@babel/plugin-transform-dotall-regex": "7.27.1", "@babel/plugin-transform-duplicate-keys": "7.27.1", "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "7.27.1", "@babel/plugin-transform-dynamic-import": "7.27.1", "@babel/plugin-transform-explicit-resource-management": "7.28.0", "@babel/plugin-transform-exponentiation-operator": "7.27.1", "@babel/plugin-transform-export-namespace-from": "7.27.1", "@babel/plugin-transform-for-of": "7.27.1", "@babel/plugin-transform-function-name": "7.27.1", "@babel/plugin-transform-json-strings": "7.27.1", "@babel/plugin-transform-literals": "7.27.1", "@babel/plugin-transform-logical-assignment-operators": "7.27.1", "@babel/plugin-transform-member-expression-literals": "7.27.1", "@babel/plugin-transform-modules-amd": "7.27.1", "@babel/plugin-transform-modules-commonjs": "7.27.1", "@babel/plugin-transform-modules-systemjs": "7.27.1", "@babel/plugin-transform-modules-umd": "7.27.1", "@babel/plugin-transform-named-capturing-groups-regex": "7.27.1", "@babel/plugin-transform-new-target": "7.27.1", "@babel/plugin-transform-nullish-coalescing-operator": "7.27.1", "@babel/plugin-transform-numeric-separator": "7.27.1", "@babel/plugin-transform-object-rest-spread": "7.28.4", "@babel/plugin-transform-object-super": "7.27.1", "@babel/plugin-transform-optional-catch-binding": "7.27.1", "@babel/plugin-transform-optional-chaining": "7.27.1", "@babel/plugin-transform-parameters": "7.27.7", "@babel/plugin-transform-private-methods": "7.27.1", "@babel/plugin-transform-private-property-in-object": "7.27.1", "@babel/plugin-transform-property-literals": "7.27.1", "@babel/plugin-transform-regenerator": "7.28.4", "@babel/plugin-transform-regexp-modifiers": "7.27.1", "@babel/plugin-transform-reserved-words": "7.27.1", "@babel/plugin-transform-shorthand-properties": "7.27.1", "@babel/plugin-transform-spread": "7.27.1", "@babel/plugin-transform-sticky-regex": "7.27.1", "@babel/plugin-transform-template-literals": "7.27.1", "@babel/plugin-transform-typeof-symbol": "7.27.1", "@babel/plugin-transform-unicode-escapes": "7.27.1", "@babel/plugin-transform-unicode-property-regex": "7.27.1", "@babel/plugin-transform-unicode-regex": "7.27.1", "@babel/plugin-transform-unicode-sets-regex": "7.27.1", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "0.4.14", "babel-plugin-polyfill-corejs3": "0.13.0", "babel-plugin-polyfill-regenerator": "0.6.5", "core-js-compat": "3.45.1", "semver": "6.3.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-ROiDcM+GbYVPYBOeCR6uBXKkQpBExLl8k9HO1ygXEyds39j+vCCsjmj7S8GOniZQlEs81QlkdJZe76IpLSiqpg=="], + + "@babel/preset-modules": ["@babel/preset-modules@0.1.6-no-external-plugins", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1", "@babel/types": "7.28.4", "esutils": "2.0.3" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA=="], + + "@babel/preset-typescript": ["@babel/preset-typescript@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "7.27.1", "@babel/helper-validator-option": "7.27.1", "@babel/plugin-syntax-jsx": "7.27.1", "@babel/plugin-transform-modules-commonjs": "7.27.1", "@babel/plugin-transform-typescript": "7.28.0" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ=="], + + "@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "7.27.1", "@babel/parser": "7.28.4", "@babel/types": "7.28.4" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="], + + "@babel/traverse": ["@babel/traverse@7.28.4", "", { "dependencies": { "@babel/code-frame": "7.27.1", "@babel/generator": "7.28.3", "@babel/helper-globals": "7.28.0", "@babel/parser": "7.28.4", "@babel/template": "7.27.2", "@babel/types": "7.28.4", "debug": "4.4.3" } }, "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ=="], + + "@babel/types": ["@babel/types@7.28.4", "", { "dependencies": { "@babel/helper-string-parser": "7.27.1", "@babel/helper-validator-identifier": "7.27.1" } }, "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q=="], + + "@balena/dockerignore": ["@balena/dockerignore@1.0.2", "", {}, "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q=="], + + "@bcoe/v8-coverage": ["@bcoe/v8-coverage@1.0.2", "", {}, "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA=="], + + "@borewit/text-codec": ["@borewit/text-codec@0.1.1", "", {}, "sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA=="], + + "@drizzle-team/brocli": ["@drizzle-team/brocli@0.10.2", "", {}, "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w=="], + + "@elysiajs/cors": ["@elysiajs/cors@1.3.3", "", { "peerDependencies": { "elysia": "1.4.15" } }, "sha512-mYIU6PyMM6xIJuj7d27Vt0/wuzVKIEnFPjcvlkyd7t/m9xspAG37cwNjFxVOnyvY43oOd2I/oW2DB85utXpA2Q=="], + + "@elysiajs/node": ["@elysiajs/node@1.3.0", "", { "dependencies": { "@hono/node-server": "1.19.3" }, "peerDependencies": { "elysia": "1.4.15" } }, "sha512-AA9rnL/FOBklxtJjFpUDBVZLuiKddDaV1KgnKawHzj5VgN99SzE+V0h1MOhp+8jlo2KQQJO/3aD3upyGgrfohQ=="], + + "@elysiajs/openapi": ["@elysiajs/openapi@1.4.11", "", { "peerDependencies": { "elysia": "1.4.15" } }, "sha512-d75bMxYJpN6qSDi/z9L1S7SLk1S/8Px+cTb3W2lrYzU8uQ5E0kXdy1oOMJEfTyVsz3OA19NP9KNxE7ztSbLBLg=="], + + "@esbuild-kit/core-utils": ["@esbuild-kit/core-utils@3.3.2", "", { "dependencies": { "esbuild": "0.18.20", "source-map-support": "0.5.21" } }, "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ=="], + + "@esbuild-kit/esm-loader": ["@esbuild-kit/esm-loader@2.6.5", "", { "dependencies": { "@esbuild-kit/core-utils": "3.3.2", "get-tsconfig": "4.10.1" } }, "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA=="], + + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.10", "", { "os": "aix", "cpu": "ppc64" }, "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.25.10", "", { "os": "android", "cpu": "arm" }, "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.10", "", { "os": "android", "cpu": "arm64" }, "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.25.10", "", { "os": "android", "cpu": "x64" }, "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.10", "", { "os": "darwin", "cpu": "arm64" }, "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.10", "", { "os": "darwin", "cpu": "x64" }, "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.10", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.10", "", { "os": "freebsd", "cpu": "x64" }, "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.10", "", { "os": "linux", "cpu": "arm" }, "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.10", "", { "os": "linux", "cpu": "ia32" }, "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.10", "", { "os": "linux", "cpu": "none" }, "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.10", "", { "os": "linux", "cpu": "none" }, "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.10", "", { "os": "linux", "cpu": "ppc64" }, "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.10", "", { "os": "linux", "cpu": "none" }, "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.10", "", { "os": "linux", "cpu": "s390x" }, "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.10", "", { "os": "linux", "cpu": "x64" }, "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA=="], + + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.10", "", { "os": "none", "cpu": "arm64" }, "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.10", "", { "os": "none", "cpu": "x64" }, "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig=="], + + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.10", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.10", "", { "os": "openbsd", "cpu": "x64" }, "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw=="], + + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.10", "", { "os": "none", "cpu": "arm64" }, "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.10", "", { "os": "sunos", "cpu": "x64" }, "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.10", "", { "os": "win32", "cpu": "arm64" }, "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw=="], + + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.10", "", { "os": "win32", "cpu": "ia32" }, "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.10", "", { "os": "win32", "cpu": "x64" }, "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw=="], + + "@grpc/grpc-js": ["@grpc/grpc-js@1.14.1", "", { "dependencies": { "@grpc/proto-loader": "0.8.0", "@js-sdsl/ordered-map": "4.4.2" } }, "sha512-sPxgEWtPUR3EnRJCEtbGZG2iX8LQDUls2wUS3o27jg07KqJFMq6YDeWvMo1wfpmy3rqRdS0rivpLwhqQtEyCuQ=="], + + "@grpc/proto-loader": ["@grpc/proto-loader@0.7.15", "", { "dependencies": { "lodash.camelcase": "4.3.0", "long": "5.3.2", "protobufjs": "7.5.4", "yargs": "17.7.2" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" } }, "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ=="], + + "@hono/node-server": ["@hono/node-server@1.19.3", "", { "peerDependencies": { "hono": "4.9.8" } }, "sha512-Fjyxfux0rMPXMSob79OmddfpK5ArJa2xLkLCV+zamHkbeXQtSNKOi0keiBKyHZ/hCRKjigjmKGp4AJnDFq8PUw=="], + + "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "5.1.2", "string-width-cjs": "npm:string-width@4.2.3", "strip-ansi": "7.1.2", "strip-ansi-cjs": "npm:strip-ansi@6.0.1", "wrap-ansi": "8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="], + + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "1.5.5", "@jridgewell/trace-mapping": "0.3.31" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], + + "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "0.3.13", "@jridgewell/trace-mapping": "0.3.31" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "3.1.2", "@jridgewell/sourcemap-codec": "1.5.5" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + + "@js-sdsl/ordered-map": ["@js-sdsl/ordered-map@4.4.2", "", {}, "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw=="], + + "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], + + "@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="], + + "@protobufjs/base64": ["@protobufjs/base64@1.1.2", "", {}, "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="], + + "@protobufjs/codegen": ["@protobufjs/codegen@2.0.4", "", {}, "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="], + + "@protobufjs/eventemitter": ["@protobufjs/eventemitter@1.1.0", "", {}, "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="], + + "@protobufjs/fetch": ["@protobufjs/fetch@1.1.0", "", { "dependencies": { "@protobufjs/aspromise": "1.1.2", "@protobufjs/inquire": "1.1.0" } }, "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ=="], + + "@protobufjs/float": ["@protobufjs/float@1.0.2", "", {}, "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="], + + "@protobufjs/inquire": ["@protobufjs/inquire@1.1.0", "", {}, "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="], + + "@protobufjs/path": ["@protobufjs/path@1.1.2", "", {}, "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="], + + "@protobufjs/pool": ["@protobufjs/pool@1.1.0", "", {}, "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="], + + "@protobufjs/utf8": ["@protobufjs/utf8@1.1.0", "", {}, "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="], + + "@rollup/plugin-typescript": ["@rollup/plugin-typescript@12.1.4", "", { "dependencies": { "@rollup/pluginutils": "5.3.0", "resolve": "1.22.10" }, "optionalDependencies": { "rollup": "4.52.0", "tslib": "2.8.1" }, "peerDependencies": { "typescript": "5.9.2" } }, "sha512-s5Hx+EtN60LMlDBvl5f04bEiFZmAepk27Q+mr85L/00zPDn1jtzlTV6FWn81MaIwqfWzKxmOJrBWHU6vtQyedQ=="], + + "@rollup/pluginutils": ["@rollup/pluginutils@5.3.0", "", { "dependencies": { "@types/estree": "1.0.8", "estree-walker": "2.0.2", "picomatch": "4.0.3" }, "optionalDependencies": { "rollup": "4.52.0" } }, "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q=="], + + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.52.0", "", { "os": "android", "cpu": "arm" }, "sha512-VxDYCDqOaR7NXzAtvRx7G1u54d2kEHopb28YH/pKzY6y0qmogP3gG7CSiWsq9WvDFxOQMpNEyjVAHZFXfH3o/A=="], + + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.52.0", "", { "os": "android", "cpu": "arm64" }, "sha512-pqDirm8koABIKvzL59YI9W9DWbRlTX7RWhN+auR8HXJxo89m4mjqbah7nJZjeKNTNYopqL+yGg+0mhCpf3xZtQ=="], + + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.52.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-YCdWlY/8ltN6H78HnMsRHYlPiKvqKagBP1r+D7SSylxX+HnsgXGCmLiV3Y4nSyY9hW8qr8U9LDUx/Lo7M6MfmQ=="], + + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.52.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-z4nw6y1j+OOSGzuVbSWdIp1IUks9qNw4dc7z7lWuWDKojY38VMWBlEN7F9jk5UXOkUcp97vA1N213DF+Lz8BRg=="], + + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.52.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-Q/dv9Yvyr5rKlK8WQJZVrp5g2SOYeZUs9u/t2f9cQ2E0gJjYB/BWoedXfUT0EcDJefi2zzVfhcOj8drWCzTviw=="], + + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.52.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-kdBsLs4Uile/fbjZVvCRcKB4q64R+1mUq0Yd7oU1CMm1Av336ajIFqNFovByipciuUQjBCPMxwJhCgfG2re3rg=="], + + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.52.0", "", { "os": "linux", "cpu": "arm" }, "sha512-aL6hRwu0k7MTUESgkg7QHY6CoqPgr6gdQXRJI1/VbFlUMwsSzPGSR7sG5d+MCbYnJmJwThc2ol3nixj1fvI/zQ=="], + + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.52.0", "", { "os": "linux", "cpu": "arm" }, "sha512-BTs0M5s1EJejgIBJhCeiFo7GZZ2IXWkFGcyZhxX4+8usnIo5Mti57108vjXFIQmmJaRyDwmV59Tw64Ap1dkwMw=="], + + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.52.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-uj672IVOU9m08DBGvoPKPi/J8jlVgjh12C9GmjjBxCTQc3XtVmRkRKyeHSmIKQpvJ7fIm1EJieBUcnGSzDVFyw=="], + + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.52.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-/+IVbeDMDCtB/HP/wiWsSzduD10SEGzIZX2945KSgZRNi4TSkjHqRJtNTVtVb8IRwhJ65ssI56krlLik+zFWkw=="], + + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.52.0", "", { "os": "linux", "cpu": "none" }, "sha512-U1vVzvSWtSMWKKrGoROPBXMh3Vwn93TA9V35PldokHGqiUbF6erSzox/5qrSMKp6SzakvyjcPiVF8yB1xKr9Pg=="], + + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.52.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-X/4WfuBAdQRH8cK3DYl8zC00XEE6aM472W+QCycpQJeLWVnHfkv7RyBFVaTqNUMsTgIX8ihMjCvFF9OUgeABzw=="], + + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.52.0", "", { "os": "linux", "cpu": "none" }, "sha512-xIRYc58HfWDBZoLmWfWXg2Sq8VCa2iJ32B7mqfWnkx5mekekl0tMe7FHpY8I72RXEcUkaWawRvl3qA55og+cwQ=="], + + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.52.0", "", { "os": "linux", "cpu": "none" }, "sha512-mbsoUey05WJIOz8U1WzNdf+6UMYGwE3fZZnQqsM22FZ3wh1N887HT6jAOjXs6CNEK3Ntu2OBsyQDXfIjouI4dw=="], + + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.52.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-qP6aP970bucEi5KKKR4AuPFd8aTx9EF6BvutvYxmZuWLJHmnq4LvBfp0U+yFDMGwJ+AIJEH5sIP+SNypauMWzg=="], + + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.52.0", "", { "os": "linux", "cpu": "x64" }, "sha512-nmSVN+F2i1yKZ7rJNKO3G7ZzmxJgoQBQZ/6c4MuS553Grmr7WqR7LLDcYG53Z2m9409z3JLt4sCOhLdbKQ3HmA=="], + + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.52.0", "", { "os": "linux", "cpu": "x64" }, "sha512-2d0qRo33G6TfQVjaMR71P+yJVGODrt5V6+T0BDYH4EMfGgdC/2HWDVjSSFw888GSzAZUwuska3+zxNUCDco6rQ=="], + + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.52.0", "", { "os": "none", "cpu": "arm64" }, "sha512-A1JalX4MOaFAAyGgpO7XP5khquv/7xKzLIyLmhNrbiCxWpMlnsTYr8dnsWM7sEeotNmxvSOEL7F65j0HXFcFsw=="], + + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.52.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-YQugafP/rH0eOOHGjmNgDURrpYHrIX0yuojOI8bwCyXwxC9ZdTd3vYkmddPX0oHONLXu9Rb1dDmT0VNpjkzGGw=="], + + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.52.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-zYdUYhi3Qe2fndujBqL5FjAFzvNeLxtIqfzNEVKD1I7C37/chv1VxhscWSQHTNfjPCrBFQMnynwA3kpZpZ8w4A=="], + + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.52.0", "", { "os": "win32", "cpu": "x64" }, "sha512-fGk03kQylNaCOQ96HDMeT7E2n91EqvCDd3RwvT5k+xNdFCeMGnj5b5hEgTGrQuyidqSsD3zJDQ21QIaxXqTBJw=="], + + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.52.0", "", { "os": "win32", "cpu": "x64" }, "sha512-6iKDCVSIUQ8jPMoIV0OytRKniaYyy5EbY/RRydmLW8ZR3cEBhxbWl5ro0rkUNe0ef6sScvhbY79HrjRm8i3vDQ=="], + + "@sinclair/typebox": ["@sinclair/typebox@0.34.41", "", {}, "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g=="], + + "@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], + + "@testcontainers/postgresql": ["@testcontainers/postgresql@11.8.0", "", { "dependencies": { "testcontainers": "11.8.0" } }, "sha512-JTMBEoLbi1eYvbsQTqbQlPVED07EB4xhB1tAkmvmirmLsjT0IH6YPKZdZD5y91KKR/FDvSbhPsYmDtByGyGqwg=="], + + "@tokenizer/inflate": ["@tokenizer/inflate@0.2.7", "", { "dependencies": { "debug": "4.4.3", "fflate": "0.8.2", "token-types": "6.1.1" } }, "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg=="], + + "@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="], + + "@types/body-parser": ["@types/body-parser@1.19.6", "", { "dependencies": { "@types/connect": "3.4.38", "@types/node": "24.5.2" } }, "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g=="], + + "@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "4.0.2", "assertion-error": "2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="], + + "@types/connect": ["@types/connect@3.4.38", "", { "dependencies": { "@types/node": "24.5.2" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="], + + "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], + + "@types/docker-modem": ["@types/docker-modem@3.0.6", "", { "dependencies": { "@types/node": "24.5.2", "@types/ssh2": "1.15.5" } }, "sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg=="], + + "@types/dockerode": ["@types/dockerode@3.3.45", "", { "dependencies": { "@types/docker-modem": "3.0.6", "@types/node": "24.5.2", "@types/ssh2": "1.15.5" } }, "sha512-iYpZF+xr5QLpIICejLdUF2r5gh8IXY1Gw3WLmt41dUbS3Vn/3hVgL+6lJBVbmrhYBWfbWPPstdr6+A0s95DTWA=="], + + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + + "@types/express": ["@types/express@5.0.3", "", { "dependencies": { "@types/body-parser": "1.19.6", "@types/express-serve-static-core": "5.0.7", "@types/serve-static": "1.15.8" } }, "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw=="], + + "@types/express-serve-static-core": ["@types/express-serve-static-core@5.0.7", "", { "dependencies": { "@types/node": "24.5.2", "@types/qs": "6.14.0", "@types/range-parser": "1.2.7", "@types/send": "0.17.5" } }, "sha512-R+33OsgWw7rOhD1emjU7dzCDHucJrgJXMA5PYCzJxVil0dsyx5iBEPHqpPfiKNJQb7lZ1vxwoLR4Z87bBUpeGQ=="], + + "@types/http-errors": ["@types/http-errors@2.0.5", "", {}, "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg=="], + + "@types/luxon": ["@types/luxon@3.7.1", "", {}, "sha512-H3iskjFIAn5SlJU7OuxUmTEpebK6TKB8rxZShDslBMZJ5u9S//KM1sbdAisiSrqwLQncVjnpi2OK2J51h+4lsg=="], + + "@types/mime": ["@types/mime@1.3.5", "", {}, "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w=="], + + "@types/node": ["@types/node@24.5.2", "", { "dependencies": { "undici-types": "7.12.0" } }, "sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ=="], + + "@types/pg": ["@types/pg@8.15.5", "", { "dependencies": { "@types/node": "24.5.2", "pg-protocol": "1.10.3", "pg-types": "2.2.0" } }, "sha512-LF7lF6zWEKxuT3/OR8wAZGzkg4ENGXFNyiV/JeOt9z5B+0ZVwbql9McqX5c/WStFq1GaGso7H1AzP/qSzmlCKQ=="], + + "@types/qs": ["@types/qs@6.14.0", "", {}, "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ=="], + + "@types/range-parser": ["@types/range-parser@1.2.7", "", {}, "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ=="], + + "@types/react": ["@types/react@19.1.13", "", { "dependencies": { "csstype": "3.1.3" } }, "sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ=="], + + "@types/send": ["@types/send@0.17.5", "", { "dependencies": { "@types/mime": "1.3.5", "@types/node": "24.5.2" } }, "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w=="], + + "@types/serve-static": ["@types/serve-static@1.15.8", "", { "dependencies": { "@types/http-errors": "2.0.5", "@types/node": "24.5.2", "@types/send": "0.17.5" } }, "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg=="], + + "@types/ssh2": ["@types/ssh2@1.15.5", "", { "dependencies": { "@types/node": "18.19.130" } }, "sha512-N1ASjp/nXH3ovBHddRJpli4ozpk6UdDYIX4RJWFa9L1YKnzdhTlVmiGHm4DZnj/jLbqZpes4aeR30EFGQtvhQQ=="], + + "@types/ssh2-streams": ["@types/ssh2-streams@0.1.13", "", { "dependencies": { "@types/node": "24.5.2" } }, "sha512-faHyY3brO9oLEA0QlcO8N2wT7R0+1sHWZvQ+y3rMLwdY1ZyS1z0W3t65j9PqT4HmQ6ALzNe7RZlNuCNE0wBSWA=="], + + "@vitest/coverage-v8": ["@vitest/coverage-v8@4.0.8", "", { "dependencies": { "@bcoe/v8-coverage": "1.0.2", "@vitest/utils": "4.0.8", "ast-v8-to-istanbul": "0.3.8", "debug": "4.4.3", "istanbul-lib-coverage": "3.2.2", "istanbul-lib-report": "3.0.1", "istanbul-lib-source-maps": "5.0.6", "istanbul-reports": "3.2.0", "magicast": "0.5.1", "std-env": "3.10.0", "tinyrainbow": "3.0.3" }, "peerDependencies": { "vitest": "4.0.8" } }, "sha512-wQgmtW6FtPNn4lWUXi8ZSYLpOIb92j3QCujxX3sQ81NTfQ/ORnE0HtK7Kqf2+7J9jeveMGyGyc4NWc5qy3rC4A=="], + + "@vitest/expect": ["@vitest/expect@4.0.8", "", { "dependencies": { "@standard-schema/spec": "1.0.0", "@types/chai": "5.2.3", "@vitest/spy": "4.0.8", "@vitest/utils": "4.0.8", "chai": "6.2.0", "tinyrainbow": "3.0.3" } }, "sha512-Rv0eabdP/xjAHQGr8cjBm+NnLHNoL268lMDK85w2aAGLFoVKLd8QGnVon5lLtkXQCoYaNL0wg04EGnyKkkKhPA=="], + + "@vitest/mocker": ["@vitest/mocker@4.0.8", "", { "dependencies": { "@vitest/spy": "4.0.8", "estree-walker": "3.0.3", "magic-string": "0.30.21" }, "optionalDependencies": { "vite": "7.2.2" } }, "sha512-9FRM3MZCedXH3+pIh+ME5Up2NBBHDq0wqwhOKkN4VnvCiKbVxddqH9mSGPZeawjd12pCOGnl+lo/ZGHt0/dQSg=="], + + "@vitest/pretty-format": ["@vitest/pretty-format@4.0.8", "", { "dependencies": { "tinyrainbow": "3.0.3" } }, "sha512-qRrjdRkINi9DaZHAimV+8ia9Gq6LeGz2CgIEmMLz3sBDYV53EsnLZbJMR1q84z1HZCMsf7s0orDgZn7ScXsZKg=="], + + "@vitest/runner": ["@vitest/runner@4.0.8", "", { "dependencies": { "@vitest/utils": "4.0.8", "pathe": "2.0.3" } }, "sha512-mdY8Sf1gsM8hKJUQfiPT3pn1n8RF4QBcJYFslgWh41JTfrK1cbqY8whpGCFzBl45LN028g0njLCYm0d7XxSaQQ=="], + + "@vitest/snapshot": ["@vitest/snapshot@4.0.8", "", { "dependencies": { "@vitest/pretty-format": "4.0.8", "magic-string": "0.30.21", "pathe": "2.0.3" } }, "sha512-Nar9OTU03KGiubrIOFhcfHg8FYaRaNT+bh5VUlNz8stFhCZPNrJvmZkhsr1jtaYvuefYFwK2Hwrq026u4uPWCw=="], + + "@vitest/spy": ["@vitest/spy@4.0.8", "", {}, "sha512-nvGVqUunyCgZH7kmo+Ord4WgZ7lN0sOULYXUOYuHr55dvg9YvMz3izfB189Pgp28w0vWFbEEfNc/c3VTrqrXeA=="], + + "@vitest/utils": ["@vitest/utils@4.0.8", "", { "dependencies": { "@vitest/pretty-format": "4.0.8", "tinyrainbow": "3.0.3" } }, "sha512-pdk2phO5NDvEFfUTxcTP8RFYjVj/kfLSPIN5ebP2Mu9kcIMeAQTbknqcFEyBcC4z2pJlJI9aS5UQjcYfhmKAow=="], + + "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "5.0.1" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="], + + "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "3.0.1", "negotiator": "1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], + + "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "archiver": ["archiver@7.0.1", "", { "dependencies": { "archiver-utils": "5.0.2", "async": "3.2.6", "buffer-crc32": "1.0.0", "readable-stream": "4.7.0", "readdir-glob": "1.1.3", "tar-stream": "3.1.7", "zip-stream": "6.0.1" } }, "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ=="], + + "archiver-utils": ["archiver-utils@5.0.2", "", { "dependencies": { "glob": "10.4.5", "graceful-fs": "4.2.11", "is-stream": "2.0.1", "lazystream": "1.0.1", "lodash": "4.17.21", "normalize-path": "3.0.0", "readable-stream": "4.7.0" } }, "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA=="], + + "asn1": ["asn1@0.2.6", "", { "dependencies": { "safer-buffer": "2.1.2" } }, "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ=="], + + "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], + + "ast-v8-to-istanbul": ["ast-v8-to-istanbul@0.3.8", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.31", "estree-walker": "3.0.3", "js-tokens": "9.0.1" } }, "sha512-szgSZqUxI5T8mLKvS7WTjF9is+MVbOeLADU73IseOcrqhxr/VAvy6wfoVE39KnKzA7JRhjF5eUagNlHwvZPlKQ=="], + + "async": ["async@3.2.6", "", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="], + + "async-lock": ["async-lock@1.4.1", "", {}, "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ=="], + + "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], + + "axios": ["axios@1.12.2", "", { "dependencies": { "follow-redirects": "1.15.11", "form-data": "4.0.4", "proxy-from-env": "1.1.0" } }, "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw=="], + + "b4a": ["b4a@1.7.3", "", {}, "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q=="], + + "babel-plugin-polyfill-corejs2": ["babel-plugin-polyfill-corejs2@0.4.14", "", { "dependencies": { "@babel/compat-data": "7.28.4", "@babel/helper-define-polyfill-provider": "0.6.5", "semver": "6.3.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg=="], + + "babel-plugin-polyfill-corejs3": ["babel-plugin-polyfill-corejs3@0.13.0", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "0.6.5", "core-js-compat": "3.45.1" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A=="], + + "babel-plugin-polyfill-regenerator": ["babel-plugin-polyfill-regenerator@0.6.5", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "0.6.5" }, "peerDependencies": { "@babel/core": "7.28.4" } }, "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg=="], + + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "bare-events": ["bare-events@2.8.2", "", {}, "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ=="], + + "bare-fs": ["bare-fs@4.5.0", "", { "dependencies": { "bare-events": "2.8.2", "bare-path": "3.0.0", "bare-stream": "2.7.0", "bare-url": "2.3.2", "fast-fifo": "1.3.2" } }, "sha512-GljgCjeupKZJNetTqxKaQArLK10vpmK28or0+RwWjEl5Rk+/xG3wkpmkv+WrcBm3q1BwHKlnhXzR8O37kcvkXQ=="], + + "bare-os": ["bare-os@3.6.2", "", {}, "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A=="], + + "bare-path": ["bare-path@3.0.0", "", { "dependencies": { "bare-os": "3.6.2" } }, "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw=="], + + "bare-stream": ["bare-stream@2.7.0", "", { "dependencies": { "streamx": "2.23.0" }, "optionalDependencies": { "bare-events": "2.8.2" } }, "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A=="], + + "bare-url": ["bare-url@2.3.2", "", { "dependencies": { "bare-path": "3.0.0" } }, "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw=="], + + "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], + + "baseline-browser-mapping": ["baseline-browser-mapping@2.8.6", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-wrH5NNqren/QMtKUEEJf7z86YjfqW/2uw3IL3/xpqZUC95SSVIFXYQeeGjL6FT/X68IROu6RMehZQS5foy2BXw=="], + + "bcrypt-pbkdf": ["bcrypt-pbkdf@1.0.2", "", { "dependencies": { "tweetnacl": "0.14.5" } }, "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w=="], + + "bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "5.7.1", "inherits": "2.0.4", "readable-stream": "3.6.2" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="], + + "body-parser": ["body-parser@2.2.0", "", { "dependencies": { "bytes": "3.1.2", "content-type": "1.0.5", "debug": "4.4.3", "http-errors": "2.0.0", "iconv-lite": "0.6.3", "on-finished": "2.4.1", "qs": "6.14.0", "raw-body": "3.0.1", "type-is": "2.0.1" } }, "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg=="], + + "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="], + + "brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "1.0.2" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + + "browserslist": ["browserslist@4.26.2", "", { "dependencies": { "baseline-browser-mapping": "2.8.6", "caniuse-lite": "1.0.30001743", "electron-to-chromium": "1.5.222", "node-releases": "2.0.21", "update-browserslist-db": "1.1.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A=="], + + "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "1.5.1", "ieee754": "1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], + + "buffer-crc32": ["buffer-crc32@1.0.0", "", {}, "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w=="], + + "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], + + "buildcheck": ["buildcheck@0.0.6", "", {}, "sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A=="], + + "bun-types": ["bun-types@1.2.22", "", { "dependencies": { "@types/node": "24.10.0" }, "peerDependencies": { "@types/react": "19.1.13" } }, "sha512-hwaAu8tct/Zn6Zft4U9BsZcXkYomzpHJX28ofvx7k0Zz2HNz54n1n+tDgxoWFGB4PcFvJXJQloPhaV2eP3Q6EA=="], + + "byline": ["byline@5.0.0", "", {}, "sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q=="], + + "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "1.3.0", "function-bind": "1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "1.0.2", "get-intrinsic": "1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + + "caniuse-lite": ["caniuse-lite@1.0.30001743", "", {}, "sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw=="], + + "chai": ["chai@6.2.0", "", {}, "sha512-aUTnJc/JipRzJrNADXVvpVqi6CO0dn3nx4EVPxijri+fj3LUUDyZQOgVeW54Ob3Y1Xh9Iz8f+CgaCl8v0mn9bA=="], + + "cheerio": ["cheerio@1.0.0-rc.12", "", { "dependencies": { "cheerio-select": "2.1.0", "dom-serializer": "2.0.0", "domhandler": "5.0.3", "domutils": "3.2.2", "htmlparser2": "8.0.2", "parse5": "7.3.0", "parse5-htmlparser2-tree-adapter": "7.1.0" } }, "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q=="], + + "cheerio-select": ["cheerio-select@2.1.0", "", { "dependencies": { "boolbase": "1.0.0", "css-select": "5.2.2", "css-what": "6.2.2", "domelementtype": "2.3.0", "domhandler": "5.0.3", "domutils": "3.2.2" } }, "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g=="], + + "chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="], + + "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "4.2.3", "strip-ansi": "6.0.1", "wrap-ansi": "7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], + + "compress-commons": ["compress-commons@6.0.2", "", { "dependencies": { "crc-32": "1.2.2", "crc32-stream": "6.0.0", "is-stream": "2.0.1", "normalize-path": "3.0.0", "readable-stream": "4.7.0" } }, "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg=="], + + "content-disposition": ["content-disposition@1.0.0", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg=="], + + "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], + + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], + + "cookie": ["cookie@1.0.2", "", {}, "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="], + + "cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="], + + "core-js-compat": ["core-js-compat@3.45.1", "", { "dependencies": { "browserslist": "4.26.2" } }, "sha512-tqTt5T4PzsMIZ430XGviK4vzYSoeNJ6CXODi6c/voxOT6IZqBht5/EKaSNnYiEjjRYxjVz7DQIsOsY0XNi8PIA=="], + + "core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="], + + "cpu-features": ["cpu-features@0.0.10", "", { "dependencies": { "buildcheck": "0.0.6", "nan": "2.23.1" } }, "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA=="], + + "crc-32": ["crc-32@1.2.2", "", { "bin": { "crc32": "bin/crc32.njs" } }, "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ=="], + + "crc32-stream": ["crc32-stream@6.0.0", "", { "dependencies": { "crc-32": "1.2.2", "readable-stream": "4.7.0" } }, "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g=="], + + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "3.1.1", "shebang-command": "2.0.0", "which": "2.0.2" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "css-select": ["css-select@5.2.2", "", { "dependencies": { "boolbase": "1.0.0", "css-what": "6.2.2", "domhandler": "5.0.3", "domutils": "3.2.2", "nth-check": "2.1.1" } }, "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw=="], + + "css-what": ["css-what@6.2.2", "", {}, "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA=="], + + "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], + + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], + + "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], + + "docker-compose": ["docker-compose@1.3.0", "", { "dependencies": { "yaml": "2.8.1" } }, "sha512-7Gevk/5eGD50+eMD+XDnFnOrruFkL0kSd7jEG4cjmqweDSUhB7i0g8is/nBdVpl+Bx338SqIB2GLKm32M+Vs6g=="], + + "docker-modem": ["docker-modem@5.0.6", "", { "dependencies": { "debug": "4.4.3", "readable-stream": "3.6.2", "split-ca": "1.0.1", "ssh2": "1.17.0" } }, "sha512-ens7BiayssQz/uAxGzH8zGXCtiV24rRWXdjNha5V4zSOcxmAZsfGVm/PPFbwQdqEkDnhG+SyR9E3zSHUbOKXBQ=="], + + "dockerode": ["dockerode@4.0.9", "", { "dependencies": { "@balena/dockerignore": "1.0.2", "@grpc/grpc-js": "1.14.1", "@grpc/proto-loader": "0.7.15", "docker-modem": "5.0.6", "protobufjs": "7.5.4", "tar-fs": "2.1.4", "uuid": "10.0.0" } }, "sha512-iND4mcOWhPaCNh54WmK/KoSb35AFqPAUWFMffTQcp52uQt36b5uNwEJTSXntJZBbeGad72Crbi/hvDIv6us/6Q=="], + + "dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "2.3.0", "domhandler": "5.0.3", "entities": "4.5.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="], + + "domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="], + + "domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="], + + "domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "2.0.0", "domelementtype": "2.3.0", "domhandler": "5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="], + + "dotenv": ["dotenv@17.2.2", "", {}, "sha512-Sf2LSQP+bOlhKWWyhFsn0UsfdK/kCWRv1iuA2gXAwt3dyNabr6QSj00I2V10pidqz69soatm9ZwZvpQMTIOd5Q=="], + + "dotenv-cli": ["dotenv-cli@8.0.0", "", { "dependencies": { "cross-spawn": "7.0.6", "dotenv": "16.6.1", "dotenv-expand": "10.0.0", "minimist": "1.2.8" }, "bin": { "dotenv": "cli.js" } }, "sha512-aLqYbK7xKOiTMIRf1lDPbI+Y+Ip/wo5k3eyp6ePysVaSqbyxjyK3dK35BTxG+rmd7djf5q2UPs4noPNH+cj0Qw=="], + + "dotenv-expand": ["dotenv-expand@10.0.0", "", {}, "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A=="], + + "drizzle-kit": ["drizzle-kit@0.31.4", "", { "dependencies": { "@drizzle-team/brocli": "0.10.2", "@esbuild-kit/esm-loader": "2.6.5", "esbuild": "0.25.10", "esbuild-register": "3.6.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-tCPWVZWZqWVx2XUsVpJRnH9Mx0ClVOf5YUHerZ5so1OKSlqww4zy1R5ksEdGRcO3tM3zj0PYN6V48TbQCL1RfA=="], + + "drizzle-orm": ["drizzle-orm@0.44.5", "", { "optionalDependencies": { "@types/pg": "8.15.5", "bun-types": "1.2.22", "pg": "8.16.3" } }, "sha512-jBe37K7d8ZSKptdKfakQFdeljtu3P2Cbo7tJoJSVZADzIKOBo9IAJPOmMsH2bZl90bZgh8FQlD8BjxXA/zuBkQ=="], + + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "1.0.2", "es-errors": "1.3.0", "gopd": "1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], + + "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], + + "electron-to-chromium": ["electron-to-chromium@1.5.222", "", {}, "sha512-gA7psSwSwQRE60CEoLz6JBCQPIxNeuzB2nL8vE03GK/OHxlvykbLyeiumQy1iH5C2f3YbRAZpGCMT12a/9ih9w=="], + + "elysia": ["elysia@1.4.15", "", { "dependencies": { "cookie": "1.0.2", "fast-decode-uri-component": "1.0.1", "memoirist": "0.4.0" }, "optionalDependencies": { "typescript": "5.9.2" }, "peerDependencies": { "@sinclair/typebox": "0.34.41", "exact-mirror": "0.2.2", "file-type": "21.0.0", "openapi-types": "12.1.3" } }, "sha512-RaDqqZdLuC4UJetfVRQ4Z5aVpGgEtQ+pZnsbI4ZzEaf3l/MzuHcqSVoL/Fue3d6qE4RV9HMB2rAZaHyPIxkyzg=="], + + "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], + + "end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="], + + "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="], + + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + + "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "1.3.0", "get-intrinsic": "1.3.0", "has-tostringtag": "1.0.2", "hasown": "2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], + + "esbuild": ["esbuild@0.25.10", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.10", "@esbuild/android-arm": "0.25.10", "@esbuild/android-arm64": "0.25.10", "@esbuild/android-x64": "0.25.10", "@esbuild/darwin-arm64": "0.25.10", "@esbuild/darwin-x64": "0.25.10", "@esbuild/freebsd-arm64": "0.25.10", "@esbuild/freebsd-x64": "0.25.10", "@esbuild/linux-arm": "0.25.10", "@esbuild/linux-arm64": "0.25.10", "@esbuild/linux-ia32": "0.25.10", "@esbuild/linux-loong64": "0.25.10", "@esbuild/linux-mips64el": "0.25.10", "@esbuild/linux-ppc64": "0.25.10", "@esbuild/linux-riscv64": "0.25.10", "@esbuild/linux-s390x": "0.25.10", "@esbuild/linux-x64": "0.25.10", "@esbuild/netbsd-arm64": "0.25.10", "@esbuild/netbsd-x64": "0.25.10", "@esbuild/openbsd-arm64": "0.25.10", "@esbuild/openbsd-x64": "0.25.10", "@esbuild/openharmony-arm64": "0.25.10", "@esbuild/sunos-x64": "0.25.10", "@esbuild/win32-arm64": "0.25.10", "@esbuild/win32-ia32": "0.25.10", "@esbuild/win32-x64": "0.25.10" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ=="], + + "esbuild-register": ["esbuild-register@3.6.0", "", { "dependencies": { "debug": "4.4.3" }, "peerDependencies": { "esbuild": "0.25.10" } }, "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg=="], + + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + + "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], + + "estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], + + "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], + + "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], + + "event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="], + + "events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="], + + "events-universal": ["events-universal@1.0.1", "", { "dependencies": { "bare-events": "2.8.2" } }, "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw=="], + + "exact-mirror": ["exact-mirror@0.2.2", "", { "optionalDependencies": { "@sinclair/typebox": "0.34.41" } }, "sha512-CrGe+4QzHZlnrXZVlo/WbUZ4qQZq8C0uATQVGVgXIrNXgHDBBNFD1VRfssRA2C9t3RYvh3MadZSdg2Wy7HBoQA=="], + + "expect-type": ["expect-type@1.2.2", "", {}, "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA=="], + + "express": ["express@5.1.0", "", { "dependencies": { "accepts": "2.0.0", "body-parser": "2.2.0", "content-disposition": "1.0.0", "content-type": "1.0.5", "cookie": "0.7.2", "cookie-signature": "1.2.2", "debug": "4.4.3", "encodeurl": "2.0.0", "escape-html": "1.0.3", "etag": "1.8.1", "finalhandler": "2.1.0", "fresh": "2.0.0", "http-errors": "2.0.0", "merge-descriptors": "2.0.0", "mime-types": "3.0.1", "on-finished": "2.4.1", "once": "1.4.0", "parseurl": "1.3.3", "proxy-addr": "2.0.7", "qs": "6.14.0", "range-parser": "1.2.1", "router": "2.2.0", "send": "1.2.0", "serve-static": "2.2.0", "statuses": "2.0.2", "type-is": "2.0.1", "vary": "1.1.2" } }, "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA=="], + + "fast-decode-uri-component": ["fast-decode-uri-component@1.0.1", "", {}, "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg=="], + + "fast-fifo": ["fast-fifo@1.3.2", "", {}, "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ=="], + + "fdir": ["fdir@6.5.0", "", { "optionalDependencies": { "picomatch": "4.0.3" } }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "fflate": ["fflate@0.8.2", "", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="], + + "file-type": ["file-type@21.0.0", "", { "dependencies": { "@tokenizer/inflate": "0.2.7", "strtok3": "10.3.4", "token-types": "6.1.1", "uint8array-extras": "1.5.0" } }, "sha512-ek5xNX2YBYlXhiUXui3D/BXa3LdqPmoLJ7rqEx2bKJ7EAUEfmXgW0Das7Dc6Nr9MvqaOnIqiPV0mZk/r/UpNAg=="], + + "finalhandler": ["finalhandler@2.1.0", "", { "dependencies": { "debug": "4.4.3", "encodeurl": "2.0.0", "escape-html": "1.0.3", "on-finished": "2.4.1", "parseurl": "1.3.3", "statuses": "2.0.2" } }, "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q=="], + + "follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="], + + "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "7.0.6", "signal-exit": "4.1.0" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], + + "form-data": ["form-data@4.0.4", "", { "dependencies": { "asynckit": "0.4.0", "combined-stream": "1.0.8", "es-set-tostringtag": "2.1.0", "hasown": "2.0.2", "mime-types": "2.1.35" } }, "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow=="], + + "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], + + "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], + + "fs-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="], + + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], + + "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], + + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "1.0.2", "es-define-property": "1.0.1", "es-errors": "1.3.0", "es-object-atoms": "1.1.1", "function-bind": "1.1.2", "get-proto": "1.0.1", "gopd": "1.2.0", "has-symbols": "1.1.0", "hasown": "2.0.2", "math-intrinsics": "1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-port": ["get-port@7.1.0", "", {}, "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "1.0.1", "es-object-atoms": "1.1.1" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "get-tsconfig": ["get-tsconfig@4.10.1", "", { "dependencies": { "resolve-pkg-maps": "1.0.0" } }, "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ=="], + + "glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "3.3.1", "jackspeak": "3.4.3", "minimatch": "9.0.5", "minipass": "7.1.2", "package-json-from-dist": "1.0.1", "path-scurry": "1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="], + + "globrex": ["globrex@0.1.2", "", {}, "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg=="], + + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "1.1.0" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], + + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + + "hono": ["hono@4.9.8", "", {}, "sha512-JW8Bb4RFWD9iOKxg5PbUarBYGM99IcxFl2FPBo2gSJO11jjUDqlP1Bmfyqt8Z/dGhIQ63PMA9LdcLefXyIasyg=="], + + "html-escaper": ["html-escaper@2.0.2", "", {}, "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg=="], + + "htmlparser2": ["htmlparser2@8.0.2", "", { "dependencies": { "domelementtype": "2.3.0", "domhandler": "5.0.3", "domutils": "3.2.2", "entities": "4.5.0" } }, "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA=="], + + "http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="], + + "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": "2.1.2" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + + "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], + + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], + + "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], + + "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], + + "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + + "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="], + + "is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], + + "isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="], + + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + + "istanbul-lib-coverage": ["istanbul-lib-coverage@3.2.2", "", {}, "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg=="], + + "istanbul-lib-report": ["istanbul-lib-report@3.0.1", "", { "dependencies": { "istanbul-lib-coverage": "3.2.2", "make-dir": "4.0.0", "supports-color": "7.2.0" } }, "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw=="], + + "istanbul-lib-source-maps": ["istanbul-lib-source-maps@5.0.6", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.31", "debug": "4.4.3", "istanbul-lib-coverage": "3.2.2" } }, "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A=="], + + "istanbul-reports": ["istanbul-reports@3.2.0", "", { "dependencies": { "html-escaper": "2.0.2", "istanbul-lib-report": "3.0.1" } }, "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA=="], + + "jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="], + + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], + + "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], + + "lazystream": ["lazystream@1.0.1", "", { "dependencies": { "readable-stream": "2.3.8" } }, "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw=="], + + "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], + + "lodash.camelcase": ["lodash.camelcase@4.3.0", "", {}, "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="], + + "lodash.debounce": ["lodash.debounce@4.0.8", "", {}, "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="], + + "long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="], + + "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "3.1.1" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], + + "luxon": ["luxon@3.7.2", "", {}, "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew=="], + + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + + "magicast": ["magicast@0.5.1", "", { "dependencies": { "@babel/parser": "7.28.5", "@babel/types": "7.28.5", "source-map-js": "1.2.1" } }, "sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw=="], + + "make-dir": ["make-dir@4.0.0", "", { "dependencies": { "semver": "7.7.3" } }, "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw=="], + + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="], + + "memoirist": ["memoirist@0.4.0", "", {}, "sha512-zxTgA0mSYELa66DimuNQDvyLq36AwDlTuVRbnQtB+VuTcKWm5Qc4z3WkSpgsFWHNhexqkIooqpv4hdcqrX5Nmg=="], + + "merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="], + + "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], + + "mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="], + + "minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "2.0.2" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="], + + "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + + "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + + "mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="], + + "mkdirp-classic": ["mkdirp-classic@0.5.3", "", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "nan": ["nan@2.23.1", "", {}, "sha512-r7bBUGKzlqk8oPBDYxt6Z0aEdF1G1rwlMcLk8LCOMbOzf0mG+JUfUzG4fIMWwHWP0iyaLWEQZJmtB7nOHEm/qw=="], + + "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + + "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], + + "node-releases": ["node-releases@2.0.21", "", {}, "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw=="], + + "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], + + "npm-check-updates": ["npm-check-updates@18.3.0", "", { "bin": { "ncu": "build/cli.js", "npm-check-updates": "build/cli.js" } }, "sha512-Wcm90Af5JuzxwPTtdLl0OH2O1TCeqPTYBch1M3bePmfqylRMiFXXh+uglE4sfMjwdTjw7aIReMwudXeqoYvh2Q=="], + + "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="], + + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], + + "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], + + "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1.0.2" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], + + "openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="], + + "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="], + + "parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "6.0.1" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], + + "parse5-htmlparser2-tree-adapter": ["parse5-htmlparser2-tree-adapter@7.1.0", "", { "dependencies": { "domhandler": "5.0.3", "parse5": "7.3.0" } }, "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g=="], + + "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], + + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], + + "path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "10.4.3", "minipass": "7.1.2" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], + + "path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="], + + "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + + "pg": ["pg@8.16.3", "", { "dependencies": { "pg-connection-string": "2.9.1", "pg-pool": "3.10.1", "pg-protocol": "1.10.3", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "optionalDependencies": { "pg-cloudflare": "1.2.7" } }, "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw=="], + + "pg-cloudflare": ["pg-cloudflare@1.2.7", "", {}, "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg=="], + + "pg-connection-string": ["pg-connection-string@2.9.1", "", {}, "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w=="], + + "pg-int8": ["pg-int8@1.0.1", "", {}, "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="], + + "pg-pool": ["pg-pool@3.10.1", "", { "peerDependencies": { "pg": "8.16.3" } }, "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg=="], + + "pg-protocol": ["pg-protocol@1.10.3", "", {}, "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ=="], + + "pg-types": ["pg-types@2.2.0", "", { "dependencies": { "pg-int8": "1.0.1", "postgres-array": "2.0.0", "postgres-bytea": "1.0.0", "postgres-date": "1.0.7", "postgres-interval": "1.2.0" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="], + + "pgpass": ["pgpass@1.0.5", "", { "dependencies": { "split2": "4.2.0" } }, "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + + "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "3.3.11", "picocolors": "1.1.1", "source-map-js": "1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], + + "postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="], + + "postgres-bytea": ["postgres-bytea@1.0.0", "", {}, "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w=="], + + "postgres-date": ["postgres-date@1.0.7", "", {}, "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="], + + "postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "4.0.2" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="], + + "process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="], + + "process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="], + + "proper-lockfile": ["proper-lockfile@4.1.2", "", { "dependencies": { "graceful-fs": "4.2.11", "retry": "0.12.0", "signal-exit": "3.0.7" } }, "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA=="], + + "properties-reader": ["properties-reader@2.3.0", "", { "dependencies": { "mkdirp": "1.0.4" } }, "sha512-z597WicA7nDZxK12kZqHr2TcvwNU1GCfA5UwfDY/HDp3hXPoPlb5rlEx9bwGTiJnc0OqbBTkU975jDToth8Gxw=="], + + "protobufjs": ["protobufjs@7.5.4", "", { "dependencies": { "@protobufjs/aspromise": "1.1.2", "@protobufjs/base64": "1.1.2", "@protobufjs/codegen": "2.0.4", "@protobufjs/eventemitter": "1.1.0", "@protobufjs/fetch": "1.1.0", "@protobufjs/float": "1.0.2", "@protobufjs/inquire": "1.1.0", "@protobufjs/path": "1.1.2", "@protobufjs/pool": "1.1.0", "@protobufjs/utf8": "1.1.0", "@types/node": "24.5.2", "long": "5.3.2" } }, "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg=="], + + "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], + + "proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], + + "pump": ["pump@3.0.3", "", { "dependencies": { "end-of-stream": "1.4.5", "once": "1.4.0" } }, "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA=="], + + "qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="], + + "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], + + "raw-body": ["raw-body@3.0.1", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.7.0", "unpipe": "1.0.0" } }, "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA=="], + + "readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "3.0.0", "buffer": "6.0.3", "events": "3.3.0", "process": "0.11.10", "string_decoder": "1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="], + + "readdir-glob": ["readdir-glob@1.1.3", "", { "dependencies": { "minimatch": "5.1.6" } }, "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA=="], + + "regenerate": ["regenerate@1.4.2", "", {}, "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A=="], + + "regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="], + + "regexpu-core": ["regexpu-core@6.3.1", "", { "dependencies": { "regenerate": "1.4.2", "regenerate-unicode-properties": "10.2.2", "regjsgen": "0.8.0", "regjsparser": "0.12.0", "unicode-match-property-ecmascript": "2.0.0", "unicode-match-property-value-ecmascript": "2.2.1" } }, "sha512-DzcswPr252wEr7Qz8AyAVbfyBDKLoYp6eRA1We2Fa9qirRFSdtkP5sHr3yglDKy2BbA0fd2T+j/CUSKes3FeVQ=="], + + "regjsgen": ["regjsgen@0.8.0", "", {}, "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q=="], + + "regjsparser": ["regjsparser@0.12.0", "", { "dependencies": { "jsesc": "3.0.2" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ=="], + + "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], + + "resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "2.16.1", "path-parse": "1.0.7", "supports-preserve-symlinks-flag": "1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="], + + "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], + + "retry": ["retry@0.12.0", "", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="], + + "rollup": ["rollup@4.52.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.52.0", "@rollup/rollup-android-arm64": "4.52.0", "@rollup/rollup-darwin-arm64": "4.52.0", "@rollup/rollup-darwin-x64": "4.52.0", "@rollup/rollup-freebsd-arm64": "4.52.0", "@rollup/rollup-freebsd-x64": "4.52.0", "@rollup/rollup-linux-arm-gnueabihf": "4.52.0", "@rollup/rollup-linux-arm-musleabihf": "4.52.0", "@rollup/rollup-linux-arm64-gnu": "4.52.0", "@rollup/rollup-linux-arm64-musl": "4.52.0", "@rollup/rollup-linux-loong64-gnu": "4.52.0", "@rollup/rollup-linux-ppc64-gnu": "4.52.0", "@rollup/rollup-linux-riscv64-gnu": "4.52.0", "@rollup/rollup-linux-riscv64-musl": "4.52.0", "@rollup/rollup-linux-s390x-gnu": "4.52.0", "@rollup/rollup-linux-x64-gnu": "4.52.0", "@rollup/rollup-linux-x64-musl": "4.52.0", "@rollup/rollup-openharmony-arm64": "4.52.0", "@rollup/rollup-win32-arm64-msvc": "4.52.0", "@rollup/rollup-win32-ia32-msvc": "4.52.0", "@rollup/rollup-win32-x64-gnu": "4.52.0", "@rollup/rollup-win32-x64-msvc": "4.52.0", "fsevents": "2.3.3" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-+IuescNkTJQgX7AkIDtITipZdIGcWF0pnVvZTWStiazUmcGA2ag8dfg0urest2XlXUi9kuhfQ+qmdc5Stc3z7g=="], + + "router": ["router@2.2.0", "", { "dependencies": { "debug": "4.4.3", "depd": "2.0.0", "is-promise": "4.0.0", "parseurl": "1.3.3", "path-to-regexp": "8.3.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="], + + "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + + "semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "send": ["send@1.2.0", "", { "dependencies": { "debug": "4.4.3", "encodeurl": "2.0.0", "escape-html": "1.0.3", "etag": "1.8.1", "fresh": "2.0.0", "http-errors": "2.0.0", "mime-types": "3.0.1", "ms": "2.1.3", "on-finished": "2.4.1", "range-parser": "1.2.1", "statuses": "2.0.2" } }, "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw=="], + + "serve-static": ["serve-static@2.2.0", "", { "dependencies": { "encodeurl": "2.0.0", "escape-html": "1.0.3", "parseurl": "1.3.3", "send": "1.2.0" } }, "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ=="], + + "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], + + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "1.3.0", "object-inspect": "1.13.4", "side-channel-list": "1.0.0", "side-channel-map": "1.0.1", "side-channel-weakmap": "1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], + + "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "1.3.0", "object-inspect": "1.13.4" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="], + + "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "1.0.4", "es-errors": "1.3.0", "get-intrinsic": "1.3.0", "object-inspect": "1.13.4" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], + + "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "1.0.4", "es-errors": "1.3.0", "get-intrinsic": "1.3.0", "object-inspect": "1.13.4", "side-channel-map": "1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + + "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="], + + "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + + "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], + + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + + "source-map-support": ["source-map-support@0.5.21", "", { "dependencies": { "buffer-from": "1.1.2", "source-map": "0.6.1" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="], + + "split-ca": ["split-ca@1.0.1", "", {}, "sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ=="], + + "split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="], + + "ssh-remote-port-forward": ["ssh-remote-port-forward@1.0.4", "", { "dependencies": { "@types/ssh2": "0.5.52", "ssh2": "1.17.0" } }, "sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ=="], + + "ssh2": ["ssh2@1.17.0", "", { "dependencies": { "asn1": "0.2.6", "bcrypt-pbkdf": "1.0.2" }, "optionalDependencies": { "cpu-features": "0.0.10", "nan": "2.23.1" } }, "sha512-wPldCk3asibAjQ/kziWQQt1Wh3PgDFpC0XpwclzKcdT1vql6KeYxf5LIt4nlFkUeR8WuphYMKqUA56X4rjbfgQ=="], + + "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], + + "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], + + "std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="], + + "streamx": ["streamx@2.23.0", "", { "dependencies": { "events-universal": "1.0.1", "fast-fifo": "1.3.2", "text-decoder": "1.2.3" } }, "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg=="], + + "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "8.0.0", "is-fullwidth-code-point": "3.0.0", "strip-ansi": "6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "8.0.0", "is-fullwidth-code-point": "3.0.0", "strip-ansi": "6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], + + "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "strtok3": ["strtok3@10.3.4", "", { "dependencies": { "@tokenizer/token": "0.3.0" } }, "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg=="], + + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], + + "tar-fs": ["tar-fs@3.1.1", "", { "dependencies": { "pump": "3.0.3", "tar-stream": "3.1.7" }, "optionalDependencies": { "bare-fs": "4.5.0", "bare-path": "3.0.0" } }, "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg=="], + + "tar-stream": ["tar-stream@3.1.7", "", { "dependencies": { "b4a": "1.7.3", "fast-fifo": "1.3.2", "streamx": "2.23.0" } }, "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ=="], + + "testcontainers": ["testcontainers@11.8.0", "", { "dependencies": { "@balena/dockerignore": "1.0.2", "@types/dockerode": "3.3.45", "archiver": "7.0.1", "async-lock": "1.4.1", "byline": "5.0.0", "debug": "4.4.3", "docker-compose": "1.3.0", "dockerode": "4.0.9", "get-port": "7.1.0", "proper-lockfile": "4.1.2", "properties-reader": "2.3.0", "ssh-remote-port-forward": "1.0.4", "tar-fs": "3.1.1", "tmp": "0.2.5", "undici": "7.16.0" } }, "sha512-kY2DfuUB1NSvmpG7wCpi/aTaIJaHcX53WSAlWHsj0La7E7fPnVFOpooheczE3fH9T+OgD5OB5IeBpFitIqqu6w=="], + + "text-decoder": ["text-decoder@1.2.3", "", { "dependencies": { "b4a": "1.7.3" } }, "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA=="], + + "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], + + "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], + + "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "6.5.0", "picomatch": "4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + + "tinyrainbow": ["tinyrainbow@3.0.3", "", {}, "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q=="], + + "tmp": ["tmp@0.2.5", "", {}, "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow=="], + + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], + + "token-types": ["token-types@6.1.1", "", { "dependencies": { "@borewit/text-codec": "0.1.1", "@tokenizer/token": "0.3.0", "ieee754": "1.2.1" } }, "sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ=="], + + "tsconfck": ["tsconfck@3.1.6", "", { "optionalDependencies": { "typescript": "5.9.2" }, "bin": { "tsconfck": "bin/tsconfck.js" } }, "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "tsx": ["tsx@4.20.5", "", { "dependencies": { "esbuild": "0.25.10", "get-tsconfig": "4.10.1" }, "optionalDependencies": { "fsevents": "2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-+wKjMNU9w/EaQayHXb7WA7ZaHY6hN8WgfvHNQ3t1PnU91/7O8TcTnIhCDYTZwnt8JsO9IBqZ30Ln1r7pPF52Aw=="], + + "tweetnacl": ["tweetnacl@0.14.5", "", {}, "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA=="], + + "type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "1.0.5", "media-typer": "1.1.0", "mime-types": "3.0.1" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="], + + "typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="], + + "uint8array-extras": ["uint8array-extras@1.5.0", "", {}, "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A=="], + + "undici": ["undici@7.16.0", "", {}, "sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g=="], + + "undici-types": ["undici-types@7.12.0", "", {}, "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ=="], + + "unicode-canonical-property-names-ecmascript": ["unicode-canonical-property-names-ecmascript@2.0.1", "", {}, "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg=="], + + "unicode-match-property-ecmascript": ["unicode-match-property-ecmascript@2.0.0", "", { "dependencies": { "unicode-canonical-property-names-ecmascript": "2.0.1", "unicode-property-aliases-ecmascript": "2.2.0" } }, "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q=="], + + "unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="], + + "unicode-property-aliases-ecmascript": ["unicode-property-aliases-ecmascript@2.2.0", "", {}, "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ=="], + + "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], + + "update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "3.2.0", "picocolors": "1.1.1" }, "peerDependencies": { "browserslist": "4.26.2" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="], + + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + + "uuid": ["uuid@10.0.0", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ=="], + + "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], + + "vite": ["vite@7.2.2", "", { "dependencies": { "esbuild": "0.25.10", "fdir": "6.5.0", "picomatch": "4.0.3", "postcss": "8.5.6", "rollup": "4.52.0", "tinyglobby": "0.2.15" }, "optionalDependencies": { "@types/node": "24.5.2", "fsevents": "2.3.3", "tsx": "4.20.5", "yaml": "2.8.1" }, "bin": { "vite": "bin/vite.js" } }, "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ=="], + + "vite-tsconfig-paths": ["vite-tsconfig-paths@5.1.4", "", { "dependencies": { "debug": "4.4.3", "globrex": "0.1.2", "tsconfck": "3.1.6" }, "optionalDependencies": { "vite": "7.2.2" } }, "sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w=="], + + "vitest": ["vitest@4.0.8", "", { "dependencies": { "@vitest/expect": "4.0.8", "@vitest/mocker": "4.0.8", "@vitest/pretty-format": "4.0.8", "@vitest/runner": "4.0.8", "@vitest/snapshot": "4.0.8", "@vitest/spy": "4.0.8", "@vitest/utils": "4.0.8", "debug": "4.4.3", "es-module-lexer": "1.7.0", "expect-type": "1.2.2", "magic-string": "0.30.21", "pathe": "2.0.3", "picomatch": "4.0.3", "std-env": "3.10.0", "tinybench": "2.9.0", "tinyexec": "0.3.2", "tinyglobby": "0.2.15", "tinyrainbow": "3.0.3", "vite": "7.2.2", "why-is-node-running": "2.3.0" }, "optionalDependencies": { "@types/node": "24.5.2" }, "bin": { "vitest": "vitest.mjs" } }, "sha512-urzu3NCEV0Qa0Y2PwvBtRgmNtxhj5t5ULw7cuKhIHh3OrkKTLlut0lnBOv9qe5OvbkMH2g38G7KPDCTpIytBVg=="], + + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + + "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="], + + "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "4.3.0", "string-width": "4.2.3", "strip-ansi": "6.0.1" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + + "wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "4.3.0", "string-width": "4.2.3", "strip-ansi": "6.0.1" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + + "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], + + "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="], + + "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], + + "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], + + "yaml": ["yaml@2.8.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw=="], + + "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "8.0.1", "escalade": "3.2.0", "get-caller-file": "2.0.5", "require-directory": "2.1.1", "string-width": "4.2.3", "y18n": "5.0.8", "yargs-parser": "21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], + + "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], + + "zip-stream": ["zip-stream@6.0.1", "", { "dependencies": { "archiver-utils": "5.0.2", "compress-commons": "6.0.2", "readable-stream": "4.7.0" } }, "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA=="], + + "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + + "@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="], + + "@grpc/grpc-js/@grpc/proto-loader": ["@grpc/proto-loader@0.8.0", "", { "dependencies": { "lodash.camelcase": "4.3.0", "long": "5.3.2", "protobufjs": "7.5.4", "yargs": "17.7.2" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" } }, "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ=="], + + "@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "0.2.0", "emoji-regex": "9.2.2", "strip-ansi": "7.1.2" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], + + "@isaacs/cliui/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "6.2.2" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], + + "@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "6.2.3", "string-width": "5.1.2", "strip-ansi": "7.1.2" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], + + "@types/ssh2/@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "5.26.5" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="], + + "@vitest/mocker/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "1.0.8" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], + + "ast-v8-to-istanbul/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "1.0.8" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], + + "ast-v8-to-istanbul/js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="], + + "bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "1.5.1", "ieee754": "1.2.1" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], + + "bl/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "2.0.4", "string_decoder": "1.3.0", "util-deprecate": "1.0.2" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], + + "bun-types/@types/node": ["@types/node@24.10.0", "", { "dependencies": { "undici-types": "7.16.0" } }, "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A=="], + + "docker-modem/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "2.0.4", "string_decoder": "1.3.0", "util-deprecate": "1.0.2" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], + + "dockerode/tar-fs": ["tar-fs@2.1.4", "", { "dependencies": { "chownr": "1.1.4", "mkdirp-classic": "0.5.3", "pump": "3.0.3", "tar-stream": "2.2.0" } }, "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ=="], + + "dotenv-cli/dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], + + "express/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], + + "foreground-child/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + + "form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + + "glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "2.0.2" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + + "http-errors/statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="], + + "lazystream/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "1.0.3", "inherits": "2.0.4", "isarray": "1.0.0", "process-nextick-args": "2.0.1", "safe-buffer": "5.1.2", "string_decoder": "1.1.1", "util-deprecate": "1.0.2" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], + + "magicast/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="], + + "magicast/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "7.27.1", "@babel/helper-validator-identifier": "7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="], + + "make-dir/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + + "parse5/entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], + + "path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + + "raw-body/iconv-lite": ["iconv-lite@0.7.0", "", { "dependencies": { "safer-buffer": "2.1.2" } }, "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ=="], + + "regjsparser/jsesc": ["jsesc@3.0.2", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g=="], + + "ssh-remote-port-forward/@types/ssh2": ["@types/ssh2@0.5.52", "", { "dependencies": { "@types/node": "24.5.2", "@types/ssh2-streams": "0.1.13" } }, "sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.18.20", "", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.18.20", "", { "os": "android", "cpu": "arm64" }, "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.18.20", "", { "os": "android", "cpu": "x64" }, "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.18.20", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.18.20", "", { "os": "darwin", "cpu": "x64" }, "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.18.20", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.18.20", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.18.20", "", { "os": "linux", "cpu": "arm" }, "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.18.20", "", { "os": "linux", "cpu": "arm64" }, "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.18.20", "", { "os": "linux", "cpu": "ia32" }, "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.18.20", "", { "os": "linux", "cpu": "ppc64" }, "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.18.20", "", { "os": "linux", "cpu": "s390x" }, "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.18.20", "", { "os": "linux", "cpu": "x64" }, "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.18.20", "", { "os": "none", "cpu": "x64" }, "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.18.20", "", { "os": "openbsd", "cpu": "x64" }, "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.18.20", "", { "os": "sunos", "cpu": "x64" }, "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.18.20", "", { "os": "win32", "cpu": "arm64" }, "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.18.20", "", { "os": "win32", "cpu": "ia32" }, "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.18.20", "", { "os": "win32", "cpu": "x64" }, "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ=="], + + "@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + + "@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + + "@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + + "@types/ssh2/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + + "bun-types/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + + "dockerode/tar-fs/tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "4.1.0", "end-of-stream": "1.4.5", "fs-constants": "1.0.0", "inherits": "2.0.4", "readable-stream": "3.6.2" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="], + + "form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + + "lazystream/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], + + "lazystream/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "5.1.2" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], + + "magicast/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + + "dockerode/tar-fs/tar-stream/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "2.0.4", "string_decoder": "1.3.0", "util-deprecate": "1.0.2" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], + } +} From 5dc9e409b703d100a2aefc96274ca744705436df Mon Sep 17 00:00:00 2001 From: sabualha Date: Thu, 4 Dec 2025 22:44:26 +0300 Subject: [PATCH 63/65] Add location reports table and admin endpoint --- src/db/schema.ts | 11 +++++++++++ src/server.ts | 43 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/db/schema.ts b/src/db/schema.ts index 357463b..0f2adfb 100644 --- a/src/db/schema.ts +++ b/src/db/schema.ts @@ -98,3 +98,14 @@ export const specialsTable = pgTable("specials", { date: date("date").notNull(), type: specialType("type").notNull(), }); + +export const locationReportsTable = pgTable("location_reports", { + id: integer("id").notNull().generatedAlwaysAsIdentity().primaryKey(), + locationId: text("location_id") + .references(() => locationDataTable.id, { + onDelete: "cascade", + }) + .notNull(), + message: text("message").notNull(), + createdAt: text("created_at").notNull(), +}); diff --git a/src/server.ts b/src/server.ts index 23338cd..cf214e4 100644 --- a/src/server.ts +++ b/src/server.ts @@ -14,6 +14,8 @@ import { openapi } from "@elysiajs/openapi"; import { initDBConnection } from "db/db"; import { DateTime } from "luxon"; import { QueryUtils } from "db/dbQueryUtils"; +import { locationDataTable, locationReportsTable } from "db/schema"; +import { eq } from "drizzle-orm"; /** only used for Slack debug diff logging */ let cachedLocations: ILocation[] = []; @@ -98,11 +100,39 @@ app.post( }), } ); -// backend for reporting locations // +// backend for reporting locations app.post( "/api/report-location", async ({ body: { locationId, message } }) => { - const slackMsg = `Location Reported\nLocation ID: ${locationId}\nMessage: ${message}`; + // Validate locationId exists in DB + const location = await db + .select() + .from(locationDataTable) + .where(eq(locationDataTable.id, String(locationId))) + .limit(1); + + if (location.length === 0) { + return { success: false, error: "Invalid location ID" }; + } + + const locationName = location[0].name ?? "Unknown"; + const timestamp = DateTime.now().setZone("America/New_York").toFormat("yyyy-MM-dd HH:mm:ss"); + + // Store report in database + await db.insert(locationReportsTable).values({ + locationId: String(locationId), + message: message, + createdAt: timestamp, + }); + + // Send formatted Slack message + const slackMsg = [ + `:warning: *Location Report Submitted*`, + `*Location:* ${locationName} (ID: ${locationId})`, + `*Message:* ${message}`, + `*Time:* ${timestamp} ET`, + `*Link:* https://apps.studentaffairs.cmu.edu/dining/conceptinfo/Concept/${locationId}`, + ].join("\n"); await notifySlack(slackMsg, env.SLACK_BACKEND_WEBHOOK_URL); return { success: true }; @@ -115,6 +145,15 @@ app.post( } ); +// Admin endpoint to view all reports +app.get("/api/reports", async () => { + const reports = await db + .select() + .from(locationReportsTable) + .orderBy(locationReportsTable.id); + return { reports }; +}); + setInterval(() => { reload().catch( (er) => `Error in reload process: ${notifySlack(String(er))}\n${er.stack}` From 256f658906382b5b9ee2717a7367e20083e2be13 Mon Sep 17 00:00:00 2001 From: sabualha Date: Tue, 9 Dec 2025 17:23:23 +0300 Subject: [PATCH 64/65] Fix conceptId lookup in report-location endpoint and add tests --- src/server.ts | 25 ++++-- tests/reportLocation.test.ts | 161 +++++++++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+), 8 deletions(-) create mode 100644 tests/reportLocation.test.ts diff --git a/src/server.ts b/src/server.ts index cf214e4..509ce3c 100644 --- a/src/server.ts +++ b/src/server.ts @@ -14,7 +14,7 @@ import { openapi } from "@elysiajs/openapi"; import { initDBConnection } from "db/db"; import { DateTime } from "luxon"; import { QueryUtils } from "db/dbQueryUtils"; -import { locationDataTable, locationReportsTable } from "db/schema"; +import { locationDataTable, locationReportsTable, conceptIdToInternalIdTable } from "db/schema"; import { eq } from "drizzle-orm"; /** only used for Slack debug diff logging */ @@ -104,23 +104,32 @@ app.post( app.post( "/api/report-location", async ({ body: { locationId, message } }) => { - // Validate locationId exists in DB - const location = await db + // Look up the internal ID from the conceptId + const conceptMapping = await db .select() - .from(locationDataTable) - .where(eq(locationDataTable.id, String(locationId))) + .from(conceptIdToInternalIdTable) + .where(eq(conceptIdToInternalIdTable.externalId, String(locationId))) .limit(1); - if (location.length === 0) { + if (conceptMapping.length === 0) { return { success: false, error: "Invalid location ID" }; } - const locationName = location[0].name ?? "Unknown"; + const internalId = conceptMapping[0].internalId; + + // Get location details + const location = await db + .select() + .from(locationDataTable) + .where(eq(locationDataTable.id, internalId)) + .limit(1); + + const locationName = location.length > 0 ? (location[0].name ?? "Unknown") : "Unknown"; const timestamp = DateTime.now().setZone("America/New_York").toFormat("yyyy-MM-dd HH:mm:ss"); // Store report in database await db.insert(locationReportsTable).values({ - locationId: String(locationId), + locationId: internalId, message: message, createdAt: timestamp, }); diff --git a/tests/reportLocation.test.ts b/tests/reportLocation.test.ts new file mode 100644 index 0000000..0131c8e --- /dev/null +++ b/tests/reportLocation.test.ts @@ -0,0 +1,161 @@ +import { + PostgreSqlContainer, + StartedPostgreSqlContainer, +} from "@testcontainers/postgresql"; +import { DBType, initDBConnection } from "db/db"; +import { addLocationDataToDb } from "db/updateLocation"; +import { test as baseTest } from "vitest"; +import { Pool } from "pg"; +import { locationReportsTable, conceptIdToInternalIdTable, locationDataTable } from "db/schema"; +import { eq } from "drizzle-orm"; +import { ILocation } from "types"; + +const testLocation: ILocation = { + name: "Test Cafe", + acceptsOnlineOrders: false, + conceptId: 82, + coordinates: { lat: 40.44, lng: -79.94 }, + description: "A test location", + today: { year: 2025, month: 1, day: 1 }, + times: [], + location: "Test Building", + menu: "menu", + shortDescription: "Test", + url: "https://test.com", + todaysSoups: [], + todaysSpecials: [], +}; + +const dbTest = baseTest.extend<{ + ctx: { + db: DBType; + container: StartedPostgreSqlContainer; + pool: Pool; + }; +}>({ + ctx: async ({}, use) => { + const container = await new PostgreSqlContainer("postgres:17.5") + .withCopyDirectoriesToContainer([ + { + source: `${__dirname}/../drizzle`, + target: "/docker-entrypoint-initdb.d", + }, + ]) + .start(); + const [pool, db] = initDBConnection(container.getConnectionUri()); + use({ container, pool, db }); + }, +}); + +dbTest.afterEach(({ ctx }) => { + ctx.pool.end(); + ctx.container.stop(); +}); + +describe("Report Location", () => { + dbTest("can store a report for a valid location", async ({ ctx: { db } }) => { + // First, add a location to the database + const internalId = await addLocationDataToDb(db, testLocation); + + // Now insert a report + const message = "This location is showing wrong hours"; + const timestamp = "2025-01-09 12:00:00"; + + await db.insert(locationReportsTable).values({ + locationId: internalId, + message: message, + createdAt: timestamp, + }); + + // Verify the report was stored + const reports = await db + .select() + .from(locationReportsTable) + .where(eq(locationReportsTable.locationId, internalId)); + + expect(reports).toHaveLength(1); + expect(reports[0].message).toBe(message); + expect(reports[0].locationId).toBe(internalId); + }); + + dbTest("conceptId maps to internal UUID correctly", async ({ ctx: { db } }) => { + // Add a location (this should automatically create the conceptId mapping) + const internalId = await addLocationDataToDb(db, testLocation); + + // Check the mapping table + const mapping = await db + .select() + .from(conceptIdToInternalIdTable) + .where(eq(conceptIdToInternalIdTable.externalId, String(testLocation.conceptId))); + + expect(mapping).toHaveLength(1); + expect(mapping[0].internalId).toBe(internalId); + }); + + dbTest("returns empty for invalid conceptId lookup", async ({ ctx: { db } }) => { + // Try to look up a conceptId that doesn't exist + const mapping = await db + .select() + .from(conceptIdToInternalIdTable) + .where(eq(conceptIdToInternalIdTable.externalId, "99999")); + + expect(mapping).toHaveLength(0); + }); + + dbTest("can look up location name from internal ID", async ({ ctx: { db } }) => { + // Add a location + const internalId = await addLocationDataToDb(db, testLocation); + + // Look up the location by internal ID + const location = await db + .select() + .from(locationDataTable) + .where(eq(locationDataTable.id, internalId)) + .limit(1); + + expect(location).toHaveLength(1); + expect(location[0].name).toBe("Test Cafe"); + }); + + dbTest("full report flow: conceptId -> internalId -> report", async ({ ctx: { db } }) => { + // This test simulates the full flow of the /api/report-location endpoint + const internalId = await addLocationDataToDb(db, testLocation); + + // Step 1: Look up internal ID from conceptId (like the endpoint does) + const conceptMapping = await db + .select() + .from(conceptIdToInternalIdTable) + .where(eq(conceptIdToInternalIdTable.externalId, String(testLocation.conceptId))) + .limit(1); + + expect(conceptMapping).toHaveLength(1); + const foundInternalId = conceptMapping[0].internalId; + + // Step 2: Get location name + const location = await db + .select() + .from(locationDataTable) + .where(eq(locationDataTable.id, foundInternalId)) + .limit(1); + + expect(location[0].name).toBe("Test Cafe"); + + // Step 3: Store the report + const message = "Hours are incorrect"; + const timestamp = "2025-01-09 14:30:00"; + + await db.insert(locationReportsTable).values({ + locationId: foundInternalId, + message: message, + createdAt: timestamp, + }); + + // Step 4: Verify report was stored correctly + const reports = await db.select().from(locationReportsTable); + + expect(reports).toHaveLength(1); + expect(reports[0].locationId).toBe(internalId); + expect(reports[0].message).toBe("Hours are incorrect"); + expect(reports[0].createdAt).toBe("2025-01-09 14:30:00"); + }); +}); From 45bb6d0a426daada4934705ba475146a54756ac5 Mon Sep 17 00:00:00 2001 From: sabualha Date: Tue, 9 Dec 2025 17:46:32 +0300 Subject: [PATCH 65/65] Add migration for location_reports table --- drizzle/0003_nice_strong_guy.sql | 8 + drizzle/meta/0003_snapshot.json | 557 +++++++++++++++++++++++++++++++ drizzle/meta/_journal.json | 7 + 3 files changed, 572 insertions(+) create mode 100644 drizzle/0003_nice_strong_guy.sql create mode 100644 drizzle/meta/0003_snapshot.json diff --git a/drizzle/0003_nice_strong_guy.sql b/drizzle/0003_nice_strong_guy.sql new file mode 100644 index 0000000..df8f719 --- /dev/null +++ b/drizzle/0003_nice_strong_guy.sql @@ -0,0 +1,8 @@ +CREATE TABLE "location_reports" ( + "id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "location_reports_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1), + "location_id" text NOT NULL, + "message" text NOT NULL, + "created_at" text NOT NULL +); +--> statement-breakpoint +ALTER TABLE "location_reports" ADD CONSTRAINT "location_reports_location_id_location_data_id_fk" FOREIGN KEY ("location_id") REFERENCES "public"."location_data"("id") ON DELETE cascade ON UPDATE no action; \ No newline at end of file diff --git a/drizzle/meta/0003_snapshot.json b/drizzle/meta/0003_snapshot.json new file mode 100644 index 0000000..9ccaf9f --- /dev/null +++ b/drizzle/meta/0003_snapshot.json @@ -0,0 +1,557 @@ +{ + "id": "b663a3a2-29e2-4398-955d-63e405412ff6", + "prevId": "0cafcb54-9cfd-41e1-b46c-748f17a03c53", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.concept_id_to_internal_id": { + "name": "concept_id_to_internal_id", + "schema": "", + "columns": { + "internal_id": { + "name": "internal_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": true, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "concept_id_to_internal_id_internal_id_location_data_id_fk": { + "name": "concept_id_to_internal_id_internal_id_location_data_id_fk", + "tableFrom": "concept_id_to_internal_id", + "tableTo": "location_data", + "columnsFrom": [ + "internal_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "concept_id_to_internal_id_external_id_unique": { + "name": "concept_id_to_internal_id_external_id_unique", + "nullsNotDistinct": false, + "columns": [ + "external_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.emails": { + "name": "emails", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "emails_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.location_data": { + "name": "location_data", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "short_description": { + "name": "short_description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "menu": { + "name": "menu", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "coordinate_lat": { + "name": "coordinate_lat", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "coordinate_lng": { + "name": "coordinate_lng", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "accepts_online_orders": { + "name": "accepts_online_orders", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.location_reports": { + "name": "location_reports", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "location_reports_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "location_id": { + "name": "location_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "location_reports_location_id_location_data_id_fk": { + "name": "location_reports_location_id_location_data_id_fk", + "tableFrom": "location_reports", + "tableTo": "location_data", + "columnsFrom": [ + "location_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.overwrites_table": { + "name": "overwrites_table", + "schema": "", + "columns": { + "location_id": { + "name": "location_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "short_description": { + "name": "short_description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "menu": { + "name": "menu", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "coordinate_lat": { + "name": "coordinate_lat", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "coordinate_lng": { + "name": "coordinate_lng", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "accepts_online_orders": { + "name": "accepts_online_orders", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "overwrites_table_location_id_location_data_id_fk": { + "name": "overwrites_table_location_id_location_data_id_fk", + "tableFrom": "overwrites_table", + "tableTo": "location_data", + "columnsFrom": [ + "location_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.specials": { + "name": "specials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "specials_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "location_id": { + "name": "location_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "date": { + "name": "date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "specialType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "specials_location_id_location_data_id_fk": { + "name": "specials_location_id_location_data_id_fk", + "tableFrom": "specials", + "tableTo": "location_data", + "columnsFrom": [ + "location_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.time_overwrites_table": { + "name": "time_overwrites_table", + "schema": "", + "columns": { + "location_id": { + "name": "location_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "date": { + "name": "date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "time_string": { + "name": "time_string", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "time_overwrites_table_location_id_location_data_id_fk": { + "name": "time_overwrites_table_location_id_location_data_id_fk", + "tableFrom": "time_overwrites_table", + "tableTo": "location_data", + "columnsFrom": [ + "location_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "time_overwrites_table_location_id_date_pk": { + "name": "time_overwrites_table_location_id_date_pk", + "columns": [ + "location_id", + "date" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.location_times": { + "name": "location_times", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "location_times_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "location_id": { + "name": "location_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "date": { + "name": "date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "start_time": { + "name": "start_time", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "end_time": { + "name": "end_time", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "date_lookup": { + "name": "date_lookup", + "columns": [ + { + "expression": "location_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "location_times_location_id_location_data_id_fk": { + "name": "location_times_location_id_location_data_id_fk", + "tableFrom": "location_times", + "tableTo": "location_data", + "columnsFrom": [ + "location_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.specialType": { + "name": "specialType", + "schema": "public", + "values": [ + "special", + "soup" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index b3c252f..e9c8c47 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -22,6 +22,13 @@ "when": 1763850203721, "tag": "0002_fair_meteorite", "breakpoints": true + }, + { + "idx": 3, + "version": "7", + "when": 1765291571490, + "tag": "0003_nice_strong_guy", + "breakpoints": true } ] } \ No newline at end of file