faeb7521ae
Build Kvitt images / build (push) Failing after 2h19m45s
SPEC §15.6 W-21 to W-23, §9.12 CP-1 to CP-14, FR-22 (D-174, D-180): - api/cards.ts: the card snapshot read through E-40 (pulled from 0, then from its cursor on focus, every 60 s and after every card write; cursor_ahead restarts), E-44 sent at once; shared because cards, expenses, accounts and settings read it. - features/cards: W-21 Kortköp (header with artwork, Uppdatera via E-42, filters with counts, day groups, Preliminär/Insättning/Utländsk valuta, the CP-10 match chip, the purchase as a side panel from 720 px and a dialog below with every S-23 sheet action, expiring/expired/fetch-failed/first-fetch/empty states, ?attempt= polling E-38 every 2 s, ?account= as S-27); W-22 catalogue and connect step (history 7/30/90/180, E-37 with client web, the bank's https page in the same tab); W-23 the connection (E-41 shorter history, renew, E-43 with erase_handled). The committed SJ Prio artwork mapped from image key sj_prio_mastercard, the D-173 drawn card for unknown keys. - lib/domain/matchCardPurchase (§14.5.8) with its cases; vectors.test.ts runs K1's match_card_purchase.json against it (moved from NOT_PORTED to PORTED). - W-5 ?cardPurchase= prefill per CP-9 and the link on save; W-6 card line with Ta bort koppling; W-3 "Från kortköp" while connected; W-13 "Anslut kort" row. - Sidebar and bottom bar: Kortköp after Översikt (D-172); icons credit_card, link_off, visibility_off added to the generated table; /cards in the dev proxy. - test/cards.ts fixtures and the fake server's card endpoints. Strings: the §13.3 keys from K2's docs/ux/strings.csv (dictionaries regenerated), plus K2's cards_foreign_amount (the foreign amount) and settings_cards_expired (W-13 row of an ended connection); web-only texts as web_cards_ and web_expenses_ keys. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
103 lines
3.7 KiB
TypeScript
103 lines
3.7 KiB
TypeScript
import { execFileSync } from 'node:child_process';
|
|
import { readFileSync } from 'node:fs';
|
|
import { fileURLToPath } from 'node:url';
|
|
import react from '@vitejs/plugin-react';
|
|
import { defineConfig } from 'vitest/config';
|
|
|
|
interface PackageJson {
|
|
version: string;
|
|
}
|
|
|
|
const pkg = JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf-8')) as PackageJson;
|
|
|
|
/**
|
|
* `X-Kvitt-App-Version` (SPEC §14.3.1): the commit count of the checkout at build time (`git rev-list --count HEAD`,
|
|
* as the app's release builds count). Without git or `.git` (the image's Node stage), `KVITT_VERSION_CODE` when set,
|
|
* else 1.
|
|
*/
|
|
function versionCode(): number {
|
|
const counted = gitCommitCount();
|
|
if (counted !== null) return counted;
|
|
const fromEnv = process.env.KVITT_VERSION_CODE;
|
|
return fromEnv !== undefined && /^[1-9]\d*$/.test(fromEnv) ? Number(fromEnv) : 1;
|
|
}
|
|
|
|
function gitCommitCount(): number | null {
|
|
try {
|
|
const count = execFileSync('git', ['rev-list', '--count', 'HEAD'], {
|
|
cwd: fileURLToPath(new URL('.', import.meta.url)),
|
|
encoding: 'utf-8',
|
|
stdio: ['ignore', 'pipe', 'ignore'],
|
|
}).trim();
|
|
return /^[1-9]\d*$/.test(count) ? Number(count) : null;
|
|
} catch {
|
|
// No git binary, or no repository around web/.
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/** API paths of contracts/openapi.yaml; `npm run dev` forwards them to a local kvitt-server (README). */
|
|
const API_PREFIXES = [
|
|
'/health',
|
|
'/setup',
|
|
'/invites',
|
|
'/me',
|
|
'/users',
|
|
'/devices',
|
|
'/accounts',
|
|
'/categories',
|
|
'/recurring-templates',
|
|
'/sync',
|
|
'/settlements',
|
|
'/apk',
|
|
'/analytics',
|
|
'/cards',
|
|
];
|
|
const devApi = process.env.KVITT_DEV_API ?? 'http://localhost:8080';
|
|
|
|
export default defineConfig({
|
|
base: '/app/',
|
|
plugins: [react()],
|
|
define: {
|
|
__KVITT_VERSION_NAME__: JSON.stringify(pkg.version),
|
|
__KVITT_VERSION_CODE__: JSON.stringify(versionCode()),
|
|
},
|
|
server: {
|
|
// web/ itself, plus contracts/ for the one contract file the app imports at build time: W2's keyword table
|
|
// (SPEC §15.7, D-055). Vite refuses `?raw` imports outside these paths, in Vitest as well as the dev server, and
|
|
// matches the id with its query, so the entry must be the directory, not the file.
|
|
fs: { allow: ['.', '../contracts'] },
|
|
proxy: Object.fromEntries(API_PREFIXES.map((prefix) => [prefix, { target: devApi, changeOrigin: true }])),
|
|
},
|
|
build: {
|
|
outDir: 'dist',
|
|
sourcemap: false,
|
|
rolldownOptions: {
|
|
output: {
|
|
// Libraries in their own chunks: they change less often than the app, so browsers keep them across deploys.
|
|
codeSplitting: {
|
|
groups: [
|
|
{ name: 'react', test: /node_modules[\\/](react|react-dom|scheduler)[\\/]/ },
|
|
// Recharts and its dependencies, loaded only by the analytics pages (S-11, S-12, Insights), so the
|
|
// shared vendor chunk stays small (it was 516 kB with them).
|
|
{
|
|
name: 'charts',
|
|
test: /node_modules[\\/](recharts|victory-vendor|d3-[a-z-]+|internmap|@reduxjs|redux|redux-thunk|react-redux|reselect|immer|decimal\.js-light|es-toolkit|eventemitter3|tiny-invariant|clsx|use-sync-external-store)[\\/]/,
|
|
},
|
|
{ name: 'vendor', test: /node_modules[\\/]/ },
|
|
],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
test: {
|
|
environment: 'jsdom',
|
|
setupFiles: ['src/test/setup.ts'],
|
|
include: ['src/**/*.test.{ts,tsx}'],
|
|
css: { modules: { classNameStrategy: 'non-scoped' } },
|
|
// No file-system module cache (node_modules/.vitest-cache): it once served a stale `import.meta.glob` of
|
|
// catalog.ts (fixed in 83f5293), and the whole suite runs in seconds without it, so every run transforms afresh.
|
|
fsModuleCache: false,
|
|
},
|
|
});
|