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,100 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const Blob_js_1 = __importDefault(require("../file/Blob.cjs"));
/**
* Clipboard API.
*
* Reference:
* https://developer.mozilla.org/en-US/docs/Web/API/Clipboard.
*/
class Clipboard {
#window;
#data = [];
/**
* Constructor.
*
* @param window Owner window.
*/
constructor(window) {
if (!window) {
throw new TypeError('Illegal constructor');
}
this.#window = window;
}
/**
* Returns data.
*
* @returns Data.
*/
async read() {
const permissionStatus = await this.#window.navigator.permissions.query({
name: 'clipboard-read'
});
if (permissionStatus.state === 'denied') {
throw new this.#window.DOMException(`Failed to execute 'read' on 'Clipboard': The request is not allowed`);
}
return this.#data;
}
/**
* Returns text.
*
* @returns Text.
*/
async readText() {
const permissionStatus = await this.#window.navigator.permissions.query({
name: 'clipboard-read'
});
if (permissionStatus.state === 'denied') {
throw new this.#window.DOMException(`Failed to execute 'readText' on 'Clipboard': The request is not allowed`);
}
let text = '';
for (const item of this.#data) {
if (item.types.includes('text/plain')) {
const data = await item.getType('text/plain');
if (typeof data === 'string') {
text += data;
}
else {
// Instance of Blob
text += await data.text();
}
}
}
return text;
}
/**
* Writes data.
*
* @param data Data.
*/
async write(data) {
const permissionStatus = await this.#window.navigator.permissions.query({
name: 'clipboard-write'
});
if (permissionStatus.state === 'denied') {
throw new this.#window.DOMException(`Failed to execute 'write' on 'Clipboard': The request is not allowed`);
}
this.#data = data;
}
/**
* Writes text.
*
* @param text Text.
*/
async writeText(text) {
const permissionStatus = await this.#window.navigator.permissions.query({
name: 'clipboard-write'
});
if (permissionStatus.state === 'denied') {
throw new this.#window.DOMException(`Failed to execute 'writeText' on 'Clipboard': The request is not allowed`);
}
this.#data = [
new this.#window.ClipboardItem({ 'text/plain': new Blob_js_1.default([text], { type: 'text/plain' }) })
];
}
}
exports.default = Clipboard;
//# sourceMappingURL=Clipboard.cjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"Clipboard.cjs","sourceRoot":"","sources":["../../src/clipboard/Clipboard.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,42 @@
import BrowserWindow from '../window/BrowserWindow.cjs';
import ClipboardItem from './ClipboardItem.cjs';
/**
* Clipboard API.
*
* Reference:
* https://developer.mozilla.org/en-US/docs/Web/API/Clipboard.
*/
export default class Clipboard {
#private;
/**
* Constructor.
*
* @param window Owner window.
*/
constructor(window: BrowserWindow);
/**
* Returns data.
*
* @returns Data.
*/
read(): Promise<ClipboardItem[]>;
/**
* Returns text.
*
* @returns Text.
*/
readText(): Promise<string>;
/**
* Writes data.
*
* @param data Data.
*/
write(data: ClipboardItem[]): Promise<void>;
/**
* Writes text.
*
* @param text Text.
*/
writeText(text: string): Promise<void>;
}
//# sourceMappingURL=Clipboard.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"Clipboard.d.ts","sourceRoot":"","sources":["../../src/clipboard/Clipboard.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,MAAM,4BAA4B,CAAC;AACvD,OAAO,aAAa,MAAM,oBAAoB,CAAC;AAG/C;;;;;GAKG;AACH,MAAM,CAAC,OAAO,OAAO,SAAS;;IAI7B;;;;OAIG;gBACS,MAAM,EAAE,aAAa;IAOjC;;;;OAIG;IACU,IAAI,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;IAY7C;;;;OAIG;IACU,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;IAwBxC;;;;OAIG;IACU,KAAK,CAAC,IAAI,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAYxD;;;;OAIG;IACU,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAanD"}

View File

@@ -0,0 +1,55 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const DOMException_js_1 = __importDefault(require("../exception/DOMException.cjs"));
const Blob_js_1 = __importDefault(require("../file/Blob.cjs"));
/**
* Clipboard Item API.
*
* Reference:
* https://developer.mozilla.org/en-US/docs/Web/API/ClipboardItem.
*/
class ClipboardItem {
presentationStyle = 'unspecified';
#data;
/**
* Constructor.
*
* @param data Data.
* @param [options] Options.
* @param [options.presentationStyle] Presentation style.
*/
constructor(data, options) {
this.#data = data;
if (options?.presentationStyle) {
this.presentationStyle = options.presentationStyle;
}
}
/**
* Returns types.
*
* @returns Types.
*/
get types() {
return Object.keys(this.#data);
}
/**
* Returns data by type.
*
* @param type Type.
* @returns Data.
*/
async getType(type) {
if (!this.#data[type]) {
throw new DOMException_js_1.default(`Failed to execute 'getType' on 'ClipboardItem': The type '${type}' was not found`);
}
if (this.#data[type] instanceof Blob_js_1.default) {
return this.#data[type];
}
return new Blob_js_1.default([await this.#data[type]], { type });
}
}
exports.default = ClipboardItem;
//# sourceMappingURL=ClipboardItem.cjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ClipboardItem.cjs","sourceRoot":"","sources":["../../src/clipboard/ClipboardItem.ts"],"names":[],"mappings":";;;;;AAAA,mFAAwD;AACxD,8DAAmC;AAEnC;;;;;GAKG;AACH,MAAqB,aAAa;IACjB,iBAAiB,GAA4C,aAAa,CAAC;IAC3F,KAAK,CAAiE;IAEtE;;;;;;OAMG;IACH,YACC,IAAoE,EACpE,OAAyE;QAEzE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,OAAO,EAAE,iBAAiB,EAAE,CAAC;YAChC,IAAI,CAAC,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;QACpD,CAAC;IACF,CAAC;IAED;;;;OAIG;IACH,IAAW,KAAK;QACf,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChC,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,OAAO,CAAC,IAAY;QAChC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACvB,MAAM,IAAI,yBAAY,CACrB,6DAA6D,IAAI,iBAAiB,CAClF,CAAC;QACH,CAAC;QACD,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,YAAY,iBAAI,EAAE,CAAC;YACtC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACzB,CAAC;QACD,OAAO,IAAI,iBAAI,CAAC,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,CAAC;CACD;AA/CD,gCA+CC"}

View File

@@ -0,0 +1,37 @@
import Blob from '../file/Blob.cjs';
/**
* Clipboard Item API.
*
* Reference:
* https://developer.mozilla.org/en-US/docs/Web/API/ClipboardItem.
*/
export default class ClipboardItem {
#private;
readonly presentationStyle: 'unspecified' | 'inline' | 'attachment';
/**
* Constructor.
*
* @param data Data.
* @param [options] Options.
* @param [options.presentationStyle] Presentation style.
*/
constructor(data: {
[mimeType: string]: Blob | string | Promise<Blob | string>;
}, options?: {
presentationStyle?: 'unspecified' | 'inline' | 'attachment';
});
/**
* Returns types.
*
* @returns Types.
*/
get types(): string[];
/**
* Returns data by type.
*
* @param type Type.
* @returns Data.
*/
getType(type: string): Promise<Blob>;
}
//# sourceMappingURL=ClipboardItem.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ClipboardItem.d.ts","sourceRoot":"","sources":["../../src/clipboard/ClipboardItem.ts"],"names":[],"mappings":"AACA,OAAO,IAAI,MAAM,iBAAiB,CAAC;AAEnC;;;;;GAKG;AACH,MAAM,CAAC,OAAO,OAAO,aAAa;;IACjC,SAAgB,iBAAiB,EAAE,aAAa,GAAG,QAAQ,GAAG,YAAY,CAAiB;IAG3F;;;;;;OAMG;gBAEF,IAAI,EAAE;QAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,MAAM,CAAC,CAAA;KAAE,EACpE,OAAO,CAAC,EAAE;QAAE,iBAAiB,CAAC,EAAE,aAAa,GAAG,QAAQ,GAAG,YAAY,CAAA;KAAE;IAQ1E;;;;OAIG;IACH,IAAW,KAAK,IAAI,MAAM,EAAE,CAE3B;IAED;;;;;OAKG;IACU,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAWjD"}