Лицензия, редактор, пульт и сборка
- 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:
@@ -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}`);
|
||||
}
|
||||
Reference in New Issue
Block a user