15 Commits
15 changed files with 1581 additions and 261 deletions
+2 -2
View File
@@ -28,8 +28,8 @@
"@elysia/server-timing": "^1.4.1", "@elysia/server-timing": "^1.4.1",
"@elysiajs/cors": "^1.4.2", "@elysiajs/cors": "^1.4.2",
"@types/pg": "^8.23.1", "@types/pg": "^8.23.1",
"elysia": "^1.4.29", "elysia": "^1.4.30",
"kysely": "^0.29.5", "kysely": "^0.29.6",
"ml-levenberg-marquardt": "^5.1.0", "ml-levenberg-marquardt": "^5.1.0",
"pg": "^8.23.0", "pg": "^8.23.0",
"zipcodes-us": "^1.1.3" "zipcodes-us": "^1.1.3"
@@ -0,0 +1,50 @@
import { Kysely, sql } from 'kysely'
import { addDefaultColumns } from '../db';
export async function up(db: Kysely<any>): Promise<void> {
// TABLE: VERIFICATIONS
await db.schema.createTable("verifications")
.$call(addDefaultColumns)
.addColumn("assessment_id", "bigint", (cb) => cb.notNull().unique())
.addColumn("status", "text", (cb) => cb.notNull().defaultTo("request")) // request | in_review | action_requested | completed
.addColumn("completed_at", "timestamptz")
.addColumn("completed_by", "uuid")
.addColumn("url_back_squat", "text")
.addColumn("url_deadlift", "text")
.addColumn("url_bench_press", "text")
.addColumn("url_run", "text")
.addColumn("url_broad_jump", "text")
.addColumn("url_cone_drill", "text")
.addColumn("verf_back_squat", "boolean")
.addColumn("verf_deadlift", "boolean")
.addColumn("verf_bench_press", "boolean")
.addColumn("verf_run", "boolean")
.addColumn("verf_broad_jump", "boolean")
.addColumn("verf_cone_drill", "boolean")
.addColumn("reviewer_notes", "text") // can be structured in the future, for now keep simple
.addForeignKeyConstraint(
"fk_verifications_assessment_id",
["assessment_id"],
"assessments",
["id"],
(cb) => cb.onDelete("cascade")
)
.addForeignKeyConstraint(
"fk_verifications_completed_by",
["completed_by"],
"accounts",
["id"],
(cb) => cb.onDelete("restrict")
)
.addCheckConstraint(
"chk_verifications_completed_fields",
sql`status != 'completed' OR (completed_at IS NOT NULL AND completed_by IS NOT NULL)`
)
.execute()
}
export async function down(db: Kysely<any>): Promise<void> {
// TABLE: VERIFICATIONS
await db.schema.dropTable("verifications").ifExists().execute()
}
@@ -0,0 +1,95 @@
import { Activity, Gender, inToCm, lbToKg, minToMs, secToMs } from "@blade-and-brawn/domain";
import { db } from "../database/db";
import { env, log } from "../util";
import type { AccountRole } from "../services/accounts";
import { VerificationsService } from "../services/verifications";
const TEST_DISCORD_ID = "test-verification-seed";
const TEST_ASSESSMENT_NAME = "Test Verification Player";
async function seedTestAccount(): Promise<string> {
const existing = await db.selectFrom("accounts")
.select("id")
.where("discord_id", "=", TEST_DISCORD_ID)
.executeTakeFirst();
if (existing) {
log.info({ id: existing.id }, "test account already seeded, reusing");
return existing.id;
}
const account = await db.insertInto("accounts")
.values({
discord_id: TEST_DISCORD_ID,
name: "Test Player",
role: "user" satisfies AccountRole,
})
.returning("id")
.executeTakeFirstOrThrow();
log.info({ id: account.id }, "seeded test account");
return account.id;
}
async function seedTestAssessment(accountId: string): Promise<string> {
const existing = await db.selectFrom("assessments")
.select("id")
.where("account_id", "=", accountId)
.where("name", "=", TEST_ASSESSMENT_NAME)
.executeTakeFirst();
if (existing) {
log.info({ id: existing.id }, "test assessment already seeded, reusing");
return existing.id;
}
const result = await db.insertInto("assessments")
.values({
account_id: accountId,
name: TEST_ASSESSMENT_NAME,
gender: Gender.Male,
age: 22,
weight: lbToKg(180),
perf_back_squat: lbToKg(225),
perf_deadlift: lbToKg(315),
perf_bench_press: lbToKg(185),
perf_broad_jump: inToCm(96),
perf_run: minToMs(7) + secToMs(30),
perf_cone_drill: secToMs(7),
})
.returning("id")
.executeTakeFirstOrThrow();
log.info({ id: result.id }, "seeded test assessment");
return result.id;
}
(async () => {
const answer = prompt(`Seed a test verification request in the database (${env.DATABASE_URL})? (y/N)`);
if (answer?.trim().toLowerCase() !== "y") {
log.info("Aborted");
await db.destroy();
return;
}
try {
const accountId = await seedTestAccount();
const assessmentId = await seedTestAssessment(accountId);
const verifications = new VerificationsService();
const verification = await verifications.request(assessmentId, accountId, {
[Activity.BackSquat]: "https://example.com/videos/back-squat.mp4",
[Activity.Deadlift]: "https://example.com/videos/deadlift.mp4",
[Activity.BenchPress]: "https://example.com/videos/bench-press.mp4",
[Activity.Run]: "https://example.com/videos/run.mp4",
[Activity.BroadJump]: "https://example.com/videos/broad-jump.mp4",
[Activity.ConeDrill]: "https://example.com/videos/cone-drill.mp4",
});
if (!verification) throw new Error("Failed to seed verification request — assessment ownership check failed");
log.info({ id: verification.id, assessmentId, accountId }, "seeded test verification request");
}
catch (err) {
log.error({ err }, "seed failed");
process.exit(1);
}
finally {
await db.destroy();
}
})();
+92 -5
View File
@@ -1,5 +1,7 @@
import { import {
ActivityPerformanceSchema, ActivityPerformanceSchema,
ActivityVerificationsSchema,
ActivityVideosSchema,
PlayerSchema, PlayerSchema,
} from "@blade-and-brawn/domain" } from "@blade-and-brawn/domain"
import { cors } from "@elysiajs/cors"; import { cors } from "@elysiajs/cors";
@@ -23,6 +25,8 @@ import { EventsService, EventStatusSchema } from "./services/events";
import { AccountsService, type AccountRole } from "./services/accounts"; import { AccountsService, type AccountRole } from "./services/accounts";
import { Value } from "@sinclair/typebox/value"; import { Value } from "@sinclair/typebox/value";
import { AssessmentsService } from "./services/assessments"; import { AssessmentsService } from "./services/assessments";
import { VerificationsService, VerificationStatusSchema } from "./services/verifications";
import { Not } from "@sinclair/typebox";
// CONSTANTS // CONSTANTS
// ----------------------- // -----------------------
@@ -39,7 +43,8 @@ const s = (() => {
const Accounts = new AccountsService(Calculator); const Accounts = new AccountsService(Calculator);
const Events = new EventsService(); const Events = new EventsService();
const Assessments = new AssessmentsService(); const Assessments = new AssessmentsService();
return { Standards, Calculator, Commerce, Accounts, Events, Assessments }; const Verifications = new VerificationsService();
return { Standards, Calculator, Commerce, Accounts, Events, Assessments, Verifications };
})(); })();
// QUEUES // QUEUES
@@ -482,7 +487,7 @@ export const app = new Elysia()
.group("/assessments", (app) => app .group("/assessments", (app) => app
.guard({ auth: true }, (app) => app .guard({ auth: true }, (app) => app
.post("/me", async ({ body: { player, activityPerformances }, accountId }) => { .post("/me", async ({ body: { player, activityPerformances }, accountId }) => {
await s.Assessments.create(player, activityPerformances, accountId); return await s.Assessments.create(player, activityPerformances, accountId);
}, { }, {
body: t.Object({ body: t.Object({
player: PlayerSchema, player: PlayerSchema,
@@ -490,9 +495,13 @@ export const app = new Elysia()
}) })
}) })
.get("/me", async ({ accountId }) => { .get("/me", async ({ accountId }) => {
const assessments = await s.Assessments.list({ filter: { accountId } }); return await s.Assessments.list({ filter: { accountId } });
if (!assessments) throw new NotFoundError("Assessments not found"); })
return assessments; .delete("/me/:id", async ({ params: { id }, accountId }) => {
const deleted = await s.Assessments.delete(id, accountId);
if (!deleted) throw new NotFoundError("Assessment not found");
}, {
params: t.Object({ id: t.String() })
}) })
) )
.guard({ authAdmin: true }, (app) => app .guard({ authAdmin: true }, (app) => app
@@ -505,6 +514,83 @@ export const app = new Elysia()
id: t.Optional(t.String()), id: t.Optional(t.String()),
}) })
}) })
.put("/:id", async ({ body: { player, activityPerformances }, params: { id } }) => {
const updated = await s.Assessments.update(id, player, activityPerformances);
if (!updated) throw new NotFoundError("Assessment not found");
}, {
params: t.Object({ id: t.String() }),
body: t.Object({
player: PlayerSchema,
activityPerformances: t.Array(ActivityPerformanceSchema),
})
})
)
)
// VERIFICATIONS
.group("/verifications", (app) => app
.guard({ auth: true }, (app) => app
.post("/me", async ({ body: { assessmentId, activityVideoUrls }, accountId }) => {
const requested = await s.Verifications.request(assessmentId, accountId, activityVideoUrls);
if (!requested) throw new NotFoundError("Assessment not found");
return requested;
}, {
body: t.Object({
assessmentId: t.String(),
activityVideoUrls: ActivityVideosSchema,
})
})
)
.guard({ authAdmin: true }, (app) => app
.get("/", async ({ query }) => {
return await s.Verifications.list({
filter: { status: query.status },
limit: query.limit,
offset: query.offset,
});
}, {
query: t.Object({
status: t.Optional(VerificationStatusSchema),
limit: t.Optional(t.Numeric()),
offset: t.Optional(t.Numeric()),
})
})
.get("/:id", async ({ params: { id } }) => {
const verification = await s.Verifications.get(id);
if (!verification) throw new NotFoundError("Verification not found");
return verification;
}, {
params: t.Object({ id: t.String() })
})
.post("/:id/request-action", async ({ params: { id }, body: { reviewerNotes, activityVerifications } }) => {
const verification = await s.Verifications.get(id);
if (!verification) throw new NotFoundError("Verification not found");
if (verification.status === "completed" || verification.status === "action_requested")
throw status(409, { error: "Cannot modify a completed or action-requested verification" });
const updated = await s.Verifications.requestAction(id, reviewerNotes ?? null, activityVerifications);
if (!updated) throw new NotFoundError("Verification not found");
}, {
params: t.Object({ id: t.String() }),
body: t.Object({
reviewerNotes: t.Optional(t.String()),
activityVerifications: ActivityVerificationsSchema,
})
})
.post("/:id/complete", async ({ params: { id }, body: { activityVerifications }, accountId }) => {
const verification = await s.Verifications.get(id);
if (!verification) throw new NotFoundError("Verification not found");
if (verification.status === "completed" || verification.status === "action_requested")
throw status(409, { error: "Cannot modify a completed or action-requested verification" });
const updated = await s.Verifications.complete(id, accountId, activityVerifications);
if (!updated) throw new NotFoundError("Verification not found");
}, {
params: t.Object({ id: t.String() }),
body: t.Object({
activityVerifications: ActivityVerificationsSchema,
})
})
) )
) )
@@ -615,3 +701,4 @@ app.listen(3000, async () => {
export type API = typeof app export type API = typeof app
export { type EventStatus } from "./services/events"; export { type EventStatus } from "./services/events";
export { type VerificationStatus } from "./services/verifications";
+36 -9
View File
@@ -2,10 +2,10 @@ import { Activity, type ActivityPerformance, type Player } from "@blade-and-braw
import { db } from "../database/db"; import { db } from "../database/db";
export class AssessmentsService { export class AssessmentsService {
async create(player: Player, activityPerformances: ActivityPerformance[], accountId?: string) { private static performanceFor = (activityPerformances: ActivityPerformance[], activity: Activity) =>
const performanceFor = (activity: Activity) => activityPerformances.find((p) => p.activity === activity)?.performance ?? null;
activityPerformances.find((p) => p.activity === activity)?.performance ?? null;
async create(player: Player, activityPerformances: ActivityPerformance[], accountId?: string) {
return await db.insertInto("assessments") return await db.insertInto("assessments")
.values({ .values({
account_id: accountId ?? null, account_id: accountId ?? null,
@@ -13,12 +13,12 @@ export class AssessmentsService {
age: player.metrics.age, age: player.metrics.age,
weight: player.metrics.weight, weight: player.metrics.weight,
gender: player.metrics.gender, gender: player.metrics.gender,
perf_back_squat: performanceFor(Activity.BackSquat), perf_back_squat: AssessmentsService.performanceFor(activityPerformances, Activity.BackSquat),
perf_deadlift: performanceFor(Activity.Deadlift), perf_deadlift: AssessmentsService.performanceFor(activityPerformances, Activity.Deadlift),
perf_bench_press: performanceFor(Activity.BenchPress), perf_bench_press: AssessmentsService.performanceFor(activityPerformances, Activity.BenchPress),
perf_broad_jump: performanceFor(Activity.BroadJump), perf_broad_jump: AssessmentsService.performanceFor(activityPerformances, Activity.BroadJump),
perf_run: performanceFor(Activity.Run), perf_run: AssessmentsService.performanceFor(activityPerformances, Activity.Run),
perf_cone_drill: performanceFor(Activity.ConeDrill), perf_cone_drill: AssessmentsService.performanceFor(activityPerformances, Activity.ConeDrill),
}) })
.returning("id") .returning("id")
.executeTakeFirstOrThrow(); .executeTakeFirstOrThrow();
@@ -39,4 +39,31 @@ export class AssessmentsService {
.$if(opt.offset !== undefined, (qb) => qb.offset(opt.offset!)) .$if(opt.offset !== undefined, (qb) => qb.offset(opt.offset!))
.execute(); .execute();
} }
async update(id: string, player: Player, activityPerformances: ActivityPerformance[]) {
const result = await db.updateTable("assessments")
.where("id", "=", id)
.set({
name: player.name ?? "Anonymous",
age: player.metrics.age,
weight: player.metrics.weight,
gender: player.metrics.gender,
perf_back_squat: AssessmentsService.performanceFor(activityPerformances, Activity.BackSquat),
perf_deadlift: AssessmentsService.performanceFor(activityPerformances, Activity.Deadlift),
perf_bench_press: AssessmentsService.performanceFor(activityPerformances, Activity.BenchPress),
perf_broad_jump: AssessmentsService.performanceFor(activityPerformances, Activity.BroadJump),
perf_run: AssessmentsService.performanceFor(activityPerformances, Activity.Run),
perf_cone_drill: AssessmentsService.performanceFor(activityPerformances, Activity.ConeDrill),
})
.executeTakeFirst();
return result.numUpdatedRows > 0n;
}
async delete(id: string, accountId: string): Promise<boolean> {
const result = await db.deleteFrom("assessments")
.where("id", "=", id)
.where("account_id", "=", accountId)
.executeTakeFirst();
return result.numDeletedRows > 0n;
}
} }
+155
View File
@@ -0,0 +1,155 @@
import { Activity, type ActivityVerifications, type ActivityVideoUrls } from "@blade-and-brawn/domain";
import type { Static } from "@sinclair/typebox";
import { t } from "elysia";
import { db } from "../database/db";
export const VerificationStatusSchema = t.Union([
t.Literal("request"),
t.Literal("in_review"),
t.Literal("action_requested"),
t.Literal("completed"),
]);
export type VerificationStatus = Static<typeof VerificationStatusSchema>;
export class VerificationsService {
async request(assessmentId: string, accountId: string, activityVideoUrls: ActivityVideoUrls) {
// Only the owning account may request verification of their own assessment.
const assessment = await db.selectFrom("assessments")
.select("id")
.where("id", "=", assessmentId)
.where("account_id", "=", accountId)
.executeTakeFirst();
if (!assessment) return undefined;
return await db.insertInto("verifications")
.values({
assessment_id: assessmentId,
status: "request" satisfies VerificationStatus,
url_back_squat: activityVideoUrls[Activity.BackSquat] ?? null,
url_deadlift: activityVideoUrls[Activity.Deadlift] ?? null,
url_bench_press: activityVideoUrls[Activity.BenchPress] ?? null,
url_broad_jump: activityVideoUrls[Activity.BroadJump] ?? null,
url_run: activityVideoUrls[Activity.Run] ?? null,
url_cone_drill: activityVideoUrls[Activity.ConeDrill] ?? null,
})
.onConflict((oc) => oc
.column("assessment_id")
.doUpdateSet((eb) => ({
status: "request" satisfies VerificationStatus,
url_back_squat: eb.ref("excluded.url_back_squat"),
url_deadlift: eb.ref("excluded.url_deadlift"),
url_bench_press: eb.ref("excluded.url_bench_press"),
url_broad_jump: eb.ref("excluded.url_broad_jump"),
url_run: eb.ref("excluded.url_run"),
url_cone_drill: eb.ref("excluded.url_cone_drill"),
// Re-requesting means new videos are under review, so the
// previous cycle's per-activity judgments and completion
// info no longer apply.
verf_back_squat: null,
verf_deadlift: null,
verf_bench_press: null,
verf_broad_jump: null,
verf_run: null,
verf_cone_drill: null,
completed_at: null,
completed_by: null,
}))
)
.returning("id")
.executeTakeFirstOrThrow();
}
// Joined with the assessment being verified, since reviewing a request
// requires seeing the player's claimed performance alongside the videos.
private static baseQuery() {
return db.selectFrom("verifications")
.innerJoin("assessments", "assessments.id", "verifications.assessment_id")
.select([
"verifications.id",
"verifications.assessment_id",
"verifications.status",
"verifications.created_at",
"verifications.completed_at",
"verifications.completed_by",
"verifications.reviewer_notes",
"verifications.url_back_squat",
"verifications.url_deadlift",
"verifications.url_bench_press",
"verifications.url_broad_jump",
"verifications.url_run",
"verifications.url_cone_drill",
"verifications.verf_back_squat",
"verifications.verf_deadlift",
"verifications.verf_bench_press",
"verifications.verf_broad_jump",
"verifications.verf_run",
"verifications.verf_cone_drill",
"assessments.name",
"assessments.gender",
"assessments.age",
"assessments.weight",
"assessments.perf_back_squat",
"assessments.perf_deadlift",
"assessments.perf_bench_press",
"assessments.perf_broad_jump",
"assessments.perf_run",
"assessments.perf_cone_drill",
]);
}
async get(id: string) {
return await VerificationsService.baseQuery()
.where("verifications.id", "=", id)
.executeTakeFirst();
}
async list(opt: {
filter?: { status?: VerificationStatus },
limit?: number,
offset?: number,
} = {}) {
return await VerificationsService.baseQuery()
.$if(opt.filter?.status !== undefined, (qb) => qb
.where("verifications.status", "=", opt.filter!.status!)
)
.orderBy("verifications.created_at", "desc")
.$if(opt.limit !== undefined, (qb) => qb.limit(opt.limit!))
.$if(opt.offset !== undefined, (qb) => qb.offset(opt.offset!))
.execute();
}
async requestAction(id: string, reviewerNotes: string | null, activityVerifications: ActivityVerifications) {
const result = await db.updateTable("verifications")
.where("id", "=", id)
.set({
status: "action_requested" satisfies VerificationStatus,
reviewer_notes: reviewerNotes,
verf_back_squat: activityVerifications[Activity.BackSquat] ?? null,
verf_deadlift: activityVerifications[Activity.Deadlift] ?? null,
verf_bench_press: activityVerifications[Activity.BenchPress] ?? null,
verf_broad_jump: activityVerifications[Activity.BroadJump] ?? null,
verf_run: activityVerifications[Activity.Run] ?? null,
verf_cone_drill: activityVerifications[Activity.ConeDrill] ?? null,
})
.executeTakeFirst();
return result.numUpdatedRows > 0n;
}
async complete(id: string, completedBy: string, activityVerifications: ActivityVerifications) {
const result = await db.updateTable("verifications")
.where("id", "=", id)
.set({
status: "completed" satisfies VerificationStatus,
completed_at: new Date(),
completed_by: completedBy,
verf_back_squat: activityVerifications[Activity.BackSquat] ?? null,
verf_deadlift: activityVerifications[Activity.Deadlift] ?? null,
verf_bench_press: activityVerifications[Activity.BenchPress] ?? null,
verf_broad_jump: activityVerifications[Activity.BroadJump] ?? null,
verf_run: activityVerifications[Activity.Run] ?? null,
verf_cone_drill: activityVerifications[Activity.ConeDrill] ?? null,
})
.executeTakeFirst();
return result.numUpdatedRows > 0n;
}
}
+4 -4
View File
@@ -5,8 +5,8 @@
"@blade-and-brawn/api": "workspace:*", "@blade-and-brawn/api": "workspace:*",
"@sveltejs/kit": "^2.70.3", "@sveltejs/kit": "^2.70.3",
"@sveltejs/vite-plugin-svelte": "^6.2.4", "@sveltejs/vite-plugin-svelte": "^6.2.4",
"elysia": "^1.4.29", "elysia": "^1.4.30",
"svelte": "^5.56.10", "svelte": "^5.57.0",
"svelte-adapter-bun": "^1.0.1", "svelte-adapter-bun": "^1.0.1",
"svelte-check": "^4.7.6", "svelte-check": "^4.7.6",
"vite": "^7.3.6" "vite": "^7.3.6"
@@ -25,8 +25,8 @@
"@blade-and-brawn/domain": "workspace:*", "@blade-and-brawn/domain": "workspace:*",
"@elysia/eden": "^1.4.10", "@elysia/eden": "^1.4.10",
"@tailwindcss/vite": "^4.3.3", "@tailwindcss/vite": "^4.3.3",
"daisyui": "^5.7.20", "daisyui": "^5.7.38",
"jose": "^6.2.10", "jose": "^6.2.12",
"tailwindcss": "^4.3.3" "tailwindcss": "^4.3.3"
} }
} }
+419 -162
View File
@@ -1,4 +1,6 @@
<script lang="ts"> <script lang="ts">
import { onMount } from "svelte";
import { beforeNavigate } from "$app/navigation";
import { import {
LevelCalculator, LevelCalculator,
type LevelCalculatorOutput, type LevelCalculatorOutput,
@@ -13,11 +15,42 @@
type ActivityPerformance, type ActivityPerformance,
type Player, type Player,
} from "@blade-and-brawn/domain"; } from "@blade-and-brawn/domain";
import { api } from "$lib/api";
type AssessmentRow = NonNullable<
Awaited<ReturnType<typeof api.assessments.me.get>>["data"]
>[number];
interface CalcData { interface CalcData {
key: string;
id?: string;
levels?: LevelCalculatorOutput; levels?: LevelCalculatorOutput;
player: Player; player: Player;
activityPerformances: ActivityPerformance[]; activityPerformances: ActivityPerformance[];
/** JSON snapshot of {player, activityPerformances} as of the last successful save/load. "" means never saved. */
savedSnapshot: string;
/** whether the metrics/performance editing panel is open — collapsed by default, since levels are the primary thing being compared */
expanded?: boolean;
saving?: boolean;
saveError?: string;
deleting?: boolean;
deleteError?: string;
}
function snapshotOf(
player: Player,
activityPerformances: ActivityPerformance[],
): string {
return JSON.stringify({ player, activityPerformances });
}
function isDirty(calculation: CalcData): boolean {
return (
snapshotOf(
calculation.player,
calculation.activityPerformances,
) !== calculation.savedSnapshot
);
} }
interface Props { interface Props {
@@ -28,7 +61,136 @@
const levelCalculator = $derived(new LevelCalculator(allStandards)); const levelCalculator = $derived(new LevelCalculator(allStandards));
let calculations = $state([] as CalcData[]); // toggle + name + overall + one column per attribute + actions
const TABLE_COLUMNS = 4 + Object.keys(Attribute).length;
let calculations = $state<CalcData[]>([]);
let loading = $state(true);
let loadError = $state<string | null>(null);
const PERF_COLUMN = {
[Activity.BackSquat]: "perf_back_squat",
[Activity.Deadlift]: "perf_deadlift",
[Activity.BenchPress]: "perf_bench_press",
[Activity.Run]: "perf_run",
[Activity.BroadJump]: "perf_broad_jump",
[Activity.ConeDrill]: "perf_cone_drill",
} as const satisfies Record<Activity, keyof AssessmentRow>;
function assessmentToCalc(assessment: AssessmentRow): CalcData {
const player: Player = {
name: assessment.name,
metrics: {
age: assessment.age,
weight: assessment.weight,
gender: assessment.gender as Gender,
},
};
const activityPerformances = Object.values(Activity).map((a) => ({
activity: a,
performance: (assessment[PERF_COLUMN[a]] as number | null) ?? 0,
}));
return {
key: crypto.randomUUID(),
id: assessment.id,
player,
activityPerformances,
savedSnapshot: snapshotOf(player, activityPerformances),
};
}
function errorMessage(err: unknown): string {
const value = (err as { value?: { error?: string } })?.value;
return (
value?.error ??
(err instanceof Error ? err.message : "Unknown error")
);
}
async function loadAssessments() {
loading = true;
loadError = null;
try {
const res = await api.assessments.me.get();
if (res.error) throw res.error;
calculations = (res.data ?? []).map(assessmentToCalc);
} catch (err) {
loadError = errorMessage(err);
} finally {
loading = false;
}
}
async function saveCalculation(calculation: CalcData) {
calculation.saving = true;
calculation.saveError = undefined;
try {
if (calculation.id) {
const res = await api
.assessments({ id: calculation.id })
.put({
player: calculation.player,
activityPerformances: calculation.activityPerformances,
});
if (res.error) throw res.error;
} else {
const res = await api.assessments.me.post({
player: calculation.player,
activityPerformances: calculation.activityPerformances,
});
if (res.error) throw res.error;
calculation.id = res.data?.id;
}
calculation.savedSnapshot = snapshotOf(
calculation.player,
calculation.activityPerformances,
);
} catch (err) {
calculation.saveError = errorMessage(err);
} finally {
calculation.saving = false;
}
}
async function deleteCalculation(calculation: CalcData, index: number) {
if (!calculation.id) {
calculations.splice(index, 1);
return;
}
calculation.deleting = true;
calculation.deleteError = undefined;
try {
const res = await api.assessments.me({ id: calculation.id }).delete();
if (res.error) throw res.error;
calculations.splice(index, 1);
} catch (err) {
calculation.deleteError = errorMessage(err);
} finally {
calculation.deleting = false;
}
}
onMount(loadAssessments);
beforeNavigate((navigation) => {
if (!calculations.some(isDirty)) return;
if (navigation.type === "leave") {
// triggers the browser's native "leave site?" confirmation
navigation.cancel();
return;
}
if (
!confirm(
"You have unsaved player changes that will be lost. Leave anyway?",
)
) {
navigation.cancel();
}
});
$effect(() => { $effect(() => {
for (const calculation of calculations) { for (const calculation of calculations) {
@@ -39,19 +201,11 @@
} }
}); });
$effect(() => { const createCalculation = function (name: string): CalcData {
const savedCalcs = localStorage.getItem("calculations");
if (savedCalcs) calculations = JSON.parse(savedCalcs);
});
$effect(() => {
localStorage.setItem("calculations", JSON.stringify(calculations));
});
$inspect(calculations[0]);
const createCalculation = function (name: string) {
return { return {
key: crypto.randomUUID(),
savedSnapshot: "", // never saved — always dirty until the first save
expanded: true, // a brand new player needs its details filled in right away
player: { player: {
name: name, name: name,
metrics: { metrics: {
@@ -67,21 +221,6 @@
}; };
}; };
function downloadObject(obj: unknown, filename = "data.json") {
const json = JSON.stringify(obj, null, 2);
const blob = new Blob([json], { type: "application/json" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
a.remove();
URL.revokeObjectURL(url);
}
const formatMs = (ms: number) => { const formatMs = (ms: number) => {
const total = Math.floor(ms / 1000); const total = Math.floor(ms / 1000);
const m = Math.floor(total / 60); const m = Math.floor(total / 60);
@@ -100,7 +239,7 @@
const formatSeconds = (ms: number) => (ms / 1000).toFixed(2) + " seconds"; const formatSeconds = (ms: number) => (ms / 1000).toFixed(2) + " seconds";
const performanceOptionsFromActivity = function (activity: Activity) { const performanceOptionsFromActivity = function (activity: Activity) {
const options: { name: string; value: string }[] = []; const options: { name: string; value: number }[] = [];
switch (activity) { switch (activity) {
case Activity.BackSquat: case Activity.BackSquat:
case Activity.Deadlift: case Activity.Deadlift:
@@ -108,7 +247,7 @@
for (let i = 0; i < 600; ++i) { for (let i = 0; i < 600; ++i) {
options.push({ options.push({
name: String(i) + " lb", name: String(i) + " lb",
value: String(lbToKg(i)), value: lbToKg(i),
}); });
} }
break; break;
@@ -116,7 +255,7 @@
case Activity.Run: { case Activity.Run: {
const MAX_MIN = 30; const MAX_MIN = 30;
for (let ms = 0; ms <= MAX_MIN * 60_000; ms += 1_000) { for (let ms = 0; ms <= MAX_MIN * 60_000; ms += 1_000) {
options.push({ name: formatMs(ms), value: String(ms) }); options.push({ name: formatMs(ms), value: ms });
} }
break; break;
} }
@@ -127,7 +266,7 @@
const inches = halfStep / 2; const inches = halfStep / 2;
options.push({ options.push({
name: inchesToFeetInches(inches), name: inchesToFeetInches(inches),
value: (inches * 2.54).toFixed(1), value: Number((inches * 2.54).toFixed(1)),
}); });
} }
break; break;
@@ -139,7 +278,7 @@
for (let ms = MIN_MS; ms <= MAX_MS; ms += 10) { for (let ms = MIN_MS; ms <= MAX_MS; ms += 10) {
options.push({ options.push({
name: formatSeconds(ms), name: formatSeconds(ms),
value: String(ms), value: ms,
}); });
} }
break; break;
@@ -149,7 +288,15 @@
}; };
</script> </script>
<div class="mt-5 mb-5 flex w-full"> <div class="mt-5 mb-5 flex w-full items-center">
<button
onclick={loadAssessments}
class="btn btn-secondary btn-sm"
disabled={loading}
>
{loading ? "Refreshing..." : "Refresh"}
</button>
<div class="ml-auto"> <div class="ml-auto">
<button <button
onclick={() => onclick={() =>
@@ -158,140 +305,250 @@
)} )}
class="btn btn-primary">New</button class="btn btn-primary">New</button
> >
<button
onclick={() => downloadObject(calculations)}
class="btn btn-secondary">Export</button
>
</div> </div>
</div> </div>
<section {#if loadError}
class="w-full px-3 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4" <div role="alert" class="alert alert-error w-full mb-4">
> <span>{loadError}</span>
{#each calculations as calculation, index} </div>
<div {/if}
class="card card-compact bg-base-200 p-4 shadow-lg max-w-sm w-full"
>
<div class="card-body gap-4 p-4">
<input
class="input input-bordered input-sm w-full"
type="text"
bind:value={calculation.player.name}
placeholder="Player name"
/>
<ul <div class="w-full overflow-x-auto">
class="bg-base-100 rounded-box shadow-xs divide-y divide-base-300" <table class="table">
> <thead>
<li class="p-3"> <tr>
<div class="flex items-center justify-between"> <th class="w-8"></th>
<div class="font-semibold text-sm">OVERALL</div> <th>Name</th>
<div class="badge badge-neutral badge-xs"> <th>Overall</th>
{calculation?.levels?.player || "N/A"} {#each Object.values(Attribute) as attribute (attribute)}
</div> <th>{attribute}</th>
</div> {/each}
</li> <th class="w-40"></th>
</tr>
{#each Object.values(Attribute) as attribute} </thead>
<li class="p-3"> <tbody>
<div class="flex items-center justify-between"> {#each calculations as calculation, index (calculation.key)}
<div class="opacity-80 text-sm"> <tr class="hover:bg-base-300">
{attribute} <td>
</div> <button
<div class="badge badge-ghost badge-xs"> onclick={() =>
{calculation?.levels?.attributes?.[ (calculation.expanded = !calculation.expanded)}
attribute class="btn btn-ghost btn-xs"
] || "N/A"} aria-label={calculation.expanded
</div> ? "Collapse details"
</div> : "Expand details"}
</li> >
{/each} {calculation.expanded ? "▾" : "▸"}
</ul> </button>
</td>
<fieldset <td>
class="fieldset bg-base-200/60 rounded-lg" <div class="flex items-center gap-2">
onchange={() =>
(calculation.levels = levelCalculator.calculate(
calculation.player,
calculation.activityPerformances,
))}
>
<div class="grid grid-cols-1 md:grid-cols-3 gap-3">
<label class="form-control">
<span class="label mb-1 text-xs">Gender</span>
<select
class="select select-bordered select-sm w-full"
bind:value={calculation.player.metrics.gender}
>
{#each Object.values(Gender) as gender}
<option value={gender}>{gender}</option>
{/each}
</select>
</label>
<label class="form-control">
<span class="label mb-1 text-xs">Age</span>
<input <input
class="input input-bordered input-sm w-full" class="input input-bordered input-sm w-full min-w-32"
type="number" type="text"
min="1" bind:value={calculation.player.name}
max="100" placeholder="Player name"
step="1"
bind:value={calculation.player.metrics.age}
/> />
</label> {#if isDirty(calculation)}
<div class="badge badge-warning badge-xs shrink-0">
<label class="form-control"> Unsaved
<span class="label mb-1 text-xs">Weight</span> </div>
<select {/if}
class="select select-bordered select-sm w-full" </div>
bind:value={calculation.player.metrics.weight} </td>
<td>
<div class="badge badge-neutral font-semibold">
{calculation?.levels?.player ?? "N/A"}
</div>
</td>
{#each Object.values(Attribute) as attribute (attribute)}
<td>
<div class="badge badge-ghost">
{calculation?.levels?.attributes?.[
attribute
] ?? "N/A"}
</div>
</td>
{/each}
<td>
<div class="flex gap-2 justify-end">
<button
onclick={() => saveCalculation(calculation)}
disabled={calculation.saving ||
!isDirty(calculation)}
class="btn btn-primary btn-xs"
> >
{#each range(400) as weight} {calculation.saving ? "Saving..." : "Save"}
<option value={lbToKg(weight)} </button>
>{weight}</option <button
> onclick={() =>
{/each} confirm(
</select> `Remove ${calculation.player.name}?`,
</label> ) && deleteCalculation(calculation, index)}
</div> disabled={calculation.deleting}
</fieldset> class="btn btn-error btn-xs"
>
<fieldset {calculation.deleting ? "..." : "Delete"}
class="fieldset bg-base-200/60 rounded-lg" </button>
onchange={() => </div>
(calculation.levels = levelCalculator.calculate( </td>
calculation.player, </tr>
calculation.activityPerformances, {#if calculation.expanded}
))} <tr>
> <td colspan={TABLE_COLUMNS}>
<div class="grid grid-cols-2 gap-3"> <div
{#each calculation.activityPerformances as activityPerformance} class="bg-base-200/60 rounded-lg p-4 flex flex-col gap-4"
<label class="form-control space-y-1"> >
<span class="label mb-1 text-xs"> <fieldset
{activityPerformance.activity} class="fieldset"
</span> onchange={() =>
<select (calculation.levels =
class="select select-bordered select-sm w-full" levelCalculator.calculate(
bind:value={activityPerformance.performance} calculation.player,
calculation.activityPerformances,
))}
> >
{#each performanceOptionsFromActivity(activityPerformance.activity) as option} <legend
<option value={option.value} class="fieldset-legend text-xs font-semibold opacity-70"
>{option.name}</option >
Metrics
</legend>
<div class="grid grid-cols-1 md:grid-cols-3 gap-3">
<label class="form-control">
<span class="label mb-1 text-xs"
>Gender</span
>
<select
class="select select-bordered select-sm w-full"
bind:value={
calculation.player.metrics
.gender
}
>
{#each Object.values(Gender) as gender (gender)}
<option value={gender}
>{gender}</option
>
{/each}
</select>
</label>
<label class="form-control">
<span class="label mb-1 text-xs"
>Age</span
>
<input
class="input input-bordered input-sm w-full"
type="number"
min="1"
max="100"
step="1"
bind:value={
calculation.player.metrics
.age
}
/>
</label>
<label class="form-control">
<span class="label mb-1 text-xs"
>Weight</span
>
<select
class="select select-bordered select-sm w-full"
bind:value={
calculation.player.metrics
.weight
}
>
{#each range(400) as weight (weight)}
<option
value={lbToKg(weight)}
>{weight}</option
>
{/each}
</select>
</label>
</div>
</fieldset>
<fieldset
class="fieldset"
onchange={() =>
(calculation.levels =
levelCalculator.calculate(
calculation.player,
calculation.activityPerformances,
))}
>
<legend
class="fieldset-legend text-xs font-semibold opacity-70"
>
Activity performances
</legend>
<div class="grid grid-cols-2 md:grid-cols-3 gap-3">
{#each calculation.activityPerformances as activityPerformance (activityPerformance.activity)}
<label
class="form-control space-y-1"
>
<span
class="label mb-1 text-xs"
>
{activityPerformance.activity}
</span>
<select
class="select select-bordered select-sm w-full"
bind:value={
activityPerformance.performance
}
>
{#each performanceOptionsFromActivity(activityPerformance.activity) as option (option.value)}
<option
value={option.value}
>{option.name}</option
>
{/each}
</select>
</label>
{/each}
</div>
</fieldset>
{#if calculation.saveError}
<div
role="alert"
class="alert alert-error alert-sm"
>
<span class="text-xs"
>{calculation.saveError}</span
> >
{/each} </div>
</select> {/if}
</label> {#if calculation.deleteError}
{/each} <div
</div> role="alert"
</fieldset> class="alert alert-error alert-sm"
</div> >
<button <span class="text-xs"
onclick={() => >{calculation.deleteError}</span
confirm(`Delete ${calculation.player.name}?`) && >
calculations.splice(index, 1)} </div>
class="btn btn-error btn-sm w-1/4">Delete</button {/if}
> </div>
</div> </td>
{/each} </tr>
</section> {/if}
{:else}
<tr>
<td
colspan={TABLE_COLUMNS}
class="text-center opacity-60"
>
{loading ? "Loading..." : "No players yet"}
</td>
</tr>
{/each}
</tbody>
</table>
</div>
@@ -25,6 +25,11 @@
path: "/activity", path: "/activity",
links: [{ name: "Events", path: "/events" }], links: [{ name: "Events", path: "/events" }],
}, },
{
name: "Verification",
path: "/verification",
links: [{ name: "Requests", path: "/requests" }],
},
]; ];
let { children } = $props(); let { children } = $props();
@@ -0,0 +1,171 @@
<script lang="ts">
import { api } from "$lib/api";
import { Activity } from "@blade-and-brawn/domain";
import type { VerificationStatus } from "@blade-and-brawn/api";
import { onMount } from "svelte";
type VerificationRow = NonNullable<
Awaited<ReturnType<typeof api.verifications.get>>["data"]
>[number];
const LIMIT = 25;
const STATUS_BADGE: Record<VerificationStatus, string> = {
request: "badge-info",
in_review: "badge-warning",
action_requested: "badge-error",
completed: "badge-success",
};
const URL_COLUMN = {
[Activity.BackSquat]: "url_back_squat",
[Activity.Deadlift]: "url_deadlift",
[Activity.BenchPress]: "url_bench_press",
[Activity.Run]: "url_run",
[Activity.BroadJump]: "url_broad_jump",
[Activity.ConeDrill]: "url_cone_drill",
} as const satisfies Record<Activity, keyof VerificationRow>;
function videoCount(row: VerificationRow): number {
return Object.values(Activity).filter((a) => row[URL_COLUMN[a]]).length;
}
function formatDate(value: string | Date | null): string {
if (!value) return "—";
return new Date(value).toLocaleString();
}
function errorMessage(err: unknown): string {
const value = (err as { value?: { error?: string } })?.value;
return (
value?.error ??
(err instanceof Error ? err.message : "Unknown error")
);
}
let requests = $state<VerificationRow[]>([]);
let loading = $state(true);
let loadError = $state<string | null>(null);
let offset = $state(0);
let status = $state<VerificationStatus | "">("request");
async function refresh() {
loading = true;
loadError = null;
try {
const res = await api.verifications.get({
query: { status: status || undefined, limit: LIMIT, offset },
});
if (res.error) throw res.error;
requests = res.data as VerificationRow[];
} catch (err) {
loadError = errorMessage(err);
} finally {
loading = false;
}
}
function setStatus(value: string) {
status = value as VerificationStatus | "";
offset = 0;
refresh();
}
function prevPage() {
offset = Math.max(0, offset - LIMIT);
refresh();
}
function nextPage() {
offset += LIMIT;
refresh();
}
onMount(refresh);
</script>
<div class="w-full flex items-center gap-2 flex-wrap mt-10 mb-5">
<select
class="select select-sm w-44"
value={status}
onchange={(e) => setStatus(e.currentTarget.value)}
>
<option value="">All statuses</option>
<option value="request">Request</option>
<option value="action_requested">Action requested</option>
<option value="completed">Completed</option>
</select>
<button
class="btn btn-secondary btn-sm"
onclick={refresh}
disabled={loading}
>
{loading ? "Refreshing..." : "Refresh"}
</button>
<div class="flex-1"></div>
<button
class="btn btn-neutral btn-sm"
onclick={prevPage}
disabled={offset === 0 || loading}
>
Prev
</button>
<button
class="btn btn-neutral btn-sm"
onclick={nextPage}
disabled={requests.length < LIMIT || loading}
>
Next
</button>
</div>
{#if loadError}
<div role="alert" class="alert alert-error w-full mb-4">
<span>{loadError}</span>
</div>
{/if}
<div class="w-full overflow-x-auto">
<table class="table">
<thead>
<tr>
<th>Player</th>
<th>Status</th>
<th>Videos</th>
<th>Requested</th>
<th></th>
</tr>
</thead>
<tbody>
{#each requests as row (row.id)}
<tr class="hover:bg-base-300">
<td>{row.name}</td>
<td>
<div class="badge {STATUS_BADGE[row.status]}">
{row.status}
</div>
</td>
<td>{videoCount(row)} / {Object.values(Activity).length}</td>
<td class="whitespace-nowrap">{formatDate(row.created_at)}</td>
<td>
<a
class="btn btn-primary btn-xs"
href="/verification/requests/{row.id}"
>
Review
</a>
</td>
</tr>
{:else}
<tr>
<td colspan="5" class="text-center opacity-60">
{loading ? "Loading..." : "No verification requests"}
</td>
</tr>
{/each}
</tbody>
</table>
</div>
@@ -0,0 +1,467 @@
<script lang="ts">
import { page } from "$app/state";
import { goto } from "$app/navigation";
import { api } from "$lib/api";
import { Activity, cmToIn, kgToLb } from "@blade-and-brawn/domain";
import type { ActivityVerifications } from "@blade-and-brawn/domain";
import type { VerificationStatus } from "@blade-and-brawn/api";
import { onMount } from "svelte";
async function loadVerification(id: string) {
return await api.verifications({ id }).get();
}
type VerificationDetail = NonNullable<
Awaited<ReturnType<typeof loadVerification>>["data"]
>;
type VerfChoice = "unset" | "pass" | "fail";
const ACTIVITIES = Object.values(Activity);
const STATUS_BADGE: Record<VerificationStatus, string> = {
request: "badge-info",
in_review: "badge-warning",
action_requested: "badge-error",
completed: "badge-success",
};
const URL_COLUMN = {
[Activity.BackSquat]: "url_back_squat",
[Activity.Deadlift]: "url_deadlift",
[Activity.BenchPress]: "url_bench_press",
[Activity.Run]: "url_run",
[Activity.BroadJump]: "url_broad_jump",
[Activity.ConeDrill]: "url_cone_drill",
} as const satisfies Record<Activity, keyof VerificationDetail>;
const VERF_COLUMN = {
[Activity.BackSquat]: "verf_back_squat",
[Activity.Deadlift]: "verf_deadlift",
[Activity.BenchPress]: "verf_bench_press",
[Activity.Run]: "verf_run",
[Activity.BroadJump]: "verf_broad_jump",
[Activity.ConeDrill]: "verf_cone_drill",
} as const satisfies Record<Activity, keyof VerificationDetail>;
const PERF_COLUMN = {
[Activity.BackSquat]: "perf_back_squat",
[Activity.Deadlift]: "perf_deadlift",
[Activity.BenchPress]: "perf_bench_press",
[Activity.Run]: "perf_run",
[Activity.BroadJump]: "perf_broad_jump",
[Activity.ConeDrill]: "perf_cone_drill",
} as const satisfies Record<Activity, keyof VerificationDetail>;
function formatPerformance(activity: Activity, value: number | null): string {
if (value === null) return "—";
switch (activity) {
case Activity.BackSquat:
case Activity.Deadlift:
case Activity.BenchPress:
return `${Math.round(kgToLb(value))} lb`;
case Activity.Run: {
const totalSeconds = Math.floor(value / 1000);
const minutes = Math.floor(totalSeconds / 60);
const seconds = totalSeconds % 60;
return `${minutes}m ${seconds}s`;
}
case Activity.ConeDrill:
return `${(value / 1000).toFixed(2)} seconds`;
case Activity.BroadJump: {
const inches = cmToIn(value);
const ft = Math.floor(inches / 12);
const rem = Math.round((inches - ft * 12) * 2) / 2;
return `${ft}' ${rem}"`;
}
default:
return "—";
}
}
function verfChoiceFrom(value: boolean | null): VerfChoice {
if (value === true) return "pass";
if (value === false) return "fail";
return "unset";
}
function toActivityVerifications(
choices: Record<Activity, VerfChoice>,
): ActivityVerifications {
const result: ActivityVerifications = {};
for (const activity of ACTIVITIES) {
if (choices[activity] === "pass") result[activity] = true;
else if (choices[activity] === "fail") result[activity] = false;
}
return result;
}
function errorMessage(err: unknown): string {
const value = (err as { value?: { error?: string } })?.value;
return (
value?.error ??
(err instanceof Error ? err.message : "Unknown error")
);
}
const id = page.params.id;
let verification = $state<VerificationDetail | null>(null);
let loading = $state(true);
let loadError = $state<string | null>(null);
let stepIndex = $state(0);
let notes = $state("");
let verf = $state<Record<Activity, VerfChoice>>(
Object.fromEntries(ACTIVITIES.map((a) => [a, "unset"])) as Record<
Activity,
VerfChoice
>,
);
let submitting = $state<"action" | "complete" | undefined>();
let submitError = $state<string | null>(null);
const SUMMARY_STEP = ACTIVITIES.length;
const isSummaryStep = $derived(stepIndex === SUMMARY_STEP);
const reviewedCount = $derived(
ACTIVITIES.filter((a) => verf[a] !== "unset").length,
);
const allReviewed = $derived(reviewedCount === ACTIVITIES.length);
const isReadOnly = $derived(
verification?.status === "completed" ||
verification?.status === "action_requested",
);
async function load() {
loading = true;
loadError = null;
try {
const res = await loadVerification(id);
if (res.error) throw res.error;
if (!res.data) throw new Error("Verification not found");
verification = res.data;
notes = verification.reviewer_notes ?? "";
verf = Object.fromEntries(
ACTIVITIES.map((a) => [
a,
verfChoiceFrom(verification![VERF_COLUMN[a]]),
]),
) as Record<Activity, VerfChoice>;
} catch (err) {
loadError = errorMessage(err);
} finally {
loading = false;
}
}
function goPrev() {
stepIndex = Math.max(0, stepIndex - 1);
}
function goNext() {
stepIndex = Math.min(SUMMARY_STEP, stepIndex + 1);
}
function setVerf(activity: Activity, choice: VerfChoice) {
if (isReadOnly) return;
verf[activity] = choice;
}
async function requestAction() {
submitting = "action";
submitError = null;
try {
const res = await api
.verifications({ id })
["request-action"].post({
reviewerNotes: notes || undefined,
activityVerifications: toActivityVerifications(verf),
});
if (res.error) throw res.error;
await goto("/verification/requests");
} catch (err) {
submitError = errorMessage(err);
} finally {
submitting = undefined;
}
}
async function complete() {
submitting = "complete";
submitError = null;
try {
const res = await api.verifications({ id }).complete.post({
activityVerifications: toActivityVerifications(verf),
});
if (res.error) throw res.error;
await goto("/verification/requests");
} catch (err) {
submitError = errorMessage(err);
} finally {
submitting = undefined;
}
}
onMount(load);
</script>
<div class="w-full mt-10 mb-5">
<a href="/verification/requests" class="link link-hover text-sm opacity-70"
>&larr; Back to requests</a
>
</div>
{#if loading}
<div class="w-full flex justify-center pt-10">
<span class="loading loading-spinner loading-lg"></span>
</div>
{:else if loadError}
<div role="alert" class="alert alert-error w-full mb-4">
<span>{loadError}</span>
</div>
{:else if verification}
{@const v = verification}
<div class="w-full flex items-center gap-3 flex-wrap mb-6">
<h1 class="text-xl font-semibold">{v.name}</h1>
<div class="badge {STATUS_BADGE[v.status]}">{v.status}</div>
<span class="text-sm opacity-70"
>{v.gender} · age {v.age} · {Math.round(kgToLb(v.weight))} lb</span
>
<div class="flex-1"></div>
<span class="text-sm opacity-70"
>{reviewedCount} / {ACTIVITIES.length} reviewed</span
>
</div>
<div role="tablist" class="tabs tabs-border w-full">
{#each ACTIVITIES as activity, i (activity)}
<button
role="tab"
class="tab gap-2"
class:tab-active={stepIndex === i}
onclick={() => (stepIndex = i)}
>
{activity}
{#if verf[activity] === "pass"}
<span class="text-success">✓</span>
{:else if verf[activity] === "fail"}
<span class="text-error">✕</span>
{/if}
</button>
{/each}
<button
role="tab"
class="tab"
class:tab-active={isSummaryStep}
onclick={() => (stepIndex = SUMMARY_STEP)}
>
Summary
</button>
</div>
{#if !isSummaryStep}
{@const activity = ACTIVITIES[stepIndex]}
<div class="w-full flex flex-col gap-4 bg-base-200/60 rounded-lg p-6 mt-4">
<div class="flex items-center justify-between flex-wrap gap-2">
<h2 class="text-lg font-semibold">{activity}</h2>
<div class="text-right">
<div class="text-xs opacity-60">Claimed</div>
<div class="text-2xl font-bold">
{formatPerformance(activity, v[PERF_COLUMN[activity]])}
</div>
</div>
</div>
{#if v[URL_COLUMN[activity]]}
{@const videoUrl = v[URL_COLUMN[activity]]}
<!-- svelte-ignore a11y_media_has_caption -->
<video
class="w-full max-h-[480px] rounded-lg bg-black"
src={videoUrl}
controls
preload="metadata"
></video>
<a
class="link link-primary text-xs self-start"
href={videoUrl}
target="_blank"
rel="noopener noreferrer"
>
Open video in new tab
</a>
{:else}
<div
class="w-full aspect-video rounded-lg bg-base-300 flex items-center justify-center opacity-60"
>
No video uploaded
</div>
{/if}
<div class="flex gap-2 justify-center mt-2">
<button
class="btn btn-sm {verf[activity] === 'pass'
? 'btn-success'
: 'btn-outline btn-success'}"
disabled={isReadOnly}
onclick={() => setVerf(activity, "pass")}
>
Pass
</button>
<button
class="btn btn-sm {verf[activity] === 'fail'
? 'btn-error'
: 'btn-outline btn-error'}"
disabled={isReadOnly}
onclick={() => setVerf(activity, "fail")}
>
Fail
</button>
<button
class="btn btn-sm btn-ghost"
disabled={isReadOnly || verf[activity] === "unset"}
onclick={() => setVerf(activity, "unset")}
>
Clear
</button>
</div>
<div class="flex justify-between mt-2">
<button
class="btn btn-neutral btn-sm"
onclick={goPrev}
disabled={stepIndex === 0}
>
&larr; Previous
</button>
<button class="btn btn-neutral btn-sm" onclick={goNext}>
Next &rarr;
</button>
</div>
</div>
{:else}
<div class="w-full flex flex-col gap-4 bg-base-200/60 rounded-lg p-6 mt-4">
<h2 class="text-lg font-semibold">Review summary</h2>
<div class="overflow-x-auto">
<table class="table table-sm">
<thead>
<tr>
<th>Activity</th>
<th>Claimed</th>
<th>Video</th>
<th>Result</th>
</tr>
</thead>
<tbody>
{#each ACTIVITIES as activity, i (activity)}
<tr>
<td>
<button
class="link link-hover"
onclick={() => (stepIndex = i)}
>
{activity}
</button>
</td>
<td>
{formatPerformance(
activity,
v[PERF_COLUMN[activity]],
)}
</td>
<td>
{#if v[URL_COLUMN[activity]]}
<a
class="link link-primary"
href={v[URL_COLUMN[activity]]}
target="_blank"
rel="noopener noreferrer"
>
View
</a>
{:else}
<span class="opacity-50">None</span>
{/if}
</td>
<td>
{#if verf[activity] === "pass"}
<span class="badge badge-success"
>Pass</span
>
{:else if verf[activity] === "fail"}
<span class="badge badge-error"
>Fail</span
>
{:else}
<span class="badge badge-ghost"
>Unreviewed</span
>
{/if}
</td>
</tr>
{/each}
</tbody>
</table>
</div>
<label class="form-control">
<span class="label mb-1 text-xs">Reviewer notes</span>
<textarea
class="textarea textarea-bordered w-full"
rows="3"
placeholder="What needs to be fixed?"
disabled={isReadOnly}
bind:value={notes}
></textarea>
</label>
{#if submitError}
<div role="alert" class="alert alert-error">
<span>{submitError}</span>
</div>
{/if}
<div class="flex items-center gap-2 justify-between">
<button
class="btn btn-neutral btn-sm"
onclick={goPrev}
disabled={stepIndex === 0}
>
&larr; Previous
</button>
{#if isReadOnly}
<span class="text-sm opacity-70">
{v.status === "completed"
? "This verification is completed and read-only."
: "This verification is waiting on the player to resubmit and is read-only."}
</span>
{:else}
<div class="flex items-center gap-2">
{#if !allReviewed}
<span class="text-xs text-warning"
>Mark every activity Pass or Fail to complete</span
>
{/if}
<button
class="btn btn-warning"
disabled={!!submitting}
onclick={requestAction}
>
{submitting === "action"
? "Submitting..."
: "Request action"}
</button>
<button
class="btn btn-success"
disabled={!!submitting || !allReviewed}
onclick={complete}
>
{submitting === "complete"
? "Submitting..."
: "Complete"}
</button>
</div>
{/if}
</div>
</div>
{/if}
{/if}
+74 -74
View File
@@ -9,7 +9,7 @@
"pino": "^10.3.1", "pino": "^10.3.1",
}, },
"devDependencies": { "devDependencies": {
"@types/bun": "^1.4.0", "@types/bun": "^1.4.2",
"typescript": "^7.0.2", "typescript": "^7.0.2",
}, },
}, },
@@ -23,8 +23,8 @@
"@elysia/server-timing": "^1.4.1", "@elysia/server-timing": "^1.4.1",
"@elysiajs/cors": "^1.4.2", "@elysiajs/cors": "^1.4.2",
"@types/pg": "^8.23.1", "@types/pg": "^8.23.1",
"elysia": "^1.4.29", "elysia": "^1.4.30",
"kysely": "^0.29.5", "kysely": "^0.29.6",
"ml-levenberg-marquardt": "^5.1.0", "ml-levenberg-marquardt": "^5.1.0",
"pg": "^8.23.0", "pg": "^8.23.0",
"zipcodes-us": "^1.1.3", "zipcodes-us": "^1.1.3",
@@ -52,16 +52,16 @@
"@blade-and-brawn/domain": "workspace:*", "@blade-and-brawn/domain": "workspace:*",
"@elysia/eden": "^1.4.10", "@elysia/eden": "^1.4.10",
"@tailwindcss/vite": "^4.3.3", "@tailwindcss/vite": "^4.3.3",
"daisyui": "^5.7.20", "daisyui": "^5.7.38",
"jose": "^6.2.10", "jose": "^6.2.12",
"tailwindcss": "^4.3.3", "tailwindcss": "^4.3.3",
}, },
"devDependencies": { "devDependencies": {
"@blade-and-brawn/api": "workspace:*", "@blade-and-brawn/api": "workspace:*",
"@sveltejs/kit": "^2.70.3", "@sveltejs/kit": "^2.70.3",
"@sveltejs/vite-plugin-svelte": "^6.2.4", "@sveltejs/vite-plugin-svelte": "^6.2.4",
"elysia": "^1.4.29", "elysia": "^1.4.30",
"svelte": "^5.56.10", "svelte": "^5.57.0",
"svelte-adapter-bun": "^1.0.1", "svelte-adapter-bun": "^1.0.1",
"svelte-check": "^4.7.6", "svelte-check": "^4.7.6",
"vite": "^7.3.6", "vite": "^7.3.6",
@@ -187,99 +187,99 @@
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.6.0", "", {}, "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw=="],
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
"@napi-rs/lzma-linux-x64-gnu": ["@napi-rs/lzma-linux-x64-gnu@1.5.1", "", { "os": "linux", "cpu": "x64" }, "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ=="], "@napi-rs/lzma-linux-x64-gnu": ["@napi-rs/lzma-linux-x64-gnu@1.5.1", "", { "os": "linux", "cpu": "x64" }, "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ=="],
"@oxc-project/types": ["@oxc-project/types@0.146.0", "", {}, "sha512-XC0QsnnhVe7sLIWmYmdPw7x5P0h4W8vUU3Nv1ySgWXtvCz8NizoAEpGXA0sOYoJQV2Rl13LgURAHQ5cI5ILCSA=="], "@oxc-project/types": ["@oxc-project/types@0.149.0", "", {}, "sha512-Efcc+iF0j3Bf67YjEqIqWXbX5XddXoK/Mw4K1/JuXwRCZ8N16VR7iT23nlCc9XrveFVh/E5Rqs2StT0V8v9LdA=="],
"@pinojs/redact": ["@pinojs/redact@0.4.0", "", {}, "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg=="], "@pinojs/redact": ["@pinojs/redact@0.4.0", "", {}, "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg=="],
"@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="], "@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="],
"@rolldown/binding-android-arm-eabi": ["@rolldown/binding-android-arm-eabi@1.2.5", "", { "os": "android", "cpu": "arm" }, "sha512-DLe/i+l8ynIBY7XEQ191TeZvCoowIGa18R+dIV30GW7DiOtp74i/xX8hs8GUjW5ARV7VZuie3d6AumSmCwbeRA=="], "@rolldown/binding-android-arm-eabi": ["@rolldown/binding-android-arm-eabi@1.2.8", "", { "os": "android", "cpu": "arm" }, "sha512-tN5aztYkKCte4i5SIrrz5yK/HMjEuCqCSCJa418jOV8tZ1cBY3YF2otxB1ktPxzsLA1BeTqwapK0bfjxNvHJVw=="],
"@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.2.5", "", { "os": "android", "cpu": "arm64" }, "sha512-zXcwKlQApYAOELHd8PwKDFkagYF9Wy4e0RJ+0qnzl9Pjnpj75TEG8ufv40p2J7kCEfwZAsNiuzRIyNNMWT38ig=="], "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.2.8", "", { "os": "android", "cpu": "arm64" }, "sha512-dIYTWl9XprMUiQFoc55KUyk/oS8SKYH3zFl0LTR7RT0Xj4hgSVyuJcroH8JUu8RcpF8fTB6E0aOwCkZoYPcDSQ=="],
"@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.2.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-dK4QakI42nzWgJT5sm4y4y/O//D4OxM75/cH28RLV+nzIN9AY+YsbuUVrUTjlLjXR6vpyxFbSsbmNuJ6BP9sww=="], "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.2.8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-PCSDQGXD2IyTEFrcgPyBM8jJuGmrbCMuoIOXdbEGVemruKACXoLQJrb+A45Z0L5t1RQkdfJprAYPkikbh7dzdA=="],
"@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.2.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-fqSALaUu1Wjd1nK2uW2kJDWdLCc8lx1IcY+MTY26Aurfdx19anlzhqXOgCFbBFQnlFDTn4TC1/7Nz4Bl2mLP3A=="], "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.2.8", "", { "os": "darwin", "cpu": "x64" }, "sha512-Uk7lRsGhPFHVX/sAUC6D5H9Ol30dFHd6iquokll2th3LpdJ3F5CzQB+7DHn0Ri2mG+U7k2zXiPHDrwZenXhwSA=="],
"@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.2.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-/vCnNxlkxs9tKxNDcyWUePpJ/PgTzxIaVhoM5SmG8UV+GR/IcPam4VYxi7GIMo7PSDuNqlJqvprqii9NqqVCMw=="], "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.2.8", "", { "os": "freebsd", "cpu": "x64" }, "sha512-DjszaTEVogPqA5bYzsEeqDCQxbcp2fexQwKcRspYji2yzR68fCf+e4fx6kBSRDwX5/brZaHw/hWS9+A/+/w9sQ=="],
"@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.2.5", "", { "os": "linux", "cpu": "arm" }, "sha512-abk0NLA519LxRCszmbE0jYKuQ9YPocOXTiOXOo6Yr+YAT95VH+PtqYAjOJvGKt3viEd/x4qzabAlwd5bHOOARg=="], "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.2.8", "", { "os": "linux", "cpu": "arm" }, "sha512-zmwa7FTmdzB6aaEEuuls18H6Ap5JmJPSoPTuXixeJZV6tG40SyLkApQtz1g8ptZtiEKqj9OM0oNLPh1AgvE31Q=="],
"@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.2.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-Y7eALiJ8lr0M2HH103Js+g7V34wf6snlpZLAsHI90uLhr3PVlNsbFVAXJC9d/V6BnPyKtpSwI+NcB/RLxsQxuA=="], "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.2.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-KdYQDPHwJVnbFwdTGMgxsI9SqblBlz6STGM+w1We/d5B8OWWidYH0MwkU/uA1wM5fIpO2MkOVxXrNzzuZhw9ew=="],
"@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.2.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-xMvZgnbZg4YVnR/AX2b3oOPDTFYJvUVaJg5FedA/LuvexAtXibZQej4cnTkw3rjsJ/ggUROB64TdtETiim+FYA=="], "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.2.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-jFJTifHnNPY+yzOoNZQfSIysrVyXzEQPhPnOUjmD1bcQGHH6s7c8cViKWar8YplQImE5N9JRqMCLrM2CdxOrZA=="],
"@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.2.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-GRjeqTUDHTo5GwntsLaAMcBahG3nlpjftXWZLN73HiYQlhwEowvarFgQnRnQZtIp4keXX7quXFbG38uPZBa2EA=="], "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.2.8", "", { "os": "linux", "cpu": "ppc64" }, "sha512-FhiOziBDWPBjbcmRzfLyIJnaP7AVMFXT7YCXPjXxj7wKU3vx24RjrCNN/zjvVa+N2vVoHJwCoUBvsrN/DG3zIA=="],
"@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.2.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-vLNTR45F2Uwc8AufkNXPmB4VliaXs+FvcheEogIzOXzO4l+LzieXF5A/TWxLy5HtqpsRCHUfd0lPVrrdgXdLHQ=="], "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.2.8", "", { "os": "linux", "cpu": "s390x" }, "sha512-WnHfADMzOV2Y55wlx1hzzQnar/wDt/VdvWSD99r18Mz9ylNieIGOkRx3UV21h7m/eJvjySYJkO26VvGNFkwsIQ=="],
"@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.2.5", "", { "os": "linux", "cpu": "x64" }, "sha512-Mgj59/HTuYeK9Gz2MA+mBWKnHsAgkBSec15ZMb1st3oIfFbX7gCjOae7GydHhzcyQi9Z/7M1QuN9bR3oFqF0jQ=="], "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.2.8", "", { "os": "linux", "cpu": "x64" }, "sha512-H9tRr5ibfXFVLxbPOseVewewFpl28zcEdjRDt2FTUZU7odxP0gEv1ki4/kGmcGOh78oRwZuuQllGLZ9zTJp84g=="],
"@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.2.5", "", { "os": "linux", "cpu": "x64" }, "sha512-mY8AP0/ichsbhAxGnLa3d3+MwV0EfgrPND2bplI3Ym8T6R2pJ0N87bvrKVwNXmdy3jnr6eQBecdqx/HMknBmpA=="], "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.2.8", "", { "os": "linux", "cpu": "x64" }, "sha512-UefiqfM3D6IVNlZ8tSGs9+Ejjud2T+oxO0IHADU45Y+lyEjD2dVFyZHbkfX0LUb5Zugo/oIv1eCO/KVYhgYJYA=="],
"@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.2.5", "", { "os": "none", "cpu": "arm64" }, "sha512-8SLssA2oweAxyRgDp789ACfRb/3P+zNRJpzZxSizxF9m8NUDQ4+3xjo8ttjhVGGw6Qxb70oZiEtIjaKikCO7Yw=="], "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.2.8", "", { "os": "none", "cpu": "arm64" }, "sha512-637Ke4kWSy6rp9cxQ9gMOXlxPgIw/c1beASV4M//3+9I4uwBVOOl74G+e3zyU3u19U7RkRl/HuewixZ/Z6+Rjg=="],
"@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.2.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-vGbruD5zquhoc8D9SViXgN2FBJtNdTyQ4DtG+SWiEGlJiAzoKcZ2xp+xuXCffhubVdt0NJlTZqkeRuERy7g8Cw=="], "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.2.8", "", { "os": "win32", "cpu": "arm64" }, "sha512-xWBkPOF1Q9k/Gv1nQXnVdLxKu74jXppuOM4Z3mnypVUJJJwLsMl7hNJGRAUJoG8A5MgOI1ACKM+wBFxSJzKy4A=="],
"@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.2.5", "", { "os": "win32", "cpu": "x64" }, "sha512-e/SXpgISz+IoqVcSSI0rx/d/he8zqLex+/rCWpnHpmVfmPIUjag9H6P7zotf0gJHwPUhQxZ/mF8tr6acebT9yw=="], "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.2.8", "", { "os": "win32", "cpu": "x64" }, "sha512-uz2ZvfgXbxqNwijjjbxrnvALwpyODDcgc1T1N8N3rf/DXKQmaFwmB4LX4yyjggpwN2obdQLb2rgirX5ffCWYng=="],
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="], "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="],
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.62.5", "", { "os": "android", "cpu": "arm" }, "sha512-jfkGfTwhQpsiSckPF8r9bU3pn3vyd72NlWaO+TgEO6WPSDnUhXzrNYCHBMOYj0ACaUgjm6eERLF+XV9a6RstoA=="], "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.63.3", "", { "os": "android", "cpu": "arm" }, "sha512-w3Jnvi1ocaVm/c7yVPpfB98XeSRBMyzp6njL5MVVbGyXjpmUkN+s6Hp4t0PqhGCCaI1ZHMKXt/w0lA1RCaLVcw=="],
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.62.5", "", { "os": "android", "cpu": "arm64" }, "sha512-oGVqyQlxnrz9/ty89oHpU857VUHEl5/Xu4R2lS+aivCTrNnSsbiENzTnNaBsjxH0CNWGPhzHArOLFwo+oKXveA=="], "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.63.3", "", { "os": "android", "cpu": "arm64" }, "sha512-uI/ESiaIbbRYAEhzy8PCUWDp1hB0bjAqM06mW9flOoNO4Q8DQpeoREhBR5Hegfl+wpXiguyJv6XSPzEN7OxyHQ=="],
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.62.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bW7B8xMEq8n99Q3ieEcPRGuphurdZAaFzQc9Efyyw3FL6DZO6pMy9xhdN+kBoD7Sy05xNXSr4OyPPnpkYriS/A=="], "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.63.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-oxhrd1jmXLwWZ83eQYDXxuqRdkqkzrjR3JobKeuUyfdNZo11FuQIvqEOZhyIT7OBHxXoGslDDjN0cQcM6T0TqQ=="],
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.62.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-YSwBS86QeHOGlrxJ1PSOIZSkzRL/JmKeunhc+lV6M1a6En8QuVCD/T/qIA0J4Gd2Y86RIOBYrLcOUtqGh9+/1w=="], "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.63.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-7/YiIMghVE8DrxKvNdorAaJVdriOFgOIpdStnPx8ppx5zfTwC3jBCSEAIzB7JD5404m65THl6H93UTTVUvypmg=="],
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.62.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-2fST8lILgl7cKbme/1KDdPCmbXbG+gqoV3bHp19L0ypX/3akYMBVdOunPleRCwonoLnXOZ/0F+Mt/v8POFmfcQ=="], "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.63.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-GXFZRRoMAytaI5z6N3Zhfw0WL18Q0M8r95D5hlC4GqE/lGk8pbSJNUBoOWDfbm6dTciqHj2nU87tI5f6XhQiOg=="],
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.62.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-cpIxQCP9J+EVad0a6LO1kY3ZGODlk80VlI+2I96B8xMcdHZ4pLVhfQ49JFpYqjPF91FFkQWftf57YlDcTiw9yQ=="], "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.63.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-77W+8X3ddYgPxUpB8nZFQs2Mq+wc4HVlcSRtApXLjYBcnPMkttrSnU8VwKQjeWYhMsITHFs5cWBQ8vz1Q+5RHQ=="],
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.62.5", "", { "os": "linux", "cpu": "arm" }, "sha512-r9fGh3eFs3e/udWh5ZjXQtxiYK/xoFxQaYR/cELxac/Udkl5Th+IsFm0CX3Kl9hmUH/we7EoMpjJgeQNnE0+IA=="], "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.63.3", "", { "os": "linux", "cpu": "arm" }, "sha512-FVkwK+iUC+mq+GipVK46rRVticfAPtvPUNlqlGXUDxdVk/UGjQiiiUVPUrEXdSpU2ufU0XxLGyTqDtBidDOVmg=="],
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.62.5", "", { "os": "linux", "cpu": "arm" }, "sha512-xdvFdp7OM6KLJviJT2g/YuRSUjnZgGHk4RNgwIbN7X6cPugOucV60DdHXWzsBVCUdrGb6qSXnJQrrAKMmQuj3Q=="], "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.63.3", "", { "os": "linux", "cpu": "arm" }, "sha512-+aGU1t3398yQOVj1Bz8o3e+KtswxAPvO+mtxtNdfXYMkXIHu7XhhkCD7/DEH9q8tF8uhDnMWvfpUKI8y1sZJsg=="],
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.62.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-rRqILAndyzHzP7T9NFQrq+4HFWNhqkqkKur7eiBpfLmz01PO0JKx5Vchu3YllE4YXI/Ftgq/szrDWg5GJ0mI8g=="], "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.63.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-cR0kjpRXR2KJ2oQK8E2KTPtphs+b9hZ8IhTZubNryt/RsqgdOZBQ2Zq0q5UedtiIi0rs3jVhJh55RE1ZHUVGUA=="],
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.62.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-Gf4X3qVMucayUvux6aXXPgXovocSFUC0rrffDuPI/S2nHhNMhjcZxsrAFYCOF350PRreW1XwzFj3CT/3bKsWCw=="], "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.63.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-y1RYi4Q3/9ByVWSSt9kX2ustE0B7kFYbJ6zZdVZVyqopZs3yhCTwRfrjIX4vezUJInma/Gs6BOFDJg7yZmJ0IQ=="],
"@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.62.5", "", { "os": "linux", "cpu": "none" }, "sha512-+s5qA0TNM0qm8PK/a5gt/1Hpx+NV08uSuCncvhziIlQzT6AEV2fnUQo7eBtFTFO0nA9scauvoR2HusfXmQnO4w=="], "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.63.3", "", { "os": "linux", "cpu": "none" }, "sha512-DNhEA5viIj3Z5bZLE4z4oV8N5ozWqDwyt7T6KG7VdLDJ0nW+rNOYlphBl4/3HQkK75qipPLsVOfStHHOwN9WSg=="],
"@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.62.5", "", { "os": "linux", "cpu": "none" }, "sha512-ybb6QvWwWJCbBWqERpc8K3pYVGIrXlG8MEQ8IIuJY6Y9KdHQxoFoNyfkAOtKn1VHu3KuLidXvwrvGR1mEjeWCw=="], "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.63.3", "", { "os": "linux", "cpu": "none" }, "sha512-17gQCqrIpXBX2Cmi9/TygnVOqGbzsba/iaqcYSL8FY7lNugg+7AiYNs5c5nKWD+NRQha36Sa0CqkJqH4XVHwnQ=="],
"@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.62.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-nZb1DtnOyhCmYvsC8A2CwOkopVg+IS1+fPUa7rMOAXtNw5+lLCLLPqd6XAiNrGtoQKsbvIBOwsHnBH/3wnb4HQ=="], "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.63.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-6LwVnZRIyINpdku/yOcI8Tm9YqLmhHK5emmlOOnW9tO0SYEm1FmKPcsSAGp0NBlqR2P04xaND4jvN6sTHqhq8A=="],
"@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.62.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-yMbj63Sp89ryrXLWyz+sy+fYD2HpOnMCLGbe4Oa1smclFSUukdtD/BgdiHaAetJNb74URD8U4hM+qG5KVzMEkg=="], "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.63.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-xMUqkTXlEUtI/p5AAukMwBRr1enU3efsTeF+bskeFfk8t1C9rcC8sLREcZXmTfAXEbvRdJVSonVJez3TMlbR3w=="],
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.62.5", "", { "os": "linux", "cpu": "none" }, "sha512-mhoan3OJw2kYV/e1jtIdmvUZgyBFeA6zGWsOswmR0Tg19TQbowZuR+JMLID6spbbBN7Zee2ejrgmy3+FxGrIdA=="], "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.63.3", "", { "os": "linux", "cpu": "none" }, "sha512-S3E94co9F9WRRqEaUoQZ38K1gCz6KiM+nL7/3ijq7fDGF3OznjS5TasgYITlvl27GQKtu4lOAOsr5MFwkijvOA=="],
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.62.5", "", { "os": "linux", "cpu": "none" }, "sha512-5ZTLmjWbb1VZdjuyhe83K/8QO0/h11midQCBP+X5OYn32ra7eOBoM0ZqtaY4nkgNsYgmdVhMYPoyVPTjUpHf3w=="], "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.63.3", "", { "os": "linux", "cpu": "none" }, "sha512-1QtRDwG42x5BJI3s9mxu5rEjDnfbSnk20HQ9/ylTAYnSwYwxMVb+Vgu34wzzTQ7ogqBybebgQNUDAvZVQ38DbA=="],
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.62.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-m53kG+br6PGxOTmgBEM2DHSDs9RVjsyEbUwjJPJGTFm1grWOG8EKJggDCTb60unD4Tjby8fi7/m9XfkEWasVWg=="], "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.63.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-BQhejF6ZXOpxbngiNTP12GCGQeaDVL2QXGeBVViKIYzFHM5RKxTxwUMB1fr1BeNFphFMpnRqC5QSXFSa4z6UQw=="],
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.62.5", "", { "os": "linux", "cpu": "x64" }, "sha512-6RHPJR1g/uvdYU8uXBnfq3nlqyZCP82Fr6NHgfGoaIeSh0YEqnX/x6uA9MmJJbnSH7swqX4F+CkGdUF+6doiQA=="], "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.63.3", "", { "os": "linux", "cpu": "x64" }, "sha512-SXagRwnI2Wlwlitllu59UK/nGVbD1CKPcNqDplHwIC4BqJcpXFjD32d1R/RbuISa95HdQrZM3/7v4bKiowFaLA=="],
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.62.5", "", { "os": "linux", "cpu": "x64" }, "sha512-xs+OXQtEXgpXT0DmA5+U3qnRZHdCST/5HRQxS8wSPZTUZN/EMWeHuSIod32LQklTBZBV9DyfncKBQ8n5V3eFdw=="], "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.63.3", "", { "os": "linux", "cpu": "x64" }, "sha512-2IPozoEALRCziGqE8O9KMK60PMu5TS1huv4fwoeCexj+WjmcwFtX9CTOVbfXCUqcELAubEwRFPYlzb/WvwY2HQ=="],
"@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.62.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-e7hD+sl3s+mcLQDZ8pbudBVsdG6r5yN4w3LqG2TJ8sQHDpblWj5lrJs/3m01Cvlxbt4x13zu5thLjgypgtkYzw=="], "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.63.3", "", { "os": "openbsd", "cpu": "x64" }, "sha512-AoxqosUHT9IX54hFn2TiN6A7d6ZKTtE6pd2bqWtqkkNJ6HJGaU6FRouGX8L1O7R/ZwsnCnpQrHzb4pDEx+UHRQ=="],
"@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.62.5", "", { "os": "none", "cpu": "arm64" }, "sha512-GiyJaCf+WpMub/17aPcKk27QMl5W6f+KhdPTjlFOn5akH5Wa/DCM9Stdx5cDfmasyKB08MqpVQ1uJE2RkkpbXg=="], "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.63.3", "", { "os": "none", "cpu": "arm64" }, "sha512-d+CaftKgmkFBzCwezMqqy1d0QNNYugqLCMcYVQWBy5SS2YfeMP8Q8ripkgx9O8IyBXXLHrJ+aaCV4U96usv6Yg=="],
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.62.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-+OQ8U2DdoEfXl8T4Fb18AjmEwbXMerKDKCL8yCPAYhKCEEKoul7rkbeGCBFCbAlaGaa7pmtRTpkAJM2LE/i5FA=="], "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.63.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-xXlDF6nR1eOuXbdDy5Hl5fmtY7teUDevF/k0O7IPoZe4Tpmdv+lgdE5JRsnhQtt37ql9P0VF2kAN9a0OCZdo+Q=="],
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.62.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-KanvAZrPKbDBFwrgiU9yEVpQoox9QPV1WZOXX7HudJQY+eSlu82CtWxDU8WtuRRvtN5EGkLczkd6Y6DTcvm9wA=="], "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.63.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-YtXAgLN+JP7Ay6qG3eWhc7IHMQPzLc8r3uvhAvlJIoCz/4Q32+Bl9Fmnywidh8v1GOIMmymjovfqY9ETAtysvA=="],
"@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.62.5", "", { "os": "win32", "cpu": "x64" }, "sha512-1aC3UEWTtRl3RK3VpDJ/Tqk1XI4SLTmXIthAq6wRWo8XiSXJNd+VprJM4/1P4+i6HIaFEFlVi9sTTziniD2tOQ=="], "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.63.3", "", { "os": "win32", "cpu": "x64" }, "sha512-WuWtSJRNo549vzcfZyEgfqb6zeSgn1F+UE5kQ+BCjzz0W4MGCjntUHkZVc1VRuAM7+ULaSyhiPxD1spyewFvkQ=="],
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.62.5", "", { "os": "win32", "cpu": "x64" }, "sha512-/gDJaRs4gl0NPIwqCz+6PkpmhhjRAD2j6P4rSNHBzUkO3naEx2mIU0pRle1vUNRQ7mE/+8OOeXLTv/J56FKiQg=="], "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.63.3", "", { "os": "win32", "cpu": "x64" }, "sha512-+lIKX7O0+IGe7WuhATaAMMeT7B76vfhXH/l9wLQL+nvyhbw2ohYCKIdWL56JfDu75CWt5oKRP4QFH/jkMtBquA=="],
"@sapphire/async-queue": ["@sapphire/async-queue@1.5.5", "", {}, "sha512-cvGzxbba6sav2zZkH8GPf2oGk9yYoD5qrNWdu9fRehifgnFZJMV+nuy2nON2roRO4yQQ+v7MK/Pktl/HgfsUXg=="], "@sapphire/async-queue": ["@sapphire/async-queue@1.5.5", "", {}, "sha512-cvGzxbba6sav2zZkH8GPf2oGk9yYoD5qrNWdu9fRehifgnFZJMV+nuy2nON2roRO4yQQ+v7MK/Pktl/HgfsUXg=="],
@@ -335,7 +335,7 @@
"@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="], "@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="],
"@types/bun": ["@types/bun@1.4.0", "", { "dependencies": { "bun-types": "1.4.0" } }, "sha512-K+lZULY23vRgK/CfTjFIV+tyifaNdSMlPh9j+6mQ/cLfpOznLyAuzgV/JQysyECpkBQLVMSyvjlr2fBUSA9wFQ=="], "@types/bun": ["@types/bun@1.4.2", "", { "dependencies": { "bun-types": "1.4.2" } }, "sha512-GimotNn7+ZV0uVArItBbriZsR1oNf0+WTzPkdcFrzShI7k2norL0uzEaJT8T33dWr7O/c9ZDuAFQrctKCi72oQ=="],
"@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="], "@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="],
@@ -345,8 +345,6 @@
"@types/pg": ["@types/pg@8.23.1", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-fKVHpikPdg4GKks3JuLEhvwSyvwzF23hnabPy6DD8ljVbC7+6J5dQzdv4arV6jqq57djnMgs1HKBxX4P8aBI3A=="], "@types/pg": ["@types/pg@8.23.1", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-fKVHpikPdg4GKks3JuLEhvwSyvwzF23hnabPy6DD8ljVbC7+6J5dQzdv4arV6jqq57djnMgs1HKBxX4P8aBI3A=="],
"@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="],
"@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
"@typescript/typescript-aix-ppc64": ["@typescript/typescript-aix-ppc64@7.0.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ=="], "@typescript/typescript-aix-ppc64": ["@typescript/typescript-aix-ppc64@7.0.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ=="],
@@ -405,7 +403,7 @@
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
"bun-types": ["bun-types@1.4.0", "", { "dependencies": { "@types/node": "*" } }, "sha512-iIKw23BspnQQYd3prITOBxeUsxBHnwzX6YJfGMuNOZzeNcMmVqzIIVGRm1l69ogaPQmb4wB6BN8mA5bE9YuC5Q=="], "bun-types": ["bun-types@1.4.2", "", { "dependencies": { "@types/node": "*" } }, "sha512-bxV1FgK7yBIzjRe5zBozIM4Bem11ZJcCXSrjWRG3YWLt8yFDePu4cLjpebO8OvPeIE9trbyPF4fuj3Cia4Fj3w=="],
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
@@ -425,7 +423,7 @@
"cosmiconfig": ["cosmiconfig@9.0.2", "", { "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0" }, "peerDependencies": { "typescript": ">=4.9.5" }, "optionalPeers": ["typescript"] }, "sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg=="], "cosmiconfig": ["cosmiconfig@9.0.2", "", { "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0" }, "peerDependencies": { "typescript": ">=4.9.5" }, "optionalPeers": ["typescript"] }, "sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg=="],
"daisyui": ["daisyui@5.7.20", "", {}, "sha512-qoL9qXXo/K/MzcteD1SvZOSeBaL8F9qBJvwX3KEpiVHQLzIEtGkNl/ZznSI7J0d+qnQJa2dAAFzTFDAx9df1rw=="], "daisyui": ["daisyui@5.7.38", "", {}, "sha512-ef3RIbteKVmlQ10ak0l4mu8F70pBJ2UBjDJNsA2vWlpNdErpHqfxddxK2ckNNYd7vQvTqP4a1A1vSsunlCGVOw=="],
"dateformat": ["dateformat@4.6.3", "", {}, "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA=="], "dateformat": ["dateformat@4.6.3", "", {}, "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA=="],
@@ -435,11 +433,11 @@
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
"devalue": ["devalue@5.9.1", "", {}, "sha512-+17vil3EVQRzvtDJSFuTWEb8XJRvXqAiV3qZyQWD398QeXUa6CxsUyMdD1fxzEhUrd4FojitFz7lhIHBTlV4fw=="], "devalue": ["devalue@5.9.2", "", {}, "sha512-po4PAY5c53tw5XMocSnf8A/5OHhbbUftpr93aEN6BBoAdntUmK7vu7wOATqvt7cXO7m1Cl4gMVn6p7n6n4mj0w=="],
"diff": ["diff@8.0.4", "", {}, "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw=="], "diff": ["diff@8.0.4", "", {}, "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw=="],
"discord-api-types": ["discord-api-types@0.38.53", "", {}, "sha512-HL1zz/UuZ+bbJjA/X8Kbxx9gk8v9rJAbTeWRNYKmIdjwJ7EovjlHgoJTxcLpATfNJ+AonOtMdy3Y5MVIJAAd/A=="], "discord-api-types": ["discord-api-types@0.38.55", "", {}, "sha512-ytuaRTzdnHUCXJ6KjL9MrItQX0xKncBKEeYI1Bst4+ud47eejH3cG6gaesYakjpPcUhh68XYS2YwGq3mmujFsA=="],
"discord.js": ["discord.js@14.27.0", "", { "dependencies": { "@discordjs/builders": "^1.14.1", "@discordjs/collection": "1.5.3", "@discordjs/formatters": "^0.6.2", "@discordjs/rest": "^2.6.2", "@discordjs/util": "^1.2.0", "@discordjs/ws": "^1.2.3", "@sapphire/snowflake": "3.5.5", "discord-api-types": "^0.38.49", "fast-deep-equal": "3.1.3", "lodash.snakecase": "4.1.1", "magic-bytes.js": "^1.13.0", "tslib": "^2.6.3", "undici": "^6.27.0" } }, "sha512-qHbFlFG2N7y3LjPySYsL6A1+BnX6bkTVgo842EX0CqVPk/KTMwZkojPHEXKsQUpWZNyz5BISNHK1cPpQw0+m4A=="], "discord.js": ["discord.js@14.27.0", "", { "dependencies": { "@discordjs/builders": "^1.14.1", "@discordjs/collection": "1.5.3", "@discordjs/formatters": "^0.6.2", "@discordjs/rest": "^2.6.2", "@discordjs/util": "^1.2.0", "@discordjs/ws": "^1.2.3", "@sapphire/snowflake": "3.5.5", "discord-api-types": "^0.38.49", "fast-deep-equal": "3.1.3", "lodash.snakecase": "4.1.1", "magic-bytes.js": "^1.13.0", "tslib": "^2.6.3", "undici": "^6.27.0" } }, "sha512-qHbFlFG2N7y3LjPySYsL6A1+BnX6bkTVgo842EX0CqVPk/KTMwZkojPHEXKsQUpWZNyz5BISNHK1cPpQw0+m4A=="],
@@ -447,11 +445,11 @@
"dotenv-expand": ["dotenv-expand@12.0.3", "", { "dependencies": { "dotenv": "^16.4.5" } }, "sha512-uc47g4b+4k/M/SeaW1y4OApx+mtLWl92l5LMPP0GNXctZqELk+YGgOPIIC5elYmUH4OuoK3JLhuRUYegeySiFA=="], "dotenv-expand": ["dotenv-expand@12.0.3", "", { "dependencies": { "dotenv": "^16.4.5" } }, "sha512-uc47g4b+4k/M/SeaW1y4OApx+mtLWl92l5LMPP0GNXctZqELk+YGgOPIIC5elYmUH4OuoK3JLhuRUYegeySiFA=="],
"elysia": ["elysia@1.4.29", "", { "dependencies": { "cookie": "^1.1.1", "exact-mirror": "^0.2.7", "fast-decode-uri-component": "^1.0.1", "memoirist": "^0.4.0" }, "peerDependencies": { "@sinclair/typebox": ">= 0.34.0 < 1", "@types/bun": ">= 1.2.0", "file-type": ">= 20.0.0", "openapi-types": ">= 12.0.0", "typescript": ">= 5.0.0" }, "optionalPeers": ["@types/bun", "typescript"] }, "sha512-GwMRGGwSdjfPt+w3LA0fqTuYJtS8uVRJicvoar98/HrO5qdFKDc9CwjIb6Kja+v39lkY+58hr2JvdR9jQzlUuA=="], "elysia": ["elysia@1.4.30", "", { "dependencies": { "cookie": "^1.1.1", "exact-mirror": "^0.2.7", "fast-decode-uri-component": "^1.0.1", "memoirist": "^0.4.0" }, "peerDependencies": { "@sinclair/typebox": ">= 0.34.0 < 1", "@types/bun": ">= 1.2.0", "file-type": ">= 20.0.0", "openapi-types": ">= 12.0.0", "typescript": ">= 5.0.0" }, "optionalPeers": ["@types/bun", "typescript"] }, "sha512-S2qqV0CbM4faB1hQQ5IYoeYnAUinjHlzRduxaBc4OoNqh0GjOc8k6tt3hv5ozWcG6Svr6hugw6JL4zNke4jwfA=="],
"end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="], "end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="],
"enhanced-resolve": ["enhanced-resolve@5.24.5", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A=="], "enhanced-resolve": ["enhanced-resolve@5.25.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-nGXts5znJzmWPu+mIE9izCOzdg63oJca2mDzGWWTth7sr4aCToKcoyFVBQwN75Ij5Pf6p510EwkTqViTRzDV+w=="],
"env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="], "env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="],
@@ -461,11 +459,11 @@
"esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="], "esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="],
"esrap": ["esrap@2.3.6", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" }, "peerDependencies": { "@typescript-eslint/types": "^8.2.0" }, "optionalPeers": ["@typescript-eslint/types"] }, "sha512-yc0OC12UjPqLoc+fe+v5GNs4TOjAigUw3sTikfC+xeBPGUw7gDRz3DtYaqEhxyMVJojcSWJw7jT0QWR+CbuE/A=="], "esrap": ["esrap@2.3.7", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" }, "peerDependencies": { "@typescript-eslint/types": "^8.2.0" }, "optionalPeers": ["@typescript-eslint/types"] }, "sha512-n2nf7fZR3c9yXf0BPEuHuXqT+KW0SJVj4cN5FMEkpCZ3scLjOQWpiccyCxVzCC2q1wubTghuEGzngJY/7Ah0Ow=="],
"exact-mirror": ["exact-mirror@0.2.7", "", { "peerDependencies": { "@sinclair/typebox": "^0.34.15" }, "optionalPeers": ["@sinclair/typebox"] }, "sha512-+MeEmDcLA4o/vjK2zujgk+1VTxPR4hdp23qLqkWfStbECtAq9gmsvQa3LW6z/0GXZyHJobrCnmy1cdeE7BjsYg=="], "exact-mirror": ["exact-mirror@0.2.7", "", { "peerDependencies": { "@sinclair/typebox": "^0.34.15" }, "optionalPeers": ["@sinclair/typebox"] }, "sha512-+MeEmDcLA4o/vjK2zujgk+1VTxPR4hdp23qLqkWfStbECtAq9gmsvQa3LW6z/0GXZyHJobrCnmy1cdeE7BjsYg=="],
"fast-copy": ["fast-copy@4.0.4", "", {}, "sha512-eVAiWVNPSEGIzDl5yPuLrx8fNMogScXvD9xp1Kzd41FjRIz2I3sSIcxsFeM5EzFfHAfobdvs8ZySffUopljvIA=="], "fast-copy": ["fast-copy@4.1.1", "", {}, "sha512-A4QTJmuiztpGtr6AMeJts9R4hbj2ZBUwtOaKrG6rw2y7t6+IaJKjz5M3XDs8BUznxDH43FVc6A0y/gWlMl4UtA=="],
"fast-decode-uri-component": ["fast-decode-uri-component@1.0.1", "", {}, "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg=="], "fast-decode-uri-component": ["fast-decode-uri-component@1.0.1", "", {}, "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg=="],
@@ -475,7 +473,7 @@
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
"file-type": ["file-type@22.0.2", "", { "dependencies": { "@tokenizer/inflate": "^0.4.1", "strtok3": "^10.3.5", "token-types": "^6.1.2", "uint8array-extras": "^1.5.0" } }, "sha512-0H8TsCUGBLx+V5adH3EY52hTAcyLKbV1D4gq5cIOJ6DnQAHeV9Z2Hhuc5CoBX4YmvB2oL+JIC84z0qO7JsCoNw=="], "file-type": ["file-type@22.1.0", "", { "dependencies": { "@tokenizer/inflate": "^0.4.1", "strtok3": "^10.3.5", "token-types": "^6.1.2", "uint8array-extras": "^1.5.0" } }, "sha512-fbvf9u6jnHWr1EP6V8R99E9iLMhNLXgZkUzWBiPpJ5/89UJ/Atuxjn5YXR+XaZWo2hc2/8mGfTez+NDAgOcpLA=="],
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
@@ -501,19 +499,19 @@
"jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="], "jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="],
"jose": ["jose@6.2.10", "", {}, "sha512-iiW7J9qRFlGxvCOIBDBDxFePQSn7ZMAnrYGhrrOo6siO/MIqwfyilLR27pkfDgUk+raLuzADS8A3S/KLBisc0g=="], "jose": ["jose@6.2.12", "", {}, "sha512-9NiFmJEex0sy2Dk58j2UGBSHgUs2ypF9eZSu4L6vjOX3Dp96Sw1F3uL+H+D1sx02jZZdzUT0HgvCy59CuvXcWw=="],
"joycon": ["joycon@3.1.1", "", {}, "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw=="], "joycon": ["joycon@3.1.1", "", {}, "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw=="],
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
"js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="], "js-yaml": ["js-yaml@4.3.2", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA=="],
"json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="], "json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="],
"kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
"kysely": ["kysely@0.29.5", "", {}, "sha512-ooa+eSbBNPTo3MycPEuW5jdrxQdQwdtB3LC3h43FiXQbIry5tR0C5lDG7eealK0E4D7XjrnOP5DIUg/LyjRMYQ=="], "kysely": ["kysely@0.29.6", "", {}, "sha512-hHaB8C/rfzDDtr/t8YZwxAuPJTT0zHyaPoVzcXwDYhYNAgH/4sIfVhi/XLLIY+bL/FqaIJnjATDbi8ObSELmxg=="],
"kysely-codegen": ["kysely-codegen@0.20.0", "", { "dependencies": { "chalk": "4.1.2", "cosmiconfig": "^9.0.0", "diff": "^8.0.3", "dotenv": "^17.2.4", "dotenv-expand": "^12.0.3", "micromatch": "^4.0.8", "minimist": "^1.2.8", "pluralize": "^8.0.0", "zod": "^4.3.6" }, "peerDependencies": { "@libsql/kysely-libsql": ">=0.3.0 <0.5.0", "@tediousjs/connection-string": "^1.0.0", "better-sqlite3": ">=7.6.2 <13.0.0", "kysely": ">=0.27.0 <1.0.0", "kysely-bun-sqlite": ">=0.3.2 <1.0.0", "kysely-bun-worker": ">=1.2.0 <2.0.0", "mysql2": ">=2.3.3 <4.0.0", "pg": ">=8.8.0 <9.0.0", "tarn": ">=3.0.0 <4.0.0", "tedious": ">=18.0.0 <20.0.0" }, "optionalPeers": ["@libsql/kysely-libsql", "@tediousjs/connection-string", "better-sqlite3", "kysely-bun-sqlite", "kysely-bun-worker", "mysql2", "pg", "tarn", "tedious"], "bin": { "kysely-codegen": "dist/cli/bin.js" } }, "sha512-LSi2KBG7uDmNCZ+XurLSA9LH7XFyyoQ6xb5DLJSInPTSYLVApjOP2KwO8mSaREWTtoX+C2AG2GTlYR0DLjTbcA=="], "kysely-codegen": ["kysely-codegen@0.20.0", "", { "dependencies": { "chalk": "4.1.2", "cosmiconfig": "^9.0.0", "diff": "^8.0.3", "dotenv": "^17.2.4", "dotenv-expand": "^12.0.3", "micromatch": "^4.0.8", "minimist": "^1.2.8", "pluralize": "^8.0.0", "zod": "^4.3.6" }, "peerDependencies": { "@libsql/kysely-libsql": ">=0.3.0 <0.5.0", "@tediousjs/connection-string": "^1.0.0", "better-sqlite3": ">=7.6.2 <13.0.0", "kysely": ">=0.27.0 <1.0.0", "kysely-bun-sqlite": ">=0.3.2 <1.0.0", "kysely-bun-worker": ">=1.2.0 <2.0.0", "mysql2": ">=2.3.3 <4.0.0", "pg": ">=8.8.0 <9.0.0", "tarn": ">=3.0.0 <4.0.0", "tedious": ">=18.0.0 <20.0.0" }, "optionalPeers": ["@libsql/kysely-libsql", "@tediousjs/connection-string", "better-sqlite3", "kysely-bun-sqlite", "kysely-bun-worker", "mysql2", "pg", "tarn", "tedious"], "bin": { "kysely-codegen": "dist/cli/bin.js" } }, "sha512-LSi2KBG7uDmNCZ+XurLSA9LH7XFyyoQ6xb5DLJSInPTSYLVApjOP2KwO8mSaREWTtoX+C2AG2GTlYR0DLjTbcA=="],
@@ -575,9 +573,9 @@
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
"nanoid": ["nanoid@3.3.18", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w=="], "nanoid": ["nanoid@3.3.19", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug=="],
"obug": ["obug@2.1.4", "", {}, "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA=="], "obug": ["obug@2.2.1", "", {}, "sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q=="],
"on-exit-leak-free": ["on-exit-leak-free@2.1.2", "", {}, "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA=="], "on-exit-leak-free": ["on-exit-leak-free@2.1.2", "", {}, "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA=="],
@@ -607,7 +605,7 @@
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
"picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="], "picomatch": ["picomatch@4.0.7", "", {}, "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA=="],
"pino": ["pino@10.3.1", "", { "dependencies": { "@pinojs/redact": "^0.4.0", "atomic-sleep": "^1.0.0", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "^3.0.0", "pino-std-serializers": "^7.0.0", "process-warning": "^5.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", "sonic-boom": "^4.0.1", "thread-stream": "^4.0.0" }, "bin": { "pino": "bin.js" } }, "sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg=="], "pino": ["pino@10.3.1", "", { "dependencies": { "@pinojs/redact": "^0.4.0", "atomic-sleep": "^1.0.0", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "^3.0.0", "pino-std-serializers": "^7.0.0", "process-warning": "^5.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", "sonic-boom": "^4.0.1", "thread-stream": "^4.0.0" }, "bin": { "pino": "bin.js" } }, "sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg=="],
@@ -619,7 +617,7 @@
"pluralize": ["pluralize@8.0.0", "", {}, "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA=="], "pluralize": ["pluralize@8.0.0", "", {}, "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA=="],
"postcss": ["postcss@8.5.26", "", { "dependencies": { "nanoid": "^3.3.17", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ=="], "postcss": ["postcss@8.5.28", "", { "dependencies": { "nanoid": "^3.3.18", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A=="],
"postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="], "postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="],
@@ -641,9 +639,9 @@
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
"rolldown": ["rolldown@1.2.5", "", { "dependencies": { "@oxc-project/types": "=0.146.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm-eabi": "1.2.5", "@rolldown/binding-android-arm64": "1.2.5", "@rolldown/binding-darwin-arm64": "1.2.5", "@rolldown/binding-darwin-x64": "1.2.5", "@rolldown/binding-freebsd-x64": "1.2.5", "@rolldown/binding-linux-arm-gnueabihf": "1.2.5", "@rolldown/binding-linux-arm64-gnu": "1.2.5", "@rolldown/binding-linux-arm64-musl": "1.2.5", "@rolldown/binding-linux-ppc64-gnu": "1.2.5", "@rolldown/binding-linux-s390x-gnu": "1.2.5", "@rolldown/binding-linux-x64-gnu": "1.2.5", "@rolldown/binding-linux-x64-musl": "1.2.5", "@rolldown/binding-openharmony-arm64": "1.2.5", "@rolldown/binding-win32-arm64-msvc": "1.2.5", "@rolldown/binding-win32-x64-msvc": "1.2.5" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-VD2IE5PUG4Oj8zz2VGykiYd5wbnjdIiSsNQb8Qu5B+noEp+A78mu2iVvpp27g8es14Tk9rofNs5Tku9iQCS4fA=="], "rolldown": ["rolldown@1.2.8", "", { "dependencies": { "@oxc-project/types": "=0.149.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm-eabi": "1.2.8", "@rolldown/binding-android-arm64": "1.2.8", "@rolldown/binding-darwin-arm64": "1.2.8", "@rolldown/binding-darwin-x64": "1.2.8", "@rolldown/binding-freebsd-x64": "1.2.8", "@rolldown/binding-linux-arm-gnueabihf": "1.2.8", "@rolldown/binding-linux-arm64-gnu": "1.2.8", "@rolldown/binding-linux-arm64-musl": "1.2.8", "@rolldown/binding-linux-ppc64-gnu": "1.2.8", "@rolldown/binding-linux-s390x-gnu": "1.2.8", "@rolldown/binding-linux-x64-gnu": "1.2.8", "@rolldown/binding-linux-x64-musl": "1.2.8", "@rolldown/binding-openharmony-arm64": "1.2.8", "@rolldown/binding-win32-arm64-msvc": "1.2.8", "@rolldown/binding-win32-x64-msvc": "1.2.8" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-Z67nTmhZe7anqnM/EjI392w5i/ANUinjip7QYsOyN37oayduxt3ksdX0hf5OOamkAd53BiIHfbfSzfUmzKFQqQ=="],
"rollup": ["rollup@4.62.5", "", { "dependencies": { "@types/estree": "1.0.9" }, "optionalDependencies": { "@napi-rs/lzma-linux-x64-gnu": "1.5.1", "@rollup/rollup-android-arm-eabi": "4.62.5", "@rollup/rollup-android-arm64": "4.62.5", "@rollup/rollup-darwin-arm64": "4.62.5", "@rollup/rollup-darwin-x64": "4.62.5", "@rollup/rollup-freebsd-arm64": "4.62.5", "@rollup/rollup-freebsd-x64": "4.62.5", "@rollup/rollup-linux-arm-gnueabihf": "4.62.5", "@rollup/rollup-linux-arm-musleabihf": "4.62.5", "@rollup/rollup-linux-arm64-gnu": "4.62.5", "@rollup/rollup-linux-arm64-musl": "4.62.5", "@rollup/rollup-linux-loong64-gnu": "4.62.5", "@rollup/rollup-linux-loong64-musl": "4.62.5", "@rollup/rollup-linux-ppc64-gnu": "4.62.5", "@rollup/rollup-linux-ppc64-musl": "4.62.5", "@rollup/rollup-linux-riscv64-gnu": "4.62.5", "@rollup/rollup-linux-riscv64-musl": "4.62.5", "@rollup/rollup-linux-s390x-gnu": "4.62.5", "@rollup/rollup-linux-x64-gnu": "4.62.5", "@rollup/rollup-linux-x64-musl": "4.62.5", "@rollup/rollup-openbsd-x64": "4.62.5", "@rollup/rollup-openharmony-arm64": "4.62.5", "@rollup/rollup-win32-arm64-msvc": "4.62.5", "@rollup/rollup-win32-ia32-msvc": "4.62.5", "@rollup/rollup-win32-x64-gnu": "4.62.5", "@rollup/rollup-win32-x64-msvc": "4.62.5", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-/tqMfgP7GPA3PHhCmuiS4vIjrSVhHLgY++i+dhbG462euyAj7FpM4D9uq1X3BgjlqRdpcOrYhcQtfiQLNc8tqw=="], "rollup": ["rollup@4.63.3", "", { "dependencies": { "@types/estree": "1.0.9" }, "optionalDependencies": { "@napi-rs/lzma-linux-x64-gnu": "1.5.1", "@rollup/rollup-android-arm-eabi": "4.63.3", "@rollup/rollup-android-arm64": "4.63.3", "@rollup/rollup-darwin-arm64": "4.63.3", "@rollup/rollup-darwin-x64": "4.63.3", "@rollup/rollup-freebsd-arm64": "4.63.3", "@rollup/rollup-freebsd-x64": "4.63.3", "@rollup/rollup-linux-arm-gnueabihf": "4.63.3", "@rollup/rollup-linux-arm-musleabihf": "4.63.3", "@rollup/rollup-linux-arm64-gnu": "4.63.3", "@rollup/rollup-linux-arm64-musl": "4.63.3", "@rollup/rollup-linux-loong64-gnu": "4.63.3", "@rollup/rollup-linux-loong64-musl": "4.63.3", "@rollup/rollup-linux-ppc64-gnu": "4.63.3", "@rollup/rollup-linux-ppc64-musl": "4.63.3", "@rollup/rollup-linux-riscv64-gnu": "4.63.3", "@rollup/rollup-linux-riscv64-musl": "4.63.3", "@rollup/rollup-linux-s390x-gnu": "4.63.3", "@rollup/rollup-linux-x64-gnu": "4.63.3", "@rollup/rollup-linux-x64-musl": "4.63.3", "@rollup/rollup-openbsd-x64": "4.63.3", "@rollup/rollup-openharmony-arm64": "4.63.3", "@rollup/rollup-win32-arm64-msvc": "4.63.3", "@rollup/rollup-win32-ia32-msvc": "4.63.3", "@rollup/rollup-win32-x64-gnu": "4.63.3", "@rollup/rollup-win32-x64-msvc": "4.63.3", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-1i2XreiAoMMXuPGD6Msj2xWrMMkHojNRKivInxGQcg7/1KuPuYlfUutLyh4drnOxUTHX9cHI4wFoat8D/NKaBw=="],
"sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="], "sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="],
@@ -667,7 +665,7 @@
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
"svelte": ["svelte@5.56.10", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.10", "@types/estree": "^1.0.5", "@types/trusted-types": "^2.0.7", "acorn": "^8.12.1", "aria-query": "5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "devalue": "^5.8.1", "esm-env": "^1.2.1", "esrap": "^2.2.12", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-Lcxbj8I/KAbpY+VjtY4ENQBV0dDCipfGAhqb51XQZ67CIQqXgsv/8dPkbILaj4Fb6/b6JAEM/PIVbILXgDQy2g=="], "svelte": ["svelte@5.57.0", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.10", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "aria-query": "5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "devalue": "^5.8.1", "esm-env": "^1.2.1", "esrap": "^2.2.12", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-NdbDn7fl4be1ViUG0oq/lvG6OZy3oENolV2ONjiqqsfVoeAfzaQAKUcEX3MrQod/Bebv1PgwET9rfXhgn9s4Kg=="],
"svelte-adapter-bun": ["svelte-adapter-bun@1.0.1", "", { "dependencies": { "rolldown": "^1.0.0-beta.38" }, "peerDependencies": { "@sveltejs/kit": "^2.4.0", "typescript": "^5" } }, "sha512-tNOvfm8BGgG+rmEA7hkmqtq07v7zoo4skLQc+hIoQ79J+1fkEMpJEA2RzCIe3aPc8JdrsMJkv3mpiZPMsgahjA=="], "svelte-adapter-bun": ["svelte-adapter-bun@1.0.1", "", { "dependencies": { "rolldown": "^1.0.0-beta.38" }, "peerDependencies": { "@sveltejs/kit": "^2.4.0", "typescript": "^5" } }, "sha512-tNOvfm8BGgG+rmEA7hkmqtq07v7zoo4skLQc+hIoQ79J+1fkEMpJEA2RzCIe3aPc8JdrsMJkv3mpiZPMsgahjA=="],
@@ -695,7 +693,7 @@
"uint8array-extras": ["uint8array-extras@1.5.0", "", {}, "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A=="], "uint8array-extras": ["uint8array-extras@1.5.0", "", {}, "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A=="],
"undici": ["undici@6.28.0", "", {}, "sha512-LIY910g9TI13YS95lrMFrs8Rm/u/irgHeTWoKCoteeJ04CUJ92eEfj0rVn+7VKMPBpUPiUoBKfhNyLI23EE/KA=="], "undici": ["undici@6.28.1", "", {}, "sha512-zWpdTVD54H48CIybL0rWQ3ukpb9d23wM7eH5RtfdmeP70cWHNjtfo7P4vZX+5CoDcO53J4Pu5uXp7lNfjc6DRA=="],
"undici-types": ["undici-types@8.3.0", "", {}, "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ=="], "undici-types": ["undici-types@8.3.0", "", {}, "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ=="],
@@ -709,11 +707,11 @@
"xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="], "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="],
"zimmerframe": ["zimmerframe@1.1.4", "", {}, "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ=="], "zimmerframe": ["zimmerframe@1.1.5", "", {}, "sha512-msJxIvYDYcoNL+PJsu+7qmpDWsYmAxTY+2TNYXXF0hzBzBk0BMecOqDOG/EckUoKCuKwObfbugIl8QpqHDXeFA=="],
"zipcodes-us": ["zipcodes-us@1.1.3", "", {}, "sha512-gOz8WAO6iWBU4aUc0lljpa+TKZckRKmgoHKcc1jWnn4eTbdc9XO5cXEaGccYn7GAjykueO0Wn+N7zdmzG0Uf5w=="], "zipcodes-us": ["zipcodes-us@1.1.3", "", {}, "sha512-gOz8WAO6iWBU4aUc0lljpa+TKZckRKmgoHKcc1jWnn4eTbdc9XO5cXEaGccYn7GAjykueO0Wn+N7zdmzG0Uf5w=="],
"zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], "zod": ["zod@4.6.5", "", {}, "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q=="],
"@discordjs/rest/@discordjs/collection": ["@discordjs/collection@2.1.1", "", {}, "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg=="], "@discordjs/rest/@discordjs/collection": ["@discordjs/collection@2.1.1", "", {}, "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg=="],
@@ -738,5 +736,7 @@
"micromatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], "micromatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
"thread-stream/real-require": ["real-require@1.0.0", "", {}, "sha512-P4nbQYQfePJxRSmY+v/KINxVucm4NF3p3s7pJveMTtom52FR4YGltUQLB8idDXwDDWW+eYrWDFbuzUnjoWHF7g=="], "thread-stream/real-require": ["real-require@1.0.0", "", {}, "sha512-P4nbQYQfePJxRSmY+v/KINxVucm4NF3p3s7pJveMTtom52FR4YGltUQLB8idDXwDDWW+eYrWDFbuzUnjoWHF7g=="],
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@tybys/wasm-util": ["@tybys/wasm-util@0.10.4", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A=="],
} }
} }
+4 -4
View File
@@ -36,14 +36,14 @@ target="${1:-migrate}"
case "$target" in case "$target" in
migrate) migrate)
DATABASE_URL="$tunneled_url" bun run --filter @blade-and-brawn/api db:migrate:latest (cd apps/api && NODE_ENV=production DATABASE_URL="$tunneled_url" bun run --env-file=../../.env.production src/database/migrate.ts latest)
;; ;;
seed) seed)
DATABASE_URL="$tunneled_url" bun run --filter @blade-and-brawn/api db:seed (cd apps/api && NODE_ENV=production DATABASE_URL="$tunneled_url" bun run --env-file=../../.env.production src/database/seed.ts)
;; ;;
both) both)
DATABASE_URL="$tunneled_url" bun run --filter @blade-and-brawn/api db:migrate:latest (cd apps/api && NODE_ENV=production DATABASE_URL="$tunneled_url" bun run --env-file=../../.env.production src/database/migrate.ts latest)
DATABASE_URL="$tunneled_url" bun run --filter @blade-and-brawn/api db:seed (cd apps/api && NODE_ENV=production DATABASE_URL="$tunneled_url" bun run --env-file=../../.env.production src/database/seed.ts)
;; ;;
*) *)
echo "Usage: $0 [migrate|seed|both]" >&2 echo "Usage: $0 [migrate|seed|both]" >&2
+1 -1
View File
@@ -13,7 +13,7 @@
"build:portal": "bun run --filter @blade-and-brawn/portal build" "build:portal": "bun run --filter @blade-and-brawn/portal build"
}, },
"devDependencies": { "devDependencies": {
"@types/bun": "^1.4.0", "@types/bun": "^1.4.2",
"typescript": "^7.0.2" "typescript": "^7.0.2"
}, },
"dependencies": { "dependencies": {
+6
View File
@@ -42,3 +42,9 @@ export const ActivityPerformanceSchema = t.Object({
performance: t.Number(), performance: t.Number(),
}); });
export type ActivityPerformance = Static<typeof ActivityPerformanceSchema>; export type ActivityPerformance = Static<typeof ActivityPerformanceSchema>;
export const ActivityVideosSchema = t.Partial(t.Record(t.Enum(Activity), t.String()));
export type ActivityVideoUrls = Static<typeof ActivityVideosSchema>;
export const ActivityVerificationsSchema = t.Partial(t.Record(t.Enum(Activity), t.Boolean()));
export type ActivityVerifications = Static<typeof ActivityVerificationsSchema>;