/** Детерминированная 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}`);
}
