Лицензия, редактор, пульт и сборка

- Main: license service, IPC, router; закрытие окон; yauzl закрытие zip (EMFILE), zipRead тест
- Editor: стабильный projectState без мигания, логотип и меню, строки UI, LayoutShell overlay
- Control: ластик для всех типов эффектов, затухание/нарастание музыки при смене сцены
- Сборка: vite, build/dev scripts, obfuscate-main и build-env скрипты с тестами; package.json

Made-with: Cursor
This commit is contained in:
Ivan Fontosh
2026-04-19 20:11:24 +08:00
parent 5e7dc5ea19
commit 2fa20da94d
40 changed files with 2629 additions and 211 deletions
+19
View File
@@ -0,0 +1,19 @@
/** Детерминированная JSON-сериализация для подписи (совпадает с `lib/canonicalJson.mjs` в DndGamePlayerLicenseServer). */
export function canonicalJson(value: unknown): string {
if (value === null) return 'null';
const t = typeof value;
if (t === 'number') {
if (!Number.isFinite(value as number)) throw new TypeError('non-finite number in license payload');
return JSON.stringify(value);
}
if (t === 'string' || t === 'boolean') return JSON.stringify(value);
if (Array.isArray(value)) {
return `[${value.map((x) => canonicalJson(x)).join(',')}]`;
}
if (t === 'object') {
const o = value as Record<string, unknown>;
const keys = Object.keys(o).sort();
return `{${keys.map((k) => `${JSON.stringify(k)}:${canonicalJson(o[k])}`).join(',')}}`;
}
throw new TypeError(`unsupported type in license payload: ${t}`);
}