feat: Reinitialize frontend with SvelteKit and TypeScript

- Delete old Vite+Svelte frontend
- Initialize new SvelteKit project with TypeScript
- Configure Tailwind CSS v4 + DaisyUI
- Implement JWT authentication with auto-refresh
- Create login page with form validation (Zod)
- Add protected route guards
- Update Docker configuration for single-stage build
- Add E2E tests with Playwright (6/11 passing)
- Fix Svelte 5 reactivity with $state() runes

Known issues:
- 5 E2E tests failing (timing/async issues)
- Token refresh implementation needs debugging
- Validation error display timing
This commit is contained in:
2026-02-17 16:19:59 -05:00
parent 54df6018f5
commit de2d83092e
28274 changed files with 3816354 additions and 90 deletions

View File

@@ -0,0 +1,93 @@
import * as PropertySymbol from '../../PropertySymbol.js';
import Attr from '../attr/Attr.js';
import HTMLElement from '../html-element/HTMLElement.js';
import HTMLFormElement from '../html-form-element/HTMLFormElement.js';
import HTMLSelectElement from '../html-select-element/HTMLSelectElement.js';
/**
* HTML Option Element.
*
* Reference:
* https://developer.mozilla.org/en-US/docs/Web/API/HTMLOptionElement.
*/
export default class HTMLOptionElement extends HTMLElement {
[PropertySymbol.selectedness]: boolean;
[PropertySymbol.dirtyness]: boolean;
[PropertySymbol.selectNode]: HTMLSelectElement | null;
/**
* Returns inner text, which is the rendered appearance of text.
*
* @returns Inner text.
*/
get text(): string;
/**
* Sets the inner text, which is the rendered appearance of text.
*
* @param innerText Inner text.
*/
set text(text: string);
/**
* Returns index.
*
* @returns Index.
*/
get index(): number;
/**
* Returns the parent form element.
*
* @returns Form.
*/
get form(): HTMLFormElement;
/**
* Returns selected.
*
* @returns Selected.
*/
get selected(): boolean;
/**
* Sets selected.
*
* @param selected Selected.
*/
set selected(selected: boolean);
/**
* Returns disabled.
*
* @returns Disabled.
*/
get disabled(): boolean;
/**
* Sets disabled.
*
* @param disabled Disabled.
*/
set disabled(disabled: boolean);
/**
* Returns value.
*
* @returns Value.
*/
get value(): string;
/**
* Sets value.
*
* @param value Value.
*/
set value(value: string);
/**
* @override
*/
[PropertySymbol.onSetAttribute](attribute: Attr, replacedAttribute: Attr | null): void;
/**
* @override
*/
[PropertySymbol.onRemoveAttribute](removedAttribute: Attr): void;
/**
* @override
*/
[PropertySymbol.connectedToNode](): void;
/**
* @override
*/
[PropertySymbol.disconnectedFromNode](): void;
}
//# sourceMappingURL=HTMLOptionElement.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"HTMLOptionElement.d.ts","sourceRoot":"","sources":["../../../src/nodes/html-option-element/HTMLOptionElement.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,cAAc,MAAM,yBAAyB,CAAC;AAE1D,OAAO,IAAI,MAAM,iBAAiB,CAAC;AACnC,OAAO,WAAW,MAAM,gCAAgC,CAAC;AACzD,OAAO,eAAe,MAAM,yCAAyC,CAAC;AACtE,OAAO,iBAAiB,MAAM,6CAA6C,CAAC;AAE5E;;;;;GAKG;AACH,MAAM,CAAC,OAAO,OAAO,iBAAkB,SAAQ,WAAW;IAClD,CAAC,cAAc,CAAC,YAAY,CAAC,UAAS;IACtC,CAAC,cAAc,CAAC,SAAS,CAAC,UAAS;IACnC,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAAQ;IAEpE;;;;OAIG;IACH,IAAW,IAAI,IAAI,MAAM,CAExB;IAED;;;;OAIG;IACH,IAAW,IAAI,CAAC,IAAI,EAAE,MAAM,EAE3B;IAED;;;;OAIG;IACH,IAAW,KAAK,IAAI,MAAM,CAQzB;IAED;;;;OAIG;IACH,IAAW,IAAI,IAAI,eAAe,CAEjC;IAED;;;;OAIG;IACH,IAAW,QAAQ,IAAI,OAAO,CAE7B;IAED;;;;OAIG;IACH,IAAW,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAWpC;IAED;;;;OAIG;IACH,IAAW,QAAQ,IAAI,OAAO,CAE7B;IAED;;;;OAIG;IACH,IAAW,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAMpC;IAED;;;;OAIG;IACH,IAAW,KAAK,IAAI,MAAM,CAEzB;IAED;;;;OAIG;IACH,IAAW,KAAK,CAAC,KAAK,EAAE,MAAM,EAE7B;IAED;;OAEG;IACa,CAAC,cAAc,CAAC,cAAc,CAAC,CAC9C,SAAS,EAAE,IAAI,EACf,iBAAiB,EAAE,IAAI,GAAG,IAAI,GAC5B,IAAI;IAiBP;;OAEG;IACa,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC,gBAAgB,EAAE,IAAI,GAAG,IAAI;IAiBhF;;OAEG;IACa,CAAC,cAAc,CAAC,eAAe,CAAC,IAAI,IAAI;IAQxD;;OAEG;IACa,CAAC,cAAc,CAAC,oBAAoB,CAAC,IAAI,IAAI;CAO7D"}

View File

@@ -0,0 +1,157 @@
import * as PropertySymbol from '../../PropertySymbol.js';
import QuerySelector from '../../query-selector/QuerySelector.js';
import HTMLElement from '../html-element/HTMLElement.js';
/**
* HTML Option Element.
*
* Reference:
* https://developer.mozilla.org/en-US/docs/Web/API/HTMLOptionElement.
*/
export default class HTMLOptionElement extends HTMLElement {
[PropertySymbol.selectedness] = false;
[PropertySymbol.dirtyness] = false;
[PropertySymbol.selectNode] = null;
/**
* Returns inner text, which is the rendered appearance of text.
*
* @returns Inner text.
*/
get text() {
return this.innerText;
}
/**
* Sets the inner text, which is the rendered appearance of text.
*
* @param innerText Inner text.
*/
set text(text) {
this.innerText = text;
}
/**
* Returns index.
*
* @returns Index.
*/
get index() {
if (!this[PropertySymbol.selectNode]) {
return 0;
}
const options = QuerySelector.querySelectorAll(this[PropertySymbol.selectNode], 'option')[PropertySymbol.elements];
return options.indexOf(this);
}
/**
* Returns the parent form element.
*
* @returns Form.
*/
get form() {
return this[PropertySymbol.selectNode]?.form || null;
}
/**
* Returns selected.
*
* @returns Selected.
*/
get selected() {
return this[PropertySymbol.selectedness];
}
/**
* Sets selected.
*
* @param selected Selected.
*/
set selected(selected) {
const selectNode = this[PropertySymbol.selectNode];
this[PropertySymbol.dirtyness] = true;
this[PropertySymbol.selectedness] = Boolean(selected);
if (selectNode) {
selectNode[PropertySymbol.updateSelectedness](this[PropertySymbol.selectedness] ? this : null);
}
}
/**
* Returns disabled.
*
* @returns Disabled.
*/
get disabled() {
return this.getAttribute('disabled') !== null;
}
/**
* Sets disabled.
*
* @param disabled Disabled.
*/
set disabled(disabled) {
if (!disabled) {
this.removeAttribute('disabled');
}
else {
this.setAttribute('disabled', '');
}
}
/**
* Returns value.
*
* @returns Value.
*/
get value() {
return this.getAttribute('value') ?? this.textContent;
}
/**
* Sets value.
*
* @param value Value.
*/
set value(value) {
this.setAttribute('value', value);
}
/**
* @override
*/
[PropertySymbol.onSetAttribute](attribute, replacedAttribute) {
super[PropertySymbol.onSetAttribute](attribute, replacedAttribute);
if (!this[PropertySymbol.dirtyness] &&
attribute[PropertySymbol.name] === 'selected' &&
replacedAttribute?.[PropertySymbol.value] !== attribute[PropertySymbol.value]) {
const selectNode = this[PropertySymbol.selectNode];
this[PropertySymbol.selectedness] = true;
if (selectNode) {
selectNode[PropertySymbol.updateSelectedness](this);
}
}
}
/**
* @override
*/
[PropertySymbol.onRemoveAttribute](removedAttribute) {
super[PropertySymbol.onRemoveAttribute](removedAttribute);
if (removedAttribute &&
!this[PropertySymbol.dirtyness] &&
removedAttribute[PropertySymbol.name] === 'selected') {
const selectNode = this[PropertySymbol.selectNode];
this[PropertySymbol.selectedness] = false;
if (selectNode) {
selectNode[PropertySymbol.updateSelectedness]();
}
}
}
/**
* @override
*/
[PropertySymbol.connectedToNode]() {
super[PropertySymbol.connectedToNode]();
if (this[PropertySymbol.selectNode]) {
this[PropertySymbol.selectNode][PropertySymbol.updateSelectedness]();
}
}
/**
* @override
*/
[PropertySymbol.disconnectedFromNode]() {
if (this[PropertySymbol.selectNode]) {
this[PropertySymbol.selectNode][PropertySymbol.updateSelectedness]();
}
super[PropertySymbol.disconnectedFromNode]();
}
}
//# sourceMappingURL=HTMLOptionElement.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"HTMLOptionElement.js","sourceRoot":"","sources":["../../../src/nodes/html-option-element/HTMLOptionElement.ts"],"names":[],"mappings":"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"}