Compare commits

..

2 Commits

Author SHA1 Message Date
Altair-sh 54ddc11b44 removed "hack" font 2026-09-22 19:32:01 +02:00
Altair-sh 5da5fd2c7e updaded typescript to 7 and fixed few bugs 2026-09-22 19:31:51 +02:00
20 changed files with 1040 additions and 522 deletions
+3
View File
@@ -40,3 +40,6 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts
# ai agent settings
.claude
+1
View File
@@ -0,0 +1 @@
package-lock.json
+1 -1
View File
@@ -2,4 +2,4 @@
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnPaste": true,
"editor.formatOnSave": true
}
}
+99 -5
View File
@@ -9,7 +9,8 @@ backend service, polls until parsing finishes, and displays the result in a
grid.
- **Next.js 16** (App Router, Turbopack) with **React 19**
- **TypeScript 6**, **ESLint 9** (flat config), **Prettier 3**
- **TypeScript 6 and 7** side by side (see "Things that will bite you"),
**ESLint 9** (flat config), **Prettier 3**
- **ag-grid** for the data grid, **Bootstrap 5** for styling
Source files live in `src/`:
@@ -21,6 +22,50 @@ Source files live in `src/`:
- `config/` for app config
- `types/` for .d.ts type declaration files
## Architecture
The parsing backend is a **separate service in its own repository**, not part
of this project. Start it manually before running the frontend; it is expected
at `http://127.0.0.1:5226`.
The browser never calls that backend directly. Requests go through a Next route
handler that forwards them:
```
SaveParserBackendService -> /api/proxy/saveParserBackend/* -> backend
(client, fetch) (server, route handler)
```
- [src/services/SaveParserBackendService.ts](src/services/SaveParserBackendService.ts)
is the only place that talks to the backend. It exposes `uploadSave`,
`getSaveStatus` and `getParsedSave` — upload, poll until parsing finishes,
then fetch the result.
- [src/app/api/proxy/saveParserBackend/[[...path]]/route.ts](src/app/api/proxy/saveParserBackend/[[...path]]/route.ts)
forwards every method to the configured backend URL. It exists only to keep
requests same-origin, and is meant to be deleted once the backend handles
CORS itself.
- Route paths live in [src/app/api/proxy/paths.ts](src/app/api/proxy/paths.ts)
rather than as string literals, so both sides of the proxy stay in sync.
Configuration comes from [src/config/AppConfig.ts](src/config/AppConfig.ts)
(defaults) merged with `runtimeConfig.json` at the repo root, which holds the
backend URL. That file is gitignored, so a fresh clone starts without one and
falls back to the defaults.
### Current status
Much of the app is still scaffold. Do not assume a feature works because the UI
for it exists:
- **The grid is not wired up.** [src/app/browse/page.tsx](src/app/browse/page.tsx)
renders two hardcoded rows and never calls the backend. Nothing connects the
upload flow to it yet.
- **Auth does not work.** `AuthService` posts to `/api/auth/login` and
`/api/auth/register`, which do not exist as routes. The service is slated for
removal in favour of Discord OAuth.
- **`hashPassword` is a no-op** that returns the password unchanged. Treat any
current auth code as a placeholder, not as something to build on.
## Terminal
**On Windows, prefer `bash.exe`** — but only a real MSYS2, Cygwin, or
@@ -43,7 +88,7 @@ problem rather than a missing install: MSYS2 defaults `MSYS2_PATH_TYPE` to
npm run dev # dev server
npm run build # production build
npm run lint # eslint
npm run format # prettier, rewrites files in src/
npm run format # prettier, rewrites everything except .gitignore/.prettierignore
npx tsc --noEmit # typecheck
```
@@ -108,9 +153,58 @@ Avoid `any`; `no-explicit-any` is enforced. Prefer `unknown` plus narrowing.
## Things that will bite you
**TypeScript is pinned to 6 on purpose.** Do not upgrade it to 7. The Next.js
ESLint config depends on typescript-eslint, which refuses to run under
TypeScript 7, and that would break `npm run lint` entirely.
**Both TypeScript 6 and 7 are installed on purpose, under swapped names.**
TypeScript 7 is the native Go compiler and type-checks much faster, but
typescript-eslint refuses to run under it, which would break `npm run lint`
entirely. So each tool gets the version it can use:
| package.json entry | resolves to | used by |
| -------------------- | ------------------------------------------------ | ----------------- |
| `typescript` | `@typescript/typescript6` (TS 6 API, bin `tsc6`) | typescript-eslint |
| `@typescript/native` | `typescript@7` (Go compiler) | `next build` |
The alias is deliberately reversed: typescript-eslint looks the compiler up by
the package _name_ `typescript`, so that name must stay on TS 6. Do not "fix"
this by renaming the entries.
`next.config.ts` sets `experimental.useTypeScriptCli`, which makes Next spawn a
`tsc` binary instead of using the compiler API. Next resolves that binary from
the package named `typescript` too, so it would also get TS 6. The `prebuild`
script [scripts/replace-tsc6-with-tsc7.mjs](scripts/replace-tsc6-with-tsc7.mjs)
works around this by rewriting `node_modules/typescript/bin/tsc6` into a
one-line stub that imports TS 7's `lib/tsc.js`.
Consequences to keep in mind:
- The patch lives in `node_modules`, so `npm install` undoes it. `prebuild`
reapplies it, so `npm run build` is always correct; nothing else needs doing.
- `npx tsc --noEmit` is unaffected either way. `node_modules/.bin/tsc` links
straight to `@typescript/native`, so the standalone typecheck is always TS 7.
- Do not drop the `prebuild` script while leaving `useTypeScriptCli` enabled.
Nothing breaks visibly — the build just silently goes back to type-checking
with TS 6.
**All of this is temporary.** The only reason TS 6 is still here is that
TypeScript 7.0 ships no stable programmatic JS API, so typescript-eslint cannot
run on it. That API arrives in **TypeScript 7.1, planned for 2026-11-24**. Once
it is out and typescript-eslint supports it, tear the whole arrangement down:
drop `@typescript/native` and the `@typescript/typescript6` alias for a plain
`"typescript": "^7.1"`, delete `scripts/replace-tsc6-with-tsc7.mjs` and the
`prebuild` script, and re-check whether `useTypeScriptCli` is still worth
keeping.
**Env vars in npm scripts must go through `cross-env`.** The `dev` and `build`
scripts set `NEXT_TELEMETRY_DISABLED=1`, which stops Next from collecting
anonymous usage data and saves the ~0.5s the telemetry flush adds to every
build. The `FOO=1 cmd` prefix is POSIX shell syntax, and on Windows npm runs
scripts through `cmd`, where it is a syntax error — so the prefix is written as
`cross-env NEXT_TELEMETRY_DISABLED=1 next ...` instead. Keep `cross-env` on any
new script that needs an env var.
Setting the variable from `prebuild` would not work: `prebuild` and `build` are
sibling processes, so an env var exported by one is not visible to the other.
Each script that needs it sets it itself. `prebuild` has no `cross-env` prefix
because it only rewrites the tsc bin and never talks to telemetry.
**`instrumentation.ts` is compiled for the edge runtime too and it can't be disabled.**
Anything using `fs`, `path`, or other Node APIs must be imported behind the
+6 -4
View File
@@ -1,7 +1,9 @@
import type { NextConfig } from "next";
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
/* config options here */
};
experimental: {
useTypeScriptCli: true,
},
}
export default nextConfig;
export default nextConfig
+873 -469
View File
File diff suppressed because it is too large Load Diff
+8 -4
View File
@@ -3,13 +3,15 @@
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev --turbopack",
"build": "next build",
"dev": "cross-env NEXT_TELEMETRY_DISABLED=1 next dev --turbopack",
"prebuild": "node scripts/replace-tsc6-with-tsc7.mjs",
"build": "cross-env NEXT_TELEMETRY_DISABLED=1 next build",
"start": "next start",
"lint": "eslint",
"format": "npx prettier src --write"
"format": "npx prettier --write ."
},
"dependencies": {
"ag-grid-community": "^36",
"ag-grid-react": "^36",
"bootstrap": "^5",
"next": "^16",
@@ -20,10 +22,12 @@
"@types/node": "^26",
"@types/react": "^19",
"@types/react-dom": "^19",
"@typescript/native": "npm:typescript@^7.0",
"cross-env": "^10.1.0",
"eslint": "^9",
"eslint-config-next": "^16",
"prettier": "^3",
"typescript": "^6"
"typescript": "npm:@typescript/typescript6@^6.0"
},
"allowScripts": {
"unrs-resolver": false
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+32
View File
@@ -0,0 +1,32 @@
/**
Replaces tsc6 script which is called by next.js to run tsc 7,
which is a native executable written in go and works faster.
The problem is typescript 7 is supported with nextjs,
but is not supported by eslint which we use for static code analysis.
This script solves the problem.
*/
import { createRequire } from 'node:module'
import { writeFileSync } from 'node:fs'
import path from 'node:path'
const require = createRequire(import.meta.url)
const tsc6 = path.join(
path.dirname(require.resolve('typescript/package.json')),
'bin',
'tsc6'
)
const nativeTsc = path.join(
path.dirname(require.resolve('@typescript/native/package.json')),
'lib',
'tsc.js'
)
const specifier = path
.relative(path.dirname(tsc6), nativeTsc)
.split(path.sep)
.join('/')
writeFileSync(tsc6, `#!/usr/bin/env node\nimport('${specifier}')\n`)
console.log(`patched ${tsc6} -> ${nativeTsc}`)
@@ -4,6 +4,10 @@ import { redirectRequest, respondToOPTIONS } from '@/app/api/proxy/functions'
import { PROXY_PATHS } from '@/app/api/proxy/paths'
import { appConfig } from '@/lib/configLoader'
//TODO: remove this proxy once the backend handles CORS itself. It only exists
// so the browser can make same-origin requests; with CORS on the backend the
// client can call it directly and this extra hop disappears.
// Forwards the incoming request to the save parser backend.
async function redirect(req: NextRequest): Promise<NextResponse> {
return await redirectRequest(
+4
View File
@@ -14,6 +14,7 @@ const DARK_SCHEME_QUERY = '(prefers-color-scheme: dark)'
const BASE_THEME = themeQuartz.withParams({
columnBorder: true,
headerColumnBorder: true,
headerCellBackgroundTransitionDuration: 0,
})
// Built once, so the grid keeps seeing the same theme object between renders.
const LIGHT_THEME = BASE_THEME
@@ -53,6 +54,9 @@ export default function BrowseSaveDataPage() {
<AgGridReact
theme={agTheme}
modules={[AllCommunityModule]}
// ddisable all animations
animateRows={false}
suppressColumnMoveAnimation={true}
rowData={[
{ id: 1, col1: 'Hello', col2: 'AG Grid' },
{ id: 2, col1: 'Dark Mode', col2: 'Support' },
-1
View File
@@ -1,7 +1,6 @@
:root {
--background: #ffffff;
--foreground: #171717;
font-family: var(--font-ibm-plex-sans), monospace;
}
@media (prefers-color-scheme: dark) {
+2 -4
View File
@@ -1,8 +1,8 @@
import 'bootstrap/dist/css/bootstrap.min.css'
import './globals.css'
import type { Metadata } from 'next'
import { ModuleRegistry, AllCommunityModule } from 'ag-grid-community'
import Navbar from '@/components/Navbar'
import { font_IbmPlexSans } from '@/lib/myFonts'
export const metadata: Metadata = {
title: 'Home',
@@ -12,14 +12,12 @@ export const metadata: Metadata = {
export default function RootLayout({
children,
}: Readonly<{ children: React.ReactNode }>) {
// Register the ag-grid community features
ModuleRegistry.registerModules([AllCommunityModule])
return (
<html lang="en">
<head>
<title>paradox-save-parser-frontend</title>
</head>
<body>
<body className={font_IbmPlexSans.className}>
<Navbar />
{children}
</body>
+1 -4
View File
@@ -1,14 +1,11 @@
'use client'
import Link from 'next/link'
import { font_Hack } from '@/lib/myFonts'
// Top navigation bar with a home link and login/register buttons.
export default function Navbar() {
return (
<nav
className={`navbar navbar-expand-lg navbar-dark bg-dark px-3 ${font_Hack.className}`}
>
<nav className={`navbar navbar-expand-lg navbar-dark bg-dark px-3`}>
<div className="container-fluid">
{/* Left: Home Link */}
<Link href="/" className="navbar-brand">
+2 -1
View File
@@ -3,6 +3,7 @@ import fs from 'fs'
import path from 'path'
import { AppConfig, defaultConfig } from '@/config/AppConfig'
// TODO: get rid of this file and read about configs for next.js. Maybe use .env file where variables NEXT_PUBLIC_* are avaliable from client.
const configFilePath = path.join(process.cwd(), 'runtimeConfig.json')
// Reads runtimeConfig.json (merging it over the defaults) and creates new config file if missing
@@ -18,7 +19,7 @@ function loadConfig(): AppConfig {
console.log(`creating default config file '${configFilePath}'`)
fs.mkdirSync(path.dirname(configFilePath), { recursive: true })
}
fs.writeFileSync(configFilePath, JSON.stringify(defaultConfig, null, 4))
fs.writeFileSync(configFilePath, JSON.stringify(config, null, 4))
return config
}
-29
View File
@@ -1,4 +1,3 @@
import localFont from 'next/font/local'
import { IBM_Plex_Sans } from 'next/font/google'
// Loads the Google-hosted IBM Plex Sans font.
@@ -9,31 +8,3 @@ export const font_IbmPlexSans = IBM_Plex_Sans({
display: 'swap',
variable: '--font-ibm-plex-sans',
})
// Loads the local Hack font used by the navbar.
export const font_Hack = localFont({
src: [
{
path: '../../public/fonts/hack-v3.003/Hack-Regular.ttf',
weight: '400',
style: 'normal',
},
{
path: '../../public/fonts/hack-v3.003/Hack-Bold.ttf',
weight: '700',
style: 'normal',
},
{
path: '../../public/fonts/hack-v3.003/Hack-Italic.ttf',
weight: '400',
style: 'italic',
},
{
path: '../../public/fonts/hack-v3.003/Hack-BoldItalic.ttf',
weight: '700',
style: 'italic',
},
],
display: 'swap',
variable: '--font-hack',
})
+4
View File
@@ -1,3 +1,7 @@
//TODO: replace this whole service with Discord OAuth. The target audience
// already uses Discord, so email/password login is not worth building. The
// /api/auth/login and /api/auth/register routes below are never implemented —
// delete them along with this service.
//TODO: controllable rate limits
class AuthService {