Measured first: the new oracle SheetRun types 77 texts into FILIUS' own settings panels and records what was marked red, what the device took and what the field showed afterwards. Java's two judgements disagree (red text taken, black text refused, any int as a modem port, a refused text dropped without a word); here one rule decides both (packages/doc fields.ts), a refused text stays marked in the field, and only what Java's model accepts reaches the document. - doc: settings.ts (name, label policy, addresses, IP forwarding, DHCP stopping the own DHCP server, LAN/WLAN with the wireless connection, switch SSID dropping its wireless hosts, retention time, cloud, modem port as its pair), sheet.ts (what the sheet shows) - fls: modemPair/modemPort in one place - editor: DeviceSheet beside the surface or below it on a phone, opened with the selection; every change one undo step - not adopted from Java: the name overwritten by the MAC address, the access point search depending on unrelated devices Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
70 lines
3.5 KiB
TypeScript
70 lines
3.5 KiB
TypeScript
// The device sheet's rules for what may be typed into a field: Java's
|
|
// EingabenUeberpruefung as FILIUS' settings panels use it
|
|
// (JHostKonfiguration, JSwitchKonfiguration, JModemKonfiguration), measured
|
|
// with tools/java-oracle's SheetRun (test/sheet.java.json).
|
|
//
|
|
// Java judges a text twice: while it is typed, the panel colours it red when
|
|
// the pattern does not match; when the field loses the focus, the model's
|
|
// setter takes it or keeps the old value — and the two disagree. Java takes
|
|
// red text ("10..0.0.1", "+10.0.0.1", "-0.0.0.1", Arabic-Indic digits, a
|
|
// retention time of "-5"), refuses black text (".0.0.1", a retention time
|
|
// past Long's range), and a modem takes any int as its port (0, 65536, -1),
|
|
// unchecked. Here one rule decides both: a text is taken exactly when it is
|
|
// not marked. What is not taken is not thrown away either (Java puts the old
|
|
// value back into the field without a word): the sheet keeps it, marked,
|
|
// until it is mended (plan I6).
|
|
//
|
|
// Spaces around an address or a number are left out (Java refuses
|
|
// " 10.0.0.1"; a phone's keyboard puts a space after a word). A name and an
|
|
// SSID are taken as typed, spaces included, as Java takes them.
|
|
|
|
import { IP_ADDRESS_PATTERN, javaIpCheck } from '@mdg-filius/core';
|
|
|
|
export type FieldRule = 'name' | 'ipAddress' | 'ipAddressOrEmpty' | 'subnetMask' | 'ssid' | 'seconds' | 'port';
|
|
|
|
/** What a field's text comes to: the value the device takes, or that it takes none. */
|
|
export type CheckedField = { readonly value: string } | { readonly invalid: true };
|
|
|
|
/** EingabenUeberpruefung.musterSubNetz. */
|
|
const SUBNET_MASK_PATTERN = /^(0*([1-9]|1?[0-9]{1,2}|2[0-4]?[0-9]|25[0-5]))\.((0*([0-9]|1?[0-9]{1,2}|2[0-4]?[0-9]|25[0-5]))\.){2}(0*([0-9]|1?[0-9]{1,2}|2[0-4]?[0-9]|25[0-5]))$/;
|
|
/** EingabenUeberpruefung.musterServiceSetIdentifier: printable ASCII, at least one character. */
|
|
const SSID_PATTERN = /^[ -~]+$/;
|
|
/** EingabenUeberpruefung.musterPort: 1 to 65535, no leading zero. */
|
|
const PORT_PATTERN = /^([1-9]|[1-9][0-9]{1,3}|[1-5][0-9]{1,4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$/;
|
|
/** EingabenUeberpruefung.musterNurZahlen, at least one digit (Java's Long.parseLong refuses none). */
|
|
const DIGITS_PATTERN = /^[0-9]+$/;
|
|
|
|
const INVALID: CheckedField = { invalid: true };
|
|
|
|
/**
|
|
* An address as the panel's pattern and the model's IP.ipCheck both accept
|
|
* it: neither alone (the pattern lets ".0.0.1" through, ipCheck "10..0.0.1"
|
|
* and "+10.0.0.1"); the device keeps ipCheck's form ("010.0.0.1" is 10.0.0.1).
|
|
*/
|
|
function address(text: string, pattern: RegExp): CheckedField {
|
|
const canonical = javaIpCheck(text);
|
|
return canonical !== null && pattern.test(text) ? { value: canonical } : INVALID;
|
|
}
|
|
|
|
export function checkField(rule: FieldRule, text: string): CheckedField {
|
|
switch (rule) {
|
|
case 'name':
|
|
return { value: text };
|
|
case 'ssid':
|
|
return SSID_PATTERN.test(text) ? { value: text } : INVALID;
|
|
case 'ipAddress':
|
|
return address(text.trim(), IP_ADDRESS_PATTERN);
|
|
case 'ipAddressOrEmpty':
|
|
return text.trim() === '' ? { value: '' } : address(text.trim(), IP_ADDRESS_PATTERN);
|
|
case 'subnetMask':
|
|
return address(text.trim(), SUBNET_MASK_PATTERN);
|
|
case 'seconds': {
|
|
const digits = text.trim();
|
|
// In milliseconds the device's number must stay exact.
|
|
return DIGITS_PATTERN.test(digits) && Number(digits) * 1000 <= Number.MAX_SAFE_INTEGER ? { value: String(Number(digits)) } : INVALID;
|
|
}
|
|
case 'port':
|
|
return PORT_PATTERN.test(text.trim()) ? { value: text.trim() } : INVALID;
|
|
}
|
|
}
|