feat: Projektgeruest und Spec-Grundlagen
This commit is contained in:
+14
@@ -0,0 +1,14 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||||
|
<base href="./" />
|
||||||
|
<title>RAM-Sortierhilfe</title>
|
||||||
|
<link rel="stylesheet" href="src/styles.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main id="app"></main>
|
||||||
|
<script type="module" src="src/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Generated
+1077
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"name": "ram-sortierhilfe",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "> Short description of what this project does.",
|
||||||
|
"main": "index.js",
|
||||||
|
"directories": {
|
||||||
|
"doc": "docs"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "node --test test/",
|
||||||
|
"dev": "vite --host 0.0.0.0",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview --host 0.0.0.0 --port ${PORT:-4173}"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"type": "module",
|
||||||
|
"private": "true",
|
||||||
|
"devDependencies": {
|
||||||
|
"vite": "^8.1.5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tesseract.js": "^7.0.0",
|
||||||
|
"zxing-wasm": "^3.1.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
document.querySelector('#app').textContent = 'RAM-Sortierhilfe';
|
||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
// Reines Modul: kein window, kein document, kein localStorage.
|
||||||
|
|
||||||
|
/** Bekannte Werte. Der Erwartungsraum ist klein - das repariert OCR-Lesefehler. */
|
||||||
|
export const KNOWN = {
|
||||||
|
capacityGb: [4, 8, 16, 32, 64, 128, 256],
|
||||||
|
speed: [
|
||||||
|
'PC4-1600', 'PC4-1866', 'PC4-2133', 'PC4-2400',
|
||||||
|
'PC4-2666', 'PC4-2933', 'PC4-3200',
|
||||||
|
],
|
||||||
|
formFactor: ['UDIMM', 'SODIMM', 'RDIMM', 'LRDIMM'],
|
||||||
|
rank: ['1Rx8', '1Rx4', '2Rx8', '2Rx4', '4Rx4', '8Rx4', '2DRx4', '4DRx4', '2DRx8'],
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @returns {{capacityGb: null, formFactor: null, rank: null, speed: null, partNumber: null, dateCode: null}} */
|
||||||
|
export function emptySpec() {
|
||||||
|
return {
|
||||||
|
capacityGb: null,
|
||||||
|
formFactor: null,
|
||||||
|
rank: null,
|
||||||
|
speed: null,
|
||||||
|
partNumber: null,
|
||||||
|
dateCode: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Grossbuchstaben, ohne Rand-Leerzeichen, mit vereinheitlichten Bindestrichen. */
|
||||||
|
export function normalizeToken(raw) {
|
||||||
|
if (typeof raw !== 'string') return '';
|
||||||
|
return raw
|
||||||
|
.replace(/[‐-―−]/g, '-')
|
||||||
|
.trim()
|
||||||
|
.toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merkmalskombination, die ueber die Stapelzugehoerigkeit entscheidet.
|
||||||
|
* Der Datumscode gehoert bewusst nicht dazu.
|
||||||
|
*/
|
||||||
|
export function fingerprint(spec) {
|
||||||
|
const parts = [
|
||||||
|
spec.formFactor,
|
||||||
|
spec.capacityGb,
|
||||||
|
spec.rank,
|
||||||
|
spec.speed,
|
||||||
|
spec.partNumber,
|
||||||
|
];
|
||||||
|
return parts
|
||||||
|
.map((value) => (value === null || value === undefined ? '?' : normalizeToken(String(value))))
|
||||||
|
.join('|');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Ohne Kapazitaet ist keine sinnvolle Zuordnung moeglich. */
|
||||||
|
export function isUsable(spec) {
|
||||||
|
return typeof spec.capacityGb === 'number' && Number.isFinite(spec.capacityGb);
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
:root { color-scheme: dark; }
|
||||||
|
body { margin: 0; font-family: system-ui, sans-serif; }
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import { test } from 'node:test';
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import { KNOWN, emptySpec, normalizeToken, fingerprint, isUsable } from '../src/spec.js';
|
||||||
|
|
||||||
|
test('KNOWN enthaelt die erwarteten Wertelisten', () => {
|
||||||
|
assert.ok(KNOWN.capacityGb.includes(64));
|
||||||
|
assert.ok(KNOWN.speed.includes('PC4-2400'));
|
||||||
|
assert.ok(KNOWN.formFactor.includes('LRDIMM'));
|
||||||
|
assert.ok(KNOWN.rank.includes('4DRx4'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('emptySpec liefert alle Felder als null', () => {
|
||||||
|
assert.deepEqual(emptySpec(), {
|
||||||
|
capacityGb: null,
|
||||||
|
formFactor: null,
|
||||||
|
rank: null,
|
||||||
|
speed: null,
|
||||||
|
partNumber: null,
|
||||||
|
dateCode: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('normalizeToken macht gross, trimmt und vereinheitlicht Bindestriche', () => {
|
||||||
|
assert.equal(normalizeToken(' pc4‑2400 '), 'PC4-2400');
|
||||||
|
assert.equal(normalizeToken('4drx4'), '4DRX4');
|
||||||
|
assert.equal(normalizeToken(''), '');
|
||||||
|
assert.equal(normalizeToken(null), '');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('fingerprint setzt die Felder in fester Reihenfolge zusammen', () => {
|
||||||
|
const spec = {
|
||||||
|
capacityGb: 64,
|
||||||
|
formFactor: 'LRDIMM',
|
||||||
|
rank: '4DRx4',
|
||||||
|
speed: 'PC4-2400',
|
||||||
|
partNumber: 'M386A8K40BM1-CRC4Y',
|
||||||
|
dateCode: '1908',
|
||||||
|
};
|
||||||
|
assert.equal(fingerprint(spec), 'LRDIMM|64|4DRX4|PC4-2400|M386A8K40BM1-CRC4Y');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('fingerprint laesst den Datumscode aussen vor', () => {
|
||||||
|
const a = { capacityGb: 64, formFactor: 'LRDIMM', rank: '4DRx4', speed: 'PC4-2400', partNumber: 'X', dateCode: '1908' };
|
||||||
|
const b = { ...a, dateCode: '2013' };
|
||||||
|
assert.equal(fingerprint(a), fingerprint(b));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('fingerprint markiert fehlende Felder mit einem Fragezeichen', () => {
|
||||||
|
const spec = { ...emptySpec(), capacityGb: 32 };
|
||||||
|
assert.equal(fingerprint(spec), '?|32|?|?|?');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('isUsable verlangt eine Kapazitaet', () => {
|
||||||
|
assert.equal(isUsable({ ...emptySpec(), capacityGb: 64 }), true);
|
||||||
|
assert.equal(isUsable(emptySpec()), false);
|
||||||
|
});
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { defineConfig } from 'vite';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
// Relative Basis, damit die App auch hinter einem Pfad-Proxy laeuft.
|
||||||
|
base: './',
|
||||||
|
server: { host: '0.0.0.0' },
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user