diff --git a/astro.config.mjs b/astro.config.mjs index 68fc0fa..3477551 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -1,11 +1,12 @@ -import { defineConfig } from 'astro/config'; +import { defineConfig } from "astro/config"; import tailwind from "@astrojs/tailwind"; import prefetch from "@astrojs/prefetch"; import sitemap from "@astrojs/sitemap"; +import db from "@astrojs/db"; // https://astro.build/config export default defineConfig({ site: "https://shinsina.github.io", base: "/Stat-N-Track", - integrations: [tailwind(), prefetch(), sitemap()] + integrations: [tailwind(), prefetch(), sitemap(), db()], }); diff --git a/db/config.ts b/db/config.ts new file mode 100644 index 0000000..0bfa004 --- /dev/null +++ b/db/config.ts @@ -0,0 +1,358 @@ +import { defineDb, defineTable, column } from "astro:db"; + +const Car = defineTable({ + columns: { + ai_enabled: column.boolean(), + allow_number_colors: column.boolean(), + allow_sponsor1: column.boolean(), + allow_sponsor2: column.boolean(), + allow_wheel_color: column.boolean(), + award_exempt: column.boolean(), + car_dirpath: column.text(), + car_id: column.number({ primaryKey: true }), + car_name: column.text(), + car_name_abbreviated: column.text(), + car_types: column.json(), + car_weight: column.number(), + categories: column.json(), + created: column.date(), + first_sale: column.date(), + forum_url: column.text({ optional: true }), + free_with_subscription: column.boolean(), + has_headlights: column.boolean(), + has_multiple_dry_tire_types: column.boolean(), + hp: column.number(), + is_ps_purchasable: column.boolean(), + max_power_adjust_pct: column.number(), + max_weight_penalty_kg: column.number(), + min_power_adjust_pct: column.number(), + package_id: column.number(), + pattern: column.number({ optional: true }), + price: column.number(), + price_display: column.text({ optional: true }), + retired: column.boolean(), + search_filters: column.text(), + sku: column.number(), + has_rain_capable_tire_types: column.boolean(), + rain_enabled: column.boolean(), + }, +}); + +const CarClass = defineTable({ + columns: { + car_class_id: column.number({ primaryKey: true }), + cars_in_class: column.json(), + cust_id: column.number(), + name: column.text(), + relative_speed: column.number(), + short_name: column.text(), + rain_enabled: column.boolean({ default: false }), + }, +}); + +const PastSeason = defineTable({ + columns: { + active: column.boolean(), + car_classes: column.json(), + driver_changes: column.boolean(), + fixed_setup: column.boolean(), + license_group: column.number(), + license_group_types: column.json(), + official: column.boolean(), + race_weeks: column.json(), + season_id: column.number({ primaryKey: true }), + season_name: column.text(), + season_quarter: column.number(), + season_short_name: column.text(), + season_year: column.number(), + series_id: column.number(), + has_supersessions: column.boolean(), + }, +}); + +const Season = defineTable({ + columns: { + active: column.boolean(), + allowed_season_members: column.json({ optional: true }), + car_class_ids: column.json(), + car_switching: column.boolean(), + car_types: column.json(), + caution_laps_do_not_count: column.boolean(), + complete: column.boolean(), + cross_license: column.boolean(), + driver_change_rule: column.number(), + driver_changes: column.boolean(), + drops: column.boolean(), + enable_pitlane_collisions: column.boolean(), + fixed_setup: column.boolean(), + green_white_checkered_limit: column.number(), + grid_by_class: column.boolean(), + hardcore_level: column.number(), + has_supersessions: column.boolean(), + ignore_license_for_practice: column.boolean(), + incident_limit: column.number(), + incident_warn_mode: column.number(), + incident_warn_param1: column.number(), + incident_warn_param2: column.number(), + is_heat_racing: column.boolean(), + license_group: column.number(), + license_group_types: column.json(), + lucky_dog: column.boolean(), + max_team_drivers: column.number(), + max_weeks: column.number(), + min_team_drivers: column.number(), + multiclass: column.boolean(), + must_use_diff_tire_types_in_race: column.boolean(), + next_race_session: column.json({ optional: true }), + num_opt_laps: column.number(), + official: column.boolean(), + op_duration: column.number(), + open_practice_session_type_id: column.number(), + qualifier_must_start_race: column.boolean(), + race_week: column.number(), + race_week_to_make_division: column.number({ optional: true }), + reg_user_count: column.number(), + region_competition: column.boolean(), + restrict_by_member: column.boolean(), + restrict_to_car: column.boolean(), + restrict_viewing: column.boolean(), + schedule_description: column.text(), + schedules: column.json(), + season_id: column.number({ primaryKey: true }), + season_name: column.text(), + season_quarter: column.number(), + season_short_name: column.text(), + season_year: column.number(), + send_to_open_practice: column.boolean(), + series_id: column.boolean(), + short_parade_lap: column.boolean(), + start_date: column.date(), + start_on_qual_tire: column.boolean(), + start_zone: column.boolean(), + track_types: column.json(), + unsport_conduct_rule_mode: column.number(), + }, +}); + +const Standing = defineTable({ + columns: { + id: column.text({ primaryKey: true }), + car_class_id: column.text(), + division: column.number(), + overall_rank: column.number(), + rank: column.number(), + cust_id: column.number(), + display_name: column.text({}), + club_id: column.number(), + license: column.json(), + helmet: column.json(), + weeks_counted: column.number(), + starts: column.number(), + wins: column.number(), + top5: column.number(), + top25_percent: column.number(), + poles: column.number(), + avg_start_position: column.number(), + avg_finish_position: column.number(), + avg_field_size: column.number(), + laps: column.number(), + laps_led: column.number(), + incidents: column.number(), + points: column.number(), + raw_points: column.number(), + week_dropped: column.boolean(), + country_code: column.text(), + country: column.text(), + season_id: column.text(), + season_name: column.text(), + division_rank: column.number(), + }, +}); + +const Subsession = defineTable({ + columns: { + allowed_licenses: column.json(), + // associated_subsession_ids: column.json(), + // can_protest: column.boolean(), + car_classes: column.json(), + // caution_type: column.number(), + // cooldown_minutes: column.number(), + corners_per_lap: column.number(), + // damage_model: column.number(), + // driver_change_param1: column.number(), + // driver_change_param2: column.number(), + // drive_change_rule: column.number(), + // driver_changes: column.boolean(), + end_time: column.date(), + event_average_lap: column.number(), + event_laps_complete: column.number(), + event_strength_of_field: column.number(), + // event_type: column.number(), + // event_type_name: column.text(), + // heat_info_id: column.number(), + license_category: column.text(), + license_category_id: column.number(), + // limit_minutes: column.number(), + // max_team_drivers: column.number(), + max_weeks: column.number(), + num_caution_laps: column.number(), + num_cautions: column.number(), + // num_laps_for_qual_average: column.number(), + // num_laps_for_solo_average: column.number(), + num_lead_changes: column.number(), + // official_session: column.boolean(), + // points_type: column.text(), + // private_session_id: column.number(), + race_summary: column.json(), + race_week_num: column.number(), + // results_restricted: column.boolean(), + season_id: column.number(), + season_name: column.text(), + season_quarter: column.number(), + season_short_name: column.text(), + season_year: column.number(), + series_id: column.number(), + series_logo: column.text(), + series_name: column.text(), + series_short_name: column.text(), + session_id: column.number(), + // special_event_type: column.number(), + start_time: column.date(), + subsession_id: column.number({ primaryKey: true }), + track: column.json(), + // track_state: column.json(), + // weather: column.json(), + }, +}); + +const SubsessionSessionResults = defineTable({ + columns: { + subsession_id: column.number(), + simsession_number: column.number(), + simsession_type: column.number(), + simsession_type_name: column.text(), + simsession_name: column.text(), + cust_id: column.number(), + display_name: column.text(), + finish_position: column.number(), + finish_position_in_class: column.number(), + laps_lead: column.number(), + laps_complete: column.number(), + opt_laps_complete: column.number(), + interval: column.number(), + class_interval: column.number(), + average_lap: column.number(), + best_lap_num: column.number(), + best_lap_time: column.number(), + best_nlaps_num: column.number(), + best_nlaps_time: column.number(), + best_qual_lap_num: column.number(), + best_qual_lap_time: column.number(), + reason_out_id: column.number(), + reason_out: column.number(), + champ_points: column.number(), + drop_race: column.boolean(), + club_points: column.number(), + position: column.number(), + qual_lap_time: column.number(), + starting_position: column.number(), + starting_position_in_class: column.number(), + car_class_id: column.number(), + car_class_name: column.text(), + car_class_short_name: column.text(), + club_id: column.number(), + club_name: column.text(), + club_shortname: column.text(), + division: column.number(), + division_name: column.text({ optional: true }), + old_license_level: column.number(), + old_sub_level: column.number(), + old_cpi: column.number(), + oldi_rating: column.number(), + old_ttrating: column.number(), + new_license_level: column.number(), + new_sub_level: column.number(), + new_cpi: column.number(), + newi_rating: column.number(), + new_ttrating: column.number(), + multiplier: column.number(), + license_change_oval: column.number(), + license_change_road: column.number(), + incidents: column.number(), + max_pct_fuel_fill: column.number(), + weight_penalty_kg: column.number(), + league_points: column.number(), + league_agg_points: column.number(), + car_id: column.number(), + car_name: column.text(), + aggregate_champ_points: column.number(), + livery: column.json(), + suit: column.json(), + helmet: column.json(), + watched: column.boolean(), + friend: column.boolean(), + ai: column.boolean(), + } +}) + +const User = defineTable({ + columns: { + account: column.json(), + ai: column.boolean(), + alpha_tester: column.boolean(), + broadcaster: column.boolean(), + bypass_hosted_password: column.boolean(), + car_packages: column.json(), + club_id: column.number(), + club_name: column.text(), + connection_type: column.text(), + cust_id: column.number({ primaryKey: true }), + dev: column.boolean(), + display_name: column.text(), + download_server: column.text(), + email: column.text(), + first_name: column.text(), + flags: column.number(), + flags_hex: column.text(), + has_read_comp_rules: column.boolean(), + has_read_pp: column.boolean(), + has_read_tc: column.boolean(), + helmet: column.json(), + hundred_pct_club: column.boolean(), + last_login: column.date(), + last_name: column.text(), + last_season: column.number(), + last_test_car: column.number(), + last_test_track: column.number(), + licenses: column.json(), + member_since: column.text(), + on_car_name: column.text(), + other_owned_packages: column.json(), + race_official: column.boolean(), + read_comp_rules: column.date(), + read_pp: column.date(), + read_tc: column.date(), + restrictions: column.json(), + suit: column.json(), + track_packages: column.json(), + twenty_pct_discount: column.boolean(), + username: column.text(), + rain_test: column.boolean({ default: false }), + }, +}); + +// https://astro.build/db/config +export default defineDb({ + tables: { + Car, + CarClass, + PastSeason, + Season, + Standing, + Subsession, + SubsessionPracticeResults: SubsessionSessionResults, + SubsessionQualifyingResults: SubsessionSessionResults, + SubsessionRaceResults: SubsessionSessionResults, + User, + }, +}); diff --git a/db/seed.ts b/db/seed.ts new file mode 100644 index 0000000..c352fe0 --- /dev/null +++ b/db/seed.ts @@ -0,0 +1,194 @@ +import { + db, + Car, + CarClass, + PastSeason, + Season, + Standing, + Subsession, + SubsessionPracticeResults, + SubsessionQualifyingResults, + SubsessionRaceResults, + User, +} from "astro:db"; +import type { Subsession as SubsessionType } from "$lib/types"; +import { connectToDatabase } from "$lib/mongodb"; +import { Collection } from "mongodb"; +import type { Document } from "mongodb"; + +async function paginatedQuery( + collection: Collection +): Promise> { + const size = 100; + const count = await collection.countDocuments({}); + const batches = Math.ceil(count / size); + const results = await Promise.all(Array(batches) + .fill({}) + .map((_, index) => { + console.log(`Processing batch ${index + 1} of ${batches} for ${collection.collectionName}...`) + return collection + .find({}) + .skip(index * size) + .limit(size) + .toArray() + } + )); + return results.flat(); +} + +// https://astro.build/db/seed +export default async function seed() { + console.log("Attempting database connection..."); + const dbConnection = await connectToDatabase(); + const mongoDb = dbConnection.db; + console.log("Connection made!"); + const userCollection = mongoDb.collection("users"); + const users = await userCollection.find({}).toArray(); + console.log("Seeding Users..."); + await db.insert(User).values( + users.map((user: any) => { + const { _id, last_login, read_comp_rules, read_pp, read_tc, ...rest } = + user; + return { + ...rest, + last_login: new Date(last_login), + read_comp_rules: new Date(read_comp_rules), + read_pp: new Date(read_pp), + read_tc: new Date(read_tc), + }; + }) + ); + console.log("Users Seeded!"); + const carCollection = mongoDb.collection("cars"); + const cars = await carCollection.find({}).toArray(); + console.log("Seeding Cars..."); + await db.insert(Car).values( + cars.map((car: any) => { + const { _id, created, first_sale, ...rest } = car; + return { + ...rest, + created: new Date(created), + first_sale: new Date(first_sale), + }; + }) + ); + console.log("Cars Seeded!"); + const carClassCollection = mongoDb.collection("carclasses"); + const carClasses = await paginatedQuery(carClassCollection); + console.log("Seeding Car Classes..."); + await db.insert(CarClass).values( + carClasses.map((carClass: any) => { + const { _id, ...rest } = carClass; + return rest; + }) + ); + console.log("Car Classes Seeded!"); + const seasonsCollection = mongoDb.collection("seasons"); + const seasons = await paginatedQuery(seasonsCollection); + console.log("Seeding Seasons..."); + await db.insert(Season).values( + seasons.map((season: any) => { + const { _id, start_date, ...rest } = season; + return { + ...rest, + start_date: new Date(start_date), + }; + }) + ); + console.log("Seeded Seasons!"); + const standingsCollection = mongoDb.collection("standings"); + const standings = await paginatedQuery(standingsCollection); + console.log("Seeding Standings..."); + await db.insert(Standing).values( + standings.map((standing: any) => { + const { _id: id, season_driver_data, ...rest } = standing; + return { + id, + ...season_driver_data, + ...rest, + }; + }) + ); + console.log("Standings Seeded!"); + const pastSeasonsCollection = mongoDb.collection("pastseasons"); + const pastSeasons = await paginatedQuery(pastSeasonsCollection); + console.log("Seeding Past Seasons..."); + await db.insert(PastSeason).values( + pastSeasons.map((pastSeason: any) => { + const { _id, ...rest } = pastSeason; + return rest; + }) + ); + console.log("Past Seasons Seeded!"); + const subsessionsCollection = mongoDb.collection("subsessions"); + // @ts-ignore + const subsessions: Array = await paginatedQuery(subsessionsCollection); + console.log("Seeding Subsessions..."); + const { + allPracticeResults, + allQualifyingResults, + allRaceResults, + allSubsessions, + } = subsessions.reduce( + (object, subsession) => { + const { _id, session_results, end_time, start_time, ...rest } = + subsession; + session_results.forEach((sessionResult) => { + const { results, ...rest } = sessionResult; + results.forEach((result) => { + const resultWithSession = { + subsession_id: _id, + ...result, + ...rest, + }; + if (rest.simsession_type_name.match("Practice")) { + // @ts-ignore + object.allPracticeResults.push(resultWithSession); + } else if (rest.simsession_type_name.match("Qualifying")) { + // @ts-ignore + object.allQualifyingResults.push(resultWithSession); + } else if (rest.simsession_type_name.match("Race")) { + // @ts-ignore + object.allRaceResults.push(resultWithSession); + } + }); + }); + // @ts-ignore + object.allSubsessions.push({ + ...rest, + end_time: new Date(end_time), + start_time: new Date(start_time), + }); + return object; + }, + { + allRaceResults: [], + allQualifyingResults: [], + allPracticeResults: [], + allSubsessions: [], + } + ); + await db.insert(Subsession).values(allSubsessions); + console.log("Seeding Subsession Practice Results..."); + await Promise.all( + allPracticeResults.map((result) => + db.insert(SubsessionPracticeResults).values(result) + ) + ); + console.log("Subsession Practice Results Seeded!"); + console.log("Seeding Subsession Qualifying Results..."); + await Promise.all( + allQualifyingResults.map((result) => + db.insert(SubsessionQualifyingResults).values(result) + ) + ); + console.log("Subsession Qualifying Results Seeded!"); + console.log("Seeding Subsession Race Results..."); + await Promise.all( + allRaceResults.map((result) => + db.insert(SubsessionRaceResults).values(result) + ) + ); + console.log("Subsession Race Results Seeded!"); + console.log("Subsessions Seeded!"); +} diff --git a/package-lock.json b/package-lock.json index 4ea26af..351636e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,12 +9,13 @@ "version": "0.0.1", "dependencies": { "@alpinejs/collapse": "^3.13.3", + "@astrojs/db": "^0.9.1", "@astrojs/prefetch": "^0.4.1", "@astrojs/sitemap": "^3.1.1", "@astrojs/tailwind": "^5.1.0", "@types/alpinejs": "^3.13.5", "alpinejs": "^3.13.3", - "astro": "^4.4.8", + "astro": "^4.5.8", "mongodb": "^6.3.0", "tailwindcss": "^3.3.5" } @@ -48,22 +49,61 @@ } }, "node_modules/@astrojs/compiler": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-2.6.0.tgz", - "integrity": "sha512-c74k8iGHL3DzkosSJ0tGcHIEBEiIfBhr7eadSaPyvWlVKaieDVzVs8OW1tnRSQyBsfMc8DZQ4RcN2KAcESD8UQ==" + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-2.7.0.tgz", + "integrity": "sha512-XpC8MAaWjD1ff6/IfkRq/5k1EFj6zhCNqXRd5J43SVJEBj/Bsmizkm8N0xOYscGcDFQkRgEw6/eKnI5x/1l6aA==" + }, + "node_modules/@astrojs/db": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@astrojs/db/-/db-0.9.1.tgz", + "integrity": "sha512-o8NRz420vIK6ouOAgCuhcoiLtN1l9I6G5Rs7Wsc2hpPNj2vpqPNIFNxAMtauY9mvLZdakq7F8GRMehSGBLX/+w==", + "dependencies": { + "@libsql/client": "^0.5.5", + "async-listen": "^3.0.1", + "deep-diff": "^1.0.2", + "drizzle-orm": "^0.30.2", + "github-slugger": "^2.0.0", + "kleur": "^4.1.5", + "nanoid": "^5.0.1", + "open": "^10.0.3", + "ora": "^7.0.1", + "prompts": "^2.4.2", + "strip-ansi": "^7.1.0", + "yargs-parser": "^21.1.1", + "zod": "^3.22.4" + } + }, + "node_modules/@astrojs/db/node_modules/nanoid": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.6.tgz", + "integrity": "sha512-rRq0eMHoGZxlvaFOUdK1Ev83Bd1IgzzR+WJ3IbDJ7QOSdAxYjlurSPqFs9s4lJg29RT6nPwizFtJhQS6V5xgiA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } }, "node_modules/@astrojs/internal-helpers": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.2.1.tgz", - "integrity": "sha512-06DD2ZnItMwUnH81LBLco3tWjcZ1lGU9rLCCBaeUCGYe9cI0wKyY2W3kDyoW1I6GmcWgt1fu+D1CTvz+FIKf8A==" + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.3.0.tgz", + "integrity": "sha512-tGmHvrhpzuz0JBHaJX8GywN9g4rldVNHtkoVDC3m/DdzBO70jGoVuc0uuNVglRYnsdwkbG0K02Iw3nOOR3/Y4g==" }, "node_modules/@astrojs/markdown-remark": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-4.2.1.tgz", - "integrity": "sha512-2RQBIwrq+2qPYtp99bH+eL5hfbK0BoxXla85lHsRpIX/IsGqFrPX6pXI2cbWPihBwGbKCdxS6uZNX2QerZWwpQ==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-4.3.1.tgz", + "integrity": "sha512-eJFi600tkRjTFiwzY9oD8AgCgB7gFqyWCKWuZ33dATVBgLiROD+zlMZ8STZzU7+ZALvmiUAun/K7umTmP5YfVQ==", "dependencies": { "@astrojs/prism": "^3.0.0", "github-slugger": "^2.0.0", + "hast-util-from-html": "^2.0.0", + "hast-util-to-text": "^4.0.0", "import-meta-resolve": "^4.0.0", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", @@ -72,9 +112,11 @@ "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "remark-smartypants": "^2.0.0", - "shikiji": "^0.9.18", + "shiki": "^1.1.2", "unified": "^11.0.4", + "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", + "unist-util-visit-parents": "^6.0.0", "vfile": "^6.0.1" } }, @@ -139,40 +181,40 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", - "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", + "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", "dependencies": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" + "@babel/highlight": "^7.24.2", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", - "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.1.tgz", + "integrity": "sha512-Pc65opHDliVpRHuKfzI+gSA4zcgr65O4cl64fFJIWEEh8JoHIHh0Oez1Eo8Arz8zq/JhgKodQaxEwUPRtZylVA==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.5.tgz", - "integrity": "sha512-Cwc2XjUrG4ilcfOw4wBAK+enbdgwAcAJCfGUItPBKR7Mjw4aEfAFYrLxeRp4jWgtNIKn3n2AlBOfwwafl+42/g==", + "version": "7.24.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.3.tgz", + "integrity": "sha512-5FcvN1JHw2sHJChotgx8Ek0lyuh4kCKelgMTTqhYJJtloNvUfpAFMeNQUtdlIaktwrSV9LtCdqwk48wL2wBacQ==", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.5", - "@babel/helper-compilation-targets": "^7.22.15", + "@babel/code-frame": "^7.24.2", + "@babel/generator": "^7.24.1", + "@babel/helper-compilation-targets": "^7.23.6", "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.23.5", - "@babel/parser": "^7.23.5", - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.5", - "@babel/types": "^7.23.5", + "@babel/helpers": "^7.24.1", + "@babel/parser": "^7.24.1", + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.1", + "@babel/types": "^7.24.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -196,13 +238,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.5.tgz", - "integrity": "sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.1.tgz", + "integrity": "sha512-DfCRfZsBcrPEHUfuBMgbJ1Ut01Y/itOs+hY2nFLgqsqXd52/iSiVq5TITtUasIUgm+IIKdY2/1I7auiQOEeC9A==", "dependencies": { - "@babel/types": "^7.23.5", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", + "@babel/types": "^7.24.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" }, "engines": { @@ -221,13 +263,13 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", - "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", "dependencies": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.15", - "browserslist": "^4.21.9", + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -358,35 +400,36 @@ } }, "node_modules/@babel/helpers": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.5.tgz", - "integrity": "sha512-oO7us8FzTEsG3U6ag9MfdF1iA/7Z6dz+MtFhifZk8C8o453rGJFFWUP1t+ULM9TUIAzC9uxXEiXjOiVMyd7QPg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.1.tgz", + "integrity": "sha512-BpU09QqEe6ZCHuIHFphEFgvNSrubve1FtyMton26ekZ85gRGi6LrTF7zArARp2YvyFxloeiRmtSCq5sjh1WqIg==", "dependencies": { - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.5", - "@babel/types": "^7.23.5" + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.1", + "@babel/types": "^7.24.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", - "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz", + "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==", "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", - "js-tokens": "^4.0.0" + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.5.tgz", - "integrity": "sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.1.tgz", + "integrity": "sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==", "bin": { "parser": "bin/babel-parser.js" }, @@ -427,32 +470,32 @@ } }, "node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", + "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.5.tgz", - "integrity": "sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.1.tgz", + "integrity": "sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==", "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.5", + "@babel/code-frame": "^7.24.1", + "@babel/generator": "^7.24.1", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.5", - "@babel/types": "^7.23.5", - "debug": "^4.1.0", + "@babel/parser": "^7.24.1", + "@babel/types": "^7.24.0", + "debug": "^4.3.1", "globals": "^11.1.0" }, "engines": { @@ -460,9 +503,9 @@ } }, "node_modules/@babel/types": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.5.tgz", - "integrity": "sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", + "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", "dependencies": { "@babel/helper-string-parser": "^7.23.4", "@babel/helper-validator-identifier": "^7.22.20", @@ -803,13 +846,13 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dependencies": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" @@ -824,9 +867,9 @@ } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "engines": { "node": ">=6.0.0" } @@ -837,18 +880,183 @@ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", - "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@medv/finder": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@medv/finder/-/finder-3.2.0.tgz", - "integrity": "sha512-JmU7JIBwyL8RAzefvzALT4sP2M0biGk8i2invAgpQmma/QgfsaqoHIvJ7S0YC8n9hUVG8X3Leul2nGa06PvhbQ==" + "node_modules/@libsql/client": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/@libsql/client/-/client-0.5.6.tgz", + "integrity": "sha512-UBjmDoxz75Z2sHdP+ETCROpeLA/77VMesiff8R4UWK1rnaWbh6/YoCLDILMJL3Rh0udQeKxjL8MjXthqohax+g==", + "dependencies": { + "@libsql/core": "^0.5.6", + "@libsql/hrana-client": "^0.5.6", + "js-base64": "^3.7.5", + "libsql": "^0.3.10" + } + }, + "node_modules/@libsql/core": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/@libsql/core/-/core-0.5.6.tgz", + "integrity": "sha512-3vicUAydq6jPth410n4AsHHm1n2psTwvkSf94nfJlSXutGSZsl0updn2N/mJBgqUHkbuFoWZtlMifF0SwBj1xQ==", + "dependencies": { + "js-base64": "^3.7.5" + } + }, + "node_modules/@libsql/darwin-arm64": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/@libsql/darwin-arm64/-/darwin-arm64-0.3.10.tgz", + "integrity": "sha512-RaexEFfPAFogd6dJlqkpCkTxdr6K14Z0286lodIJ8Ny77mWuWyBkWKxf70OYWXXAMxMJFUW+6al1F3/Osf/pTg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@libsql/darwin-x64": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/@libsql/darwin-x64/-/darwin-x64-0.3.10.tgz", + "integrity": "sha512-SNVN6n4qNUdMW1fJMFmx4qn4n5RnXsxjFbczpkzG/V7m/5VeTFt1chhGcrahTHCr3+K6eRJWJUEQHRGqjBwPkw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@libsql/hrana-client": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/@libsql/hrana-client/-/hrana-client-0.5.6.tgz", + "integrity": "sha512-mjQoAmejZ1atG+M3YR2ZW+rg6ceBByH/S/h17ZoYZkqbWrvohFhXyz2LFxj++ARMoY9m6w3RJJIRdJdmnEUlFg==", + "dependencies": { + "@libsql/isomorphic-fetch": "^0.1.12", + "@libsql/isomorphic-ws": "^0.1.5", + "js-base64": "^3.7.5", + "node-fetch": "^3.3.2" + } + }, + "node_modules/@libsql/isomorphic-fetch": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@libsql/isomorphic-fetch/-/isomorphic-fetch-0.1.12.tgz", + "integrity": "sha512-MRo4UcmjAGAa3ac56LoD5OE13m2p0lu0VEtZC2NZMcogM/jc5fU9YtMQ3qbPjFJ+u2BBjFZgMPkQaLS1dlMhpg==", + "dependencies": { + "@types/node-fetch": "^2.6.11", + "node-fetch": "^2.7.0" + } + }, + "node_modules/@libsql/isomorphic-fetch/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@libsql/isomorphic-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/@libsql/isomorphic-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/@libsql/isomorphic-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/@libsql/isomorphic-ws": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@libsql/isomorphic-ws/-/isomorphic-ws-0.1.5.tgz", + "integrity": "sha512-DtLWIH29onUYR00i0GlQ3UdcTRC6EP4u9w/h9LxpUZJWRMARk6dQwZ6Jkd+QdwVpuAOrdxt18v0K2uIYR3fwFg==", + "dependencies": { + "@types/ws": "^8.5.4", + "ws": "^8.13.0" + } + }, + "node_modules/@libsql/linux-arm64-gnu": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/@libsql/linux-arm64-gnu/-/linux-arm64-gnu-0.3.10.tgz", + "integrity": "sha512-2uXpi9d8qtyIOr7pyG4a88j6YXgemyIHEs2Wbp+PPletlCIPsFS+E7IQHbz8VwTohchOzcokGUm1Bc5QC+A7wg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@libsql/linux-arm64-musl": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/@libsql/linux-arm64-musl/-/linux-arm64-musl-0.3.10.tgz", + "integrity": "sha512-72SN1FUavLvzHddCS861ynSpQndcW5oLGKA3U8CyMfgIZIwJAPc7+48Uj1plW00htXBx4GBpcntFp68KKIx3YQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@libsql/linux-x64-gnu": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/@libsql/linux-x64-gnu/-/linux-x64-gnu-0.3.10.tgz", + "integrity": "sha512-hXyNqVRi7ONuyWZ1SX6setxL0QaQ7InyS3bHLupsi9s7NpOGD5vcpTaYicJOqmIIm+6kt8vJfmo7ZxlarIHy7Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@libsql/linux-x64-musl": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/@libsql/linux-x64-musl/-/linux-x64-musl-0.3.10.tgz", + "integrity": "sha512-kNmIRxomVwt9S+cLyYS497F/3gXFF4r8wW12YSBQgxG75JYft07AHVd8J7HINg+oqRkLzT0s+mVX5dM6nk68EQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@libsql/win32-x64-msvc": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/@libsql/win32-x64-msvc/-/win32-x64-msvc-0.3.10.tgz", + "integrity": "sha512-c/6rjdtGULKrJkLgfLobFefObfOtxjXGmCfPxv6pr0epPCeUEssfDbDIeEH9fQUgzogIMWEHwT8so52UJ/iT1Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] }, "node_modules/@mongodb-js/saslprep": { "version": "1.1.1", @@ -858,6 +1066,11 @@ "sparse-bitfield": "^3.0.3" } }, + "node_modules/@neon-rs/load": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@neon-rs/load/-/load-0.0.4.tgz", + "integrity": "sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw==" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1046,6 +1259,11 @@ "win32" ] }, + "node_modules/@shikijs/core": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.2.0.tgz", + "integrity": "sha512-OlFvx+nyr5C8zpcMBnSGir0YPD6K11uYhouqhNmm1qLiis4GA7SsGtu07r9gKS9omks8RtQqHrJL4S+lqWK01A==" + }, "node_modules/@types/alpinejs": { "version": "3.13.5", "resolved": "https://registry.npmjs.org/@types/alpinejs/-/alpinejs-3.13.5.tgz", @@ -1143,6 +1361,15 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/node-fetch": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, "node_modules/@types/sax": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", @@ -1169,6 +1396,14 @@ "@types/webidl-conversions": "*" } }, + "node_modules/@types/ws": { + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", @@ -1305,21 +1540,20 @@ } }, "node_modules/astro": { - "version": "4.4.8", - "resolved": "https://registry.npmjs.org/astro/-/astro-4.4.8.tgz", - "integrity": "sha512-jAAfzvgHOfuZmqJjnSDownVNuWKYmsGkrFqecEppHarMA/cZ5QIboW6GQHZ8eGZQdtYRCjON3h4i64XFZfuwSg==", + "version": "4.5.8", + "resolved": "https://registry.npmjs.org/astro/-/astro-4.5.8.tgz", + "integrity": "sha512-bhKsoZQWT6LW8aYMvTGglZTPa3rYRbcdc2buiJyFPCvSWOOyZmxqpp6vtQ+x6fXxD8P1NecM/c4g5GaPi+MokQ==", "dependencies": { - "@astrojs/compiler": "^2.5.3", - "@astrojs/internal-helpers": "0.2.1", - "@astrojs/markdown-remark": "4.2.1", + "@astrojs/compiler": "^2.7.0", + "@astrojs/internal-helpers": "0.3.0", + "@astrojs/markdown-remark": "4.3.1", "@astrojs/telemetry": "3.0.4", - "@babel/core": "^7.23.3", + "@babel/core": "^7.24.3", "@babel/generator": "^7.23.3", "@babel/parser": "^7.23.3", "@babel/plugin-transform-react-jsx": "^7.22.5", "@babel/traverse": "^7.23.3", "@babel/types": "^7.23.3", - "@medv/finder": "^3.1.0", "@types/babel__core": "^7.20.4", "acorn": "^8.11.2", "aria-query": "^5.3.0", @@ -1350,7 +1584,6 @@ "js-yaml": "^4.1.0", "kleur": "^4.1.4", "magic-string": "^0.30.3", - "mdast-util-to-hast": "13.0.2", "mime": "^3.0.0", "ora": "^7.0.1", "p-limit": "^5.0.0", @@ -1361,8 +1594,7 @@ "rehype": "^13.0.1", "resolve": "^1.22.4", "semver": "^7.5.4", - "shikiji": "^0.9.19", - "shikiji-core": "^0.9.19", + "shiki": "^1.1.2", "string-width": "^7.0.0", "strip-ansi": "^7.1.0", "tsconfck": "^3.0.0", @@ -1372,7 +1604,8 @@ "vitefu": "^0.2.5", "which-pm": "^2.1.1", "yargs-parser": "^21.1.1", - "zod": "^3.22.4" + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.4" }, "bin": { "astro": "astro.js" @@ -1443,6 +1676,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/async-listen": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/async-listen/-/async-listen-3.0.1.tgz", + "integrity": "sha512-cWMaNwUJnf37C/S5TfCkk/15MwbPRwVYALA2jtjkbHjCmAPiDXyNJy2q3p1KAZzDLHAWyarUWSujUoHR4pEgrA==", + "engines": { + "node": ">= 14" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/autoprefixer": { "version": "10.4.16", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.16.tgz", @@ -1623,9 +1869,9 @@ } }, "node_modules/browserslist": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", - "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", "funding": [ { "type": "opencollective", @@ -1641,9 +1887,9 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001541", - "electron-to-chromium": "^1.4.535", - "node-releases": "^2.0.13", + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", "update-browserslist-db": "^1.0.13" }, "bin": { @@ -1684,6 +1930,20 @@ "ieee754": "^1.2.1" } }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/camelcase": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", @@ -1704,9 +1964,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001565", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001565.tgz", - "integrity": "sha512-xrE//a3O7TP0vaJ8ikzkD2c2NgcVUvsEe2IvFTntV4Yd1Z9FVzh+gW+enX96L0psrbaFMcVcH2l90xNuGDWc8w==", + "version": "1.0.30001599", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001599.tgz", + "integrity": "sha512-LRAQHZ4yT1+f9LemSMeqdMpMxZcc4RMWdj4tiFe3G8tNkWK+E58g+/tzotb5cU6TbcVJLr4fySiAW7XmxQvZQA==", "funding": [ { "type": "opencollective", @@ -1744,14 +2004,6 @@ "node": ">=4" } }, - "node_modules/chalk/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/character-entities": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", @@ -1923,6 +2175,17 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "optional": true }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/comma-separated-tokens": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", @@ -1987,6 +2250,14 @@ "node": ">=4" } }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "engines": { + "node": ">= 12" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -2030,6 +2301,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/deep-diff": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-1.0.2.tgz", + "integrity": "sha512-aWS3UIVH+NPGCD1kki+DCU9Dua032iSsO43LqQpcs4R3+dVv7tX0qBGjiVHJHjplsoUM2XRO/KB92glqc68awg==" + }, "node_modules/deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -2039,6 +2315,51 @@ "node": ">=4.0.0" } }, + "node_modules/default-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -2051,7 +2372,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", - "optional": true, "engines": { "node": ">=8" } @@ -2102,6 +2422,111 @@ "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" }, + "node_modules/drizzle-orm": { + "version": "0.30.4", + "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.30.4.tgz", + "integrity": "sha512-kWoSMGbrOFkmkAweLTFtHJMpN+nwhx89q0mLELqT2aEU+1szNV8jrnBmJwZ0WGNp7J7yQn/ezEtxBI/qzTSElQ==", + "peerDependencies": { + "@aws-sdk/client-rds-data": ">=3", + "@cloudflare/workers-types": ">=3", + "@libsql/client": "*", + "@neondatabase/serverless": ">=0.1", + "@op-engineering/op-sqlite": ">=2", + "@opentelemetry/api": "^1.4.1", + "@planetscale/database": ">=1", + "@types/better-sqlite3": "*", + "@types/pg": "*", + "@types/react": ">=18", + "@types/sql.js": "*", + "@vercel/postgres": "*", + "@xata.io/client": "*", + "better-sqlite3": ">=7", + "bun-types": "*", + "expo-sqlite": ">=13.2.0", + "knex": "*", + "kysely": "*", + "mysql2": ">=2", + "pg": ">=8", + "postgres": ">=3", + "react": ">=18", + "sql.js": ">=1", + "sqlite3": ">=5" + }, + "peerDependenciesMeta": { + "@aws-sdk/client-rds-data": { + "optional": true + }, + "@cloudflare/workers-types": { + "optional": true + }, + "@libsql/client": { + "optional": true + }, + "@neondatabase/serverless": { + "optional": true + }, + "@op-engineering/op-sqlite": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@types/better-sqlite3": { + "optional": true + }, + "@types/pg": { + "optional": true + }, + "@types/react": { + "optional": true + }, + "@types/sql.js": { + "optional": true + }, + "@vercel/postgres": { + "optional": true + }, + "@xata.io/client": { + "optional": true + }, + "better-sqlite3": { + "optional": true + }, + "bun-types": { + "optional": true + }, + "expo-sqlite": { + "optional": true + }, + "knex": { + "optional": true + }, + "kysely": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "pg": { + "optional": true + }, + "postgres": { + "optional": true + }, + "react": { + "optional": true + }, + "sql.js": { + "optional": true + }, + "sqlite3": { + "optional": true + } + } + }, "node_modules/dset": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.3.tgz", @@ -2116,9 +2541,9 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, "node_modules/electron-to-chromium": { - "version": "1.4.598", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.598.tgz", - "integrity": "sha512-0JnipX0scPUlwsptQVCZggoCpREv+IrVD3h0ZG+sldmK9L27tSV3QjV8+QdaA4qQTzDf3PluNS45YYJky1oASw==" + "version": "1.4.712", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.712.tgz", + "integrity": "sha512-ncfPC8UnGIyGFrPE03J5Xn6yTZ6R+clkcZbuG1PJbjAaZBFS4Kn3UKfzu8eilzru6SfC8TPsHuwv0p0eYVu+ww==" }, "node_modules/emoji-regex": { "version": "8.0.0", @@ -2195,14 +2620,11 @@ } }, "node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.8.0" } }, "node_modules/esprima": { @@ -2306,6 +2728,28 @@ "reusify": "^1.0.4" } }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -2349,6 +2793,30 @@ "node": ">=8" } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -2558,6 +3026,18 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hast-util-is-element": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", + "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hast-util-parse-selector": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", @@ -2635,6 +3115,21 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hast-util-to-text": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.0.tgz", + "integrity": "sha512-EWiE1FSArNBPUo1cKWtzqgnuRQwEeQbQtnFJRYV1hb1BWDgrAlBU0ExptvZMM/KSA82cDpm2sFGf3Dmc5Mza3w==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "unist-util-find-after": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hast-util-whitespace": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", @@ -2933,6 +3428,11 @@ "jiti": "bin/jiti.js" } }, + "node_modules/js-base64": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.7.tgz", + "integrity": "sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -2992,6 +3492,34 @@ "node": ">=6" } }, + "node_modules/libsql": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/libsql/-/libsql-0.3.10.tgz", + "integrity": "sha512-/8YMTbwWFPmrDWY+YFK3kYqVPFkMgQre0DGmBaOmjogMdSe+7GHm1/q9AZ61AWkEub/vHmi+bA4tqIzVhKnqzg==", + "cpu": [ + "x64", + "arm64", + "wasm32" + ], + "os": [ + "darwin", + "linux", + "win32" + ], + "dependencies": { + "@neon-rs/load": "^0.0.4", + "detect-libc": "2.0.2" + }, + "optionalDependencies": { + "@libsql/darwin-arm64": "0.3.10", + "@libsql/darwin-x64": "0.3.10", + "@libsql/linux-arm64-gnu": "0.3.10", + "@libsql/linux-arm64-musl": "0.3.10", + "@libsql/linux-x64-gnu": "0.3.10", + "@libsql/linux-x64-musl": "0.3.10", + "@libsql/win32-x64-msvc": "0.3.10" + } + }, "node_modules/lilconfig": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz", @@ -3137,6 +3665,17 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mdast-util-from-markdown": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.0.tgz", @@ -3894,6 +4433,25 @@ "node": ">=10.0.0" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/mimic-fn": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", @@ -4065,10 +4623,45 @@ "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", "optional": true }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, "node_modules/node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==" + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" }, "node_modules/normalize-path": { "version": "3.0.0", @@ -4149,6 +4742,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/open": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.1.0.tgz", + "integrity": "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ora": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/ora/-/ora-7.0.1.tgz", @@ -5328,6 +5938,17 @@ "fsevents": "~2.3.2" } }, + "node_modules/run-applescript": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", + "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -5435,19 +6056,14 @@ "node": ">=8" } }, - "node_modules/shikiji": { - "version": "0.9.19", - "resolved": "https://registry.npmjs.org/shikiji/-/shikiji-0.9.19.tgz", - "integrity": "sha512-Kw2NHWktdcdypCj1GkKpXH4o6Vxz8B8TykPlPuLHOGSV8VkhoCLcFOH4k19K4LXAQYRQmxg+0X/eM+m2sLhAkg==", + "node_modules/shiki": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.2.0.tgz", + "integrity": "sha512-xLhiTMOIUXCv5DqJ4I70GgQCtdlzsTqFLZWcMHHG3TAieBUbvEGthdrlPDlX4mL/Wszx9C6rEcxU6kMlg4YlxA==", "dependencies": { - "shikiji-core": "0.9.19" + "@shikijs/core": "1.2.0" } }, - "node_modules/shikiji-core": { - "version": "0.9.19", - "resolved": "https://registry.npmjs.org/shikiji-core/-/shikiji-core-0.9.19.tgz", - "integrity": "sha512-AFJu/vcNT21t0e6YrfadZ+9q86gvPum6iywRyt1OtIPjPFe25RQnYJyxHQPMLKCCWA992TPxmEmbNcOZCAJclw==" - }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -6023,6 +6639,19 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/unist-util-find-after": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", + "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/unist-util-is": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", @@ -6065,6 +6694,19 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/unist-util-remove-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", + "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/unist-util-stringify-position": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", @@ -6271,6 +6913,14 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "engines": { + "node": ">= 8" + } + }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", @@ -6413,6 +7063,26 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, + "node_modules/ws": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", @@ -6453,6 +7123,14 @@ "url": "https://github.com/sponsors/colinhacks" } }, + "node_modules/zod-to-json-schema": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.22.4.tgz", + "integrity": "sha512-2Ed5dJ+n/O3cU383xSY28cuVi0BCQhF8nYqWU5paEpl7fVdqdAmiLdqLyfblbNdfOFwFfi/mqU4O1pwc60iBhQ==", + "peerDependencies": { + "zod": "^3.22.4" + } + }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", diff --git a/package.json b/package.json index edd0814..128de7c 100644 --- a/package.json +++ b/package.json @@ -11,12 +11,13 @@ }, "dependencies": { "@alpinejs/collapse": "^3.13.3", + "@astrojs/db": "^0.9.1", "@astrojs/prefetch": "^0.4.1", "@astrojs/sitemap": "^3.1.1", "@astrojs/tailwind": "^5.1.0", "@types/alpinejs": "^3.13.5", "alpinejs": "^3.13.3", - "astro": "^4.4.8", + "astro": "^4.5.8", "mongodb": "^6.3.0", "tailwindcss": "^3.3.5" } diff --git a/src/env.d.ts b/src/env.d.ts index d1cce7c..0751073 100644 --- a/src/env.d.ts +++ b/src/env.d.ts @@ -1,3 +1,5 @@ +/// +/// /// interface ImportMetaEnv { readonly MONGODB_URI: string; diff --git a/src/lib/components/session-results/race.astro b/src/lib/components/session-results/race.astro index f4b9158..35a74ea 100644 --- a/src/lib/components/session-results/race.astro +++ b/src/lib/components/session-results/race.astro @@ -1,10 +1,6 @@ --- import SessionResults from "$lib/components/subsession/session-results.astro"; import keysToDisplay from "$lib/utils/race-session-keys-to-display"; -import type { Session } from "$lib/types"; -interface Props { - session: Session; -} const { session } = Astro.props; --- diff --git a/src/lib/components/subsession/header.astro b/src/lib/components/subsession/header.astro index 09b862d..4eb4920 100644 --- a/src/lib/components/subsession/header.astro +++ b/src/lib/components/subsession/header.astro @@ -1,9 +1,5 @@ --- import { Image } from "astro:assets"; -import type { Subsession } from "$lib/types"; -interface Props { - subsession: Subsession; -} const { subsession } = Astro.props; const { series_logo, season_name, race_week_num, max_weeks } = subsession; --- diff --git a/src/lib/components/subsession/licenseInfo.astro b/src/lib/components/subsession/licenseInfo.astro index 2fc76d3..18dc527 100644 --- a/src/lib/components/subsession/licenseInfo.astro +++ b/src/lib/components/subsession/licenseInfo.astro @@ -1,9 +1,5 @@ --- import type { License } from "$lib/types"; -interface Props { - licenses: Array; - licenseCategory: string; -} const { licenses, licenseCategory } = Astro.props; const [allowedLicenseOne, allowLicenseTwo] = licenses || []; const allowedLicenses = [allowedLicenseOne, allowLicenseTwo]; diff --git a/src/lib/components/subsession/raceSummary.astro b/src/lib/components/subsession/raceSummary.astro index ea83bfd..b8b2b4b 100644 --- a/src/lib/components/subsession/raceSummary.astro +++ b/src/lib/components/subsession/raceSummary.astro @@ -1,9 +1,5 @@ --- import parseLapTime from "$lib/utils/parse-lap-time"; -import type { Subsession } from "$lib/types"; -interface Props { - subsession: Subsession; -} const { subsession } = Astro.props; const { event_average_lap, diff --git a/src/lib/components/subsession/relevantIds.astro b/src/lib/components/subsession/relevantIds.astro index e9ca185..9096006 100644 --- a/src/lib/components/subsession/relevantIds.astro +++ b/src/lib/components/subsession/relevantIds.astro @@ -1,9 +1,5 @@ --- import type { Session, SessionResult, Subsession } from "$lib/types"; -interface Props { - subsession: Subsession; - sessions: Array; -} const { subsession, sessions } = Astro.props; const relevantIds = Object.keys(subsession) .sort() @@ -16,7 +12,7 @@ const relevantIds = Object.keys(subsession) (key) => `${key}: ${JSON.stringify(subsession[key as keyof Subsession])}` ); const relevantIdMap: Record> = {}; -const excludeKeys = new Set(["cust_id", "reason_out_id"]); +const excludeKeys = new Set(["_id", "cust_id", "reason_out_id"]); sessions.forEach((session: Session) => { const { results } = session; results.forEach((result) => { diff --git a/src/lib/components/track/info.astro b/src/lib/components/track/info.astro index e325c4c..812a239 100644 --- a/src/lib/components/track/info.astro +++ b/src/lib/components/track/info.astro @@ -1,9 +1,4 @@ --- -import type { Track } from "$lib/types"; -interface Props { - cornersPerLap: number; - track: Track; -} const { cornersPerLap, track } = Astro.props; const { track_name, config_name, category } = track; --- diff --git a/src/lib/layouts/standings.astro b/src/lib/layouts/standings.astro index 99eaea6..1a0f0ff 100644 --- a/src/lib/layouts/standings.astro +++ b/src/lib/layouts/standings.astro @@ -9,11 +9,8 @@ const finalData: Array> = standingsResults.map( division, division_rank, overall_rank, - season_driver_data, season_id, season_name, - } = result; - const { weeks_counted, starts, wins, @@ -27,7 +24,7 @@ const finalData: Array> = standingsResults.map( laps_led, incidents, points, - } = season_driver_data; + } = result; const [, year] = season_name.match(/([0-9]{4}) Season/) || [0, 0]; const [, season_number_of_year] = season_name.match( /Season ([0-9]{1})/ diff --git a/src/lib/layouts/subsessions.astro b/src/lib/layouts/subsessions.astro index 337e620..23b8e3e 100644 --- a/src/lib/layouts/subsessions.astro +++ b/src/lib/layouts/subsessions.astro @@ -5,7 +5,7 @@ import lapTimeFields from "$lib/utils/lap-time-fields"; import parseLapTime from "$lib/utils/parse-lap-time"; import type { Subsession } from "$lib/types"; import DefaultLayout from "$lib/layouts/default.astro"; -const { id, seoTitle, seoDescription, subsessions } = Astro.props; +const { seoTitle, seoDescription, subsessions } = Astro.props; const results = subsessions.map((subsession: Subsession) => { const { subsession_id, @@ -13,6 +13,7 @@ const results = subsessions.map((subsession: Subsession) => { corners_per_lap, allowed_licenses, license_category, + userResult, } = subsession; // Current user in Race session; const { @@ -34,9 +35,7 @@ const results = subsessions.map((subsession: Subsession) => { incidents, car_name, aggregate_champ_points, - } = subsession.session_results[2].results.filter( - (v: any) => v.cust_id === Number(id) - )[0]; + } = userResult; const { group_name: License } = allowed_licenses.slice(0, 2).pop() || {}; return { diff --git a/src/lib/types/index.ts b/src/lib/types/index.ts index 6b95228..89d0d2e 100644 --- a/src/lib/types/index.ts +++ b/src/lib/types/index.ts @@ -1,51 +1,10 @@ -export type Car = { - _id: number; - ai_enabled: boolean; - allow_number_colors: boolean; - allow_number_font: boolean; - allow_sponsor1: boolean; - allow_sponsor2: boolean; - allow_wheel_color: boolean; - award_exempt: boolean; - car_dirpath: string; - car_id: number; - car_name: string; - car_name_abbreviated: string; - car_types: Array<{ car_type: string }>; - car_weight: number; - categories: Array; - created: string; - first_sale: string; - forum_url: string; - free_with_subscription: boolean; - has_headlights: boolean; - has_multiple_dry_tires_types: boolean; - hp: number; - is_ps_purchasable: boolean; - max_power_adjust_pct: number; - max_weight_penalty_kg: number; - min_power_adjust_pct: number; - package_id: number; - patterns: number; - price: number; - price_display: string; - retired: boolean; - search_filters: string; - sku: number; -}; - -export type CarClass = { +export type CarClassType = { car_class_id: number; cars_in_class: Array<{ car_id: number; car_name: string | undefined }>; name: string; short_name: string; }; -export type CarClassMapping = { - car_class_id: number; - car_class_short_name: string; -}; - export type Helmet = { pattern: number; color1: string; @@ -95,155 +54,16 @@ export type RaceSummary = { special_event_type_text: string; }; -export type RaceWeek = { - season_id: number; - race_week_num: number; - series_id: number; - series_name: string; - season_name: string; - schedule_name: string; - start_date: string; - simulated_time_multiplier: number; - race_lap_limit: number | null; - race_time_limit: number | null; - start_type: string; - restart_type: string; - qual_attached: boolean; - full_course_cautions: boolean; - special_event_type: unknown; - start_zone: boolean; - enable_pitlane_collisions: boolean; - short_parade_lap: boolean; - track: Track; - weather: Weather; - track_state: TrackState; - race_time_descriptors: Array; - car_restrictions: Array; - race_week_cars: Array; -}; - -export type SchedulingSeason = { - _id: number; - active: boolean; - allowed_season_members: unknown; - car_class_ids: Array; - car_switching: boolean; - car_types: Array<{ car_type: string }>; - caution_laps_do_not_count: boolean; - complete: boolean; - cross_license: boolean; - driver_change_rule: number; - driver_changes: boolean; - drops: number; - enable_pitlane_collisions: boolean; - fixed_setup: boolean; - green_white_checkered_limit: number; - grid_by_class: boolean; - hardcore_level: number; - ignore_license_for_practice: boolean; - incident_limit: number; - incidient_warn_mode: number; - incident_warn_param1: number; - incident_warn_param2: number; - is_heat_racing: boolean; - license_group: number; - license_group_types: Array<{ license_group_type: number }>; - lucky_dog: boolean; - max_team_drivers: number; - max_weeks: number; - min_team_drivers: number; - multiclass: boolean; - must_user_diff_tire_types_in_race: boolean; - next_race_session: unknown; - num_opt_laps: number; - official: boolean; - op_duration: number; - open_practice_session_type_id: number; - qualifier_must_start_race: boolean; - race_week: number; - race_week_to_make_division: number; - reg_user_count: number; - region_competition: boolean; - restrict_by_member: boolean; - restrict_to_car: boolean; - restrict_viewing: boolean; - schedule_description: string; - schedules: Array; - season_id: number; - season_name: string; - season_quarter: number; - season_short_name: string; - season_year: number; - send_to_open_practice: boolean; - series_id: number; - short_parade_lap: boolean; - start_date: string; - start_on_qual_tire: boolean; - start_zone: boolean; - track_types: Array<{ track_type: string }>; - unsport_conduct_rule_mode: number; -}; - export type SchedulingTableResult = { "Season Name": string; "License Level": string; - carClassData: Array; + carClassData: Array; tracks: Array; ownedTrackWeeks: Array; "Own Car": string; "Overall Track Percentage Owned": number; }; -export type Season = { - _id: string; - car_class_id: string; - division: number; - overall_rank: number; - season_driver_data: SeasonDriverData; - season_id: string; - season_name: string; - division_rank: number; -}; - -export type SeasonDriverData = { - rank: number; - cust_id: number; - display_name: number; - division: number; - club_id: number; - club_name: string; - license: SeasonDriverLicense; - helmet: Helmet; - weeks_counted: number; - starts: number; - wins: number; - top5: number; - top25_percent: number; - poles: number; - avg_start_position: number; - avg_finish_position: number; - avg_field_size: number; - laps: number; - laps_led: number; - incidents: number; - points: number; - raw_points: number; - week_dropped: boolean; - country_code: string; - country: string; -}; - -export type SeasonDriverLicense = { - category_id: number; - category: string; - license_level: number; - safety_rating: number; - irating: number; - color: string; - group_name: string; - group_id: number; -}; - export type Session = { simsession_number: number; simsession_type: number; @@ -322,7 +142,7 @@ export type Subsession = { allowed_licenses: Array; associated_subsession_ids: Array; can_protest: boolean; - car_classes: Array; + car_classes: Array; caution_type: number; cooldown_minutes: number; corners_per_lap: number; @@ -366,6 +186,7 @@ export type Subsession = { series_short_name: string; session_id: number; session_results: Array; + userResult: SessionResult, special_event_type: number; start_time: string; subsession_id: number; @@ -402,16 +223,6 @@ export type TrackState = { race_grip_compound: number; }; -export type UserIdCarClassIdsMapping = { - userId: number; - carClassIds: Array; -}; - -export type UserIdYearsMapping = { - userId: number; - years: Array; -}; - export type Weather = { version: number; type: number; diff --git a/src/pages/shared-subsessions.astro b/src/pages/shared-subsessions.astro index 9b21db7..80ee110 100644 --- a/src/pages/shared-subsessions.astro +++ b/src/pages/shared-subsessions.astro @@ -1,25 +1,57 @@ --- -import { connectToDatabase } from "$lib/mongodb"; -import type { Session, SessionResult, Subsession } from "$lib/types"; +import { + db, + gt, + sql, + count, + desc, + Subsession, + SubsessionRaceResults, +} from "astro:db"; import DefaultLayout from "$lib/layouts/default.astro"; import RaceResults from "$lib/components/session-results/race.astro"; import userIds from "$lib/utils/user-ids"; import parseLapTime from "$lib/utils/parse-lap-time"; import fieldIdToLabelMap from "$lib/utils/field-id-to-label-map"; -import keysToDisplay from "$lib/utils/race-session-keys-to-display"; import { bgColorByUserDisplayName } from "$lib/utils/background-color-by-user"; -const dbConnection = await connectToDatabase(); -const { db } = dbConnection; -const collection = db.collection("subsessions"); -const $and = userIds.map((userId) => ({ - "session_results.2.results": { $elemMatch: { cust_id: userId } }, -})); -const subsessions = await collection.find({ $and }).sort({ _id: -1 }).toArray(); +import type { Track } from "$lib/types"; +const sharedRaceSessions = await db + .select({ + subsession_id: SubsessionRaceResults.subsession_id, + count: count(SubsessionRaceResults.subsession_id), + }) + .from(SubsessionRaceResults) + .where( + sql`${SubsessionRaceResults.cust_id} IN ${userIds} AND ${SubsessionRaceResults.simsession_number} = 0 GROUP BY ${SubsessionRaceResults.subsession_id}` + ) + .having(({ count }) => gt(count, 1)); +const sharedSubsessionIds = sharedRaceSessions.map((v) => v.subsession_id); +const sharedSubsessions = await db + .select() + .from(Subsession) + .where(sql`${Subsession.subsession_id} IN ${sharedSubsessionIds}`) + .orderBy(desc(Subsession.subsession_id)); +const sharedSubsessionRaceResults = await db + .select() + .from(SubsessionRaceResults) + .where( + sql`${SubsessionRaceResults.cust_id} IN ${userIds} AND ${SubsessionRaceResults.simsession_number} = 0 AND ${SubsessionRaceResults.subsession_id} IN ${sharedSubsessionIds}` + ); +const subsessionIdToSharedRaceResultsMap = sharedSubsessionRaceResults.reduce( + (map: Map>, value) => { + const { subsession_id } = value; + const currentValue = map.get(subsession_id); + if (currentValue) { + map.set(subsession_id, [...currentValue, value]); + } else { + map.set(subsession_id, [value]); + } + return map; + }, + new Map() +); const headToHeadWins: Record> = {}; -const results: Array<{ - subsessionInfo: Record; - session: Session; -}> = subsessions.map((subsession: Subsession) => { +const results = sharedSubsessions.map((subsession) => { const { subsession_id, track, @@ -34,44 +66,42 @@ const results: Array<{ race_week_num, season_name, } = subsession; - const { results: allUserResults, ...rest } = subsession.session_results[2]; // Current Users in race session - const results = allUserResults - .filter((v: SessionResult) => userIds.includes(v.cust_id)) - .map((w: SessionResult) => { - return Object.keys(w).reduce((object, key) => { - if (keysToDisplay.has(key)) { - // @ts-ignore - object[key] = w[key]; - } - return object; - }, {} as SessionResult); - }) - .sort((x: SessionResult, y: SessionResult) => { - if (x.finish_position > y.finish_position) { - return 1; - } else if (y.finish_position > x.finish_position) { - return -1; - } - return 0; - }); + const subsessionResultsForId = + subsessionIdToSharedRaceResultsMap.get(subsession_id); + const results = + subsessionResultsForId && Array.isArray(subsessionResultsForId) + ? subsessionResultsForId.sort((x, y) => { + if (x.finish_position > y.finish_position) { + return 1; + } else if (y.finish_position > x.finish_position) { + return -1; + } + return 0; + }) + : []; + + const [winner] = results; + const { display_name, simsession_type_name } = winner; const session = { - ...rest, + simsession_type_name, results, }; - const [winner] = results; - const { display_name } = winner; if (headToHeadWins[display_name]) { headToHeadWins[display_name].push(subsession_id); } else { headToHeadWins[display_name] = [subsession_id]; } - const { group_name: License } = allowed_licenses.slice(0, 2).pop() || {}; + const { group_name: License } = Array.isArray(allowed_licenses) + ? allowed_licenses.slice(0, 2).pop() + : { group_name: "" }; + const assertTrackAsTrackType = () => track as Track; + const trackInfo = assertTrackAsTrackType(); return { subsessionInfo: { subsession_id, - Track: `${track.track_name} ${ - track.config_name !== "N/A" ? track.config_name : "" + Track: `${trackInfo.track_name} ${ + trackInfo.config_name !== "N/A" ? trackInfo.config_name : "" }`, License, license_category, diff --git a/src/pages/subsession/[id].astro b/src/pages/subsession/[id].astro index d5a1daf..59934ae 100644 --- a/src/pages/subsession/[id].astro +++ b/src/pages/subsession/[id].astro @@ -1,5 +1,13 @@ --- -import { connectToDatabase } from "$lib/mongodb"; +import { + db, + eq, + asc, + Subsession, + SubsessionPracticeResults, + SubsessionQualifyingResults, + SubsessionRaceResults, +} from "astro:db"; import DefaultLayout from "$lib/layouts/default.astro"; import Header from "$lib/components/subsession/header.astro"; import TrackInfo from "$lib/components/track/info.astro"; @@ -7,22 +15,98 @@ import LicenseInfo from "$lib/components/subsession/licenseInfo.astro"; import RaceSummary from "$lib/components/subsession/raceSummary.astro"; import RelevantIds from "$lib/components/subsession/relevantIds.astro"; import SessionResultsTables from "$lib/components/session-results/tables.astro"; -import type { Subsession } from "$lib/types"; export async function getStaticPaths() { - const dbConnection = await connectToDatabase(); - const db = dbConnection.db; - const collection = db.collection("subsessions"); - const subsessionIds: Array = await collection.distinct("_id", {}); - return subsessionIds.map((id) => ({ params: { id } })); + const subsessions = await db + .selectDistinct({ subsession_id: Subsession.subsession_id }) + .from(Subsession); + return subsessions.map(({ subsession_id: id }) => ({ params: { id } })); } const { id } = Astro.params; -const dbConnection = await connectToDatabase(); -const db = dbConnection.db; -const collection = db.collection("subsessions"); -const subsession: Subsession = await collection.findOne({ _id: Number(id) }); +const [subsession] = await db + .select() + .from(Subsession) + .where(eq(Subsession.subsession_id, Number(id))); +const practiceResults = await db + .select() + .from(SubsessionPracticeResults) + .where(eq(SubsessionPracticeResults.subsession_id, Number(id))) + .orderBy(asc(SubsessionPracticeResults.finish_position)); +const qualifyingResults = await db + .select() + .from(SubsessionQualifyingResults) + .where(eq(SubsessionQualifyingResults.subsession_id, Number(id))) + .orderBy(asc(SubsessionQualifyingResults.finish_position)); +const raceResults = await db + .select() + .from(SubsessionRaceResults) + .where(eq(SubsessionRaceResults.subsession_id, Number(id))) + .orderBy(asc(SubsessionRaceResults.finish_position)); +const [practiceResultOne] = practiceResults || [{}]; +const [qualifyingResultOne] = qualifyingResults || [{}]; +const splitRaceResults = raceResults.find((result) => + result.simsession_name.match(/HEAT/i) +) + ? raceResults.reduce( + (object, raceResult) => { + const { simsession_name } = raceResult; + if (simsession_name.match(/HEAT/i)) { + // @ts-ignore + object.heatResults.push(raceResult); + } else { + // @ts-ignore + object.mainResults.push(raceResult); + } + return object; + }, + { heatResults: [], mainResults: [] } + ) + : { heatResults: [], mainResults: [] }; +const session_results = [ + { + simsession_type_name: + practiceResultOne?.simsession_type_name || "Open Practice", + simsession_name: practiceResultOne?.simsession_name || "PRACTICE", + results: practiceResults, + }, + { + simsession_type_name: + qualifyingResultOne?.simsession_type_name || "Lone Qualifying", + simsession_name: qualifyingResultOne?.simsession_name || "QUALIFY", + results: qualifyingResults, + }, +]; +const { heatResults, mainResults } = splitRaceResults; +if (heatResults.length && mainResults.length) { + const [heatResultOne] = heatResults; + const [mainResultOne] = mainResults; + session_results.push( + ...[ + { + // @ts-ignore + simsession_type_name: heatResultOne?.simsession_type_name || "Race", + // @ts-ignore + simsession_name: heatResultOne?.simsession_name || "HEAT", + results: heatResults, + }, + { + // @ts-ignore + simsession_type_name: mainResultOne?.simsession_type_name || "Race", + // @ts-ignore + simsession_name: mainResultOne?.simsession_name || "RACE", + results: mainResults, + }, + ] + ); +} else { + const [raceResultOne] = raceResults || [{}]; + session_results.push({ + simsession_type_name: raceResultOne.simsession_type_name || "Race", + simsession_name: raceResultOne.simsession_name || "RACE", + results: raceResults, + }); +} const { track, - session_results, corners_per_lap: cornersPerLap, allowed_licenses: licenses, license_category: licenseCategory, diff --git a/src/pages/user/[id]/scheduling/index.astro b/src/pages/user/[id]/scheduling/index.astro index f4ffb14..ef03ee6 100644 --- a/src/pages/user/[id]/scheduling/index.astro +++ b/src/pages/user/[id]/scheduling/index.astro @@ -1,103 +1,55 @@ --- -import { connectToDatabase } from "$lib/mongodb"; +import { db, eq, User, Car, CarClass, Season } from "astro:db"; import DefaultLayout from "$lib/layouts/default.astro"; -import type { - Car, - CarClass, - SchedulingSeason, - SchedulingTableResult, - Track, -} from "$lib/types"; +import type { CarClassType, SchedulingTableResult, Track } from "$lib/types"; export async function getStaticPaths() { - const dbConnection = await connectToDatabase(); - const db = dbConnection.db; - const userCollection = db.collection("users"); - const userIds: Array = await userCollection.distinct("_id", {}); - return userIds.map((id) => ({ params: { id } })); + const userIds = await db.selectDistinct({ id: User.cust_id }).from(User); + return userIds.map(({ id }) => ({ params: { id } })); } const { id } = Astro.params; -const dbConnection = await connectToDatabase(); -const db = dbConnection.db; -const userCollection = db.collection("users"); -const carPackages: Array<{ content_ids: Array }> = await userCollection - .aggregate([ - { - $match: { - _id: Number(id), - }, - }, - { - $unwind: "$car_packages", - }, - { - $replaceRoot: { - newRoot: { - $mergeObjects: ["$car_packages", "$$ROOT"], - }, - }, - }, - { - $project: { - _id: 0, - content_ids: 1, - }, - }, - ]) - .toArray(); -const carIds = carPackages.map((carPackage) => carPackage.content_ids).flat(); -const trackPackages: Array<{ content_ids: Array }> = - await userCollection - .aggregate([ - { - $match: { - _id: Number(id), - }, - }, - { - $unwind: "$track_packages", - }, - { - $replaceRoot: { - newRoot: { - $mergeObjects: ["$track_packages", "$$ROOT"], - }, - }, - }, - { - $project: { - _id: 0, - content_ids: 1, - }, - }, - ]) - .toArray(); -const trackIds = trackPackages - .map((trackPackage) => trackPackage.content_ids) - .flat(); -const carsCollection = db.collection("cars"); -const cars: Array = await carsCollection.find().toArray(); +const [user] = await db + .select({ + carPackages: User.car_packages, + trackPackages: User.track_packages, + }) + .from(User) + .where(eq(User.cust_id, id)); +const { carPackages, trackPackages } = user; +const carIds = Array.isArray(carPackages) + ? carPackages.map((carPackage) => carPackage.content_ids).flat() + : []; +const trackIds = Array.isArray(trackPackages) + ? trackPackages.map((trackPackage) => trackPackage.content_ids).flat() + : []; +const cars = await db.select().from(Car); const carsMap = new Map(); cars.forEach((car) => { const { car_id } = car; carsMap.set(car_id, car); }); -const carClassCollection = db.collection("carclasses"); -const carClassIdsForCarIds = await carClassCollection.distinct("car_class_id", { - "cars_in_class.car_id": { $in: carIds }, -}); -const carClasses: Array = await carClassCollection.find().toArray(); +// @todo Improve this query to not have to do this +const carClassesResults = await db + .select({ + carClassId: CarClass.car_class_id, + carsInClass: CarClass.cars_in_class, + }) + .from(CarClass); +const carClassIdsForCarIds = carClassesResults + .filter((result: any) => + result.carsInClass.some((v: any) => carIds.includes(v.car_id)) + ) + .map((result) => result.carClassId); +// End comment of previous todo +const carClasses = await db.select().from(CarClass); const carClassesMap = new Map(); carClasses.forEach((carClass) => { const { car_class_id, cars_in_class } = carClass; - const carsInClass = cars_in_class.map((carInClass) => - carsMap.get(carInClass.car_id) - ); + const carsInClass = Array.isArray(cars_in_class) + ? cars_in_class.map((carInClass) => carsMap.get(carInClass.car_id)) + : []; carClassesMap.set(car_class_id, { ...carClass, cars_in_class: carsInClass }); }); -const seasonsCollection = db.collection("seasons"); -const seasons: Array = await seasonsCollection - .find({}, {}) - .toArray(); +const seasons = await db.select().from(Season); const carClassIdSet = new Set(carClassIdsForCarIds); const trackIdSet = new Set(trackIds); const licenseGroupToNameMap: Record = { @@ -110,24 +62,28 @@ const licenseGroupToNameMap: Record = { const results: Array = seasons .map((season) => { const { schedules, season_name, car_class_ids } = season; - const carClassData: Array = car_class_ids.map( - (car_class_id: number) => carClassesMap.get(car_class_id) - ); - const tracks: Array = schedules.map( - (schedule: any, index: number) => ({ - race_week_num: index + 1, - ...schedule.track, - }) - ); + const carClassData: Array = Array.isArray(car_class_ids) + ? car_class_ids.map((car_class_id: number) => + carClassesMap.get(car_class_id) + ) + : []; + const tracks: Array = Array.isArray(schedules) + ? schedules.map((schedule: any, index: number) => ({ + race_week_num: index + 1, + ...schedule.track, + })) + : []; const ownedTrackWeeks: Array = tracks .filter((track: Track) => trackIdSet.has(track.track_id)) .map((track) => Number(track.race_week_num)); const trackPercentageOwned = Math.round( (ownedTrackWeeks.length / tracks.length) * 100 ); - const ownedCarClasses = car_class_ids.filter((carClassId: number) => - carClassIdSet.has(carClassId) - ); + const ownedCarClasses = Array.isArray(car_class_ids) + ? car_class_ids.filter((carClassId: number) => + carClassIdSet.has(carClassId) + ) + : []; return { "Season Name": String(season_name), "License Level": licenseGroupToNameMap[String(season.license_group)], diff --git a/src/pages/user/[id]/season/[seasonId]/car-class/[carClassId]/index.astro b/src/pages/user/[id]/season/[seasonId]/car-class/[carClassId]/index.astro index 172d7a4..0ec5f41 100644 --- a/src/pages/user/[id]/season/[seasonId]/car-class/[carClassId]/index.astro +++ b/src/pages/user/[id]/season/[seasonId]/car-class/[carClassId]/index.astro @@ -1,101 +1,118 @@ --- +import { + db, + and, + eq, + sql, + Standing, + Subsession, + SubsessionRaceResults, +} from "astro:db"; import DefaultLayout from "$lib/layouts/default.astro"; import handleResults from "$lib/utils/handle-session-results"; import fieldIdToLabelMap from "$lib/utils/field-id-to-label-map"; import lapTimeFields from "$lib/utils/lap-time-fields"; import parseLapTime from "$lib/utils/parse-lap-time"; -import { connectToDatabase } from "$lib/mongodb"; -import type { Season, Subsession } from "$lib/types"; import userIds from "$lib/utils/user-ids"; +import type { Track } from "$lib/types"; export async function getStaticPaths() { - const dbConnection = await connectToDatabase(); - const db = dbConnection.db; - const collection = db.collection("standings"); - const seasons: Array = await collection - .find({ "season_driver_data.cust_id": { $in: userIds } }) - .toArray(); + const seasons = await db + .select() + .from(Standing) + .where(sql`${Standing.cust_id} IN ${userIds}`); return seasons.map((season) => ({ params: { - id: Number(season.season_driver_data.cust_id), + id: Number(season.cust_id), seasonId: Number(season.season_id), carClassId: Number(season.car_class_id), }, })); } const { id, seasonId, carClassId } = Astro.params; -const dbConnection = await connectToDatabase(); -const db = dbConnection.db; -const collection = db.collection("subsessions"); -const subsessions: Array = await collection - .find({ - season_id: Number(seasonId), - "session_results.2.results": { - $elemMatch: { cust_id: Number(id), car_class_id: Number(carClassId) }, - }, - }) - .sort({ _id: 1 }) - .toArray(); -const results = subsessions.map((subsession) => { - const { - subsession_id, - track, - corners_per_lap, - allowed_licenses, - license_category, - } = subsession; - // Current user in Race session; - const { - finish_position, - finish_position_in_class, - laps_lead, - laps_complete, - average_lap, - best_lap_time, - reason_out, - champ_points, - starting_position, - starting_position_in_class, - car_class_short_name, - division_name, - new_license_level, - new_cpi, - newi_rating, - incidents, - car_name, - aggregate_champ_points, - } = subsession.session_results[2].results.filter( - (v: any) => v.cust_id === Number(id) - )[0]; - const { group_name: License } = allowed_licenses.slice(0, 2).pop() || {}; - - return { - subsession_id, - Track: `${track.track_name} ${ - track.config_name !== "N/A" ? track.config_name : "" - }`, - Corners: corners_per_lap, - License, - license_category, - finish_position, - finish_position_in_class, - laps_lead, - laps_complete, - average_lap, - best_lap_time, - reason_out, - champ_points, - starting_position, - starting_position_in_class, - car_class_short_name, - division_name, - new_license_level, - new_cpi, - newi_rating, - incidents, - car_name, - aggregate_champ_points, - }; -}); +const allSeasonSubsessions = await db + .select() + .from(Subsession) + .where(eq(Subsession.season_id, Number(seasonId))); +const allSeasonRaceResultsForUser = await db + .select() + .from(SubsessionRaceResults) + .where( + and( + eq(SubsessionRaceResults.cust_id, Number(id)), + eq(SubsessionRaceResults.car_class_id, Number(carClassId)) + ) + ); +const results = allSeasonSubsessions + .filter((v) => + allSeasonRaceResultsForUser + .map((w) => w.subsession_id) + .includes(v.subsession_id) + ) + .map((subsession) => { + const { + subsession_id, + track, + corners_per_lap, + allowed_licenses, + license_category, + } = subsession; + // Current user in Race session; + const { + finish_position, + finish_position_in_class, + laps_lead, + laps_complete, + average_lap, + best_lap_time, + reason_out, + champ_points, + starting_position, + starting_position_in_class, + car_class_short_name, + division_name, + new_license_level, + new_cpi, + newi_rating, + incidents, + car_name, + aggregate_champ_points, + } = + allSeasonRaceResultsForUser.find( + (v) => v.subsession_id === subsession_id + ) || {}; + const { group_name: License } = Array.isArray(allowed_licenses) + ? allowed_licenses.slice(0, 2).pop() + : { group_name: "" }; + const assertTrackAsTrackType = () => track as Track; + const trackInfo = assertTrackAsTrackType(); + return { + subsession_id, + Track: `${trackInfo.track_name} ${ + trackInfo.config_name !== "N/A" ? trackInfo.config_name : "" + }`, + Corners: corners_per_lap, + License, + license_category, + finish_position, + finish_position_in_class, + laps_lead, + laps_complete, + average_lap, + best_lap_time, + reason_out, + champ_points, + starting_position, + starting_position_in_class, + car_class_short_name, + division_name, + new_license_level, + new_cpi, + newi_rating, + incidents, + car_name, + aggregate_champ_points, + }; + }); const keysToDisplay = new Set(Object.keys(results[0])); const { keysArray, handledResults } = handleResults({ keysToDisplay, results }); const title = `Season information for season ID ${seasonId} - car class ID - ${carClassId} and user ID - ${id}`; diff --git a/src/pages/user/[id]/season/[seasonId]/car-class/[carClassId]/season-summary.astro b/src/pages/user/[id]/season/[seasonId]/car-class/[carClassId]/season-summary.astro index 53a4254..fc44625 100644 --- a/src/pages/user/[id]/season/[seasonId]/car-class/[carClassId]/season-summary.astro +++ b/src/pages/user/[id]/season/[seasonId]/car-class/[carClassId]/season-summary.astro @@ -1,67 +1,74 @@ --- +import { + db, + eq, + and, + sql, + PastSeason, + Standing, + Subsession, + SubsessionRaceResults, +} from "astro:db"; import DefaultLayout from "$lib/layouts/default.astro"; import handleResults from "$lib/utils/handle-session-results"; import fieldIdToLabelMap from "$lib/utils/field-id-to-label-map"; import lapTimeFields from "$lib/utils/lap-time-fields"; import parseLapTime from "$lib/utils/parse-lap-time"; -import { connectToDatabase } from "$lib/mongodb"; -import type { - License, - Season, - SessionResult, - Subsession, - Track, -} from "$lib/types"; +import type { License, SessionResult, Track } from "$lib/types"; import userIds from "$lib/utils/user-ids"; export async function getStaticPaths() { - const dbConnection = await connectToDatabase(); - const db = dbConnection.db; - const collection = db.collection("standings"); - const seasons: Array = await collection - .find({ "season_driver_data.cust_id": { $in: userIds } }) - .toArray(); + const seasons = await db + .select() + .from(Standing) + .where(sql`${Standing.cust_id} IN ${userIds}`); return seasons.map((season) => ({ params: { - id: Number(season.season_driver_data.cust_id), + id: Number(season.cust_id), seasonId: Number(season.season_id), carClassId: Number(season.car_class_id), }, })); } const { id, seasonId, carClassId } = Astro.params; -const dbConnection = await connectToDatabase(); -const db = dbConnection.db; -const subsessionCollection = db.collection("subsessions"); -const subsessions: Array = await subsessionCollection - .find({ - season_id: Number(seasonId), - "session_results.2.results": { - $elemMatch: { cust_id: Number(id), car_class_id: Number(carClassId) }, - }, - }) - .sort({ _id: 1 }) - .toArray(); -const standingsCollection = db.collection("standings"); -const season = await standingsCollection.findOne({ - _id: `${seasonId}_${carClassId}_${id}`, -}); -const pastSeasonsCollection = db.collection("pastseasons"); -const pastSeasonInformation = await pastSeasonsCollection.findOne({ - _id: Number(seasonId), -}); -const trackSchedule: Array = pastSeasonInformation.race_weeks.map( - (raceWeek: Record) => ({ - ...raceWeek.track, - race_week_num: raceWeek.race_week_num, - }) +const allSeasonSubsessions = await db + .select() + .from(Subsession) + .where(eq(Subsession.season_id, Number(seasonId))); +const allSeasonRaceResultsForUser = await db + .select() + .from(SubsessionRaceResults) + .where( + and( + eq(SubsessionRaceResults.cust_id, Number(id)), + eq(SubsessionRaceResults.car_class_id, Number(carClassId)) + ) + ); +const subsessions = allSeasonSubsessions.filter((v) => + allSeasonRaceResultsForUser + .map((w) => w.subsession_id) + .includes(v.subsession_id) ); -const trackScheduleMap: Map = new Map(); +const [season] = await db + .select() + .from(Standing) + .where(eq(Standing.id, `${seasonId}_${carClassId}_${id}`)); +const [pastSeasonInformation] = await db + .select() + .from(PastSeason) + .where(eq(PastSeason.season_id, Number(seasonId))); +const trackSchedule = Array.isArray(pastSeasonInformation.race_weeks) + ? pastSeasonInformation.race_weeks.map((raceWeek: Record) => ({ + ...raceWeek.track, + race_week_num: raceWeek.race_week_num, + })) + : []; +const trackScheduleMap: Map = new Map(); trackSchedule.forEach((track) => { trackScheduleMap.set(Number(track.race_week_num), track); }); const resultsPerWeek: Array<{ - subsession: Subsession; + subsession: typeof Subsession; userResult: SessionResult; }> = []; trackScheduleMap.forEach((value) => { @@ -71,29 +78,34 @@ trackScheduleMap.forEach((value) => { ); const userResults = respectiveSubsessions.map((subsession) => ({ subsession, - userResult: subsession.session_results[2].results.filter( - (v: any) => v.cust_id === Number(id) - )[0], + userResult: allSeasonRaceResultsForUser.find( + (v) => v.subsession_id === subsession.subsession_id + ), })); if (userResults.length > 1) { const bestUserResult = userResults .sort( (a, b) => + // @ts-expect-error a.userResult.aggregate_champ_points - + // @ts-expect-error b.userResult.aggregate_champ_points ) .pop(); if (bestUserResult) { + // @ts-expect-error resultsPerWeek.push(bestUserResult); } } else if (userResults.length === 0) { const bestUserResult = { - subsession: { track: value } as Subsession, - userResult: {} as SessionResult, + subsession: { track: value }, + userResult: {}, }; + // @ts-expect-error resultsPerWeek.push(bestUserResult); } else { const [bestUserResult] = userResults; + // @ts-expect-error resultsPerWeek.push(bestUserResult); } }); @@ -128,19 +140,21 @@ const results = resultsPerWeek.map((result) => { aggregate_champ_points, } = userResult; const { group_name: License } = - allowed_licenses && allowed_licenses.length + allowed_licenses && + Array.isArray(allowed_licenses) && + allowed_licenses.length ? (allowed_licenses.slice(0, 2).pop() as License) : ({} as License); const sof = { "Strength Of Field": subsession.race_summary - ? subsession.race_summary.field_strength + ? // @ts-expect-error + subsession.race_summary.field_strength : null, }; return { subsession_id, - Track: `${track.track_name} ${ - track.config_name && track.config_name !== "N/A" ? track.config_name : "" - }`, + // @ts-expect-error + Track: `${track.track_name} ${track.config_name && track.config_name !== "N/A" ? track.config_name : ""}`, Corners: corners_per_lap, License, license_category, @@ -183,10 +197,10 @@ const incidents = results .map((result) => result.incidents) .reduce((a, b) => a + b, 0); const { division, division_rank, overall_rank, season_name } = season; -const title = `${season_name}: Season Summary for ${season.season_driver_data.display_name}`; +const title = `${season_name}: Season Summary for ${season.display_name}`; const description = title; const overviewItems = [ - `Points: ${season.season_driver_data.points}`, + `Points: ${season.points}`, `Wins: ${wins}`, `Average Start: ${ averageStart / diff --git a/src/pages/user/[id]/standings/by-car-class/[carClassId].astro b/src/pages/user/[id]/standings/by-car-class/[carClassId].astro index 170f700..77df197 100644 --- a/src/pages/user/[id]/standings/by-car-class/[carClassId].astro +++ b/src/pages/user/[id]/standings/by-car-class/[carClassId].astro @@ -1,19 +1,17 @@ --- -import { connectToDatabase } from "$lib/mongodb"; +import { db, desc, eq, and, Standing } from "astro:db"; import StandingsLayout from "$lib/layouts/standings.astro"; import userIds from "$lib/utils/user-ids"; -import type { UserIdCarClassIdsMapping } from "$lib/types"; export async function getStaticPaths() { - const dbConnection = await connectToDatabase(); - const db = dbConnection.db; - const collection = db.collection("standings"); - const mappings: Array = await Promise.all( - userIds.map(async (userId) => ({ - userId, - carClassIds: await collection.distinct("car_class_id", { - "season_driver_data.cust_id": Number(userId), - }), - })) + const mappings = await Promise.all( + userIds.map(async (userId) => { + const values = await db + .selectDistinct({ car_class_id: Standing.car_class_id }) + .from(Standing) + .where(eq(Standing.cust_id, Number(userId))); + const carClassIds = values.map((value) => Number(value.car_class_id)); + return { userId, carClassIds }; + }) ); return mappings .map((mapping) => { @@ -28,16 +26,16 @@ export async function getStaticPaths() { const { id, carClassId } = Astro.params; const seoTitle = `Standings list for car class ID - ${carClassId} and user ID - ${id}`; const seoDescription = `Car class standings page for car class ID: ${carClassId} and user ID: ${id}`; -const dbConnection = await connectToDatabase(); -const { db } = dbConnection; -const collection = db.collection("standings"); -const standingsResults = await collection - .find({ - "season_driver_data.cust_id": Number(id), - car_class_id: String(carClassId), - }) - .sort({ season_id: -1 }) - .toArray(); +const standingsResults = await db + .select() + .from(Standing) + .where( + and( + eq(Standing.cust_id, Number(id)), + eq(Standing.car_class_id, String(carClassId)) + ) + ) + .orderBy(desc(Standing.season_id)); --- diff --git a/src/pages/user/[id]/standings/by-car-class/index.astro b/src/pages/user/[id]/standings/by-car-class/index.astro index 95e4fc7..c83797f 100644 --- a/src/pages/user/[id]/standings/by-car-class/index.astro +++ b/src/pages/user/[id]/standings/by-car-class/index.astro @@ -1,62 +1,18 @@ --- - import { connectToDatabase } from "$lib/mongodb"; + import { db, eq, SubsessionRaceResults } from "astro:db"; import DefaultLayout from "$lib/layouts/default.astro"; import userIds from "$lib/utils/user-ids"; - import type { CarClassMapping } from "$lib/types"; export function getStaticPaths() { return userIds.map((id) => ({ params: { id } })); } const { id } = Astro.params; - const dbConnection = await connectToDatabase(); - const db = dbConnection.db; - const collection = db.collection("subsessions"); - const allUserSubsessionCarClasses: Array = await collection.aggregate([ - { - "$match": { - "session_results.2.results": { $elemMatch: { cust_id: Number(id) } }, - } - }, - { - "$unwind": "$session_results" - }, - { - "$match": { - "session_results.results": { $elemMatch: { cust_id: Number(id) } }, - "session_results.simsession_number": 0, - } - }, - { - "$project": { - _id: 0, - results: { - "$filter": { - input: "$session_results.results", - cond: { $eq: ["$$result.cust_id", Number(id)]}, - as: "result", - }, - } - } - }, - { - "$unwind": "$results", - }, - { - "$replaceRoot": { - "newRoot": { - "$mergeObjects": ["$results", "$$ROOT"], - } - } - }, - { - "$project": { - car_class_id: 1, - car_class_short_name: 1, - }, - }, - ]).toArray(); + const carClassMappings = await db + .selectDistinct({ car_class_id: SubsessionRaceResults.car_class_id, car_class_short_name: SubsessionRaceResults.car_class_short_name }) + .from(SubsessionRaceResults) + .where(eq(SubsessionRaceResults.cust_id, Number(id))); const carClassMap = new Map(); const carClassNames: Set = new Set(); - allUserSubsessionCarClasses.forEach((carClassMapping) => { + carClassMappings.forEach((carClassMapping) => { const { car_class_id, car_class_short_name } = carClassMapping; const carClassNameTrimmed = car_class_short_name.trim(); if (!carClassMap.get(car_class_short_name)) { diff --git a/src/pages/user/[id]/standings/by-year/[year].astro b/src/pages/user/[id]/standings/by-year/[year].astro index 8d16254..1e548b0 100644 --- a/src/pages/user/[id]/standings/by-year/[year].astro +++ b/src/pages/user/[id]/standings/by-year/[year].astro @@ -1,40 +1,55 @@ --- -import { connectToDatabase } from "$lib/mongodb"; +import { + db, + and, + eq, + desc, + like, + sql, + Standing, + SubsessionRaceResults, + Subsession, +} from "astro:db"; import StandingsLayout from "$lib/layouts/standings.astro"; import userIds from "$lib/utils/user-ids"; -import type { UserIdYearsMapping } from "$lib/types"; export async function getStaticPaths() { - const dbConnection = await connectToDatabase(); - const db = dbConnection.db; - const collection = db.collection("subsessions"); - const mappings: Array = await Promise.all( - userIds.map(async (userId) => ({ - userId, - years: await collection.distinct("season_year", { - "session_results.2.results": { $elemMatch: { cust_id: userId } }, - }), - })) + const mappings = await Promise.all( + userIds.map(async (userId) => { + const subsessions = await db + .selectDistinct({ subsession_id: SubsessionRaceResults.subsession_id }) + .from(SubsessionRaceResults) + .where(eq(SubsessionRaceResults.cust_id, Number(userId))); + const years = await db + .selectDistinct({ season_year: Subsession.season_year }) + .from(Subsession) + .where( + sql`${Subsession.subsession_id} IN ${subsessions.map((subsession) => subsession.subsession_id)}` + ); + return { userId, years }; + }) ); return mappings .map((mapping) => { const { userId, years } = mapping; - return years.map((year) => ({ params: { id: userId, year } })); + return years.map((year) => ({ + params: { id: userId, year: year.season_year }, + })); }) .flat(); } const { id, year } = Astro.params; const seoTitle = `Standings list for user ID - ${id} and year - ${year}`; const seoDescription = seoTitle; -const dbConnection = await connectToDatabase(); -const { db } = dbConnection; -const collection = db.collection("standings"); -const standingsResults = await collection - .find({ - "season_driver_data.cust_id": Number(id), - season_name: { $regex: year }, - }) - .sort({ season_id: -1 }) - .toArray(); +const standingsResults = await db + .select() + .from(Standing) + .where( + and( + eq(Standing.cust_id, Number(id)), + like(Standing.season_name, `%${year}%`) + ) + ) + .orderBy(desc(Standing.season_id)); --- diff --git a/src/pages/user/[id]/standings/by-year/index.astro b/src/pages/user/[id]/standings/by-year/index.astro index 97c935c..10ea167 100644 --- a/src/pages/user/[id]/standings/by-year/index.astro +++ b/src/pages/user/[id]/standings/by-year/index.astro @@ -1,15 +1,27 @@ --- - import { connectToDatabase } from "$lib/mongodb"; +import { + db, + eq, + sql, + SubsessionRaceResults, + Subsession, +} from "astro:db"; import DefaultLayout from "$lib/layouts/default.astro"; import userIds from "$lib/utils/user-ids"; export function getStaticPaths() { return userIds.map((id) => ({ params: { id } })); } const { id } = Astro.params; - const dbConnection = await connectToDatabase(); - const db = dbConnection.db; - const collection = db.collection("subsessions"); - const years: Array = await collection.distinct("season_year", { "session_results.2.results": { $elemMatch: { cust_id: Number(id) } } }); + const subsessions = await db + .selectDistinct({ subsession_id: SubsessionRaceResults.subsession_id }) + .from(SubsessionRaceResults) + .where(eq(SubsessionRaceResults.cust_id, Number(id))); + const years = await db + .selectDistinct({ season_year: Subsession.season_year }) + .from(Subsession) + .where( + sql`${Subsession.subsession_id} IN ${subsessions.map((subsession) => subsession.subsession_id)}` + ); const title = `Standings By Year - ${id}`; const description = `Standings by year page for user ID: ${id}`; --- @@ -17,6 +29,6 @@
- {years.map((year) => ())} + {years.map((year) => ())}
diff --git a/src/pages/user/[id]/standings/full-participation.astro b/src/pages/user/[id]/standings/full-participation.astro index 0648c72..8e2b024 100644 --- a/src/pages/user/[id]/standings/full-participation.astro +++ b/src/pages/user/[id]/standings/full-participation.astro @@ -1,5 +1,5 @@ --- -import { connectToDatabase } from "$lib/mongodb"; +import { db, and, eq, desc, Standing } from "astro:db"; import StandingsLayout from "$lib/layouts/standings.astro"; import userIds from "$lib/utils/user-ids"; export function getStaticPaths() { @@ -8,16 +8,11 @@ export function getStaticPaths() { const { id } = Astro.params; const seoTitle = `Fully Participated Standings - ${id}`; const seoDescription = `Fully partipacipated standings page for user ID: ${id}`; -const dbConnection = await connectToDatabase(); -const { db } = dbConnection; -const collection = db.collection("standings"); -const standingsResults = await collection - .find({ - "season_driver_data.cust_id": Number(id), - "season_driver_data.weeks_counted": 8, - }) - .sort({ season_id: -1 }) - .toArray(); +const standingsResults = await db + .select() + .from(Standing) + .where(and(eq(Standing.cust_id, Number(id)), eq(Standing.weeks_counted, 8))) + .orderBy(desc(Standing.season_id)); --- diff --git a/src/pages/user/[id]/standings/index.astro b/src/pages/user/[id]/standings/index.astro index e91da41..04e8614 100644 --- a/src/pages/user/[id]/standings/index.astro +++ b/src/pages/user/[id]/standings/index.astro @@ -1,5 +1,5 @@ --- -import { connectToDatabase } from "$lib/mongodb"; +import { db, eq, desc, Standing } from "astro:db"; import StandingsLayout from "$lib/layouts/standings.astro"; import userIds from "$lib/utils/user-ids"; export function getStaticPaths() { @@ -8,13 +8,11 @@ export function getStaticPaths() { const { id } = Astro.params; const seoTitle = `All Standings - ${id}`; const seoDescription = `All standings page for user ID: ${id}`; -const dbConnection = await connectToDatabase(); -const { db } = dbConnection; -const collection = db.collection("standings"); -const standingsResults = await collection - .find({ "season_driver_data.cust_id": Number(id) }) - .sort({ season_id: -1 }) - .toArray(); +const standingsResults = await db + .select() + .from(Standing) + .where(eq(Standing.cust_id, Number(id))) + .orderBy(desc(Standing.season_id)); --- diff --git a/src/pages/user/[id]/subsessions/by-car-class/[carClassId].astro b/src/pages/user/[id]/subsessions/by-car-class/[carClassId].astro index 10cd0a9..81919c9 100644 --- a/src/pages/user/[id]/subsessions/by-car-class/[carClassId].astro +++ b/src/pages/user/[id]/subsessions/by-car-class/[carClassId].astro @@ -1,24 +1,31 @@ --- -import { connectToDatabase } from "$lib/mongodb"; +import { + db, + desc, + eq, + and, + sql, + Standing, + Subsession, + SubsessionRaceResults, +} from "astro:db"; import SubsessionsLayout from "$lib/layouts/subsessions.astro"; import userIds from "$lib/utils/user-ids"; -import type { UserIdCarClassIdsMapping } from "$lib/types"; export async function getStaticPaths() { - const dbConnection = await connectToDatabase(); - const db = dbConnection.db; - const collection = db.collection("standings"); - const mappings: Array = await Promise.all( - userIds.map(async (userId) => ({ - userId, - carClassIds: await collection.distinct("car_class_id", { - "season_driver_data.cust_id": Number(userId), - }), - })) + const mappings = await Promise.all( + userIds.map(async (userId) => { + const values = await db + .selectDistinct({ car_class_id: Standing.car_class_id }) + .from(Standing) + .where(eq(Standing.cust_id, Number(userId))); + const carClassIds = values.map((value) => Number(value.car_class_id)); + return { userId, carClassIds }; + }) ); return mappings .map((mapping) => { const { userId, carClassIds } = mapping; - return carClassIds.map((carClassId) => ({ + return carClassIds.map((carClassId: number) => ({ params: { id: userId, carClassId }, })); }) @@ -28,17 +35,33 @@ export async function getStaticPaths() { const { id, carClassId } = Astro.params; const seoTitle = `Subsessions list for user ID - ${id} and car class ID - ${carClassId}`; const seoDescription = seoTitle; -const dbConnection = await connectToDatabase(); -const { db } = dbConnection; -const collection = db.collection("subsessions"); -const subsessions = await collection - .find({ - "session_results.2.results": { - $elemMatch: { cust_id: Number(id), car_class_id: Number(carClassId) }, - }, - }) - .sort({ _id: -1 }) - .toArray(); +const subsessionRaceResults = await db + .select() + .from(SubsessionRaceResults) + .where( + and( + eq(SubsessionRaceResults.cust_id, Number(id)), + eq(SubsessionRaceResults.car_class_id, Number(carClassId)) + ) + ); +const subsessionsWithoutResult = await db + .select() + .from(Subsession) + .where( + sql`${Subsession.subsession_id} IN ${subsessionRaceResults.map((subsession) => subsession.subsession_id)}` + ) + .orderBy(desc(Subsession.subsession_id)); +const subsessions = subsessionsWithoutResult.map((subsessionWithoutResult) => { + const { subsession_id } = subsessionWithoutResult; + const userResult = subsessionRaceResults.find( + (subsessionRaceResult) => + subsessionRaceResult.subsession_id === subsession_id + ); + return { + ...subsessionWithoutResult, + userResult, + }; +}); --- diff --git a/src/pages/user/[id]/subsessions/by-car-class/index.astro b/src/pages/user/[id]/subsessions/by-car-class/index.astro index ad924f0..c22d341 100644 --- a/src/pages/user/[id]/subsessions/by-car-class/index.astro +++ b/src/pages/user/[id]/subsessions/by-car-class/index.astro @@ -1,62 +1,18 @@ --- - import { connectToDatabase } from "$lib/mongodb"; + import { db, eq, SubsessionRaceResults } from "astro:db"; import DefaultLayout from "$lib/layouts/default.astro"; import userIds from "$lib/utils/user-ids"; - import type { CarClassMapping } from "$lib/types"; export function getStaticPaths() { return userIds.map((id) => ({ params: { id } })); } const { id } = Astro.params; - const dbConnection = await connectToDatabase(); - const db = dbConnection.db; - const collection = db.collection("subsessions"); - const allUserSubsessionCarClasses: Array = await collection.aggregate([ - { - "$match": { - "session_results.2.results": { $elemMatch: { cust_id: Number(id) } }, - } - }, - { - "$unwind": "$session_results" - }, - { - "$match": { - "session_results.results": { $elemMatch: { cust_id: Number(id) } }, - "session_results.simsession_number": 0, - } - }, - { - "$project": { - _id: 0, - results: { - "$filter": { - input: "$session_results.results", - cond: { $eq: ["$$result.cust_id", Number(id)]}, - as: "result", - }, - } - } - }, - { - "$unwind": "$results", - }, - { - "$replaceRoot": { - "newRoot": { - "$mergeObjects": ["$results", "$$ROOT"], - } - } - }, - { - "$project": { - car_class_id: 1, - car_class_short_name: 1, - }, - }, - ]).toArray(); + const carClassMappings = await db + .selectDistinct({ car_class_id: SubsessionRaceResults.car_class_id, car_class_short_name: SubsessionRaceResults.car_class_short_name }) + .from(SubsessionRaceResults) + .where(eq(SubsessionRaceResults.cust_id, Number(id))); const carClassMap = new Map(); const carClassNames: Set = new Set(); - allUserSubsessionCarClasses.forEach((carClassMapping) => { + carClassMappings.forEach((carClassMapping) => { const { car_class_id, car_class_short_name } = carClassMapping; const carClassNameTrimmed = car_class_short_name.trim(); if (!carClassMap.get(car_class_short_name)) { diff --git a/src/pages/user/[id]/subsessions/by-year/[year].astro b/src/pages/user/[id]/subsessions/by-year/[year].astro index 0f2e099..c6e9bf6 100644 --- a/src/pages/user/[id]/subsessions/by-year/[year].astro +++ b/src/pages/user/[id]/subsessions/by-year/[year].astro @@ -1,40 +1,60 @@ --- -import { connectToDatabase } from "$lib/mongodb"; +import { db, eq, desc, sql, SubsessionRaceResults, Subsession } from "astro:db"; import SubsessionsLayout from "$lib/layouts/subsessions.astro"; import userIds from "$lib/utils/user-ids"; -import type { UserIdYearsMapping } from "$lib/types"; export async function getStaticPaths() { - const dbConnection = await connectToDatabase(); - const db = dbConnection.db; - const collection = db.collection("subsessions"); - const mappings: Array = await Promise.all( - userIds.map(async (userId) => ({ - userId, - years: await collection.distinct("season_year", { - "session_results.2.results": { $elemMatch: { cust_id: userId } }, - }), - })) + const mappings = await Promise.all( + userIds.map(async (userId) => { + const subsessions = await db + .selectDistinct({ subsession_id: SubsessionRaceResults.subsession_id }) + .from(SubsessionRaceResults) + .where(eq(SubsessionRaceResults.cust_id, Number(userId))); + const years = await db + .selectDistinct({ season_year: Subsession.season_year }) + .from(Subsession) + .where( + sql`${Subsession.subsession_id} IN ${subsessions.map((subsession) => subsession.subsession_id)}` + ); + return { userId, years }; + }) ); return mappings .map((mapping) => { const { userId, years } = mapping; - return years.map((year) => ({ params: { id: userId, year } })); + return years.map((year) => ({ + params: { id: userId, year: year.season_year }, + })); }) .flat(); } const { id, year } = Astro.params; const seoTitle = `Subsessions list for user ID - ${id} and year - ${year}`; const seoDescription = seoTitle; -const dbConnection = await connectToDatabase(); -const { db } = dbConnection; -const collection = db.collection("subsessions"); -const subsessions = await collection - .find({ - season_year: Number(year), - "session_results.2.results": { $elemMatch: { cust_id: Number(id) } }, +const subsessionsWithoutResult = await db + .select() + .from(Subsession) + .where(eq(Subsession.season_year, Number(year))) + .orderBy(desc(Subsession.subsession_id)); +const subsessionRaceResults = await db + .select() + .from(SubsessionRaceResults) + .where( + sql`${SubsessionRaceResults.cust_id} IS ${id} AND ${SubsessionRaceResults.subsession_id} IN ${subsessionsWithoutResult.map((subsessionWithoutResult) => subsessionWithoutResult.subsession_id)}` + ); + +const subsessions = subsessionsWithoutResult + .map((subsessionWithoutResult) => { + const { subsession_id } = subsessionWithoutResult; + const userResult = subsessionRaceResults.find( + (subsessionRaceResult) => + subsessionRaceResult.subsession_id === subsession_id + ); + return { + ...subsessionWithoutResult, + userResult, + }; }) - .sort({ _id: -1 }) - .toArray(); + .filter((v) => v.userResult); --- diff --git a/src/pages/user/[id]/subsessions/by-year/index.astro b/src/pages/user/[id]/subsessions/by-year/index.astro index 0b8cc95..a12f919 100644 --- a/src/pages/user/[id]/subsessions/by-year/index.astro +++ b/src/pages/user/[id]/subsessions/by-year/index.astro @@ -1,15 +1,27 @@ --- - import { connectToDatabase } from "$lib/mongodb"; +import { + db, + eq, + sql, + SubsessionRaceResults, + Subsession, +} from "astro:db"; import DefaultLayout from "$lib/layouts/default.astro"; import userIds from "$lib/utils/user-ids"; export function getStaticPaths() { return userIds.map((id) => ({ params: { id } })); } const { id } = Astro.params; - const dbConnection = await connectToDatabase(); - const db = dbConnection.db; - const collection = db.collection("subsessions"); - const years: Array = await collection.distinct("season_year", { "session_results.2.results": { $elemMatch: { cust_id: Number(id) } } }); + const subsessions = await db + .selectDistinct({ subsession_id: SubsessionRaceResults.subsession_id }) + .from(SubsessionRaceResults) + .where(eq(SubsessionRaceResults.cust_id, Number(id))); + const years = await db + .selectDistinct({ season_year: Subsession.season_year }) + .from(Subsession) + .where( + sql`${Subsession.subsession_id} IN ${subsessions.map((subsession) => subsession.subsession_id)}` + ); const title = `Subsessions By Year - ${id}`; const description = `Subsessions by year page for user ID: ${id}`; --- @@ -17,6 +29,6 @@
- {years.map((year) => ())} + {years.map((year) => ())}
diff --git a/src/pages/user/[id]/subsessions/index.astro b/src/pages/user/[id]/subsessions/index.astro index 0bc1f46..3c28749 100644 --- a/src/pages/user/[id]/subsessions/index.astro +++ b/src/pages/user/[id]/subsessions/index.astro @@ -1,6 +1,5 @@ --- -import { connectToDatabase } from "$lib/mongodb"; -import type { Subsession } from "$lib/types"; +import { db, desc, eq, sql, Subsession, SubsessionRaceResults } from "astro:db"; import SubsessionsLayout from "$lib/layouts/subsessions.astro"; import userIds from "$lib/utils/user-ids"; export function getStaticPaths() { @@ -9,15 +8,28 @@ export function getStaticPaths() { const { id } = Astro.params; const seoTitle = `Subsessions - ${id}`; const seoDescription = `Subsessions page for user ID: ${id}`; -const dbConnection = await connectToDatabase(); -const { db } = dbConnection; -const collection = db.collection("subsessions"); -const subsessions = await collection - .find({ - "session_results.2.results": { $elemMatch: { cust_id: Number(id) } }, - }) - .sort({ _id: -1 }) - .toArray(); +const subsessionRaceResults = await db + .select() + .from(SubsessionRaceResults) + .where(eq(SubsessionRaceResults.cust_id, Number(id))); +const subsessionsWithoutResult = await db + .select() + .from(Subsession) + .where( + sql`${Subsession.subsession_id} IN ${subsessionRaceResults.map((subsession) => subsession.subsession_id)}` + ) + .orderBy(desc(Subsession.subsession_id)); +const subsessions = subsessionsWithoutResult.map((subsessionWithoutResult) => { + const { subsession_id } = subsessionWithoutResult; + const userResult = subsessionRaceResults.find( + (subsessionRaceResult) => + subsessionRaceResult.subsession_id === subsession_id + ); + return { + ...subsessionWithoutResult, + userResult, + }; +}); ---