chore: update to Svelte@5

This commit is contained in:
Thomas 2025-01-04 15:35:07 +00:00
parent 43513a8236
commit 8b9b1185a7
No known key found for this signature in database
47 changed files with 669 additions and 2144 deletions

View file

@ -16,7 +16,7 @@
"@sveltejs/adapter-auto": "^3.3.1", "@sveltejs/adapter-auto": "^3.3.1",
"@sveltejs/adapter-netlify": "^4.4.0", "@sveltejs/adapter-netlify": "^4.4.0",
"@sveltejs/kit": "^2.15.1", "@sveltejs/kit": "^2.15.1",
"@sveltejs/vite-plugin-svelte": "^3.1.2", "@sveltejs/vite-plugin-svelte": "^4.0.0",
"@types/leaflet": "^1.9.15", "@types/leaflet": "^1.9.15",
"@types/sanitize-html": "^2.13.0", "@types/sanitize-html": "^2.13.0",
"@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/eslint-plugin": "^5.62.0",
@ -27,9 +27,9 @@
"prettier": "^3.4.2", "prettier": "^3.4.2",
"prettier-plugin-svelte": "^3.3.2", "prettier-plugin-svelte": "^3.3.2",
"sass": "^1.83.1", "sass": "^1.83.1",
"svelte": "^4.2.19", "svelte": "^5.0.0",
"svelte-check": "^3.8.6", "svelte-check": "^4.0.0",
"svelte-preprocess": "^5.1.4", "svelte-preprocess": "^6.0.0",
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.7.2", "typescript": "^5.7.2",
"vite": "^5.4.11", "vite": "^5.4.11",

View file

@ -1,5 +1,9 @@
<script lang="ts"> <script lang="ts">
export let isActive: boolean; interface Props {
isActive: boolean;
}
let { isActive }: Props = $props();
</script> </script>
<div class="summer-hours"> <div class="summer-hours">

View file

@ -1,50 +0,0 @@
<script lang="ts">
import { writable } from "svelte/store";
import { onMount, onDestroy, createEventDispatcher } from "svelte";
const dispatch = createEventDispatcher<{ change: string }>();
let apiPassword = "";
let state: "edit" | "view" = "edit";
let unsubscribe: () => void;
function onSubmit() {
state = "view";
if (localStorage) {
localStorage.setItem("apiPassword", apiPassword);
}
dispatch("change", apiPassword);
}
function onEdit() {
state = "edit";
}
onMount(() => {
if (localStorage !== undefined) {
apiPassword = localStorage.getItem("apiPassword") || "";
}
if (apiPassword.length > 0) {
dispatch("change", apiPassword);
state = "view";
}
});
</script>
<section>
{#if apiPassword.length === 0}
<p>
To save things to the ledger you need to enter the password. Right now you
haven't set one.
</p>
{/if}
{#if state === "view"}
<button on:click={onEdit}>Edit Password</button>
{:else}
<form on:submit|preventDefault={onSubmit}>
<input type="text" bind:value={apiPassword} />
<input type="submit" value="Set Password" />
</form>
{/if}
</section>

View file

@ -1,63 +0,0 @@
<script lang="ts">
import { FloriferousPlayer } from '$lib/floriferous';
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
let nameInput: HTMLInputElement;
let name = '';
let score = 0;
let rowAtEndOfGame = 1;
function handleFormSubmit() {
const player = new FloriferousPlayer({
name,
score,
rowAtEndOfGame: rowAtEndOfGame
});
dispatch('submit', player);
name = '';
score = 0;
rowAtEndOfGame = 1;
nameInput.focus();
}
</script>
<form on:submit|preventDefault={() => handleFormSubmit()}>
<div class="field">
<label for="player-name">Name</label>
<input bind:this={nameInput} bind:value={name} type="text" id="player-name" />
</div>
<div class="field">
<label for="player-score">Score</label>
<input bind:value={score} type="number" step="1" min="0" id="player-score" />
</div>
<div class="field">
<label for="player-score">Finishing Row</label>
<input bind:value={rowAtEndOfGame} type="number" step="1" min="0" id="player-score" />
<p class="example-text">"1" for the highest row, "2" for the second highest, etc.</p>
</div>
<input type="submit" value="add" />
</form>
<style>
form {
display: flex;
flex-direction: column;
padding: var(--spacing-sm);
border: 1px solid var(--gray-600);
}
.field {
display: flex;
flex-flow: column;
padding: var(--spacing-sm) 0;
}
.example-text {
color: var(--grey800);
font-size: var(--font-size-sm);
}
</style>

View file

@ -1,3 +0,0 @@
import FloriferousPlayerForm from './FloriferousPlayerForm.svelte';
export { FloriferousPlayerForm, PreviousGameScores };

View file

@ -1,10 +1,19 @@
<script lang="ts"> <script lang="ts">
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
export let id: string; interface Props {
export let name: string; id: string;
export let salary: number; name: string;
export let count: number; salary: number;
count: number;
}
let {
id,
name = $bindable(),
salary = $bindable(),
count = $bindable(),
}: Props = $props();
const dispatch = createEventDispatcher<{ const dispatch = createEventDispatcher<{
change: { name: string; salary: number; count: number }; change: { name: string; salary: number; count: number };
@ -27,8 +36,8 @@
<input <input
type="text" type="text"
placeholder="Junior Software Engineer" placeholder="Junior Software Engineer"
bind:value="{name}" bind:value={name}
on:input="{handleChange}" oninput={handleChange}
/> />
</div> </div>
<div class="form__field"> <div class="form__field">
@ -37,8 +46,8 @@
type="number" type="number"
step="1" step="1"
placeholder="30,000" placeholder="30,000"
bind:value="{salary}" bind:value={salary}
on:change="{handleChange}" onchange={handleChange}
/> />
</div> </div>
<div class="form__field"> <div class="form__field">
@ -47,11 +56,11 @@
type="number" type="number"
step="1" step="1"
placeholder="30,000" placeholder="30,000"
bind:value="{count}" bind:value={count}
on:change="{handleChange}" onchange={handleChange}
/> />
</div> </div>
<button type="button" on:click="{handleRemove}"> Remove </button> <button type="button" onclick={handleRemove}> Remove </button>
</form> </form>
<style> <style>

View file

@ -29,7 +29,7 @@
</div> </div>
<div class="right"> <div class="right">
<button class="colour-theme-toggle" on:click={onColourSchemeChange}> <button class="colour-theme-toggle" onclick={onColourSchemeChange}>
<img <img
class="icon" class="icon"
src={$colourSchemeStore.name === "light" ? sunSvg : moonSvg} src={$colourSchemeStore.name === "light" ? sunSvg : moonSvg}

View file

@ -1,8 +0,0 @@
import type { FloriferousGame } from './floriferous-game';
export interface FloriferousGameRepository {
save(game: FloriferousGame): Promise<FloriferousGame>;
getById(id: string): Promise<FloriferousGame | null>;
getRecent(count: number): Promise<FloriferousGame[]>;
}

View file

@ -1,117 +0,0 @@
import { describe, it, expect } from 'vitest';
import type { ApiGamesFloriferousPostRequest } from './floriferous-api-controller';
import { FloriferousApiController } from './floriferous-api-controller';
import { StubFloriferousGameRepository } from './stub-floriferous-game-repository';
import { FloriferousGame } from './floriferous-game';
import { SimplePasswordAuthenticator } from '../simple-password-authenticator';
import { Headers } from 'node-fetch';
const isDate = (value = 'invalid'): boolean => {
return value !== 'invalid' && !isNaN(Date.parse(value));
};
describe('FloriferousApiController', () => {
const stubGameRepository = new StubFloriferousGameRepository();
const authenticator = new SimplePasswordAuthenticator('expected-password');
const controller = new FloriferousApiController(stubGameRepository, authenticator);
it('should validate a request with a proper password', async () => {
// GIVEN
const headers: Headers = new Headers();
headers.set('x-api-password', 'expected-password');
// WHEN
const result = controller.isRequestAuthenticated({ headers: headers });
// THEN
expect(result).toBe(true);
});
it('should not validate a request with an invalid password', async () => {
// GIVEN
const headers = new Headers();
headers.set('x-api-password', 'invalid-password');
// WHEN
const result = controller.isRequestAuthenticated({ headers });
// THEN
expect(result).toBe(false);
});
it('should not validate a request without a password', async () => {
// GIVEN
const request = {
headers: new Headers()
};
// WHEN
const result = controller.isRequestAuthenticated(request);
// THEN
expect(result).toBe(false);
});
it('should get a list of recent games', async () => {
// GIVEN
const gameOne = new FloriferousGame({
id: 'game-one',
players: [{ name: 'Alice', score: 10, rowAtEndOfGame: 1 }],
playedTs: new Date('2022-07-01T07:00Z')
});
const gameTwo = new FloriferousGame({
id: 'game-two',
players: [],
playedTs: new Date('2022-08-20T13:25Z')
});
stubGameRepository.setAllGames([gameOne, gameTwo]);
// WHEN
const result = await controller.getRecentGames(10);
// THEN
expect(result).toStrictEqual([
{
id: 'game-one',
playedTs: '2022-07-01T07:00:00.000Z',
players: [{ name: 'Alice', score: 10, rowAtEndOfGame: 1 }]
},
{
id: 'game-two',
playedTs: '2022-08-20T13:25:00.000Z',
players: []
}
]);
});
it('should save a new game', async () => {
// GIVEN
const requestBody: ApiGamesFloriferousPostRequest = {
players: [
{
name: 'Alice',
rowAtEndOfGame: 1,
score: 10
}
]
};
// WHEN
const response = await controller.createNewGame(requestBody);
// THEN
expect(isDate(response.playedTs)).toBe(true);
expect(response).toStrictEqual({
id: expect.any(String),
playedTs: expect.any(String),
players: [
{
name: 'Alice',
score: 10,
rowAtEndOfGame: 1
}
]
});
});
});

View file

@ -1,88 +0,0 @@
import { z } from 'zod';
import type { ZodError } from 'zod';
import type { FloriferousGameJson } from './floriferous-game-api-port';
import type { FloriferousGameRepository } from './FloriferousGameRepository';
import { FloriferousGameApiPort } from './floriferous-game-api-port';
import { FloriferousGame } from './floriferous-game';
import { FloriferousPlayer } from './floriferous-player';
import type { Authenticator } from '../Authenticator';
export interface ApiGamesFloriferousPostRequest {
players: {
name: string;
score: number;
rowAtEndOfGame: number;
}[];
}
const apiGamesFloriferousPostRequestSchema = z.object({
players: z
.array(
z.object({
name: z.string().min(1, { message: 'Player names must be at least 1 character long' }),
score: z
.number({ invalid_type_error: 'Player Score must be a number.' })
.int({ message: 'Player Score must be a whole number.' })
.min(0, { message: 'Player score cannot be less than 0.' }),
rowAtEndOfGame: z.number().int().min(0)
})
)
.min(1)
});
export class FloriferousApiController {
constructor(
private readonly repository: FloriferousGameRepository,
private readonly validator: Authenticator
) {}
isRequestAuthenticated(request: any): boolean {
const password = request?.headers?.get('x-api-password') ?? '';
if (password === undefined) {
return false;
}
return this.validator.authenticate(password);
}
async getRecentGames(count = 10): Promise<FloriferousGameJson[]> {
const games = await this.repository.getRecent(count);
return games.map((game) => FloriferousGameApiPort.gameToJson(game));
}
async createNewGame(data: ApiGamesFloriferousPostRequest): Promise<FloriferousGameJson> {
try {
apiGamesFloriferousPostRequestSchema.parse(data);
} catch (e: any) {
if (e.issues !== undefined) {
const zodError: ZodError = e;
throw new Error(zodError.issues[0].message);
}
console.error({
message: `Caught error validating body data in createNewGame`,
error: e,
data: JSON.stringify(data)
});
throw new Error(e?.message ?? e);
}
const validatedData = data;
const players: FloriferousPlayer[] = validatedData.players.map(
({ name, rowAtEndOfGame, score }) => {
return new FloriferousPlayer({ name, rowAtEndOfGame, score });
}
);
const game = new FloriferousGame({
players,
playedTs: new Date()
});
const savedGame = await this.repository.save(game);
return FloriferousGameApiPort.gameToJson(game);
}
}

View file

@ -1,57 +0,0 @@
import { FloriferousGame } from './floriferous-game';
import { FloriferousPlayer } from './floriferous-player';
import { FloriferousGameApiPort } from './floriferous-game-api-port';
import { it, expect } from 'vitest';
it('should stringify a FloriferousGame into JSON', () => {
// GIVEN
const game = new FloriferousGame({
id: 'the-id',
playedTs: new Date('2020-01-01T00:00Z'),
players: [
new FloriferousPlayer({ name: 'first player', rowAtEndOfGame: 1, score: 2 }),
new FloriferousPlayer({ name: 'second player', rowAtEndOfGame: 3, score: 4 })
]
});
// WHEN
const gameAsJson = FloriferousGameApiPort.gameToJson(game);
// THEN
expect(gameAsJson).toStrictEqual({
id: 'the-id',
playedTs: '2020-01-01T00:00:00.000Z',
players: [
{ name: 'first player', rowAtEndOfGame: 1, score: 2 },
{ name: 'second player', rowAtEndOfGame: 3, score: 4 }
]
});
});
it('should parse JSON into a floriferous game', () => {
// GIVEN
const gameAsJson = {
id: 'the-id',
playedTs: '2020-01-01T00:00:00.000Z',
players: [
{ name: 'first player', rowAtEndOfGame: 1, score: 2 },
{ name: 'second player', rowAtEndOfGame: 3, score: 4 }
]
};
// WHEN
const game = FloriferousGameApiPort.jsonToGame(gameAsJson);
// THEN
expect(game).toStrictEqual(
new FloriferousGame({
id: 'the-id',
playedTs: new Date('2020-01-01T00:00Z'),
players: [
{ name: 'first player', rowAtEndOfGame: 1, score: 2 },
{ name: 'second player', rowAtEndOfGame: 3, score: 4 }
]
})
);
});

View file

@ -1,39 +0,0 @@
import { FloriferousGame } from './floriferous-game';
export interface FloriferousGameJson {
id: string;
playedTs: string;
players: {
name: string;
score: number;
rowAtEndOfGame: number;
}[];
}
export class FloriferousGameApiPort {
static jsonToGame(json: FloriferousGameJson): FloriferousGame {
const players = json.players.map((player) => ({
name: player.name,
score: player.score,
rowAtEndOfGame: player.rowAtEndOfGame
}));
return new FloriferousGame({
id: json.id,
playedTs: new Date(json.playedTs),
players
});
}
static gameToJson(game: FloriferousGame): FloriferousGameJson {
return {
id: game.id,
playedTs: game.playedTs.toISOString(),
players: game.players.map((player) => ({
name: player.name,
score: player.score,
rowAtEndOfGame: player.rowAtEndOfGame
}))
};
}
}

View file

@ -1,62 +0,0 @@
import { describe, it, expect } from 'vitest';
import { FloriferousGame } from './floriferous-game.js';
import { FloriferousPlayer } from './floriferous-player.js';
describe('FloriferousGame', () => {
const alice = new FloriferousPlayer({
name: 'Alice',
score: 2,
rowAtEndOfGame: 0,
});
const bob = new FloriferousPlayer({
name: 'Bob',
score: 1,
rowAtEndOfGame: 1,
});
const bobWithTwoPoints = new FloriferousPlayer({
name: 'Bob',
score: 2,
rowAtEndOfGame: 1,
});
it('Determines a winner', () => {
const game = new FloriferousGame();
// WHEN
game.addPlayer(alice);
game.addPlayer(bob);
// THEN
expect(game.winner).toBe('Alice');
});
it('Breaks a tie using the player closest to the top of the board', () => {
// GIVEN
const game = new FloriferousGame();
// WHEN
game.addPlayer(alice);
game.addPlayer(bobWithTwoPoints);
// THEN
expect(game.winner).toBe('Alice');
});
it('Can give a pretty summary', () => {
// GIVEN
const game = new FloriferousGame({
playedTs: new Date('2022-08-28T13:12Z'),
players: [alice, bob],
});
// WHEN
const prettySummary = game.prettySummary;
// THEN
expect(prettySummary).toBe('Sunday, 28 August 2022 at 14:12: Alice won with 2 points. Bob: 1 point.');
});
});

View file

@ -1,85 +0,0 @@
import type { FloriferousPlayer } from './floriferous-player.js';
import { intlFormat as formatDate } from 'date-fns';
import { nanoid } from 'nanoid';
export interface FloriferousGameParams {
playedTs?: Date;
id?: string;
players?: FloriferousPlayer[];
}
export class FloriferousGame {
readonly id: string;
readonly playedTs: Date;
private _players: FloriferousPlayer[] = [];
constructor({ id = nanoid(), playedTs = new Date(), players = [] }: FloriferousGameParams = {}) {
this.id = id;
this.playedTs = playedTs;
this._players = players;
}
addPlayer(player: FloriferousPlayer): void {
this._players.push(player);
}
addPlayers(players: FloriferousPlayer[]): void {
players.forEach((player) => {
this.addPlayer(player);
});
}
get prettySummary(): string {
if (this._players.length === 0) {
return '';
}
const formattedDate = formatDate(this.playedTs, {
localeMatcher: 'best fit',
weekday: 'long',
day: 'numeric',
month: 'long',
year: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
const winnerStatement = `${this.winningPlayer.name} won with ${this.winningPlayer.score} points`;
const otherPlayerStatements = this.nonWinningsPlayers.map((player) => {
return `${player.name}: ${player.score} point${player.score === 1 ? '' : 's'}.`;
});
return [`${formattedDate}: ${winnerStatement}`, ...otherPlayerStatements].join('. ');
}
get players(): FloriferousPlayer[] {
return this._players;
}
private get winningPlayer(): FloriferousPlayer | undefined {
if (this._players.length === 0) {
return undefined;
}
const playersSortedByScore = this._players.sort((a, b) => {
return b.score - a.score;
});
if (playersSortedByScore[0].score === playersSortedByScore[1].score) {
const playersSortedByRowAtEndOfGame = this._players.sort((a, b) => {
return a.rowAtEndOfGame - b.rowAtEndOfGame;
});
return playersSortedByRowAtEndOfGame[0];
}
return playersSortedByScore[0];
}
private get nonWinningsPlayers(): FloriferousPlayer[] {
return this._players.filter((player) => player.name !== this.winner);
}
get winner(): string | undefined {
return this.winningPlayer?.name;
}
}

View file

@ -1,18 +0,0 @@
import { describe, expect, it } from 'vitest';
import { FloriferousPlayer } from './floriferous-player';
describe('FloriferousPlayer', () => {
it('should construct with properties', () => {
// GIVEN
const player = new FloriferousPlayer({
name: 'Alice',
score: 2,
rowAtEndOfGame: 0
});
// THEN
expect(player.name).toBe('Alice');
expect(player.score).toBe(2);
expect(player.rowAtEndOfGame).toBe(0);
});
});

View file

@ -1,17 +0,0 @@
export interface FloriferousPlayerParams {
name: string;
score: number;
rowAtEndOfGame: number;
}
export class FloriferousPlayer {
readonly name: string;
readonly score: number;
readonly rowAtEndOfGame: number;
constructor(params: FloriferousPlayerParams) {
this.name = params.name;
this.score = params.score;
this.rowAtEndOfGame = params.rowAtEndOfGame;
}
}

View file

@ -1,2 +0,0 @@
export { FloriferousGame } from './floriferous-game';
export { FloriferousPlayer } from './floriferous-player';

View file

@ -1,128 +0,0 @@
import { describe, expect, it, beforeEach, afterAll } from 'vitest';
import { FloriferousGame, type FloriferousGameParams } from './floriferous-game.js';
import { FloriferousPlayer, type FloriferousPlayerParams } from './floriferous-player.js';
import { customAlphabet } from 'nanoid';
import { MongodbFloriferousGameRepository } from './mongodb-floriferous-game-repository.js';
import { MongoClient } from 'mongodb';
describe('MongoDB FloriferousGame Repository', () => {
const mongoDbUrl = import.meta.env.VITE_MONGO_URL;
const mongoDbName = import.meta.env.VITE_MONGO_DB_NAME;
const janitor = new MongodbJanitor(mongoDbUrl, mongoDbName);
let playerOne: FloriferousPlayer;
let playerTwo: FloriferousPlayer;
let game: FloriferousGame;
beforeEach(async () => {
playerOne = floriferousPlayerFactory({ name: 'Player 1' });
playerTwo = floriferousPlayerFactory({ name: 'Player 2' });
game = floriferousGameFactory();
game.addPlayer(playerOne);
game.addPlayer(playerTwo);
});
afterAll(async () => {
await janitor.deleteAllDocumentsInConnection('floriferous-games');
});
const repository = new MongodbFloriferousGameRepository(mongoDbUrl, mongoDbName);
it('should save', async () => {
// WHEN
const savedGame = await repository.save(game);
// THEN
expect(savedGame).toBeDefined();
});
it('should get by ID when it exists', async () => {
// GIVEN
const savedGame = await repository.save(game);
// WHEN
const foundGame = await repository.getById(game.id);
// THEN
expect(foundGame).toStrictEqual(game);
});
it('should return null when fetching by ID for a game which did not happen', async () => {
// WHEN
const foundGame = await repository.getById('any-random-id');
// THEN
expect(foundGame).toBeNull();
});
it('should get the last 10 games, sorted by playedTs', async () => {
// GIVEN
await janitor.deleteAllDocumentsInConnection('floriferous-games');
const gameOne = floriferousGameFactory({
playedTs: new Date('2022-08-07T19:00Z')
});
const gameTwo = floriferousGameFactory({
playedTs: new Date('2022-08-07T06:00Z')
});
await repository.save(gameOne);
await repository.save(gameTwo);
// WHEN
const recentGames = await repository.getRecent(10);
// THEN
expect(recentGames).toStrictEqual([gameTwo, gameOne]);
});
it('should return an empty array when no games have taken place', async () => {
// GIVEN
await janitor.deleteAllDocumentsInConnection('floriferous-games');
// WHEN
const recentGames = await repository.getRecent(5);
// THEN
expect(recentGames).toStrictEqual([]);
});
});
class MongodbJanitor {
private client: MongoClient;
constructor(private readonly url: string, private readonly dbName: string) {
this.client = new MongoClient(url);
}
async deleteAllDocumentsInConnection(collectionName: string): Promise<void> {
console.info(`Deleting all documents in ${collectionName}`);
const connection = await this.client.connect();
const deltedDocuments = await connection
.db(this.dbName)
.collection(collectionName)
.deleteMany({});
await connection.close();
console.info(`Deleted ${deltedDocuments.deletedCount} documents`);
}
}
function floriferousPlayerFactory(
overrides: Partial<FloriferousPlayerParams> = {}
): FloriferousPlayer {
return new FloriferousPlayer({
name: `name-${customAlphabet('abcdefghijklmnopqrstuvwxyz', 8)}`,
score: Math.floor(Math.random() * 100),
rowAtEndOfGame: Math.floor(Math.random() * 10),
...overrides
});
}
function floriferousGameFactory(overrides: Partial<FloriferousGameParams> = {}): FloriferousGame {
return new FloriferousGame(overrides);
}

View file

@ -1,123 +0,0 @@
import { FloriferousGame } from './floriferous-game';
import { MongoClient } from 'mongodb';
import { FloriferousPlayer } from './floriferous-player';
import type { FloriferousGameRepository } from './FloriferousGameRepository';
interface FloriferousGameMongoDocument {
id: string;
playedTs: Date;
players: {
name: string;
score: number;
rowAtEndOfGame: number;
}[];
}
export class MongodbFloriferousGameRepository implements FloriferousGameRepository {
private client: MongoClient;
private connection: MongoClient | null = null;
private collectionName = 'floriferous-games';
constructor(private readonly mongodbUrl: string, private readonly dbName: string) {
this.client = new MongoClient(mongodbUrl);
}
private async connect(): Promise<void> {
this.connection = await this.client.connect();
}
private async disconnect(): Promise<void> {
if (this.connection === null) {
return;
}
await this.connection.close();
}
async save(game: FloriferousGame): Promise<FloriferousGame> {
await this.connect();
const data = MongodbFloriferousGameRepository.gameToMongoDocument(game);
const document = await this.connection
.db(this.dbName)
.collection<FloriferousGameMongoDocument>(this.collectionName)
.insertOne(data);
await this.disconnect();
return game;
}
async getById(id: string): Promise<FloriferousGame | null> {
await this.connect();
const document = await this.connection
.db(this.dbName)
.collection<FloriferousGameMongoDocument>(this.collectionName)
.findOne({ id });
await this.disconnect();
if (document === null) {
return null;
}
return MongodbFloriferousGameRepository.documentToGame(document);
}
async getRecent(count = 10): Promise<FloriferousGame[]> {
await this.connect();
const documents = await this.connection
.db(this.dbName)
.collection<FloriferousGameMongoDocument>(this.collectionName)
.find({})
.sort({ playedTs: 1 })
.limit(count)
.toArray();
await this.disconnect();
if (!documents) {
return [];
}
const formatted = documents.map((document) =>
MongodbFloriferousGameRepository.documentToGame(document)
);
return formatted;
}
private static documentToGame(document: FloriferousGameMongoDocument): FloriferousGame {
const players: FloriferousPlayer[] = document.players.map(
({ name, rowAtEndOfGame, score }) =>
new FloriferousPlayer({
name,
rowAtEndOfGame: rowAtEndOfGame,
score
})
);
const game = new FloriferousGame({
id: document.id,
playedTs: document.playedTs,
players
});
return game;
}
private static gameToMongoDocument(game: FloriferousGame): FloriferousGameMongoDocument {
return {
id: game.id,
playedTs: game.playedTs,
players: game.players.map((player) => ({
rowAtEndOfGame: player.rowAtEndOfGame,
name: player.name,
score: player.score
}))
};
}
}

View file

@ -1,30 +0,0 @@
import type { FloriferousGameRepository } from './FloriferousGameRepository';
import type { FloriferousGame } from './floriferous-game';
export class StubFloriferousGameRepository implements FloriferousGameRepository{
private games: FloriferousGame[] = [];
setAllGames(games: FloriferousGame[]): void {
this.games = games;
}
getById(id: string): Promise<FloriferousGame | null> {
throw new Error('Method not implemented.');
}
getRecent(count: number): Promise<FloriferousGame[]> {
return Promise.resolve(this.games.map((game, index) => {
if (index < count -1) {
return game;
}
return undefined
}).filter((game) => game !== undefined));
}
save(game: FloriferousGame): Promise<FloriferousGame> {
return Promise.resolve(undefined);
}
}

View file

@ -8,6 +8,11 @@
} from "../stores/colourSchemeStore.ts"; } from "../stores/colourSchemeStore.ts";
import { browser } from "$app/environment"; import { browser } from "$app/environment";
import { onMount } from "svelte"; import { onMount } from "svelte";
interface Props {
children?: import('svelte').Snippet;
}
let { children }: Props = $props();
onMount(() => { onMount(() => {
const prefersDarkmode: boolean = window.matchMedia( const prefersDarkmode: boolean = window.matchMedia(
@ -60,4 +65,4 @@
<title>Thomas Wilson</title> <title>Thomas Wilson</title>
</svelte:head> </svelte:head>
<slot /> {@render children?.()}

View file

@ -2,7 +2,7 @@
import Navbar from "$lib/components/Navbar.svelte"; import Navbar from "$lib/components/Navbar.svelte";
import HomepageHeader from "./home/HomepageHeader.svelte"; import HomepageHeader from "./home/HomepageHeader.svelte";
export let data = { latestBlogPosts: [] } let { data = { latestBlogPosts: [] } } = $props();
</script> </script>

View file

@ -1,51 +0,0 @@
import { MONGO_URL, MONGO_DB_NAME, API_PASSWORD } from '$env/static/private';
import type { error } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
import { MongodbFloriferousGameRepository } from '$lib/floriferous/mongodb-floriferous-game-repository';
import { FloriferousApiController } from '$lib/floriferous/floriferous-api-controller';
import { SimplePasswordAuthenticator } from '$lib/simple-password-authenticator';
import {
FloriferousGameApiPort,
type FloriferousGameJson
} from '$lib/floriferous/floriferous-game-api-port';
export const GET: RequestHandler = async () => {
const controller = new FloriferousApiController(
new MongodbFloriferousGameRepository(MONGO_URL, MONGO_DB_NAME),
new SimplePasswordAuthenticator(API_PASSWORD)
);
const response = await controller.getRecentGames(10);
return new Response(JSON.stringify(response), { status: 200 });
};
export const POST = async ({ request }) => {
const controller = new FloriferousApiController(
new MongodbFloriferousGameRepository(MONGO_URL, MONGO_DB_NAME),
new SimplePasswordAuthenticator(API_PASSWORD)
);
const isAuthenticated = controller.isRequestAuthenticated(request);
if (!isAuthenticated) {
return {
status: 401,
body: 'Unauthorized'
};
}
try {
const requestBody = await request.json();
const response = await controller.createNewGame(requestBody);
return new Response(JSON.stringify(response), {
status: 200
});
} catch (e) {
return new Response(JSON.stringify(e), {
status: 500
});
}
};

View file

@ -3,16 +3,20 @@
import Navbar from "$lib/components/Navbar.svelte"; import Navbar from "$lib/components/Navbar.svelte";
import BlogPostListItem from "./BlogPostListItem.svelte"; import BlogPostListItem from "./BlogPostListItem.svelte";
export let data: PageData; interface Props {
data: PageData;
}
$: ({ let { data }: Props = $props();
let {
posts, posts,
numberOfPosts, numberOfPosts,
daysSinceLastPublish, daysSinceLastPublish,
daysSinceFirstPost, daysSinceFirstPost,
averageDaysBetweenPosts, averageDaysBetweenPosts,
numberOfBlogPostsThisYear, numberOfBlogPostsThisYear,
} = data); } = $derived(data);
</script> </script>
<svelte:head> <svelte:head>

View file

@ -1,16 +1,29 @@
<script lang="ts"> <script lang="ts">
import { format as formatDate } from "date-fns"; import { format as formatDate } from "date-fns";
export let index: number; interface Props {
export let numberOfPosts: number; index: number;
export let book_review: boolean; numberOfPosts: number;
export let title: string; book_review: boolean;
export let preview: string; title: string;
export let slug: string; preview: string;
export let date: string; slug: string;
export let content_type: "blog" | "book_review" | "snout_street_studios"; date: string;
content_type: "blog" | "book_review" | "snout_street_studios";
}
$: formattedDate = formatDate(new Date(date), "yyyy-MM-dd"); let {
index,
numberOfPosts,
book_review,
title,
preview,
slug,
date,
content_type
}: Props = $props();
let formattedDate = $derived(formatDate(new Date(date), "yyyy-MM-dd"));
</script> </script>
<li <li

View file

@ -1,9 +1,14 @@
<script> <script lang="ts">
import "../../../styles/prism.css"; import "../../../styles/prism.css";
interface Props {
children?: import('svelte').Snippet;
}
let { children }: Props = $props();
</script> </script>
<svelte:head> <svelte:head>
<script src="/prism.js"></script> <script src="/prism.js"></script>
</svelte:head> </svelte:head>
<slot /> {@render children?.()}

View file

@ -4,8 +4,12 @@
import Navbar from "$lib/components/Navbar.svelte"; import Navbar from "$lib/components/Navbar.svelte";
import { onMount } from "svelte"; import { onMount } from "svelte";
export let data: PageData; interface Props {
$: ({ date, post } = data); data: PageData;
}
let { data }: Props = $props();
let { date, post } = $derived(data);
onMount(() => { onMount(() => {
console.log({ date, post }); console.log({ date, post });

View file

@ -1,12 +1,14 @@
<script lang="ts"> <script lang="ts">
import { preventDefault } from 'svelte/legacy';
import { format as formatDate } from "date-fns"; import { format as formatDate } from "date-fns";
import { BlogPost } from "$lib/blog/BlogPost.js"; import { BlogPost } from "$lib/blog/BlogPost.js";
import { goto } from "$app/navigation"; import { goto } from "$app/navigation";
let title = ""; let title = $state("");
let author = "Thomas Wilson"; let author = $state("Thomas Wilson");
let date = new Date(); let date = new Date();
let content = ""; let content = $state("");
let slug = ""; let slug = $state("");
let blogPost: BlogPost | null = null; let blogPost: BlogPost | null = null;
function slugifyString(originalString: string): string { function slugifyString(originalString: string): string {
@ -52,7 +54,7 @@
<section class="new-blog-post"> <section class="new-blog-post">
<a href="/blog">Back to Blog</a> <a href="/blog">Back to Blog</a>
<h1>New Blog Post</h1> <h1>New Blog Post</h1>
<form on:submit|preventDefault={onCreate}> <form onsubmit={preventDefault(onCreate)}>
<div class="field"> <div class="field">
<label class="field__label" for="title">Title</label> <label class="field__label" for="title">Title</label>
<input <input
@ -60,7 +62,7 @@
id="title" id="title"
required required
bind:value={title} bind:value={title}
on:change={handleTitleChange} onchange={handleTitleChange}
/> />
</div> </div>
<div class="field"> <div class="field">
@ -75,7 +77,7 @@
<div class="field"> <div class="field">
<label class="field__label" for="content">Content</label> <label class="field__label" for="content">Content</label>
<textarea id="content" rows="10" cols="50" bind:value={content} /> <textarea id="content" rows="10" cols="50" bind:value={content}></textarea>
</div> </div>
<div class="submit"> <div class="submit">

View file

@ -1 +0,0 @@
export const prerender = true;

View file

@ -1,18 +0,0 @@
<script lang="ts">
import Navbar from '$lib/components/Navbar.svelte';
</script>
<Navbar />
<main class="thomaswilson-container">
<section class="thomaswilson-strapline section">
<h1>Board Game Scoring & Ledgers</h1>
<p>
I like to play board games, but tallying up scores for some of them is a nightmare. I'm
building some tools to help me keep score.
</p>
<ul>
<li><a href="/games/floriferous">Floriferous</a></li>
</ul>
</section>
</main>

View file

@ -1,39 +0,0 @@
<script lang="ts">
import { slide, fade } from 'svelte/transition';
export let gameSummaries: string[];
const pluralRule = new Intl.PluralRules('en', { type: 'ordinal' });
let isVisible = false;
</script>
<div class="previous-scores">
{#if isVisible}
<button
transition:fade={{ duration: 30, delay: 0 }}
class="thomaswilson-button"
on:click={() => (isVisible = false)}>Hide Previous Scores</button
>
<ul>
{#each gameSummaries as summary}
<li transition:slide>
{summary}
</li>
{/each}
</ul>
{:else}
<button
transition:slide={{ delay: 0, duration: 30 }}
class="thomaswilson-button"
on:click={() => (isVisible = true)}
>Show {gameSummaries.length} Previous {gameSummaries.length === 1 ? 'Game' : 'Games'}</button
>
{/if}
</div>
<style>
.previous-scores {
display: grid;
grid-template-columns: 100%;
width: 100%;
}
</style>

View file

@ -1,199 +0,0 @@
<script lang="ts">
import { onMount } from 'svelte';
import { slide } from 'svelte/transition';
import type { PageData } from './$types.js';
import Navbar from '$lib/components/Navbar.svelte';
import FloriferousScoreCalculator from './FloriferousScoreCalculator.svelte';
import { FloriferousGame } from '../../../lib/floriferous/floriferous-game.js';
import type { FloriferousPlayer } from '../../../lib/floriferous';
import PreviousGameScores from '../PreviousGameScores.svelte';
import { FloriferousPlayerForm } from '../../../components/games';
import {
FloriferousGameApiPort,
type FloriferousGameJson
} from '../../../lib/floriferous/floriferous-game-api-port.js';
import ApiPasswordFrom from '../../../components/games/ApiPasswordForm.svelte';
import type { ApiGamesFloriferousPostRequest } from '$lib/floriferous/floriferous-api-controller';
export let data: PageData;
let previousGames: FloriferousGame[] = data.previousGames;
let apiPassword = '';
let players: FloriferousPlayer[] = [];
let isScoreCalculatorVisible = true;
let isPlayersVisible = false;
let isPreviousScoresVisible = false;
let isWinnerVisible = false;
let isSaveSubmitting = false;
let isGameSaved = false;
function handleShowWinner() {
isWinnerVisible = true;
}
function onAddPlayer(event: CustomEvent<FloriferousPlayer>) {
players = [...players, event.detail];
}
function onRemovePlayer(playerToRemove: FloriferousPlayer) {
players = players.filter((player) => {
return playerToRemove.name !== player.name;
});
}
function clearGameData() {
players = [];
isWinnerVisible = false;
}
async function onSaveGame() {
isSaveSubmitting = true;
if (players.length < 2) {
console.warn(`Not enough players to save game`);
isSaveSubmitting = false;
return;
}
const body: { players: FloriferousPlayer[] } = { players };
fetch('/api/games/floriferous.json', {
method: 'POST',
body: JSON.stringify(body),
headers: {
'Content-Type': 'application/json',
'x-api-password': apiPassword
}
})
.then((response) => {
return response.json();
})
.then((gameAsJson: FloriferousGameJson) => {
const game = FloriferousGameApiPort.jsonToGame(gameAsJson);
previousGames = [...previousGames, game];
clearGameData();
})
.catch((error) => {
if (error.status === 401) {
console.warn(`Invalid API password`);
return;
}
console.error(error);
isSaveSubmitting = false;
});
}
$: game = new FloriferousGame({ playedTs: new Date(), players });
$: previousGameSummaries = previousGames.map((game) => game.prettySummary);
</script>
<Navbar />
<main class="thomaswilson-container">
<h1>Floriferous Scoring</h1>
<p>
Floriferous is a board game published by Pencil First games, in which you find find joy in the
abundance of nature.
</p>
<section class="score-calculator">
<div class="score-calculator__header">
<h2>Score Calculator</h2>
{#if isScoreCalculatorVisible}
<button on:click={() => (isScoreCalculatorVisible = false)}>Hide</button>
{:else}
<button on:click={() => (isScoreCalculatorVisible = true)}>Show</button>
{/if}
</div>
<FloriferousScoreCalculator isVisible={isScoreCalculatorVisible} />
</section>
<section class="players">
<div class="players__header">
<h2>Players</h2>
{#if isPlayersVisible}
<button on:click={() => (isPlayersVisible = false)}>Hide</button>
{:else}
<button on:click={() => (isPlayersVisible = true)}>Show</button>
{/if}
</div>
{#if isPlayersVisible}
{#if players.length > 0}
<ul>
{#each players as player}
<li>
{player.name} ({player.score} points, finished on row {player.rowAtEndOfGame}) (<button
on:click={() => onRemovePlayer(player)}>Remove</button
>)
</li>
{/each}
</ul>
{#if players.length > 1}
{#if isWinnerVisible}
<p transition:slide>And the winner is:<strong>{game.winner}</strong></p>
<h3>Add to Ledger</h3>
<ApiPasswordFrom on:change={(event) => (apiPassword = event.detail)} />
{#if apiPassword.length > 0}
<p>You can save this game in the Ledger</p>
<button on:click={onSaveGame}>Save Game</button>
{/if}
{:else}
<button on:click={handleShowWinner}>Show me the winner</button>
{/if}
{/if}
{:else}
<p>Add at least one player to get started</p>
{/if}
{#if !isWinnerVisible}
<h3>Add a New Player</h3>
<FloriferousPlayerForm on:submit={onAddPlayer} />
{/if}
{/if}
</section>
{#if previousGames.length > 0}
<section class="previous-games">
<h2>Previous Games</h2>
<PreviousGameScores gameSummaries={previousGameSummaries} />
</section>
{/if}
</main>
<style>
section {
display: flex;
flex-direction: column;
padding: var(--spacing-sm);
max-width: 600px;
}
.players {
padding: var(--spacing-md) 0;
width: 100%;
}
.players__header {
display: grid;
grid-template-columns: auto min-content;
}
.score-calculator {
padding: var(--spacing-md) 0;
width: 100%;
}
.score-calculator__header {
display: grid;
grid-template-columns: auto min-content;
}
.previous-games {
padding: var(--spacing-md) 0;
width: 100%;
}
</style>

View file

@ -1,12 +0,0 @@
import {
FloriferousGameApiPort,
type FloriferousGameJson
} from '$lib/floriferous/floriferous-game-api-port';
import type { Load } from '@sveltejs/kit';
export const load: Load = async ({ fetch }): Promise<{ previousGames: FloriferousGameJson[] }> => {
const previousGames = await fetch('/api/games/floriferous.json').then((res) => res.json());
return {
previousGames: previousGames.map(FloriferousGameApiPort.jsonToGame)
};
};

View file

@ -1,188 +0,0 @@
<script lang="ts">
import { slide } from 'svelte/transition';
export let isVisible: boolean;
let currentScore = 0;
let actions = [];
let newActionScore = 0;
let newActionName = '';
let newActionNameInput;
let isNewActionNameVisible = false;
function incrementNewActionScore() {
newActionScore += 1;
}
function decrementNewActionScore() {
newActionScore -= 1;
}
function onNewActionSubmit(score: number, name = '') {
actions = [...actions, { score, name }];
currentScore += score;
newActionScore = 0;
newActionName = '';
}
function toggleIsNewActionNameVisible() {
isNewActionNameVisible = true;
newActionNameInput.focus();
newActionName = '';
}
const suggestedDescriptions = [
'Arrangement Card',
'Desire Card',
'Bounty',
'Stones',
'Cup of Tea'
];
</script>
{#if isVisible}
<div transition:slide>
<div>
<p>Your score is {currentScore}</p>
<ul class="actions-list">
{#each actions as action}
<li class="actions-list__item">
+{action.score}
{#if action.name.length > 0} ({action.name}) {/if}
</li>
{/each}
</ul>
</div>
<form
class="form"
on:submit|preventDefault={() => onNewActionSubmit(newActionScore, newActionName)}
>
<div class="form-field">
<label class="form__label" for="points">Points*</label>
<input required name="points" type="number" bind:value={newActionScore} step="1" />
</div>
<div class="increment-decrement">
<button type="button" on:click={decrementNewActionScore}>-</button>
<button type="button" on:click={incrementNewActionScore}>+</button>
</div>
<div class="form-field">
<fieldset class="suggested-descriptions">
<legend>Reason for Points</legend>
{#each suggestedDescriptions as suggestion}
<label
class="suggested-descriptions__label"
class:selected={newActionName === suggestion}
for={`suggestion-${suggestion}`}
>
<input
type="radio"
name="suggestion"
id={`suggestion-${suggestion}`}
value={suggestion}
class="suggested-descriptions__item"
on:click={() => (newActionName = suggestion)}
checked={newActionName === suggestion}
/>
{suggestion}</label
>
{/each}
<label class="suggested-descriptions__item">
<button
type="button"
class="suggested-descriptions__button"
on:click={toggleIsNewActionNameVisible}
>
Other
</button>
</label>
<input
transition:slide
name="action-name"
type="text"
step="1"
class:sr-only={!isNewActionNameVisible}
bind:value={newActionName}
bind:this={newActionNameInput}
/>
</fieldset>
</div>
<div class="submit">
<input type="submit" value="Add Points" class="thomaswilson-button form__submit" />
</div>
</form>
</div>
{/if}
<style>
.actions-list {
padding: 0;
}
.actions-list__item {
padding: 0;
}
.form {
display: grid;
grid-template-columns: 100%;
gap: var(--spacing-md);
width: 100%;
}
.form__label {
font-size: var(--font-size-md);
}
.form-field {
display: grid;
grid-template-columns: 100%;
}
.increment-decrement {
padding: var(--spacing-md) 0;
display: grid;
grid-template-columns: 50% 50%;
gap: var(--spacing-md);
}
.suggested-descriptions {
padding: var(--spacing-sm);
margin: 0;
display: grid;
grid-template-columns: 100%;
}
.suggested-descriptions__item {
padding: var(--spacing-sm) 0;
}
.suggested-descriptions__label {
border: none;
background: transparent;
text-decoration: underline;
transition: all 0.15s;
padding: var(--spacing-sm) 0;
}
.suggested-descriptions__label.selected {
color: var(--brand-blue);
}
.submit {
padding-top: var(--spacing-lg);
display: grid;
grid-template-columns: 100%;
width: 100%;
}
.form__submit {
background: var(--brand-blue);
color: white;
border: none;
}
</style>

View file

@ -1,3 +0,0 @@
export interface PreviousGame {
prettyString: string;
}

View file

@ -7,7 +7,11 @@
date: string; date: string;
} }
export let latestBlogPosts: BlogPost[] = []; interface Props {
latestBlogPosts?: BlogPost[];
}
let { latestBlogPosts = [] }: Props = $props();
</script> </script>
<section class="homepage-header"> <section class="homepage-header">

View file

@ -1,8 +1,8 @@
<script lang="ts"> <script lang="ts">
import { onDestroy, onMount } from 'svelte'; import { onDestroy, onMount } from "svelte";
import Employee from '../../components/salary-calculator/employee.svelte'; import Employee from "../../components/salary-calculator/employee.svelte";
type Employee = { type IEmployee = {
id: string; id: string;
name: string; name: string;
salary: number; salary: number;
@ -11,21 +11,22 @@
function makeId(): string { function makeId(): string {
return ( return (
Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15) Math.random().toString(36).substring(2, 15) +
Math.random().toString(36).substring(2, 15)
); );
} }
function makeRandomJobTitle(): string { function makeRandomJobTitle(): string {
const randomJobNames = [ const randomJobNames = [
'Junior Software Engineer', "Junior Software Engineer",
'Finance Associate', "Finance Associate",
'Growth Marketeer', "Growth Marketeer",
'Customer Support', "Customer Support",
'Data Scientist', "Data Scientist",
'Logistics Manager', "Logistics Manager",
'General Manager', "General Manager",
'Process Manager', "Process Manager",
'Head of Department' "Head of Department",
]; ];
const index = Math.floor(Math.random() * randomJobNames.length); const index = Math.floor(Math.random() * randomJobNames.length);
@ -37,17 +38,17 @@
return 5_000 * b; return 5_000 * b;
} }
function makeEmployee(salaryOverride?: number): Employee { function makeEmployee(salaryOverride?: number): IEmployee {
return { return {
id: makeId(), id: makeId(),
name: makeRandomJobTitle(), name: makeRandomJobTitle(),
salary: salaryOverride ?? makeRandomSalary(), salary: salaryOverride ?? makeRandomSalary(),
count: 1 count: 1,
}; };
} }
let INITIAL_SALARY = makeRandomSalary(); let INITIAL_SALARY = makeRandomSalary();
let employees: Employee[] = [makeEmployee(INITIAL_SALARY)]; let employees: IEmployee[] = [makeEmployee(INITIAL_SALARY)];
let totalCost = 0; let totalCost = 0;
@ -57,8 +58,8 @@
let secondsElapsed = 0; let secondsElapsed = 0;
let intervalRef; let intervalRef;
let currency: 'GBP' | 'USD' = 'GBP'; let currency: "GBP" | "USD" = "GBP";
let salaryCalculationMethod: 'average' | 'individual' = 'average'; let salaryCalculationMethod: "average" | "individual" = "average";
function handleEmployeeChange(employeeId: string) { function handleEmployeeChange(employeeId: string) {
return function (event: CustomEvent) { return function (event: CustomEvent) {
@ -109,21 +110,21 @@
salaryCostPerMinute = allEmployeesSalaryToPerMinuteCost(employees); salaryCostPerMinute = allEmployeesSalaryToPerMinuteCost(employees);
} }
function handleAverageMethodChanged(method: 'average' | 'individual') { function handleAverageMethodChanged(method: "average" | "individual") {
return () => { return () => {
salaryCalculationMethod = method; salaryCalculationMethod = method;
secondsElapsed = 0; secondsElapsed = 0;
if (method === 'average') { if (method === "average") {
salaryCostPerMinute = annualSalaryToPerMinuteCost(averageAnnualSalary); salaryCostPerMinute = annualSalaryToPerMinuteCost(averageAnnualSalary);
} else if (method === 'individual') { } else if (method === "individual") {
salaryCostPerMinute = allEmployeesSalaryToPerMinuteCost(employees); salaryCostPerMinute = allEmployeesSalaryToPerMinuteCost(employees);
totalNumberOfEmployees = getNumberOfEmployees(employees); totalNumberOfEmployees = getNumberOfEmployees(employees);
} }
}; };
} }
function getNumberOfEmployees(employees: Employee[]): number { function getNumberOfEmployees(employees: IEmployee[]): number {
return employees.reduce((runningCount, employee) => { return employees.reduce((runningCount, employee) => {
return runningCount + employee.count; return runningCount + employee.count;
}, 0); }, 0);
@ -140,20 +141,20 @@
return annualSalary / workingDaysInYear / hoursInWorkingDay / minutesInHour; return annualSalary / workingDaysInYear / hoursInWorkingDay / minutesInHour;
} }
function allEmployeesSalaryToPerMinuteCost(employees: Employee[]) { function allEmployeesSalaryToPerMinuteCost(employees: IEmployee[]) {
return employees.reduce((acc, employee) => { return employees.reduce((acc, employee) => {
return acc + annualSalaryToPerMinuteCost(employee.salary); return acc + annualSalaryToPerMinuteCost(employee.salary);
}, 0); }, 0);
} }
function formatCurrency(amount: number, currency: 'GBP' | 'USD') { function formatCurrency(amount: number, currency: "GBP" | "USD") {
return `${currency === 'GBP' ? '£' : '$'}${amount.toFixed(2)}`; return `${currency === "GBP" ? "£" : "$"}${amount.toFixed(2)}`;
} }
function formatSecondsToMinutes(seconds: number) { function formatSecondsToMinutes(seconds: number) {
const minutes = Math.floor(seconds / 60); const minutes = Math.floor(seconds / 60);
const secondsRemaining = seconds % 60; const secondsRemaining = seconds % 60;
return `${minutes}:${secondsRemaining < 10 ? '0' : ''}${secondsRemaining}`; return `${minutes}:${secondsRemaining < 10 ? "0" : ""}${secondsRemaining}`;
} }
onMount(() => {}); onMount(() => {});
@ -162,8 +163,11 @@
clearInterval(intervalRef); clearInterval(intervalRef);
}); });
$: totalCost = (secondsElapsed / 60) * salaryCostPerMinute * totalNumberOfEmployees; $: totalCost =
$: totalCostPerMinute = (salaryCostPerMinute * totalNumberOfEmployees).toFixed(2); (secondsElapsed / 60) * salaryCostPerMinute * totalNumberOfEmployees;
$: totalCostPerMinute = (
salaryCostPerMinute * totalNumberOfEmployees
).toFixed(2);
// TODO: Milestones in cost, e.g. price of a kit kat chunky, price of X // TODO: Milestones in cost, e.g. price of a kit kat chunky, price of X
</script> </script>
@ -174,14 +178,20 @@
name="description" name="description"
content="Calculate the cost of a meeting, or at least the salaries of people attending a meeting." content="Calculate the cost of a meeting, or at least the salaries of people attending a meeting."
/> />
<meta name="twitter:card" content="https://www.thomaswilson.xyz/meeting-cost-calculator.png" /> <meta
name="twitter:card"
content="https://www.thomaswilson.xyz/meeting-cost-calculator.png"
/>
<meta name="twitter:site" content="@tjwilson92" /> <meta name="twitter:site" content="@tjwilson92" />
<meta name="twitter:title" content="Meeting Cost Calculator" /> <meta name="twitter:title" content="Meeting Cost Calculator" />
<meta <meta
name="twitter:description" name="twitter:description"
content="Calculate the cost of a meeting, or at least the salaries of people attending a meeting." content="Calculate the cost of a meeting, or at least the salaries of people attending a meeting."
/> />
<meta name="twitter:image" content="https://www.thomaswilson.xyz/meeting-cost-calculator.png" /> <meta
name="twitter:image"
content="https://www.thomaswilson.xyz/meeting-cost-calculator.png"
/>
<meta name="twitter:image:alt" content="Meeting Cost Calculator" /> <meta name="twitter:image:alt" content="Meeting Cost Calculator" />
<meta property="og:title" content="Meeting Cost Calculator" /> <meta property="og:title" content="Meeting Cost Calculator" />
@ -189,7 +199,10 @@
property="og:description" property="og:description"
content="Calculate the cost of a meeting, or at least the salaries of people attending a meeting." content="Calculate the cost of a meeting, or at least the salaries of people attending a meeting."
/> />
<meta property="og:image" content="https://www.thomaswilson.xyz/meeting-cost-calculator.png" /> <meta
property="og:image"
content="https://www.thomaswilson.xyz/meeting-cost-calculator.png"
/>
<meta property="og:image:alt" content="Meeting Cost Calculator" /> <meta property="og:image:alt" content="Meeting Cost Calculator" />
<meta property="og:url" content="https://www.thomaswilson.xyz/mcc" /> <meta property="og:url" content="https://www.thomaswilson.xyz/mcc" />
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />
@ -202,7 +215,9 @@
<main> <main>
<section> <section>
<h1>Meeting Cost Calculator</h1> <h1>Meeting Cost Calculator</h1>
<p class="subtitle">Meetings aren't free. See how much you're paying for them.</p> <p class="subtitle">
Meetings aren't free. See how much you're paying for them.
</p>
</section> </section>
<section> <section>
@ -210,19 +225,21 @@
<div class="modes"> <div class="modes">
<button <button
class="modes__button" class="modes__button"
class:selected={salaryCalculationMethod == 'average'} class:selected={salaryCalculationMethod == "average"}
on:click={handleAverageMethodChanged('average')}>A simple average</button on:click={handleAverageMethodChanged("average")}
>A simple average</button
> >
<button <button
class="modes__button" class="modes__button"
class:selected={salaryCalculationMethod == 'individual'} class:selected={salaryCalculationMethod == "individual"}
on:click={() => (salaryCalculationMethod = 'individual')}>In different bands</button on:click={() => (salaryCalculationMethod = "individual")}
>In different bands</button
> >
</div> </div>
</section> </section>
<section class="form"> <section class="form">
{#if salaryCalculationMethod == 'average'} {#if salaryCalculationMethod == "average"}
<h2>Salary Details</h2> <h2>Salary Details</h2>
<form class="simple-average-form"> <form class="simple-average-form">
<div class="simple-average-form__field"> <div class="simple-average-form__field">
@ -265,13 +282,11 @@
<section class="result"> <section class="result">
<h2>Projected Costs</h2> <h2>Projected Costs</h2>
{#if salaryCalculationMethod === 'average'} {#if salaryCalculationMethod === "average"}
<p> <p>
With {totalNumberOfEmployees ?? 0} With {totalNumberOfEmployees ?? 0}
{totalNumberOfEmployees === 1 ? 'Attendee' : 'Attendees'}, each costing aprox. {formatCurrency( {totalNumberOfEmployees === 1 ? "Attendee" : "Attendees"}, each costing
salaryCostPerMinute, aprox. {formatCurrency(salaryCostPerMinute, currency)}
currency
)}
per minute, this meeting will cost £{totalCostPerMinute} per minute. per minute, this meeting will cost £{totalCostPerMinute} per minute.
</p> </p>
{:else} {:else}
@ -288,13 +303,22 @@
<ul class="duration-list"> <ul class="duration-list">
<li class="duration-list__item"> <li class="duration-list__item">
{formatCurrency(salaryCostPerMinute * totalNumberOfEmployees * 30, currency)} for 30 minutes {formatCurrency(
salaryCostPerMinute * totalNumberOfEmployees * 30,
currency
)} for 30 minutes
</li> </li>
<li class="duration-list__item"> <li class="duration-list__item">
{formatCurrency(salaryCostPerMinute * totalNumberOfEmployees * 45, currency)} for 45 minutes {formatCurrency(
salaryCostPerMinute * totalNumberOfEmployees * 45,
currency
)} for 45 minutes
</li> </li>
<li class="duration-list__item"> <li class="duration-list__item">
{formatCurrency(salaryCostPerMinute * totalNumberOfEmployees * 60, currency)} for 60 minutes {formatCurrency(
salaryCostPerMinute * totalNumberOfEmployees * 60,
currency
)} for 60 minutes
</li> </li>
</ul> </ul>
</section> </section>
@ -315,7 +339,7 @@
{/if} {/if}
</button> </button>
<button on:click={stop} disabled={secondsElapsed === 0} id="pause" <button on:click={stop} disabled={secondsElapsed === 0} id="pause"
>{intervalRef ? 'Pause' : 'Start'}</button >{intervalRef ? "Pause" : "Start"}</button
> >
</div> </div>
<p class="total-cost"> <p class="total-cost">

View file

@ -7,14 +7,14 @@
differenceInCalendarDays differenceInCalendarDays
} from "date-fns"; } from "date-fns";
let lastDayOfMonth = new Date(); let lastDayOfMonth = $state(new Date());
let daysUntilPayDay = 0; let daysUntilPayDay = $state(0);
function prettyPrintDays(numberOfDays: number): string { function prettyPrintDays(numberOfDays: number): string {
return `${numberOfDays} ${numberOfDays === 1 ? "day" : "days"}`; return `${numberOfDays} ${numberOfDays === 1 ? "day" : "days"}`;
} }
$: pluralisedDays = prettyPrintDays(Math.abs(daysUntilPayDay)); let pluralisedDays = $derived(prettyPrintDays(Math.abs(daysUntilPayDay)));
onMount(() => { onMount(() => {
lastDayOfMonth = endOfMonth(new Date()); lastDayOfMonth = endOfMonth(new Date());

View file

@ -1,3 +1,11 @@
<script lang="ts">
interface Props {
children?: import('svelte').Snippet;
}
let { children }: Props = $props();
</script>
<h1>Snout St. Studios</h1> <h1>Snout St. Studios</h1>
<slot /> {@render children?.()}

View file

@ -12,7 +12,7 @@
import { SunriseSunsetStreakCalculator } from "./SunriseSunsetStreakCalculator.js"; import { SunriseSunsetStreakCalculator } from "./SunriseSunsetStreakCalculator.js";
import type { ISunriseSunsetGuessingHistory } from "./ISunriseSunsetGuessingHistory.js"; import type { ISunriseSunsetGuessingHistory } from "./ISunriseSunsetGuessingHistory.js";
let hasGuessingHistoryBeenLoaded = false; let hasGuessingHistoryBeenLoaded = $state(false);
let debug = false; let debug = false;
let visibleNotification: Writable<"none" | "success" | "failure"> = let visibleNotification: Writable<"none" | "success" | "failure"> =
writable("none"); writable("none");
@ -25,15 +25,19 @@
incorrectDays: [] incorrectDays: []
}); });
export let data: PageData; interface Props {
data: PageData;
}
let { data }: Props = $props();
const now = new Date(); const now = new Date();
const todaysDateString = formatDate(now, "yyyy-MM-dd"); const todaysDateString = formatDate(now, "yyyy-MM-dd");
const localStorageKey = "sunrise-sunset-guessing-history"; const localStorageKey = "sunrise-sunset-guessing-history";
let currentStreakLength = 0; let currentStreakLength = $state(0);
const streakCalculator = new SunriseSunsetStreakCalculator(todaysDateString); const streakCalculator = new SunriseSunsetStreakCalculator(todaysDateString);
$: picture = data.body.photo; let picture = $derived(data.body.photo);
function debugRemoveLocalStorage() { function debugRemoveLocalStorage() {
localStorage.removeItem(localStorageKey); localStorage.removeItem(localStorageKey);
@ -119,7 +123,7 @@
</section> </section>
{#if debug} {#if debug}
<button on:click={debugRemoveLocalStorage}>Remove Local Storage</button> <button onclick={debugRemoveLocalStorage}>Remove Local Storage</button>
{/if} {/if}
<section class="picture"> <section class="picture">

View file

@ -3,7 +3,11 @@
import { fade } from "svelte/transition"; import { fade } from "svelte/transition";
import type { Writable } from "svelte/store"; import type { Writable } from "svelte/store";
export let visibleNotification: Writable<"none" | "success" | "failure">; interface Props {
visibleNotification: Writable<"none" | "success" | "failure">;
}
let { visibleNotification }: Props = $props();
let hasAnimationTriggered = false; let hasAnimationTriggered = false;
const revealResultDelayDurationMs = 550; const revealResultDelayDurationMs = 550;

View file

@ -1,8 +1,12 @@
<script lang="ts"> <script lang="ts">
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
export let isDisabled: boolean; interface Props {
export let hasAlreadyGuessedToday: boolean; isDisabled: boolean;
hasAlreadyGuessedToday: boolean;
}
let { isDisabled, hasAlreadyGuessedToday }: Props = $props();
const eventDispatcher = createEventDispatcher<{ const eventDispatcher = createEventDispatcher<{
optionSelected: { option: "sunrise" | "sunset" }; optionSelected: { option: "sunrise" | "sunset" };
@ -18,13 +22,13 @@
<button <button
disabled={isDisabled} disabled={isDisabled}
class="options__button option--sunrise" class="options__button option--sunrise"
on:click={() => onOptionSelected("sunrise")}>Sunrise</button onclick={() => onOptionSelected("sunrise")}>Sunrise</button
> >
<button <button
disabled={isDisabled} disabled={isDisabled}
class="options__button option--sunset" class="options__button option--sunset"
id="button-sunset" id="button-sunset"
on:click={() => onOptionSelected("sunset")}>Sunset</button onclick={() => onOptionSelected("sunset")}>Sunset</button
> >
</div> </div>
{#if hasAlreadyGuessedToday} {#if hasAlreadyGuessedToday}

View file

@ -2,20 +2,29 @@
import { format as formatDate } from "date-fns"; import { format as formatDate } from "date-fns";
import { SunriseSunsetStreakCalculator } from "./SunriseSunsetStreakCalculator.js"; import { SunriseSunsetStreakCalculator } from "./SunriseSunsetStreakCalculator.js";
import { browser } from "$app/environment"; import { browser } from "$app/environment";
export let doesUserHaveGuessingHistory: boolean; interface Props {
export let correctGuessDays: string[]; doesUserHaveGuessingHistory: boolean;
export let incorrectGuessDays: string[]; correctGuessDays: string[];
export let currentStreakLength: number; incorrectGuessDays: string[];
currentStreakLength: number;
}
let {
doesUserHaveGuessingHistory,
correctGuessDays,
incorrectGuessDays,
currentStreakLength
}: Props = $props();
const todayAsString = formatDate(new Date(), "yyyy-MM-dd"); const todayAsString = formatDate(new Date(), "yyyy-MM-dd");
const calculator = new SunriseSunsetStreakCalculator(todayAsString); const calculator = new SunriseSunsetStreakCalculator(todayAsString);
let hasTextBeenCopied = false; let hasTextBeenCopied = $state(false);
$: historyStatement = calculator.getShareableStatement( let historyStatement = $derived(calculator.getShareableStatement(
correctGuessDays, correctGuessDays,
incorrectGuessDays, incorrectGuessDays,
new Date() new Date()
); ));
function copyHistory() { function copyHistory() {
if (browser) { if (browser) {
@ -32,7 +41,7 @@
<p class="score__text"> <p class="score__text">
{historyStatement} {historyStatement}
</p> </p>
<button on:click={() => copyHistory()}> Copy to Clipboard </button> <button onclick={() => copyHistory()}> Copy to Clipboard </button>
{#if hasTextBeenCopied} {#if hasTextBeenCopied}
<p>Copied!</p> <p>Copied!</p>

View file

@ -1,3 +1,11 @@
<script lang="ts">
interface Props {
children?: import('svelte').Snippet;
}
let { children }: Props = $props();
</script>
<svelte:head> <svelte:head>
<link <link
rel="stylesheet" rel="stylesheet"
@ -7,4 +15,4 @@
/> />
</svelte:head> </svelte:head>
<slot /> {@render children?.()}

View file

@ -5,9 +5,13 @@
import type { Wainwright } from './Wainwright.js'; import type { Wainwright } from './Wainwright.js';
import { browser } from '$app/environment'; import { browser } from '$app/environment';
export let data: PageData; interface Props {
data: PageData;
}
$: ({ wainwrights } = data); let { data }: Props = $props();
let { wainwrights } = $derived(data);
onMount(async () => { onMount(async () => {
const L = await import('leaflet'); const L = await import('leaflet');
@ -58,7 +62,7 @@
and forteen fells (including four mountains). These have become known as the Wainwrights. and forteen fells (including four mountains). These have become known as the Wainwrights.
</p> </p>
<div id="map" style="height: 400px;" /> <div id="map" style="height: 400px;"></div>
<style lang="scss"> <style lang="scss">
:global .wainwright-popup { :global .wainwright-popup {

367
yarn.lock
View file

@ -2,7 +2,7 @@
# yarn lockfile v1 # yarn lockfile v1
"@ampproject/remapping@^2.2.1": "@ampproject/remapping@^2.3.0":
version "2.3.0" version "2.3.0"
resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4"
integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==
@ -863,7 +863,7 @@
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a"
integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24": "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
version "0.3.25" version "0.3.25"
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0"
integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==
@ -1520,25 +1520,24 @@
sirv "^3.0.0" sirv "^3.0.0"
tiny-glob "^0.2.9" tiny-glob "^0.2.9"
"@sveltejs/vite-plugin-svelte-inspector@^2.1.0": "@sveltejs/vite-plugin-svelte-inspector@^3.0.0-next.0||^3.0.0":
version "2.1.0" version "3.0.1"
resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-2.1.0.tgz#116ba2b73be43c1d7d93de749f37becc7e45bb8c" resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-3.0.1.tgz#006bcab6ea90e09c65459133d4e3eaa6b1e83e28"
integrity sha512-9QX28IymvBlSCqsCll5t0kQVxipsfhFFL+L2t3nTWfXnddYwxBuAEtTtlaVQpRz9c37BhJjltSeY4AJSC03SSg== integrity sha512-2CKypmj1sM4GE7HjllT7UKmo4Q6L5xFRd7VMGEWhYnZ+wc6AUVU01IBd7yUi6WnFndEwWoMNOd6e8UjoN0nbvQ==
dependencies: dependencies:
debug "^4.3.4" debug "^4.3.7"
"@sveltejs/vite-plugin-svelte@^3.1.2": "@sveltejs/vite-plugin-svelte@^4.0.0":
version "3.1.2" version "4.0.4"
resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-3.1.2.tgz#be3120b52e6d9facb55d58392b0dad9e5a35ba6f" resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-4.0.4.tgz#79dfc00377f5456f4c3d95f56817d6486cc0df6c"
integrity sha512-Txsm1tJvtiYeLUVRNqxZGKR/mI+CzuIQuc2gn+YCs9rMTowpNZ2Nqt53JdL8KF9bLhAf2ruR/dr9eZCwdTriRA== integrity sha512-0ba1RQ/PHen5FGpdSrW7Y3fAMQjrXantECALeOiOdBdzR5+5vPP6HVZRLmZaQL+W8m++o+haIAKq5qT+MiZ7VA==
dependencies: dependencies:
"@sveltejs/vite-plugin-svelte-inspector" "^2.1.0" "@sveltejs/vite-plugin-svelte-inspector" "^3.0.0-next.0||^3.0.0"
debug "^4.3.4" debug "^4.3.7"
deepmerge "^4.3.1" deepmerge "^4.3.1"
kleur "^4.1.5" kleur "^4.1.5"
magic-string "^0.30.10" magic-string "^0.30.12"
svelte-hmr "^0.16.0" vitefu "^1.0.3"
vitefu "^0.2.5"
"@types/cookie@^0.6.0": "@types/cookie@^0.6.0":
version "0.6.0" version "0.6.0"
@ -1552,7 +1551,7 @@
dependencies: dependencies:
"@types/ms" "*" "@types/ms" "*"
"@types/estree@1.0.6", "@types/estree@^1.0.0", "@types/estree@^1.0.1", "@types/estree@^1.0.6": "@types/estree@1.0.6", "@types/estree@^1.0.0", "@types/estree@^1.0.5", "@types/estree@^1.0.6":
version "1.0.6" version "1.0.6"
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50"
integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==
@ -1610,11 +1609,6 @@
resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-6.0.3.tgz#705bb349e789efa06f43f128cef51240753424cb" resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-6.0.3.tgz#705bb349e789efa06f43f128cef51240753424cb"
integrity sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g== integrity sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==
"@types/pug@^2.0.6":
version "2.0.10"
resolved "https://registry.yarnpkg.com/@types/pug/-/pug-2.0.10.tgz#52f8dbd6113517aef901db20b4f3fca543b88c1f"
integrity sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==
"@types/sanitize-html@^2.13.0": "@types/sanitize-html@^2.13.0":
version "2.13.0" version "2.13.0"
resolved "https://registry.yarnpkg.com/@types/sanitize-html/-/sanitize-html-2.13.0.tgz#ac3620e867b7c68deab79c72bd117e2049cdd98e" resolved "https://registry.yarnpkg.com/@types/sanitize-html/-/sanitize-html-2.13.0.tgz#ac3620e867b7c68deab79c72bd117e2049cdd98e"
@ -1778,6 +1772,11 @@ acorn-jsx@^5.3.2:
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
acorn-typescript@^1.4.13:
version "1.4.13"
resolved "https://registry.yarnpkg.com/acorn-typescript/-/acorn-typescript-1.4.13.tgz#5f851c8bdda0aa716ffdd5f6ac084df8acc6f5ea"
integrity sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q==
acorn-walk@^8.3.2: acorn-walk@^8.3.2:
version "8.3.4" version "8.3.4"
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7"
@ -1785,7 +1784,7 @@ acorn-walk@^8.3.2:
dependencies: dependencies:
acorn "^8.11.0" acorn "^8.11.0"
acorn@^8.10.0, acorn@^8.11.0, acorn@^8.14.0, acorn@^8.9.0: acorn@^8.11.0, acorn@^8.12.1, acorn@^8.14.0, acorn@^8.9.0:
version "8.14.0" version "8.14.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0"
integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==
@ -1812,20 +1811,12 @@ ansi-styles@^5.0.0:
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b"
integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==
anymatch@~3.1.2:
version "3.1.3"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"
integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==
dependencies:
normalize-path "^3.0.0"
picomatch "^2.0.4"
argparse@^2.0.1: argparse@^2.0.1:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
aria-query@^5.3.0: aria-query@^5.3.1:
version "5.3.2" version "5.3.2"
resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.2.tgz#93f81a43480e33a338f19163a3d10a50c01dcd59" resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.2.tgz#93f81a43480e33a338f19163a3d10a50c01dcd59"
integrity sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw== integrity sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==
@ -1840,7 +1831,7 @@ assertion-error@^1.1.0:
resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b"
integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==
axobject-query@^4.0.0: axobject-query@^4.1.0:
version "4.1.0" version "4.1.0"
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-4.1.0.tgz#28768c76d0e3cff21bc62a9e2d0b6ac30042a1ee" resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-4.1.0.tgz#28768c76d0e3cff21bc62a9e2d0b6ac30042a1ee"
integrity sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ== integrity sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==
@ -1860,11 +1851,6 @@ base64-js@^1.3.1:
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
binary-extensions@^2.0.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522"
integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==
bowser@^2.11.0: bowser@^2.11.0:
version "2.11.0" version "2.11.0"
resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.11.0.tgz#5ca3c35757a7aa5771500c70a73a9f91ef420a8f" resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.11.0.tgz#5ca3c35757a7aa5771500c70a73a9f91ef420a8f"
@ -1878,7 +1864,7 @@ brace-expansion@^1.1.7:
balanced-match "^1.0.0" balanced-match "^1.0.0"
concat-map "0.0.1" concat-map "0.0.1"
braces@^3.0.3, braces@~3.0.2: braces@^3.0.3:
version "3.0.3" version "3.0.3"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
@ -1892,11 +1878,6 @@ bson@^4.7.2:
dependencies: dependencies:
buffer "^5.6.0" buffer "^5.6.0"
buffer-crc32@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-1.0.0.tgz#a10993b9055081d55304bd9feb4a072de179f405"
integrity sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==
buffer@^5.6.0: buffer@^5.6.0:
version "5.7.1" version "5.7.1"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
@ -1963,38 +1944,17 @@ check-error@^1.0.3:
dependencies: dependencies:
get-func-name "^2.0.2" get-func-name "^2.0.2"
chokidar@^3.4.1: chokidar@^4.0.0, chokidar@^4.0.1:
version "3.6.0"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b"
integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==
dependencies:
anymatch "~3.1.2"
braces "~3.0.2"
glob-parent "~5.1.2"
is-binary-path "~2.1.0"
is-glob "~4.0.1"
normalize-path "~3.0.0"
readdirp "~3.6.0"
optionalDependencies:
fsevents "~2.3.2"
chokidar@^4.0.0:
version "4.0.3" version "4.0.3"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30"
integrity sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA== integrity sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==
dependencies: dependencies:
readdirp "^4.0.1" readdirp "^4.0.1"
code-red@^1.0.3: clsx@^2.1.1:
version "1.0.4" version "2.1.1"
resolved "https://registry.yarnpkg.com/code-red/-/code-red-1.0.4.tgz#59ba5c9d1d320a4ef795bc10a28bd42bfebe3e35" resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999"
integrity sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw== integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==
dependencies:
"@jridgewell/sourcemap-codec" "^1.4.15"
"@types/estree" "^1.0.1"
acorn "^8.10.0"
estree-walker "^3.0.3"
periscopic "^3.1.0"
color-convert@^2.0.1: color-convert@^2.0.1:
version "2.0.1" version "2.0.1"
@ -2037,14 +1997,6 @@ cross-spawn@^7.0.3, cross-spawn@^7.0.6:
shebang-command "^2.0.0" shebang-command "^2.0.0"
which "^2.0.1" which "^2.0.1"
css-tree@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.3.1.tgz#10264ce1e5442e8572fc82fbe490644ff54b5c20"
integrity sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==
dependencies:
mdn-data "2.0.30"
source-map-js "^1.0.1"
cssesc@^3.0.0: cssesc@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
@ -2062,7 +2014,7 @@ date-fns@^2.30.0:
dependencies: dependencies:
"@babel/runtime" "^7.21.0" "@babel/runtime" "^7.21.0"
debug@^4.0.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: debug@^4.0.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.7:
version "4.4.0" version "4.4.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a"
integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==
@ -2098,11 +2050,6 @@ dequal@^2.0.0:
resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be"
integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==
detect-indent@^6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6"
integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==
detect-libc@^1.0.3: detect-libc@^1.0.3:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
@ -2165,11 +2112,6 @@ entities@^4.2.0, entities@^4.4.0:
resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
es6-promise@^3.1.2:
version "3.3.1"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613"
integrity sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==
esbuild@^0.21.3: esbuild@^0.21.3:
version "0.21.5" version "0.21.5"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d"
@ -2368,6 +2310,13 @@ esquery@^1.5.0:
dependencies: dependencies:
estraverse "^5.1.0" estraverse "^5.1.0"
esrap@^1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/esrap/-/esrap-1.3.2.tgz#a0644603f7f8e9f068c77052d6e16cd4062b5f88"
integrity sha512-C4PXusxYhFT98GjLSmb20k9PREuUdporer50dhzGuJu9IJXktbMddVCMLAERl5dAHyAi73GWWCE4FVHGP1794g==
dependencies:
"@jridgewell/sourcemap-codec" "^1.4.15"
esrecurse@^4.3.0: esrecurse@^4.3.0:
version "4.3.0" version "4.3.0"
resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921"
@ -2385,7 +2334,7 @@ estraverse@^5.1.0, estraverse@^5.2.0:
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
estree-walker@^3.0.0, estree-walker@^3.0.3: estree-walker@^3.0.3:
version "3.0.3" version "3.0.3"
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d" resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d"
integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g== integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==
@ -2464,6 +2413,11 @@ fault@^2.0.0:
dependencies: dependencies:
format "^0.2.0" format "^0.2.0"
fdir@^6.2.0:
version "6.4.2"
resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.4.2.tgz#ddaa7ce1831b161bc3657bb99cb36e1622702689"
integrity sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==
feed@^4.2.2: feed@^4.2.2:
version "4.2.2" version "4.2.2"
resolved "https://registry.yarnpkg.com/feed/-/feed-4.2.2.tgz#865783ef6ed12579e2c44bbef3c9113bc4956a7e" resolved "https://registry.yarnpkg.com/feed/-/feed-4.2.2.tgz#865783ef6ed12579e2c44bbef3c9113bc4956a7e"
@ -2526,11 +2480,6 @@ formdata-polyfill@^4.0.10:
dependencies: dependencies:
fetch-blob "^3.1.2" fetch-blob "^3.1.2"
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
fsevents@~2.3.2, fsevents@~2.3.3: fsevents@~2.3.2, fsevents@~2.3.3:
version "2.3.3" version "2.3.3"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
@ -2546,7 +2495,7 @@ get-stream@^8.0.1:
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2"
integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA== integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==
glob-parent@^5.1.2, glob-parent@~5.1.2: glob-parent@^5.1.2:
version "5.1.2" version "5.1.2"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
@ -2560,18 +2509,6 @@ glob-parent@^6.0.2:
dependencies: dependencies:
is-glob "^4.0.3" is-glob "^4.0.3"
glob@^7.1.3:
version "7.2.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.1.1"
once "^1.3.0"
path-is-absolute "^1.0.0"
globals@^14.0.0: globals@^14.0.0:
version "14.0.0" version "14.0.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e"
@ -2599,11 +2536,6 @@ globrex@^0.1.2:
resolved "https://registry.yarnpkg.com/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098" resolved "https://registry.yarnpkg.com/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098"
integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg== integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==
graceful-fs@^4.1.3:
version "4.2.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
graphemer@^1.4.0: graphemer@^1.4.0:
version "1.4.0" version "1.4.0"
resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6"
@ -2749,19 +2681,6 @@ imurmurhash@^0.1.4:
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==
inflight@^1.0.4:
version "1.0.6"
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
dependencies:
once "^1.3.0"
wrappy "1"
inherits@2:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
ip-address@^9.0.5: ip-address@^9.0.5:
version "9.0.5" version "9.0.5"
resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-9.0.5.tgz#117a960819b08780c3bd1f14ef3c1cc1d3f3ea5a" resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-9.0.5.tgz#117a960819b08780c3bd1f14ef3c1cc1d3f3ea5a"
@ -2770,13 +2689,6 @@ ip-address@^9.0.5:
jsbn "1.1.0" jsbn "1.1.0"
sprintf-js "^1.1.3" sprintf-js "^1.1.3"
is-binary-path@~2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
dependencies:
binary-extensions "^2.0.0"
is-buffer@^2.0.0: is-buffer@^2.0.0:
version "2.0.5" version "2.0.5"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191"
@ -2787,7 +2699,7 @@ is-extglob@^2.1.1:
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3:
version "4.0.3" version "4.0.3"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
@ -2809,7 +2721,7 @@ is-plain-object@^5.0.0:
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344"
integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==
is-reference@^3.0.0, is-reference@^3.0.1: is-reference@^3.0.3:
version "3.0.3" version "3.0.3"
resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-3.0.3.tgz#9ef7bf9029c70a67b2152da4adf57c23d718910f" resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-3.0.3.tgz#9ef7bf9029c70a67b2152da4adf57c23d718910f"
integrity sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw== integrity sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==
@ -2935,7 +2847,7 @@ loupe@^2.3.6, loupe@^2.3.7:
dependencies: dependencies:
get-func-name "^2.0.1" get-func-name "^2.0.1"
magic-string@^0.30.10, magic-string@^0.30.4, magic-string@^0.30.5: magic-string@^0.30.11, magic-string@^0.30.12, magic-string@^0.30.5:
version "0.30.17" version "0.30.17"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453"
integrity sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA== integrity sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==
@ -3021,11 +2933,6 @@ mdast-util-to-string@^3.0.0, mdast-util-to-string@^3.1.0:
dependencies: dependencies:
"@types/mdast" "^3.0.0" "@types/mdast" "^3.0.0"
mdn-data@2.0.30:
version "2.0.30"
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc"
integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==
mdsvex@^0.10.6: mdsvex@^0.10.6:
version "0.10.6" version "0.10.6"
resolved "https://registry.yarnpkg.com/mdsvex/-/mdsvex-0.10.6.tgz#5ba975f4616e5255ca31cd93d33e2c2a22845631" resolved "https://registry.yarnpkg.com/mdsvex/-/mdsvex-0.10.6.tgz#5ba975f4616e5255ca31cd93d33e2c2a22845631"
@ -3268,30 +3175,13 @@ mimic-fn@^4.0.0:
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc"
integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==
min-indent@^1.0.0: minimatch@^3.1.2:
version "1.0.1"
resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
minimatch@^3.1.1, minimatch@^3.1.2:
version "3.1.2" version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
dependencies: dependencies:
brace-expansion "^1.1.7" brace-expansion "^1.1.7"
minimist@^1.2.0, minimist@^1.2.6:
version "1.2.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
mkdirp@^0.5.1:
version "0.5.6"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6"
integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==
dependencies:
minimist "^1.2.6"
mlly@^1.7.3: mlly@^1.7.3:
version "1.7.3" version "1.7.3"
resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.7.3.tgz#d86c0fcd8ad8e16395eb764a5f4b831590cee48c" resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.7.3.tgz#d86c0fcd8ad8e16395eb764a5f4b831590cee48c"
@ -3376,11 +3266,6 @@ node-fetch@^3.3.2:
fetch-blob "^3.1.4" fetch-blob "^3.1.4"
formdata-polyfill "^4.0.10" formdata-polyfill "^4.0.10"
normalize-path@^3.0.0, normalize-path@~3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
npm-run-path@^5.1.0: npm-run-path@^5.1.0:
version "5.3.0" version "5.3.0"
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.3.0.tgz#e23353d0ebb9317f174e93417e4a4d82d0249e9f" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.3.0.tgz#e23353d0ebb9317f174e93417e4a4d82d0249e9f"
@ -3388,13 +3273,6 @@ npm-run-path@^5.1.0:
dependencies: dependencies:
path-key "^4.0.0" path-key "^4.0.0"
once@^1.3.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
dependencies:
wrappy "1"
onetime@^6.0.0: onetime@^6.0.0:
version "6.0.0" version "6.0.0"
resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4"
@ -3457,11 +3335,6 @@ path-exists@^4.0.0:
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
path-is-absolute@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
path-key@^3.1.0: path-key@^3.1.0:
version "3.1.1" version "3.1.1"
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
@ -3487,21 +3360,12 @@ pathval@^1.1.1:
resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d"
integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==
periscopic@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/periscopic/-/periscopic-3.1.0.tgz#7e9037bf51c5855bd33b48928828db4afa79d97a"
integrity sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==
dependencies:
"@types/estree" "^1.0.0"
estree-walker "^3.0.0"
is-reference "^3.0.0"
picocolors@^1.0.0, picocolors@^1.1.1: picocolors@^1.0.0, picocolors@^1.1.1:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: picomatch@^2.3.1:
version "2.3.1" version "2.3.1"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
@ -3609,13 +3473,6 @@ readdirp@^4.0.1:
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.0.2.tgz#388fccb8b75665da3abffe2d8f8ed59fe74c230a" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.0.2.tgz#388fccb8b75665da3abffe2d8f8ed59fe74c230a"
integrity sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA== integrity sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==
readdirp@~3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
dependencies:
picomatch "^2.2.1"
regenerator-runtime@^0.14.0: regenerator-runtime@^0.14.0:
version "0.14.1" version "0.14.1"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f"
@ -3688,13 +3545,6 @@ reusify@^1.0.4:
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
rimraf@^2.5.2:
version "2.7.1"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
dependencies:
glob "^7.1.3"
rollup@^4.20.0: rollup@^4.20.0:
version "4.29.1" version "4.29.1"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.29.1.tgz#a9aaaece817e5f778489e5bf82e379cc8a5c05bc" resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.29.1.tgz#a9aaaece817e5f778489e5bf82e379cc8a5c05bc"
@ -3737,16 +3587,6 @@ sade@^1.7.3, sade@^1.7.4, sade@^1.8.1:
dependencies: dependencies:
mri "^1.1.0" mri "^1.1.0"
sander@^0.5.0:
version "0.5.1"
resolved "https://registry.yarnpkg.com/sander/-/sander-0.5.1.tgz#741e245e231f07cafb6fdf0f133adfa216a502ad"
integrity sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA==
dependencies:
es6-promise "^3.1.2"
graceful-fs "^4.1.3"
mkdirp "^0.5.1"
rimraf "^2.5.2"
sanitize-html@^2.14.0: sanitize-html@^2.14.0:
version "2.14.0" version "2.14.0"
resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-2.14.0.tgz#bd2a7b97ee1d86a7f0e0babf3a4468f639c3a429" resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-2.14.0.tgz#bd2a7b97ee1d86a7f0e0babf3a4468f639c3a429"
@ -3834,17 +3674,7 @@ socks@^2.7.1:
ip-address "^9.0.5" ip-address "^9.0.5"
smart-buffer "^4.2.0" smart-buffer "^4.2.0"
sorcery@^0.11.0: "source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.2.1:
version "0.11.1"
resolved "https://registry.yarnpkg.com/sorcery/-/sorcery-0.11.1.tgz#7cac27ae9c9549b3cd1e4bb85317f7b2dc7b7e22"
integrity sha512-o7npfeJE6wi6J9l0/5LKshFzZ2rMatRiCDwYeDQaOzqdzRJwALhX7mk/A/ecg6wjMu7wdZbmXfD2S/vpOg0bdQ==
dependencies:
"@jridgewell/sourcemap-codec" "^1.4.14"
buffer-crc32 "^1.0.0"
minimist "^1.2.0"
sander "^0.5.0"
"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1, source-map-js@^1.2.1:
version "1.2.1" version "1.2.1"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
@ -3889,13 +3719,6 @@ strip-final-newline@^3.0.0:
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd"
integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==
strip-indent@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001"
integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==
dependencies:
min-indent "^1.0.0"
strip-json-comments@^3.1.1: strip-json-comments@^3.1.1:
version "3.1.1" version "3.1.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
@ -3929,17 +3752,16 @@ supports-color@^7.1.0:
dependencies: dependencies:
has-flag "^4.0.0" has-flag "^4.0.0"
svelte-check@^3.8.6: svelte-check@^4.0.0:
version "3.8.6" version "4.1.1"
resolved "https://registry.yarnpkg.com/svelte-check/-/svelte-check-3.8.6.tgz#2f0ab90533f20b8a549a55fccd8142374a316184" resolved "https://registry.yarnpkg.com/svelte-check/-/svelte-check-4.1.1.tgz#4d6a97651bdcff84ad10521d0394ce094dee187a"
integrity sha512-ij0u4Lw/sOTREP13BdWZjiXD/BlHE6/e2e34XzmVmsp5IN4kVa3PWP65NM32JAgwjZlwBg/+JtiNV1MM8khu0Q== integrity sha512-NfaX+6Qtc8W/CyVGS/F7/XdiSSyXz+WGYA9ZWV3z8tso14V2vzjfXviKaTFEzB7g8TqfgO2FOzP6XT4ApSTUTw==
dependencies: dependencies:
"@jridgewell/trace-mapping" "^0.3.17" "@jridgewell/trace-mapping" "^0.3.25"
chokidar "^3.4.1" chokidar "^4.0.1"
fdir "^6.2.0"
picocolors "^1.0.0" picocolors "^1.0.0"
sade "^1.7.4" sade "^1.7.4"
svelte-preprocess "^5.1.3"
typescript "^5.0.3"
svelte-eslint-parser@^0.43.0: svelte-eslint-parser@^0.43.0:
version "0.43.0" version "0.43.0"
@ -3952,41 +3774,30 @@ svelte-eslint-parser@^0.43.0:
postcss "^8.4.39" postcss "^8.4.39"
postcss-scss "^4.0.9" postcss-scss "^4.0.9"
svelte-hmr@^0.16.0: svelte-preprocess@^6.0.0:
version "0.16.0" version "6.0.3"
resolved "https://registry.yarnpkg.com/svelte-hmr/-/svelte-hmr-0.16.0.tgz#9f345b7d1c1662f1613747ed7e82507e376c1716" resolved "https://registry.yarnpkg.com/svelte-preprocess/-/svelte-preprocess-6.0.3.tgz#fdc1f9dc41b6f22bf8b1f059e9f21eaaae181eeb"
integrity sha512-Gyc7cOS3VJzLlfj7wKS0ZnzDVdv3Pn2IuVeJPk9m2skfhcu5bq3wtIZyQGggr7/Iim5rH5cncyQft/kRLupcnA== integrity sha512-PLG2k05qHdhmRG7zR/dyo5qKvakhm8IJ+hD2eFRQmMLHp7X3eJnjeupUtvuRpbNiF31RjVw45W+abDwHEmP5OA==
svelte-preprocess@^5.1.3, svelte-preprocess@^5.1.4: svelte@^5.0.0:
version "5.1.4" version "5.16.1"
resolved "https://registry.yarnpkg.com/svelte-preprocess/-/svelte-preprocess-5.1.4.tgz#14ada075c94bbd2b71c5ec70ff72f8ebe1c95b91" resolved "https://registry.yarnpkg.com/svelte/-/svelte-5.16.1.tgz#c3030a4b3f477801c669008e5ab12104df3ab05a"
integrity sha512-IvnbQ6D6Ao3Gg6ftiM5tdbR6aAETwjhHV+UKGf5bHGYR69RQvF1ho0JKPcbUON4vy4R7zom13jPjgdOWCQ5hDA== integrity sha512-FsA1OjAKMAFSDob6j/Tv2ZV9rY4SeqPd1WXQlQkFkePAozSHLp6tbkU9qa1xJ+uTRzMSM2Vx3USdsYZBXd3H3g==
dependencies: dependencies:
"@types/pug" "^2.0.6" "@ampproject/remapping" "^2.3.0"
detect-indent "^6.1.0" "@jridgewell/sourcemap-codec" "^1.5.0"
magic-string "^0.30.5" "@types/estree" "^1.0.5"
sorcery "^0.11.0" acorn "^8.12.1"
strip-indent "^3.0.0" acorn-typescript "^1.4.13"
aria-query "^5.3.1"
svelte@^4.2.19: axobject-query "^4.1.0"
version "4.2.19" clsx "^2.1.1"
resolved "https://registry.yarnpkg.com/svelte/-/svelte-4.2.19.tgz#4e6e84a8818e2cd04ae0255fcf395bc211e61d4c" esm-env "^1.2.1"
integrity sha512-IY1rnGr6izd10B0A8LqsBfmlT5OILVuZ7XsI0vdGPEvuonFV7NYEUK4dAkm9Zg2q0Um92kYjTpS1CAP3Nh/KWw== esrap "^1.3.2"
dependencies: is-reference "^3.0.3"
"@ampproject/remapping" "^2.2.1"
"@jridgewell/sourcemap-codec" "^1.4.15"
"@jridgewell/trace-mapping" "^0.3.18"
"@types/estree" "^1.0.1"
acorn "^8.9.0"
aria-query "^5.3.0"
axobject-query "^4.0.0"
code-red "^1.0.3"
css-tree "^2.3.1"
estree-walker "^3.0.3"
is-reference "^3.0.1"
locate-character "^3.0.0" locate-character "^3.0.0"
magic-string "^0.30.4" magic-string "^0.30.11"
periscopic "^3.1.0" zimmerframe "^1.1.2"
tiny-glob@^0.2.9: tiny-glob@^0.2.9:
version "0.2.9" version "0.2.9"
@ -4077,7 +3888,7 @@ type-detect@^4.0.0, type-detect@^4.1.0:
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.1.0.tgz#deb2453e8f08dcae7ae98c626b13dddb0155906c" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.1.0.tgz#deb2453e8f08dcae7ae98c626b13dddb0155906c"
integrity sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw== integrity sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==
typescript@^5.0.3, typescript@^5.7.2: typescript@^5.7.2:
version "5.7.2" version "5.7.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.2.tgz#3169cf8c4c8a828cde53ba9ecb3d2b1d5dd67be6" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.2.tgz#3169cf8c4c8a828cde53ba9ecb3d2b1d5dd67be6"
integrity sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg== integrity sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==
@ -4238,10 +4049,10 @@ vite@^5.0.0, vite@^5.4.11:
optionalDependencies: optionalDependencies:
fsevents "~2.3.3" fsevents "~2.3.3"
vitefu@^0.2.5: vitefu@^1.0.3:
version "0.2.5" version "1.0.5"
resolved "https://registry.yarnpkg.com/vitefu/-/vitefu-0.2.5.tgz#c1b93c377fbdd3e5ddd69840ea3aa70b40d90969" resolved "https://registry.yarnpkg.com/vitefu/-/vitefu-1.0.5.tgz#eab501e07da167bbb68e957685823e6b425e7ce2"
integrity sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q== integrity sha512-h4Vflt9gxODPFNGPwp4zAMZRpZR7eslzwH2c5hn5kNZ5rhnKyRJ50U+yGCdc2IRaBs8O4haIgLNGrV5CrpMsCA==
vitest@^1.6.0: vitest@^1.6.0:
version "1.6.0" version "1.6.0"
@ -4312,11 +4123,6 @@ word-wrap@^1.2.5:
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34"
integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
xml-js@^1.6.11: xml-js@^1.6.11:
version "1.6.11" version "1.6.11"
resolved "https://registry.yarnpkg.com/xml-js/-/xml-js-1.6.11.tgz#927d2f6947f7f1c19a316dd8eea3614e8b18f8e9" resolved "https://registry.yarnpkg.com/xml-js/-/xml-js-1.6.11.tgz#927d2f6947f7f1c19a316dd8eea3614e8b18f8e9"
@ -4339,6 +4145,11 @@ yocto-queue@^1.0.0:
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.1.1.tgz#fef65ce3ac9f8a32ceac5a634f74e17e5b232110" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.1.1.tgz#fef65ce3ac9f8a32ceac5a634f74e17e5b232110"
integrity sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g== integrity sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==
zimmerframe@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/zimmerframe/-/zimmerframe-1.1.2.tgz#5b75f1fa83b07ae2a428d51e50f58e2ae6855e5e"
integrity sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==
zod@^3.24.1: zod@^3.24.1:
version "3.24.1" version "3.24.1"
resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.1.tgz#27445c912738c8ad1e9de1bea0359fa44d9d35ee" resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.1.tgz#27445c912738c8ad1e9de1bea0359fa44d9d35ee"