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,119 @@
import Node from '../node/Node.js';
import * as PropertySymbol from '../../PropertySymbol.js';
import Element from '../element/Element.js';
import HTMLCollection from '../element/HTMLCollection.js';
import NodeTypeEnum from '../node/NodeTypeEnum.js';
import IHTMLElementTagNameMap from '../../config/IHTMLElementTagNameMap.js';
import ISVGElementTagNameMap from '../../config/ISVGElementTagNameMap.js';
import NodeList from '../node/NodeList.js';
/**
* DocumentFragment.
*/
export default class DocumentFragment extends Node {
[PropertySymbol.children]: HTMLCollection<Element> | null;
[PropertySymbol.rootNode]: Node;
[PropertySymbol.nodeType]: NodeTypeEnum;
cloneNode: (deep?: boolean) => DocumentFragment;
/**
* Returns the document fragment children.
*/
get children(): HTMLCollection<Element>;
/**
* Last element child.
*
* @returns Element.
*/
get childElementCount(): number;
/**
* First element child.
*
* @returns Element.
*/
get firstElementChild(): Element;
/**
* Last element child.
*
* @returns Element.
*/
get lastElementChild(): Element;
/**
* Get text value of children.
*
* @returns Text content.
*/
get textContent(): string;
/**
* Sets text content.
*
* @param textContent Text content.
*/
set textContent(textContent: string);
/**
* Inserts a set of Node objects or DOMString objects after the last child of the ParentNode. DOMString objects are inserted as equivalent Text nodes.
*
* @param nodes List of Node or DOMString.
*/
append(...nodes: (Node | string)[]): void;
/**
* Inserts a set of Node objects or DOMString objects before the first child of the ParentNode. DOMString objects are inserted as equivalent Text nodes.
*
* @param nodes List of Node or DOMString.
*/
prepend(...nodes: (Node | string)[]): void;
/**
* Replaces the existing children of a node with a specified new set of children.
*
* @param nodes List of Node or DOMString.
*/
replaceChildren(...nodes: (Node | string)[]): void;
/**
* Query CSS selector to find matching nodes.
*
* @param selector CSS selector.
* @returns Matching elements.
*/
querySelectorAll<K extends keyof IHTMLElementTagNameMap>(selector: K): NodeList<IHTMLElementTagNameMap[K]>;
/**
* Query CSS selector to find matching elments.
*
* @param selector CSS selector.
* @returns Matching elements.
*/
querySelectorAll<K extends keyof ISVGElementTagNameMap>(selector: K): NodeList<ISVGElementTagNameMap[K]>;
/**
* Query CSS selector to find matching elments.
*
* @param selector CSS selector.
* @returns Matching elements.
*/
querySelectorAll(selector: string): NodeList<Element>;
/**
* Query CSS Selector to find matching node.
*
* @param selector CSS selector.
* @returns Matching element.
*/
querySelector<K extends keyof IHTMLElementTagNameMap>(selector: K): IHTMLElementTagNameMap[K] | null;
/**
* Query CSS Selector to find a matching element.
*
* @param selector CSS selector.
* @returns Matching element.
*/
querySelector<K extends keyof ISVGElementTagNameMap>(selector: K): ISVGElementTagNameMap[K] | null;
/**
* Query CSS Selector to find a matching element.
*
* @param selector CSS selector.
* @returns Matching element.
*/
querySelector(selector: string): Element | null;
/**
* Returns an element by ID.
*
* @param id ID.
* @returns Matching element.
*/
getElementById(id: string): Element | null;
}
//# sourceMappingURL=DocumentFragment.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"DocumentFragment.d.ts","sourceRoot":"","sources":["../../../src/nodes/document-fragment/DocumentFragment.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,iBAAiB,CAAC;AACnC,OAAO,KAAK,cAAc,MAAM,yBAAyB,CAAC;AAC1D,OAAO,OAAO,MAAM,uBAAuB,CAAC;AAG5C,OAAO,cAAc,MAAM,8BAA8B,CAAC;AAC1D,OAAO,YAAY,MAAM,yBAAyB,CAAC;AACnD,OAAO,sBAAsB,MAAM,wCAAwC,CAAC;AAC5E,OAAO,qBAAqB,MAAM,uCAAuC,CAAC;AAC1E,OAAO,QAAQ,MAAM,qBAAqB,CAAC;AAE3C;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,gBAAiB,SAAQ,IAAI;IAC1C,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,cAAc,CAAC,OAAO,CAAC,GAAG,IAAI,CAAQ;IACjE,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAQ;IACvC,CAAC,cAAc,CAAC,QAAQ,CAAC,eAAqC;IACtD,SAAS,EAAE,CAAC,IAAI,CAAC,EAAE,OAAO,KAAK,gBAAgB,CAAC;IAE/D;;OAEG;IACH,IAAW,QAAQ,IAAI,cAAc,CAAC,OAAO,CAAC,CAS7C;IAED;;;;OAIG;IACH,IAAW,iBAAiB,IAAI,MAAM,CAErC;IAED;;;;OAIG;IACH,IAAW,iBAAiB,IAAI,OAAO,CAEtC;IAED;;;;OAIG;IACH,IAAW,gBAAgB,IAAI,OAAO,CAGrC;IAED;;;;OAIG;IACH,IAAW,WAAW,IAAI,MAAM,CAW/B;IAED;;;;OAIG;IACH,IAAW,WAAW,CAAC,WAAW,EAAE,MAAM,EAQzC;IAED;;;;OAIG;IACI,MAAM,CAAC,GAAG,KAAK,EAAE,CAAC,IAAI,GAAG,MAAM,CAAC,EAAE,GAAG,IAAI;IAIhD;;;;OAIG;IACI,OAAO,CAAC,GAAG,KAAK,EAAE,CAAC,IAAI,GAAG,MAAM,CAAC,EAAE,GAAG,IAAI;IAIjD;;;;OAIG;IACI,eAAe,CAAC,GAAG,KAAK,EAAE,CAAC,IAAI,GAAG,MAAM,CAAC,EAAE,GAAG,IAAI;IAIzD;;;;;OAKG;IACI,gBAAgB,CAAC,CAAC,SAAS,MAAM,sBAAsB,EAC7D,QAAQ,EAAE,CAAC,GACT,QAAQ,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;IAEtC;;;;;OAKG;IACI,gBAAgB,CAAC,CAAC,SAAS,MAAM,qBAAqB,EAC5D,QAAQ,EAAE,CAAC,GACT,QAAQ,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;IAErC;;;;;OAKG;IACI,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC;IAY5D;;;;;OAKG;IACI,aAAa,CAAC,CAAC,SAAS,MAAM,sBAAsB,EAC1D,QAAQ,EAAE,CAAC,GACT,sBAAsB,CAAC,CAAC,CAAC,GAAG,IAAI;IAEnC;;;;;OAKG;IACI,aAAa,CAAC,CAAC,SAAS,MAAM,qBAAqB,EACzD,QAAQ,EAAE,CAAC,GACT,qBAAqB,CAAC,CAAC,CAAC,GAAG,IAAI;IAElC;;;;;OAKG;IACI,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI;IAYtD;;;;;OAKG;IACI,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI;CAGjD"}

View File

@@ -0,0 +1,130 @@
import Node from '../node/Node.js';
import * as PropertySymbol from '../../PropertySymbol.js';
import QuerySelector from '../../query-selector/QuerySelector.js';
import ParentNodeUtility from '../parent-node/ParentNodeUtility.js';
import HTMLCollection from '../element/HTMLCollection.js';
import NodeTypeEnum from '../node/NodeTypeEnum.js';
/**
* DocumentFragment.
*/
export default class DocumentFragment extends Node {
[PropertySymbol.children] = null;
[PropertySymbol.rootNode] = this;
[PropertySymbol.nodeType] = NodeTypeEnum.documentFragmentNode;
/**
* Returns the document fragment children.
*/
get children() {
if (!this[PropertySymbol.children]) {
const elements = this[PropertySymbol.elementArray];
this[PropertySymbol.children] = new HTMLCollection(PropertySymbol.illegalConstructor, () => elements);
}
return this[PropertySymbol.children];
}
/**
* Last element child.
*
* @returns Element.
*/
get childElementCount() {
return this[PropertySymbol.elementArray].length;
}
/**
* First element child.
*
* @returns Element.
*/
get firstElementChild() {
return this[PropertySymbol.elementArray][0] ?? null;
}
/**
* Last element child.
*
* @returns Element.
*/
get lastElementChild() {
const children = this[PropertySymbol.elementArray];
return children[children.length - 1] ?? null;
}
/**
* Get text value of children.
*
* @returns Text content.
*/
get textContent() {
let result = '';
for (const childNode of this[PropertySymbol.nodeArray]) {
if (childNode[PropertySymbol.nodeType] === NodeTypeEnum.elementNode ||
childNode[PropertySymbol.nodeType] === NodeTypeEnum.textNode) {
result += childNode.textContent;
}
}
return result;
}
/**
* Sets text content.
*
* @param textContent Text content.
*/
set textContent(textContent) {
const childNodes = this[PropertySymbol.nodeArray];
while (childNodes.length) {
this.removeChild(childNodes[0]);
}
if (textContent) {
this.appendChild(this[PropertySymbol.ownerDocument].createTextNode(textContent));
}
}
/**
* Inserts a set of Node objects or DOMString objects after the last child of the ParentNode. DOMString objects are inserted as equivalent Text nodes.
*
* @param nodes List of Node or DOMString.
*/
append(...nodes) {
ParentNodeUtility.append(this, ...nodes);
}
/**
* Inserts a set of Node objects or DOMString objects before the first child of the ParentNode. DOMString objects are inserted as equivalent Text nodes.
*
* @param nodes List of Node or DOMString.
*/
prepend(...nodes) {
ParentNodeUtility.prepend(this, ...nodes);
}
/**
* Replaces the existing children of a node with a specified new set of children.
*
* @param nodes List of Node or DOMString.
*/
replaceChildren(...nodes) {
ParentNodeUtility.replaceChildren(this, ...nodes);
}
/**
* Query CSS selector to find matching elments.
*
* @param selector CSS selector.
* @returns Matching elements.
*/
querySelectorAll(selector) {
return QuerySelector.querySelectorAll(this, selector);
}
/**
* Query CSS Selector to find a matching element.
*
* @param selector CSS selector.
* @returns Matching element.
*/
querySelector(selector) {
return QuerySelector.querySelector(this, selector);
}
/**
* Returns an element by ID.
*
* @param id ID.
* @returns Matching element.
*/
getElementById(id) {
return ParentNodeUtility.getElementById(this, id);
}
}
//# sourceMappingURL=DocumentFragment.js.map

View File

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