├── .bun-version
├── .github
└── workflows
│ ├── ci.yml
│ └── deploy.yml
├── .gitignore
├── .node-version
├── .npmrc
├── .prettierignore
├── .prettierrc
├── LICENSE
├── README.md
├── bun.lockb
├── eslint.config.js
├── package.json
├── postcss.config.js
├── src
├── app.css
├── app.d.ts
├── app.html
├── lib
│ ├── AtCoderContestResult.ts
│ ├── calculateRating.test.ts
│ ├── calculateRating.ts
│ ├── getRatingColor.ts
│ ├── index.ts
│ └── server
│ │ └── fetchAtCoderContestResults.ts
└── routes
│ ├── +layout.svelte
│ ├── +page.svelte
│ └── api
│ └── [userId]
│ └── [contestType]
│ └── +server.ts
├── static
└── favicon.svg
├── svelte.config.js
├── tailwind.config.ts
├── tsconfig.json
├── vite.config.ts
└── wrangler.toml
/.bun-version:
--------------------------------------------------------------------------------
1 | 1.1.42
2 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | workflow_dispatch:
9 |
10 | jobs:
11 | lint:
12 | runs-on: ubuntu-24.04
13 | steps:
14 | - name: Checkout
15 | uses: actions/checkout@v3
16 | - name: Setup tools
17 | uses: jdx/mise-action@v2
18 | - name: Install Dependencies
19 | run: bun install --frozen-lockfile
20 | - name: Check format and lint
21 | run: bun run lint
22 |
23 | check:
24 | runs-on: ubuntu-24.04
25 | steps:
26 | - name: Checkout
27 | uses: actions/checkout@v3
28 | - name: Setup tools
29 | uses: jdx/mise-action@v2
30 | - name: Install Dependencies
31 | run: bun install --frozen-lockfile
32 | - name: Check TypeScript
33 | run: bun run check
34 |
35 | test:
36 | runs-on: ubuntu-24.04
37 | steps:
38 | - name: Checkout
39 | uses: actions/checkout@v3
40 | - name: Setup tools
41 | uses: jdx/mise-action@v2
42 | - name: Install Dependencies
43 | run: bun install --frozen-lockfile
44 | - name: Run tests
45 | run: bun run test
46 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | name: Deploy Cloudflare Pages
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | workflow_dispatch:
9 |
10 | jobs:
11 | publish:
12 | runs-on: ubuntu-24.04
13 | permissions:
14 | contents: read
15 | deployments: write
16 | steps:
17 | - name: Checkout
18 | uses: actions/checkout@v3
19 | - name: Setup tools
20 | uses: jdx/mise-action@v2
21 | - name: Install Dependencies
22 | run: bun install --frozen-lockfile
23 | - name: Build
24 | run: bun run build
25 | - name: Publish to Cloudflare Pages
26 | uses: cloudflare/wrangler-action@v3
27 | with:
28 | apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
29 | accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
30 | command: pages deploy --commit-dirty=true
31 | gitHubToken: ${{ secrets.GITHUB_TOKEN }}
32 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
3 | # Output
4 | .output
5 | .vercel
6 | .netlify
7 | .wrangler
8 | /.svelte-kit
9 | /build
10 |
11 | # OS
12 | .DS_Store
13 | Thumbs.db
14 |
15 | # Env
16 | .env
17 | .env.*
18 | !.env.example
19 | !.env.test
20 |
21 | # Vite
22 | vite.config.js.timestamp-*
23 | vite.config.ts.timestamp-*
24 |
--------------------------------------------------------------------------------
/.node-version:
--------------------------------------------------------------------------------
1 | 22.12.0
2 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | engine-strict=true
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # Package Managers
2 | package-lock.json
3 | pnpm-lock.yaml
4 | yarn.lock
5 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "useTabs": true,
3 | "singleQuote": true,
4 | "trailingComma": "none",
5 | "printWidth": 100,
6 | "plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"],
7 | "overrides": [
8 | {
9 | "files": "*.svelte",
10 | "options": {
11 | "parser": "svelte"
12 | }
13 | }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 3w36zj6
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AtCoder Rating Estimator
2 |
--------------------------------------------------------------------------------
/bun.lockb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/3w36zj6/atcoder-rating-estimator/be2432b283504198e353a72045301d5f9f892221/bun.lockb
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | import prettier from 'eslint-config-prettier';
2 | import js from '@eslint/js';
3 | import { includeIgnoreFile } from '@eslint/compat';
4 | import svelte from 'eslint-plugin-svelte';
5 | import globals from 'globals';
6 | import { fileURLToPath } from 'node:url';
7 | import ts from 'typescript-eslint';
8 | const gitignorePath = fileURLToPath(new URL('./.gitignore', import.meta.url));
9 |
10 | export default ts.config(
11 | includeIgnoreFile(gitignorePath),
12 | js.configs.recommended,
13 | ...ts.configs.recommended,
14 | ...svelte.configs['flat/recommended'],
15 | prettier,
16 | ...svelte.configs['flat/prettier'],
17 | {
18 | languageOptions: {
19 | globals: {
20 | ...globals.browser,
21 | ...globals.node
22 | }
23 | }
24 | },
25 | {
26 | files: ['**/*.svelte'],
27 |
28 | languageOptions: {
29 | parserOptions: {
30 | parser: ts.parser
31 | }
32 | }
33 | }
34 | );
35 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "atcoder-rating-estimator",
3 | "private": true,
4 | "version": "0.0.1",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite dev",
8 | "build": "vite build",
9 | "preview": "vite preview",
10 | "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
11 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
12 | "format": "prettier --write .",
13 | "lint": "prettier --check . && eslint .",
14 | "test:unit": "vitest",
15 | "test": "npm run test:unit -- --run",
16 | "deploy": "bun run build && wrangler pages deploy",
17 | "cf-typegen": "wrangler types && mv worker-configuration.d.ts src/"
18 | },
19 | "devDependencies": {
20 | "@cloudflare/workers-types": "^4.20241224.0",
21 | "@eslint/compat": "^1.2.3",
22 | "@sveltejs/adapter-auto": "^3.0.0",
23 | "@sveltejs/adapter-cloudflare": "^5.0.0",
24 | "@sveltejs/kit": "^2.0.0",
25 | "@sveltejs/vite-plugin-svelte": "^5.0.0",
26 | "autoprefixer": "^10.4.20",
27 | "eslint": "^9.7.0",
28 | "eslint-config-prettier": "^9.1.0",
29 | "eslint-plugin-svelte": "^2.36.0",
30 | "globals": "^15.0.0",
31 | "prettier": "^3.3.2",
32 | "prettier-plugin-svelte": "^3.2.6",
33 | "prettier-plugin-tailwindcss": "^0.6.5",
34 | "svelte": "^5.0.0",
35 | "svelte-check": "^4.0.0",
36 | "tailwindcss": "^3.4.9",
37 | "typescript": "^5.0.0",
38 | "typescript-eslint": "^8.0.0",
39 | "vite": "^6.0.0",
40 | "vitest": "^2.0.4",
41 | "wrangler": "^3.99.0"
42 | },
43 | "dependencies": {
44 | "@tailwindcss/container-queries": "^0.1.1",
45 | "@tailwindcss/typography": "^0.5.15",
46 | "chart.js": "^4.4.7"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {}
5 | }
6 | };
7 |
--------------------------------------------------------------------------------
/src/app.css:
--------------------------------------------------------------------------------
1 | @import 'tailwindcss/base';
2 | @import 'tailwindcss/components';
3 | @import 'tailwindcss/utilities';
4 |
--------------------------------------------------------------------------------
/src/app.d.ts:
--------------------------------------------------------------------------------
1 | // See https://svelte.dev/docs/kit/types#app.d.ts
2 | // for information about these interfaces
3 | declare global {
4 | namespace App {
5 | // interface Error {}
6 | // interface Locals {}
7 | // interface PageData {}
8 | // interface PageState {}
9 | // interface Platform {}
10 | }
11 | }
12 |
13 | export {};
14 |
--------------------------------------------------------------------------------
/src/app.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | %sveltekit.head%
8 |
9 |
10 | %sveltekit.body%
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/lib/AtCoderContestResult.ts:
--------------------------------------------------------------------------------
1 | export type AtCoderContestResult = {
2 | isRated: boolean;
3 | place: number;
4 | oldRating: number;
5 | newRating: number;
6 | performance: number;
7 | innerPerformance: number;
8 | contestScreenName: string;
9 | contestName: string;
10 | contestNameEn: string;
11 | endTime: string;
12 | };
13 |
--------------------------------------------------------------------------------
/src/lib/calculateRating.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, expect, it } from 'vitest';
2 | import { calculateAlgorithmRating, calculateHeuristicRating } from './calculateRating';
3 |
4 | describe('calculateAlgorithmRating', () => {
5 | it("should return the correct rating for tourist's performances", () => {
6 | // https://atcoder.jp/users/tourist/history
7 | const contestResults = [
8 | { performance: 3920 },
9 | { performance: 3200 },
10 | { performance: 4310 },
11 | { performance: 4391 },
12 | { performance: 4356 },
13 | { performance: 4017 },
14 | { performance: 3687 },
15 | { performance: 4152 },
16 | { performance: 4395 },
17 | { performance: 4415 }
18 | ];
19 | const expectedRatings = [2720, 2851, 3368, 3647, 3802, 3834, 3806, 3860, 3949, 4021];
20 |
21 | for (const i of Array(contestResults.length).keys()) {
22 | expect(calculateAlgorithmRating(contestResults.slice(0, i + 1))).toBeCloseTo(
23 | expectedRatings[i],
24 | 0
25 | );
26 | }
27 | });
28 | it("should return the correct rating for chokudai's performances", () => {
29 | // https://atcoder.jp/users/chokudai/history
30 | const contestResults = [
31 | { performance: 2455 },
32 | { performance: 3192 },
33 | { performance: 3623 },
34 | { performance: 2974 },
35 | { performance: 2813 },
36 | { performance: 3002 },
37 | { performance: 3311 },
38 | { performance: 2998 },
39 | { performance: 2898 },
40 | { performance: 3457 }
41 | ];
42 | const expectedRatings = [1255, 2155, 2676, 2728, 2733, 2775, 2872, 2887, 2885, 2978];
43 |
44 | for (const i of Array(contestResults.length).keys()) {
45 | expect(calculateAlgorithmRating(contestResults.slice(0, i + 1))).toBeCloseTo(
46 | expectedRatings[i],
47 | 0
48 | );
49 | }
50 | });
51 | });
52 |
53 | describe('calculateHeuristicRating', () => {
54 | it("should return the correct rating for tourist's performances", () => {
55 | // https://atcoder.jp/users/tourist/history
56 |
57 | const contestResults = [
58 | { performance: 3254 },
59 | { performance: 2119 },
60 | { performance: 2471 },
61 | { performance: 1297 },
62 | { performance: 1446 }
63 | ];
64 | const expectedRatings = [2254, 2299, 2382, 2382, 2383];
65 |
66 | for (const i of Array(contestResults.length).keys()) {
67 | expect(calculateHeuristicRating(contestResults.slice(0, i + 1))).toBeCloseTo(
68 | expectedRatings[i],
69 | 0
70 | );
71 | }
72 | });
73 | it("should return the correct rating for chokudai's performances", () => {
74 | // https://atcoder.jp/users/chokudai/history
75 | const contestResults = [
76 | { performance: 1338 },
77 | { performance: 2399 },
78 | { performance: 2531 },
79 | { performance: 2789 },
80 | { performance: 2269 }
81 | ];
82 | const expectedRatings = [343, 1453, 1850, 2160, 2215];
83 |
84 | for (const i of Array(contestResults.length).keys()) {
85 | expect(calculateHeuristicRating(contestResults.slice(0, i + 1))).toBeCloseTo(
86 | expectedRatings[i],
87 | 0
88 | );
89 | }
90 | });
91 | });
92 |
--------------------------------------------------------------------------------
/src/lib/calculateRating.ts:
--------------------------------------------------------------------------------
1 | import type { AtCoderContestResult } from './AtCoderContestResult';
2 |
3 | export const applyRatingCorrection = (rating: number): number => {
4 | if (rating < 400) {
5 | return 400 / Math.exp((400 - rating) / 400);
6 | }
7 | return rating;
8 | };
9 |
10 | export const inverseRatingCorrection = (correctedRating: number): number => {
11 | if (correctedRating >= 400) {
12 | return correctedRating;
13 | }
14 | return 400 - 400 * Math.log(400 / correctedRating);
15 | };
16 |
17 | type ContestResult = Pick;
18 |
19 | export const calculateAlgorithmRating = (contestResults: ContestResult[]): number => {
20 | if (contestResults.length === 0) {
21 | throw new Error('performances is empty');
22 | }
23 | const f = (n: number) => {
24 | const F = (n: number) => {
25 | let numerator = 0;
26 | let denominator = 0;
27 | for (let i = 1; i <= n; i++) {
28 | numerator += Math.pow(0.81, i);
29 | denominator += Math.pow(0.9, i);
30 | }
31 | return Math.sqrt(numerator) / denominator;
32 | };
33 | const FInf = Math.sqrt(0.81 / (1 - 0.81)) / (0.9 / (1 - 0.9));
34 | return ((F(n) - FInf) / (F(1) - FInf)) * 1200;
35 | };
36 | const g = (x: number) => Math.pow(2, x / 800);
37 | const gInv = (y: number) => 800 * Math.log2(y);
38 |
39 | let weightedGSum = 0;
40 | let weightSum = 0;
41 | const performances = contestResults.map((result) => result.performance).reverse();
42 | for (let i = 0; i < performances.length; i++) {
43 | const weight = Math.pow(0.9, i);
44 | weightedGSum += g(performances[i]) * weight;
45 | weightSum += weight;
46 | }
47 |
48 | let rating = gInv(weightedGSum / weightSum) - f(performances.length);
49 | rating = applyRatingCorrection(rating);
50 | return rating;
51 | };
52 |
53 | export const calculateHeuristicRating = (contestResults: ContestResult[]): number => {
54 | const S = 724.4744301;
55 | const R = 0.8271973364;
56 |
57 | const extendedPerformances = contestResults
58 | .map((result) => result.performance)
59 | .flatMap((p) => Array.from({ length: 100 }, (_, j) => p - S * Math.log(j + 1)))
60 | .sort((a, b) => b - a);
61 |
62 | let ratingDenominator = 0;
63 | let ratingNumerator = 0;
64 |
65 | for (let i = 0; i < 100; i++) {
66 | const power = R ** (i + 1);
67 | ratingDenominator += extendedPerformances[i] * power;
68 | ratingNumerator += power;
69 | }
70 |
71 | let rating = ratingDenominator / ratingNumerator;
72 | rating = applyRatingCorrection(rating);
73 | return rating;
74 | };
75 |
--------------------------------------------------------------------------------
/src/lib/getRatingColor.ts:
--------------------------------------------------------------------------------
1 | export const getRatingColor = (rating: number): [number, number, number] => {
2 | if (rating < 400) return [128, 128, 128];
3 | else if (rating < 800) return [128, 64, 0];
4 | else if (rating < 1200) return [0, 128, 0];
5 | else if (rating < 1600) return [0, 192, 192];
6 | else if (rating < 2000) return [0, 0, 255];
7 | else if (rating < 2400) return [192, 192, 0];
8 | else if (rating < 2800) return [255, 128, 0];
9 | return [255, 0, 0];
10 | };
11 |
--------------------------------------------------------------------------------
/src/lib/index.ts:
--------------------------------------------------------------------------------
1 | // place files you want to import through the `$lib` alias in this folder.
2 |
--------------------------------------------------------------------------------
/src/lib/server/fetchAtCoderContestResults.ts:
--------------------------------------------------------------------------------
1 | import type { AtCoderContestResult } from '$lib/AtCoderContestResult';
2 |
3 | export type ContestType = 'algorithm' | 'heuristic';
4 |
5 | type RawAtCoderContestResult = {
6 | IsRated: boolean;
7 | Place: number;
8 | OldRating: number;
9 | NewRating: number;
10 | Performance: number;
11 | InnerPerformance: number;
12 | ContestScreenName: string;
13 | ContestName: string;
14 | ContestNameEn: string;
15 | EndTime: string;
16 | };
17 |
18 | export const fetchAtCoderContestResults = async (
19 | userId: string,
20 | contestType: ContestType
21 | ): Promise => {
22 | // NOTE: Using proxy to bypass AtCoder's Web Application Firewall.
23 | //const url = `https://atcoder.jp/users/${userId}/history/json?contestType=${contestType === 'algorithm' ? 'algo' : 'heuristic'}`;
24 | const url = `https://script.google.com/macros/s/AKfycby-CKUsGoe7YZBeIf9FMwvCK6JIfqnVkN-8764iNkRApGPns158Q6vJPxpDdnGfsDXU/exec?type=${contestType === 'algorithm' ? 'algo' : 'heuristic'}&id=${userId}`;
25 | const response = await fetch(url);
26 | const contestResults = await response.json();
27 |
28 | return contestResults.map((contestResult) => {
29 | return {
30 | isRated: contestResult.IsRated,
31 | place: contestResult.Place,
32 | oldRating: contestResult.OldRating,
33 | newRating: contestResult.NewRating,
34 | performance: contestResult.Performance,
35 | innerPerformance: contestResult.InnerPerformance,
36 | contestScreenName: contestResult.ContestScreenName,
37 | contestName: contestResult.ContestName,
38 | contestNameEn: contestResult.ContestNameEn,
39 | endTime: contestResult.EndTime
40 | };
41 | });
42 | };
43 |
--------------------------------------------------------------------------------
/src/routes/+layout.svelte:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 | {@render children()}
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/routes/+page.svelte:
--------------------------------------------------------------------------------
1 |
198 |
199 |
200 | AtCoder Rating Estimator
201 |
205 |
206 |
207 |
208 |
209 | AtCoder Rating Estimator
210 |
211 | AtCoderのRatedコンテストで目標レーティングへの到達に必要なパフォーマンスを計算しグラフで表示します。
212 |
213 |
214 |
247 |
248 | 過去のパフォーマンス
249 |
250 |
258 |
259 |
260 |
261 |
267 |
268 |
269 | 改行区切りで入力してください。パフォーマンスの値はマイナス補正適用後の値を入力してください。
270 |
271 |
272 |
277 |
278 |
279 |
285 |
286 |
287 |
288 | 現在のレーティング
289 | {rating || '未参加'}
290 |
291 |
292 | 次回のレーティング
293 |
294 |
295 |
296 | Twitterでつぶやく
297 |
298 | -
299 | Tweet
304 |
305 |
306 |
307 |
308 | View on GitHub
309 |
310 |
316 |
317 |
318 |
319 | Bibliography
320 |
330 |
331 |
332 |
--------------------------------------------------------------------------------
/src/routes/api/[userId]/[contestType]/+server.ts:
--------------------------------------------------------------------------------
1 | import { fetchAtCoderContestResults } from '$lib/server/fetchAtCoderContestResults';
2 | import { json } from '@sveltejs/kit';
3 |
4 | export const GET = async ({ params }) => {
5 | if (params.contestType !== 'algorithm' && params.contestType !== 'heuristic') {
6 | return json([], { status: 400 });
7 | }
8 |
9 | const contestResults = await fetchAtCoderContestResults(params.userId, params.contestType);
10 |
11 | return json(contestResults);
12 | };
13 |
--------------------------------------------------------------------------------
/static/favicon.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/svelte.config.js:
--------------------------------------------------------------------------------
1 | import adapter from '@sveltejs/adapter-cloudflare';
2 | import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
3 |
4 | /** @type {import('@sveltejs/kit').Config} */
5 | const config = {
6 | // Consult https://svelte.dev/docs/kit/integrations
7 | // for more information about preprocessors
8 | preprocess: vitePreprocess(),
9 |
10 | kit: {
11 | // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
12 | // If your environment is not supported, or you settled on a specific environment, switch out the adapter.
13 | // See https://svelte.dev/docs/kit/adapters for more information about adapters.
14 | adapter: adapter()
15 | }
16 | };
17 |
18 | export default config;
19 |
--------------------------------------------------------------------------------
/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import containerQueries from '@tailwindcss/container-queries';
2 | import typography from '@tailwindcss/typography';
3 | import type { Config } from 'tailwindcss';
4 |
5 | export default {
6 | content: ['./src/**/*.{html,js,svelte,ts}'],
7 |
8 | theme: {
9 | extend: {}
10 | },
11 |
12 | plugins: [typography, containerQueries]
13 | } satisfies Config;
14 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./.svelte-kit/tsconfig.json",
3 | "compilerOptions": {
4 | "allowJs": true,
5 | "checkJs": true,
6 | "esModuleInterop": true,
7 | "forceConsistentCasingInFileNames": true,
8 | "resolveJsonModule": true,
9 | "skipLibCheck": true,
10 | "sourceMap": true,
11 | "strict": true,
12 | "moduleResolution": "bundler",
13 | "types": ["@cloudflare/workers-types/2023-07-01"]
14 | }
15 | // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
16 | // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
17 | //
18 | // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
19 | // from the referenced tsconfig.json - TypeScript does not merge them in
20 | }
21 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vitest/config';
2 | import { sveltekit } from '@sveltejs/kit/vite';
3 |
4 | export default defineConfig({
5 | // @ts-expect-error https://github.com/sveltejs/kit/issues/13102#issuecomment-2515203461
6 | plugins: [sveltekit()],
7 |
8 | test: {
9 | include: ['src/**/*.{test,spec}.{js,ts}']
10 | }
11 | });
12 |
--------------------------------------------------------------------------------
/wrangler.toml:
--------------------------------------------------------------------------------
1 | #:schema node_modules/wrangler/config-schema.json
2 | name = "atcoder-rating-estimator"
3 | compatibility_date = "2024-12-24"
4 | pages_build_output_dir = ".svelte-kit/cloudflare"
5 |
--------------------------------------------------------------------------------