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,32 @@
declare enum PermissionNameEnum {
geolocation = "geolocation",
notifications = "notifications",
push = "push",
midi = "midi",
camera = "camera",
microphone = "microphone",
backgroundFetch = "background-fetch",
backgroundSync = "background-sync",
persistentStorage = "persistent-storage",
ambientLightSensor = "ambient-light-sensor",
accelerometer = "accelerometer",
gyroscope = "gyroscope",
magnetometer = "magnetometer",
screenWakeLock = "screen-wake-lock",
nfc = "nfc",
displayCapture = "display-capture",
accessibilityEvents = "accessibility-events",
clipboardRead = "clipboard-read",
clipboardWrite = "clipboard-write",
paymentHandler = "payment-handler",
idleDetection = "idle-detection",
periodicBackgroundSync = "periodic-background-sync",
systemWakeLock = "system-wake-lock",
storageAccess = "storage-access",
windowManagement = "window-management",
windowPlacement = "window-placement",
localFonts = "local-fonts",
topLevelStorageAccess = "top-level-storage-access"
}
export default PermissionNameEnum;
//# sourceMappingURL=PermissionNameEnum.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"PermissionNameEnum.d.ts","sourceRoot":"","sources":["../../src/permissions/PermissionNameEnum.ts"],"names":[],"mappings":"AAAA,aAAK,kBAAkB;IACtB,WAAW,gBAAgB;IAC3B,aAAa,kBAAkB;IAC/B,IAAI,SAAS;IACb,IAAI,SAAS;IACb,MAAM,WAAW;IACjB,UAAU,eAAe;IACzB,eAAe,qBAAqB;IACpC,cAAc,oBAAoB;IAClC,iBAAiB,uBAAuB;IACxC,kBAAkB,yBAAyB;IAC3C,aAAa,kBAAkB;IAC/B,SAAS,cAAc;IACvB,YAAY,iBAAiB;IAC7B,cAAc,qBAAqB;IACnC,GAAG,QAAQ;IACX,cAAc,oBAAoB;IAClC,mBAAmB,yBAAyB;IAC5C,aAAa,mBAAmB;IAChC,cAAc,oBAAoB;IAClC,cAAc,oBAAoB;IAClC,aAAa,mBAAmB;IAChC,sBAAsB,6BAA6B;IACnD,cAAc,qBAAqB;IACnC,aAAa,mBAAmB;IAChC,gBAAgB,sBAAsB;IACtC,eAAe,qBAAqB;IACpC,UAAU,gBAAgB;IAC1B,qBAAqB,6BAA6B;CAClD;AAED,eAAe,kBAAkB,CAAC"}

View File

@@ -0,0 +1,33 @@
var PermissionNameEnum;
(function (PermissionNameEnum) {
PermissionNameEnum["geolocation"] = "geolocation";
PermissionNameEnum["notifications"] = "notifications";
PermissionNameEnum["push"] = "push";
PermissionNameEnum["midi"] = "midi";
PermissionNameEnum["camera"] = "camera";
PermissionNameEnum["microphone"] = "microphone";
PermissionNameEnum["backgroundFetch"] = "background-fetch";
PermissionNameEnum["backgroundSync"] = "background-sync";
PermissionNameEnum["persistentStorage"] = "persistent-storage";
PermissionNameEnum["ambientLightSensor"] = "ambient-light-sensor";
PermissionNameEnum["accelerometer"] = "accelerometer";
PermissionNameEnum["gyroscope"] = "gyroscope";
PermissionNameEnum["magnetometer"] = "magnetometer";
PermissionNameEnum["screenWakeLock"] = "screen-wake-lock";
PermissionNameEnum["nfc"] = "nfc";
PermissionNameEnum["displayCapture"] = "display-capture";
PermissionNameEnum["accessibilityEvents"] = "accessibility-events";
PermissionNameEnum["clipboardRead"] = "clipboard-read";
PermissionNameEnum["clipboardWrite"] = "clipboard-write";
PermissionNameEnum["paymentHandler"] = "payment-handler";
PermissionNameEnum["idleDetection"] = "idle-detection";
PermissionNameEnum["periodicBackgroundSync"] = "periodic-background-sync";
PermissionNameEnum["systemWakeLock"] = "system-wake-lock";
PermissionNameEnum["storageAccess"] = "storage-access";
PermissionNameEnum["windowManagement"] = "window-management";
PermissionNameEnum["windowPlacement"] = "window-placement";
PermissionNameEnum["localFonts"] = "local-fonts";
PermissionNameEnum["topLevelStorageAccess"] = "top-level-storage-access";
})(PermissionNameEnum || (PermissionNameEnum = {}));
export default PermissionNameEnum;
//# sourceMappingURL=PermissionNameEnum.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"PermissionNameEnum.js","sourceRoot":"","sources":["../../src/permissions/PermissionNameEnum.ts"],"names":[],"mappings":"AAAA,IAAK,kBA6BJ;AA7BD,WAAK,kBAAkB;IACtB,iDAA2B,CAAA;IAC3B,qDAA+B,CAAA;IAC/B,mCAAa,CAAA;IACb,mCAAa,CAAA;IACb,uCAAiB,CAAA;IACjB,+CAAyB,CAAA;IACzB,0DAAoC,CAAA;IACpC,wDAAkC,CAAA;IAClC,8DAAwC,CAAA;IACxC,iEAA2C,CAAA;IAC3C,qDAA+B,CAAA;IAC/B,6CAAuB,CAAA;IACvB,mDAA6B,CAAA;IAC7B,yDAAmC,CAAA;IACnC,iCAAW,CAAA;IACX,wDAAkC,CAAA;IAClC,kEAA4C,CAAA;IAC5C,sDAAgC,CAAA;IAChC,wDAAkC,CAAA;IAClC,wDAAkC,CAAA;IAClC,sDAAgC,CAAA;IAChC,yEAAmD,CAAA;IACnD,yDAAmC,CAAA;IACnC,sDAAgC,CAAA;IAChC,4DAAsC,CAAA;IACtC,0DAAoC,CAAA;IACpC,gDAA0B,CAAA;IAC1B,wEAAkD,CAAA;AACnD,CAAC,EA7BI,kBAAkB,KAAlB,kBAAkB,QA6BtB;AAED,eAAe,kBAAkB,CAAC"}

View File

@@ -0,0 +1,19 @@
import EventTarget from '../event/EventTarget.js';
import Event from '../event/Event.js';
/**
* Permission status.
*
* Reference:
* https://developer.mozilla.org/en-US/docs/Web/API/PermissionStatus
*/
export default class PermissionStatus extends EventTarget {
state: 'granted' | 'denied' | 'prompt';
onchange: ((event: Event) => void) | null;
/**
* Constructor.
*
* @param [state] State.
*/
constructor(state?: 'granted' | 'denied' | 'prompt');
}
//# sourceMappingURL=PermissionStatus.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"PermissionStatus.d.ts","sourceRoot":"","sources":["../../src/permissions/PermissionStatus.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,MAAM,yBAAyB,CAAC;AAClD,OAAO,KAAK,MAAM,mBAAmB,CAAC;AAEtC;;;;;GAKG;AACH,MAAM,CAAC,OAAO,OAAO,gBAAiB,SAAQ,WAAW;IACjD,KAAK,EAAE,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACvC,QAAQ,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC,GAAG,IAAI,CAAQ;IAExD;;;;OAIG;gBACS,KAAK,GAAE,SAAS,GAAG,QAAQ,GAAG,QAAoB;CAI9D"}

View File

@@ -0,0 +1,21 @@
import EventTarget from '../event/EventTarget.js';
/**
* Permission status.
*
* Reference:
* https://developer.mozilla.org/en-US/docs/Web/API/PermissionStatus
*/
export default class PermissionStatus extends EventTarget {
state;
onchange = null;
/**
* Constructor.
*
* @param [state] State.
*/
constructor(state = 'granted') {
super();
this.state = state;
}
}
//# sourceMappingURL=PermissionStatus.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"PermissionStatus.js","sourceRoot":"","sources":["../../src/permissions/PermissionStatus.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,MAAM,yBAAyB,CAAC;AAGlD;;;;;GAKG;AACH,MAAM,CAAC,OAAO,OAAO,gBAAiB,SAAQ,WAAW;IACjD,KAAK,CAAkC;IACvC,QAAQ,GAAoC,IAAI,CAAC;IAExD;;;;OAIG;IACH,YAAY,QAAyC,SAAS;QAC7D,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACpB,CAAC;CACD"}

View File

@@ -0,0 +1,32 @@
import PermissionStatus from './PermissionStatus.js';
import BrowserWindow from '../window/BrowserWindow.js';
/**
* Permissions API.
*
* Reference:
* https://developer.mozilla.org/en-US/docs/Web/API/Permissions.
*/
export default class Permissions {
#private;
/**
* Constructor.
*
* @param window Window.
*/
constructor(window: BrowserWindow);
/**
* Returns scroll restoration.
*
* @param permissionDescriptor Permission descriptor.
* @param permissionDescriptor.name Permission name.
* @param [permissionDescriptor.userVisibleOnly] User visible only.
* @param [permissionDescriptor.sysex] Sysex.
* @returns Permission status.
*/
query(permissionDescriptor: {
name: string;
userVisibleOnly?: boolean;
sysex?: boolean;
}): Promise<PermissionStatus>;
}
//# sourceMappingURL=Permissions.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"Permissions.d.ts","sourceRoot":"","sources":["../../src/permissions/Permissions.ts"],"names":[],"mappings":"AAAA,OAAO,gBAAgB,MAAM,uBAAuB,CAAC;AAErD,OAAO,aAAa,MAAM,4BAA4B,CAAC;AAEvD;;;;;GAKG;AACH,MAAM,CAAC,OAAO,OAAO,WAAW;;IAM/B;;;;OAIG;gBACS,MAAM,EAAE,aAAa;IAOjC;;;;;;;;OAQG;IACU,KAAK,CAAC,oBAAoB,EAAE;QACxC,IAAI,EAAE,MAAM,CAAC;QACb,eAAe,CAAC,EAAE,OAAO,CAAC;QAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;KAChB,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAmB7B"}

View File

@@ -0,0 +1,42 @@
import PermissionNameEnum from './PermissionNameEnum.js';
/**
* Permissions API.
*
* Reference:
* https://developer.mozilla.org/en-US/docs/Web/API/Permissions.
*/
export default class Permissions {
#permissionStatus = {};
#window;
/**
* Constructor.
*
* @param window Window.
*/
constructor(window) {
if (!window?.document) {
new TypeError('Invalid constructor');
}
this.#window = window;
}
/**
* Returns scroll restoration.
*
* @param permissionDescriptor Permission descriptor.
* @param permissionDescriptor.name Permission name.
* @param [permissionDescriptor.userVisibleOnly] User visible only.
* @param [permissionDescriptor.sysex] Sysex.
* @returns Permission status.
*/
async query(permissionDescriptor) {
if (this.#permissionStatus[permissionDescriptor.name]) {
return this.#permissionStatus[permissionDescriptor.name];
}
if (!Object.values(PermissionNameEnum).includes(permissionDescriptor.name)) {
throw new Error(`Failed to execute 'query' on 'Permissions': Failed to read the 'name' property from 'PermissionDescriptor': The provided value '${permissionDescriptor.name}' is not a valid enum value of type PermissionName.`);
}
this.#permissionStatus[permissionDescriptor.name] = new this.#window.PermissionStatus('granted');
return this.#permissionStatus[permissionDescriptor.name];
}
}
//# sourceMappingURL=Permissions.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"Permissions.js","sourceRoot":"","sources":["../../src/permissions/Permissions.ts"],"names":[],"mappings":"AACA,OAAO,kBAAkB,MAAM,yBAAyB,CAAC;AAGzD;;;;;GAKG;AACH,MAAM,CAAC,OAAO,OAAO,WAAW;IAC/B,iBAAiB,GAEb,EAAE,CAAC;IACP,OAAO,CAAgB;IAEvB;;;;OAIG;IACH,YAAY,MAAqB;QAChC,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC;YACvB,IAAI,SAAS,CAAC,qBAAqB,CAAC,CAAC;QACtC,CAAC;QACD,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;IACvB,CAAC;IAED;;;;;;;;OAQG;IACI,KAAK,CAAC,KAAK,CAAC,oBAIlB;QACA,IAAI,IAAI,CAAC,iBAAiB,CAAC,oBAAoB,CAAC,IAAI,CAAC,EAAE,CAAC;YACvD,OAAO,IAAI,CAAC,iBAAiB,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC;QAC1D,CAAC;QAED,IACC,CAAC,MAAM,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC,QAAQ,CAAqB,oBAAoB,CAAC,IAAI,CAAC,EACzF,CAAC;YACF,MAAM,IAAI,KAAK,CACd,mIAAmI,oBAAoB,CAAC,IAAI,qDAAqD,CACjN,CAAC;QACH,CAAC;QAED,IAAI,CAAC,iBAAiB,CAAC,oBAAoB,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,gBAAgB,CACpF,SAAS,CACT,CAAC;QAEF,OAAO,IAAI,CAAC,iBAAiB,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC;IAC1D,CAAC;CACD"}