/inspect shows the public note under the verdict, as text of the creator
that nobody checked, with the warning of Go's showNote. The pages no longer
show the message of an unexpected exception: unexpectedProblem says in
Spanish what to do, and logs the exception. vite.config.ts pre-bundles the
dependencies the pages load on demand, so that the dev server does not
reload the page on the first opening and break the import in flight.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ -4,6 +4,13 @@ Cambios notables de la librería TypeScript y de la página. El proyecto usa ver
## Especificación 0.10, en la rama `v0.10` — sin versión
### La nota pública en `/inspect` (06-10-2026)
- `/inspect` muestra la nota pública justo debajo del veredicto: el texto del creador, en la letra serif de la página y sobre el fondo de aviso, con dos líneas que dicen que nadie la ha comprobado, que la lee cualquiera que tenga el fichero y que antes de la fecha nadie puede comprobar quién creó la cápsula ni si va firmada, como `showNote` de la CLI de Go. Una nota que no cumple las reglas de texto no se muestra, y la página lo dice.
- `buildReport` lleva la nota en `Report.note`, con su prueba. Comprobado en el navegador con el fixture `format3_note`.
- **Errores que no se esperaban.** Las páginas ya no enseñan el mensaje de una excepción, que podía venir en inglés y con una URL interna, como «Failed to fetch dynamically imported module: http://localhost:5188/src/lib/inspector/opener.ts». `unexpectedProblem` de `format.ts` lo dice en español y con lo que hay que hacer: recargar si no llegó una parte de la página, volver a elegir un fichero que el navegador ya no deja leer, liberar espacio, o, en otro caso, recargar y usar la herramienta de línea de órdenes. La excepción va a la consola. Lo usan la apertura, la lectura de una cápsula en `/inspect` y la creación en `/create`.
- **El servidor de desarrollo ya no recarga la página al abrir.**`vite.config.ts` hace que Vite prepare desde el arranque las dependencias que las páginas cargan bajo demanda (`optimizeDeps.include`). Sin eso, la primera apertura le hacía preparar dos módulos de noble y recargar la página, y la carga en curso fallaba con ese mensaje. `dependencies.test.ts` comprueba que la lista está completa. La versión compilada no tenía el problema.
### NAT64, del borrador v0.13 (06-10-2026)
- `testdata` se sincroniza con `datekeys-go` en `a83b44d`, el borrador v0.13, sin aprobar: añade `vectors/resolved_ip.json`, y ningún otro fichero cambia. `SPEC_VERSION` sigue en `0.12`.
@ -140,6 +140,7 @@ Sitio SvelteKit estático (`@sveltejs/adapter-static`, `strict`): las tres pági
- cada paso con su número, su nombre de la CLI, `superado` o el código normativo, y el detalle (los caracteres invisibles o de control se escriben como `\uXXXX`);
- el veredicto, `capsule_id`, la DateKey compacta y decodificada (red y ronda), el perfil fijado, la fecha de apertura en UTC y en la hora local del navegador, `access_policy`, los campos del prelude con el formato de la cápsula (con un aviso en el veredicto si es el 1, que no oculta el número de credenciales ni la longitud exacta del contenido, §55.2), los argumentos del stanza `tlock` frente al perfil fijado y el número y tipo de stanzas de OUTER_TIME_AGE y PAYLOAD_AGE;
- la nota pública, justo debajo del veredicto, como texto del creador sin comprobar, en la letra serif de la página y sobre el fondo de aviso, con el aviso de que antes de la fecha nadie puede comprobar quién creó la cápsula ni si va firmada, como `showNote` de la CLI de Go; si la cabecera lleva una nota que no cumple las reglas de texto, solo dice que no se muestra (§24.1);
- las extensiones de PUBLIC_HEADER según el contrato del plan §8: id (entre comillas y escapado si tiene caracteres no imprimibles), versión, crítica o no, conocida o no, longitud y hex (plegado si pasa de 64 bytes); texto si los bytes son UTF-8 imprimible; vista CBOR con `walk` si son un ítem del perfil de §58, marcada "informativo, no validado por el protocolo"; y el aviso de que son públicas, de que nada las vincula al resto de la cápsula antes del paso 15 y de que ni entonces prueban autoría (§55.1);
- **Copiar JSON**, que copia exactamente la salida de `datekeys inspect -json` (`cliJSON`: el `json.Encoder` de Go con sangría de dos espacios, `<`, `>`, `&`, U+2028 y U+2029 escapados y salto de línea final). `file` es el nombre del fichero.
constlazy=newTypeError('Failed to fetch dynamically imported module: http://localhost:5188/src/lib/inspector/opener.ts');
expect(unexpectedProblem('abrir la cápsula',lazy)).toBe(
'No se pudo abrir la cápsula: la página no consiguió cargar una de sus partes. Recarga la página y vuelve a intentarlo.',
);
expect(log).toHaveBeenCalledWith(lazy);
expect(unexpectedProblem('x',newTypeError('Importing a module script failed.'))).toMatch(/cargar una de sus partes/);
expect(unexpectedProblem('x',newTypeError('error loading dynamically imported module'))).toMatch(/cargar una de sus partes/);
expect(unexpectedProblem('leer a.dkc',newDOMException('The file could not be read','NotReadableError'))).toBe(
'No se pudo leer a.dkc: el navegador ya no deja leer el fichero, quizá porque cambió o se movió. Vuelve a elegirlo.',
);
expect(unexpectedProblem('x',newDOMException('gone','NotFoundError'))).toMatch(/Vuelve a elegirlo/);
expect(unexpectedProblem('crear la cápsula',newDOMException('full','QuotaExceededError'))).toMatch(/no deja más espacio/);
constother=unexpectedProblem('crear la cápsula',newError('capsule: internal error: secret'));
expect(other).toBe(
'No se pudo crear la cápsula por un fallo de la página. Recarga la página y vuelve a intentarlo; si vuelve a pasar, usa la herramienta de línea de órdenes de DateKeys.',
);
expect(unexpectedProblem('x','a string')).toMatch(/por un fallo de la página/);
return`No se pudo ${what}: el navegador ya no deja leer el fichero, quizá porque cambió o se movió. Vuelve a elegirlo.`;
}
if(name==='QuotaExceededError'){
return`No se pudo ${what}: el navegador no deja más espacio a esta página. Libera espacio y vuelve a intentarlo.`;
}
return`No se pudo ${what} por un fallo de la página. Recarga la página y vuelve a intentarlo; si vuelve a pasar, usa la herramienta de línea de órdenes de DateKeys.`;
import {typeCapsulePlan,chooseFiles,typeCreateField,downloadName,planCapsule,readRecipients} from '$lib/inspector/create-input.ts';
import {escapeInvisible,formatByteCount,formatDateTime,formatInteger,formatRelative, viewerTimeZone } from '$lib/inspector/format.ts';
import {escapeInvisible,formatByteCount,formatDateTime,formatInteger,formatRelative,unexpectedProblem,viewerTimeZone } from '$lib/inspector/format.ts';
import {isTimeZone,localParts,supportedTimeZones,timeZoneList,UTC} from '$lib/inspector/localtime.ts';
import {buildReport,typeReport} from '$lib/inspector/report.ts';
import {browserPlatform,CREATE_AREA,createTempFile,freeSpace,OPEN_AREA,removeStaleTempFiles,typeTempFile} from '$lib/inspector/tempfile.ts';
@ -453,7 +453,7 @@
notice = announcement =
err instanceof TooManyFiles
? tooMany()
: `No se pudo leer lo que has soltado: ${escapeInvisible(errinstanceofError?err.message : String(err))}`;
: unexpectedProblem('leer lo que has soltado', err);
} finally {
reading = false;
}
@ -470,7 +470,7 @@
try {
c = await loadChecker();
} catch (err) {
await fail(`No se pudieron cargar las reglas de las rutas: ${escapeInvisible(errinstanceofError?err.message : String(err))}`);
await fail(unexpectedProblem('preparar la comprobación de los nombres de los ficheros', err));
return;
}
if (epoch !== createId || busy || reading) return;
@ -587,7 +587,7 @@
nowMs = Date.now();
await fail(PAST, 'date');
} else {
await fail(`No se pudo crear la cápsula: ${escapeInvisible(errinstanceofError?err.message : String(err))}`);
await fail(unexpectedProblem('crear la cápsula', err));
import {typeCapsuleBytes,inspect,readCapsule} from '$lib/dkc/index.ts';
import {FIXTURES,typeFixture,fetchFixture} from '$lib/inspector/fixtures.ts';
import {buildReport,typeReport} from '$lib/inspector/report.ts';
import {displayText,escapeInvisible,viewerTimeZone} from '$lib/inspector/format.ts';
import {displayText,unexpectedProblem,viewerTimeZone} from '$lib/inspector/format.ts';
import {browserPlatform,CREATE_AREA,OPEN_AREA,removeStaleTempFiles} from '$lib/inspector/tempfile.ts';
import InspectionReport from '$lib/components/InspectionReport.svelte';
import OpenPanel from '$lib/components/OpenPanel.svelte';
@ -102,7 +102,7 @@
if (/Web Crypto/.test(message)) {
return 'Este navegador solo ofrece Web Crypto (crypto.subtle) en contextos seguros, y el inspector lo necesita para comprobar el perfil fijado. Abre la página por https o en localhost.';
}
return `No se pudo leer ${name}: ${escapeInvisible(message)}`;
return unexpectedProblem(`leer ${name}`, err);
}
function inspectFile(file: File, readNotice?: string): void {