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,35 @@
import Node from '../node/Node.js';
import IChildNode from './IChildNode.js';
/**
* Child node utility.
*/
export default class ChildNodeUtility {
/**
* Removes the node from its parent.
*
* @param childNode Child node.
*/
static remove(childNode: IChildNode): void;
/**
* The Node.replaceWith() method replaces this Node in the children list of its parent with a set of Node or DOMString objects.
*
* @param childNode Child node.
* @param nodes List of Node or DOMString.
*/
static replaceWith(childNode: IChildNode, ...nodes: (Node | string)[]): void;
/**
* Inserts a set of Node or DOMString objects in the children list of this ChildNode's parent, just before this ChildNode. DOMString objects are inserted as equivalent Text nodes.
*
* @param childNode Child node.
* @param nodes List of Node or DOMString.
*/
static before(childNode: IChildNode, ...nodes: (string | Node)[]): void;
/**
* Inserts a set of Node or DOMString objects in the children list of this ChildNode's parent, just after this ChildNode. DOMString objects are inserted as equivalent Text nodes.
*
* @param childNode Child node.
* @param nodes List of Node or DOMString.
*/
static after(childNode: IChildNode, ...nodes: (string | Node)[]): void;
}
//# sourceMappingURL=ChildNodeUtility.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ChildNodeUtility.d.ts","sourceRoot":"","sources":["../../../src/nodes/child-node/ChildNodeUtility.ts"],"names":[],"mappings":"AAEA,OAAO,IAAI,MAAM,iBAAiB,CAAC;AAEnC,OAAO,UAAU,MAAM,iBAAiB,CAAC;AAEzC;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,gBAAgB;IACpC;;;;OAIG;WACW,MAAM,CAAC,SAAS,EAAE,UAAU,GAAG,IAAI;IAMjD;;;;;OAKG;WACW,WAAW,CAAC,SAAS,EAAE,UAAU,EAAE,GAAG,KAAK,EAAE,CAAC,IAAI,GAAG,MAAM,CAAC,EAAE,GAAG,IAAI;IAqBnF;;;;;OAKG;WACW,MAAM,CAAC,SAAS,EAAE,UAAU,EAAE,GAAG,KAAK,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,GAAG,IAAI;IAmB9E;;;;;OAKG;WACW,KAAK,CAAC,SAAS,EAAE,UAAU,EAAE,GAAG,KAAK,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,GAAG,IAAI;CAqB7E"}

View File

@@ -0,0 +1,84 @@
import DOMException from '../../exception/DOMException.js';
import * as PropertySymbol from '../../PropertySymbol.js';
import Node from '../node/Node.js';
/**
* Child node utility.
*/
export default class ChildNodeUtility {
/**
* Removes the node from its parent.
*
* @param childNode Child node.
*/
static remove(childNode) {
if (childNode[PropertySymbol.parentNode]) {
childNode[PropertySymbol.parentNode].removeChild(childNode);
}
}
/**
* The Node.replaceWith() method replaces this Node in the children list of its parent with a set of Node or DOMString objects.
*
* @param childNode Child node.
* @param nodes List of Node or DOMString.
*/
static replaceWith(childNode, ...nodes) {
const parent = childNode[PropertySymbol.parentNode];
if (!parent) {
throw new DOMException('This element has no parent node.');
}
for (const node of nodes) {
if (node instanceof Node) {
parent.insertBefore(node, childNode);
}
else {
parent.insertBefore(parent[PropertySymbol.ownerDocument].createTextNode(String(node)), childNode);
}
}
parent.removeChild(childNode);
}
/**
* Inserts a set of Node or DOMString objects in the children list of this ChildNode's parent, just before this ChildNode. DOMString objects are inserted as equivalent Text nodes.
*
* @param childNode Child node.
* @param nodes List of Node or DOMString.
*/
static before(childNode, ...nodes) {
const parent = childNode[PropertySymbol.parentNode];
if (!parent) {
return;
}
for (const node of nodes) {
if (node instanceof Node) {
parent.insertBefore(node, childNode);
}
else {
parent.insertBefore(parent[PropertySymbol.ownerDocument].createTextNode(String(node)), childNode);
}
}
}
/**
* Inserts a set of Node or DOMString objects in the children list of this ChildNode's parent, just after this ChildNode. DOMString objects are inserted as equivalent Text nodes.
*
* @param childNode Child node.
* @param nodes List of Node or DOMString.
*/
static after(childNode, ...nodes) {
const parent = childNode[PropertySymbol.parentNode];
if (!parent) {
return;
}
const nextSibling = childNode.nextSibling;
for (const node of nodes) {
const insertedNode = node instanceof Node
? node
: parent[PropertySymbol.ownerDocument].createTextNode(String(node));
if (!nextSibling) {
parent.appendChild(insertedNode);
}
else {
parent.insertBefore(insertedNode, nextSibling);
}
}
}
}
//# sourceMappingURL=ChildNodeUtility.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ChildNodeUtility.js","sourceRoot":"","sources":["../../../src/nodes/child-node/ChildNodeUtility.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,iCAAiC,CAAC;AAC3D,OAAO,KAAK,cAAc,MAAM,yBAAyB,CAAC;AAC1D,OAAO,IAAI,MAAM,iBAAiB,CAAC;AAInC;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,gBAAgB;IACpC;;;;OAIG;IACI,MAAM,CAAC,MAAM,CAAC,SAAqB;QACzC,IAAI,SAAS,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,CAAC;YAC1C,SAAS,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QAC7D,CAAC;IACF,CAAC;IAED;;;;;OAKG;IACI,MAAM,CAAC,WAAW,CAAC,SAAqB,EAAE,GAAG,KAAwB;QAC3E,MAAM,MAAM,GAAgB,SAAS,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;QAEjE,IAAI,CAAC,MAAM,EAAE,CAAC;YACb,MAAM,IAAI,YAAY,CAAC,kCAAkC,CAAC,CAAC;QAC5D,CAAC;QAED,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YAC1B,IAAI,IAAI,YAAY,IAAI,EAAE,CAAC;gBAC1B,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;YACtC,CAAC;iBAAM,CAAC;gBACP,MAAM,CAAC,YAAY,CAClB,MAAM,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EACjE,SAAS,CACT,CAAC;YACH,CAAC;QACF,CAAC;QAED,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;IAC/B,CAAC;IAED;;;;;OAKG;IACI,MAAM,CAAC,MAAM,CAAC,SAAqB,EAAE,GAAG,KAAwB;QACtE,MAAM,MAAM,GAAgB,SAAS,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;QAEjE,IAAI,CAAC,MAAM,EAAE,CAAC;YACb,OAAO;QACR,CAAC;QAED,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YAC1B,IAAI,IAAI,YAAY,IAAI,EAAE,CAAC;gBAC1B,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;YACtC,CAAC;iBAAM,CAAC;gBACP,MAAM,CAAC,YAAY,CAClB,MAAM,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EACjE,SAAS,CACT,CAAC;YACH,CAAC;QACF,CAAC;IACF,CAAC;IAED;;;;;OAKG;IACI,MAAM,CAAC,KAAK,CAAC,SAAqB,EAAE,GAAG,KAAwB;QACrE,MAAM,MAAM,GAAgB,SAAS,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;QAEjE,IAAI,CAAC,MAAM,EAAE,CAAC;YACb,OAAO;QACR,CAAC;QAED,MAAM,WAAW,GAAG,SAAS,CAAC,WAAW,CAAC;QAE1C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YAC1B,MAAM,YAAY,GACjB,IAAI,YAAY,IAAI;gBACnB,CAAC,CAAC,IAAI;gBACN,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;YACtE,IAAI,CAAC,WAAW,EAAE,CAAC;gBAClB,MAAM,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC;YAClC,CAAC;iBAAM,CAAC;gBACP,MAAM,CAAC,YAAY,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;YAChD,CAAC;QACF,CAAC;IACF,CAAC;CACD"}

View File

@@ -0,0 +1,26 @@
import Node from '../node/Node.js';
export default interface IChildNode extends Node {
/**
* Removes the node from its parent.
*/
remove(): void;
/**
* Inserts a set of Node or DOMString objects in the children list of this ChildNode's parent, just before this ChildNode. DOMString objects are inserted as equivalent Text nodes.
*
* @param nodes List of Node or DOMString.
*/
before(...nodes: (Node | string)[]): void;
/**
* Inserts a set of Node or DOMString objects in the children list of this ChildNode's parent, just after this ChildNode. DOMString objects are inserted as equivalent Text nodes.
*
* @param nodes List of Node or DOMString.
*/
after(...nodes: (Node | string)[]): void;
/**
* The Node.replaceWith() method replaces this Node in the children list of its parent with a set of Node or DOMString objects.
*
* @param nodes List of Node or DOMString.
*/
replaceWith(...nodes: (Node | string)[]): void;
}
//# sourceMappingURL=IChildNode.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"IChildNode.d.ts","sourceRoot":"","sources":["../../../src/nodes/child-node/IChildNode.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,iBAAiB,CAAC;AAEnC,MAAM,CAAC,OAAO,WAAW,UAAW,SAAQ,IAAI;IAC/C;;OAEG;IACH,MAAM,IAAI,IAAI,CAAC;IAEf;;;;OAIG;IACH,MAAM,CAAC,GAAG,KAAK,EAAE,CAAC,IAAI,GAAG,MAAM,CAAC,EAAE,GAAG,IAAI,CAAC;IAE1C;;;;OAIG;IACH,KAAK,CAAC,GAAG,KAAK,EAAE,CAAC,IAAI,GAAG,MAAM,CAAC,EAAE,GAAG,IAAI,CAAC;IAEzC;;;;OAIG;IACH,WAAW,CAAC,GAAG,KAAK,EAAE,CAAC,IAAI,GAAG,MAAM,CAAC,EAAE,GAAG,IAAI,CAAC;CAC/C"}

View File

@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=IChildNode.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"IChildNode.js","sourceRoot":"","sources":["../../../src/nodes/child-node/IChildNode.ts"],"names":[],"mappings":""}

View File

@@ -0,0 +1,7 @@
import Element from '../element/Element.js';
import Node from '../node/Node.js';
export default interface INonDocumentTypeChildNode extends Node {
readonly previousElementSibling: Element;
readonly nextElementSibling: Element;
}
//# sourceMappingURL=INonDocumentTypeChildNode.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"INonDocumentTypeChildNode.d.ts","sourceRoot":"","sources":["../../../src/nodes/child-node/INonDocumentTypeChildNode.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,uBAAuB,CAAC;AAC5C,OAAO,IAAI,MAAM,iBAAiB,CAAC;AAEnC,MAAM,CAAC,OAAO,WAAW,yBAA0B,SAAQ,IAAI;IAC9D,QAAQ,CAAC,sBAAsB,EAAE,OAAO,CAAC;IACzC,QAAQ,CAAC,kBAAkB,EAAE,OAAO,CAAC;CACrC"}

View File

@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=INonDocumentTypeChildNode.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"INonDocumentTypeChildNode.js","sourceRoot":"","sources":["../../../src/nodes/child-node/INonDocumentTypeChildNode.ts"],"names":[],"mappings":""}

View File

@@ -0,0 +1,22 @@
import Element from '../element/Element.js';
import INonDocumentTypeChildNode from './INonDocumentTypeChildNode.js';
/**
* Non Document Child node utility.
*/
export default class NonDocumentChildNodeUtility {
/**
* Previous element sibling.
*
* @param childNode Child node.
* @returns Element.
*/
static previousElementSibling(childNode: INonDocumentTypeChildNode): Element;
/**
* Next element sibling.
*
* @param childNode Child node.
* @returns Element.
*/
static nextElementSibling(childNode: INonDocumentTypeChildNode): Element;
}
//# sourceMappingURL=NonDocumentChildNodeUtility.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"NonDocumentChildNodeUtility.d.ts","sourceRoot":"","sources":["../../../src/nodes/child-node/NonDocumentChildNodeUtility.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,uBAAuB,CAAC;AAE5C,OAAO,yBAAyB,MAAM,gCAAgC,CAAC;AAGvE;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,2BAA2B;IAC/C;;;;;OAKG;WACW,sBAAsB,CAAC,SAAS,EAAE,yBAAyB,GAAG,OAAO;IAQnF;;;;;OAKG;WACW,kBAAkB,CAAC,SAAS,EAAE,yBAAyB,GAAG,OAAO;CAO/E"}

View File

@@ -0,0 +1,34 @@
import NodeTypeEnum from '../node/NodeTypeEnum.js';
import * as PropertySymbol from '../../PropertySymbol.js';
/**
* Non Document Child node utility.
*/
export default class NonDocumentChildNodeUtility {
/**
* Previous element sibling.
*
* @param childNode Child node.
* @returns Element.
*/
static previousElementSibling(childNode) {
let sibling = childNode.previousSibling;
while (sibling && sibling[PropertySymbol.nodeType] !== NodeTypeEnum.elementNode) {
sibling = sibling.previousSibling;
}
return sibling;
}
/**
* Next element sibling.
*
* @param childNode Child node.
* @returns Element.
*/
static nextElementSibling(childNode) {
let sibling = childNode.nextSibling;
while (sibling && sibling[PropertySymbol.nodeType] !== NodeTypeEnum.elementNode) {
sibling = sibling.nextSibling;
}
return sibling;
}
}
//# sourceMappingURL=NonDocumentChildNodeUtility.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"NonDocumentChildNodeUtility.js","sourceRoot":"","sources":["../../../src/nodes/child-node/NonDocumentChildNodeUtility.ts"],"names":[],"mappings":"AACA,OAAO,YAAY,MAAM,yBAAyB,CAAC;AAEnD,OAAO,KAAK,cAAc,MAAM,yBAAyB,CAAC;AAE1D;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,2BAA2B;IAC/C;;;;;OAKG;IACI,MAAM,CAAC,sBAAsB,CAAC,SAAoC;QACxE,IAAI,OAAO,GAAG,SAAS,CAAC,eAAe,CAAC;QACxC,OAAO,OAAO,IAAI,OAAO,CAAC,cAAc,CAAC,QAAQ,CAAC,KAAK,YAAY,CAAC,WAAW,EAAE,CAAC;YACjF,OAAO,GAAG,OAAO,CAAC,eAAe,CAAC;QACnC,CAAC;QACD,OAAgB,OAAO,CAAC;IACzB,CAAC;IAED;;;;;OAKG;IACI,MAAM,CAAC,kBAAkB,CAAC,SAAoC;QACpE,IAAI,OAAO,GAAG,SAAS,CAAC,WAAW,CAAC;QACpC,OAAO,OAAO,IAAI,OAAO,CAAC,cAAc,CAAC,QAAQ,CAAC,KAAK,YAAY,CAAC,WAAW,EAAE,CAAC;YACjF,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC;QAC/B,CAAC;QACD,OAAgB,OAAO,CAAC;IACzB,CAAC;CACD"}