codex/estilos-web-publica
codex/redisenio-dominio-musical
master
${ noResults }
5 Commits (be28a296935edf2c570929544d94cca231cca765)
| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
e0a979bf5d |
La ficha de género pierde la barra lateral y gana su franja de datos
Los datos del género —compás, metro, tempo, rima— pasan de una lista de definiciones apretada en una columna estrecha a la derecha a una franja a todo lo ancho encima del texto. El compás y la métrica de un género se leen juntos o no se leen, y en la lateral quedaban en letra pequeña con el texto escorado. La estructura, la temática y los rasgos bajan debajo de la prosa como bloques a lo ancho: la temática en rejilla —son etiquetas disfrazadas de frase— y los rasgos en lista, que son frases de verdad. Es el sitio donde entrarán los instrumentos, los letristas, las obras de referencia y el linaje cuando tengan datos. Y el volcado de medios: 55 archivos a la tabla `medio` con su huella, su peso y su duración MEDIDA, no escrita. De ahí salen dos cosas que no se veían: el mismo audio está copiado bajo doce nombres distintos, y las duraciones del catálogo dicen 252 segundos donde el archivo dura 6. La clave de un medio lleva su almacén delante, porque `singles/dorina.mp3` existe en audio y en muestras y sin eso serían la misma fila. Es además la ruta con la que se servirán desde m.senzapaura.es/archives. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
4 weeks ago |
|
|
54df820056 |
Vuelca el catálogo a PostgreSQL, y lo comprueba
Los 48 archivos entran en la base: 18 canciones con sus 5 versiones como filas hermanas, 4 discos, 4 estilos, 8 géneros, 14 capítulos, 9 personas, 53 etiquetas, 2 entradas, 5 legales y una página. La web sigue leyendo de los archivos; esto solo llena el sitio donde caben. Reutiliza los analizadores del sitio en vez de leer el frontmatter otra vez. Es la decisión que sostiene todo lo demás: si un valor por defecto o una normalización cambian, cambian para los dos. Un segundo analizador «parecido» es lo que garantiza que un día la base y la web cuenten cosas distintas sin que nadie lo note. Todo en una transacción, y al final comprueba lo escrito contra lo leído —la letra de cada tema carácter a carácter— porque «no dio error» no es lo mismo que «está bien». Si no cuadra, deshace y dice qué falló. Y es idempotente: se puede lanzar las veces que haga falta sin duplicar nada, sin tocar cuentas, pedidos ni compras. Para poder reutilizar ese código desde Node hizo falta un gancho de resolución —`$lib`, imports sin extensión y un sustituto de `$app/paths`— y quitar una propiedad de parámetro de `ErrorDeContenido`: es sintaxis exclusiva de TypeScript y ningún cargador que solo borre tipos puede con ella. Y una corrección: el cuerpo del Markdown de una canción NO era un comentario, es la letra. Lo dije al revés en el documento del modelo y lo repetí varias veces. El campo `letra:` del frontmatter —que aparece en un solo archivo— son los créditos de quién la firma. Corregido en el documento, en el esquema, y quitada la columna `nota` que sobraba. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
4 weeks ago |
|
|
1a8c904ae8 |
Fase 2: el esquema entero en PostgreSQL
52 tablas nuevas, una vista y las guardas que Drizzle no sabe expresar. El sitio sigue leyendo de los archivos: esto solo crea el sitio donde caben. El esquema pasa de archivo a carpeta —con el catálogo dentro pasaría de las dos mil líneas—, y el corte se hizo por rangos de línea para no reescribir los comentarios que explican decisiones. Se comprobó que era un no-op: «No schema changes, nothing to migrate». Lo que Drizzle no genera va escrito a mano al final de la migración, y es justo lo que impide que el modelo mienta: la clave foránea de las versiones contra su propia tabla, dos columnas generadas y una clave compuesta que impiden a la vez la versión de una versión y los ciclos, la vista `cancion_principal`, el disparador que exige que el reparto de autoría sume cien, el que impide ciclos en el linaje de géneros, y los índices parciales del estilo principal y de la arista principal. Y las guardas se comprueban intentando lo que deben rechazar, en `scripts/db/comprobar-guardas.mjs`. Que una restricción exista en `pg_constraint` no significa que impida nada. Tres cosas que salieron de hacerlo y no de suponerlo: - `creado_en` era obligatorio SIN valor por defecto en la base: lo ponía JavaScript al insertar con Drizzle, así que cualquier INSERT escrito a mano fallaba y la invariante la sostenía la aplicación, no la tabla. Ahora es `defaultNow()`. - `drizzle-kit migrate` se atasca con los cuerpos `$$` de plpgsql. La migración se aplicó con `scripts/db/probar-migracion.mjs`, que además dice en qué sentencia falla, y se anotó en su registro. - Dos pruebas pasaban por el motivo equivocado: una por clave duplicada en vez de por la suma, y otra porque un disparador aplazado no salta dentro de un savepoint que nunca se cierra. Las dos corregidas; una guarda dada por buena sin ejecutarse es peor que no tenerla. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
4 weeks ago |
|
|
3aebf91085 |
Mueve la base de datos de SQLite a PostgreSQL
El usuario ha puesto un servidor con PostgreSQL 16, así que la base pasa allí. El catálogo no se toca: sigue siendo Markdown del repositorio; lo que se muda es lo que generan las personas, que es lo único que estaba en la base. Dos diferencias entre motores que habrían pasado desapercibidas y se cazaron comprobando con datos de verdad, no compilando: - `integer` en Postgres son cuatro bytes y en SQLite ocho. El limitador de envíos guarda su vencimiento en milisegundos desde 1970 —trece dígitos, del orden del billón— y en `integer` desbordaría, dejando de limitar. Va en `bigint`. - `avg()` y `count()` los entrega el driver como cadena para no perder precisión. Sin convertirlas, `cuantas === 0` nunca se cumpliría y «1 voto» saldría siempre en plural. Se convierten en la consulta. Verificado contra el servidor: once tablas creadas, las 113 pruebas de extremo a extremo y las 224 unitarias en verde, el servidor de producción arrancando, el limitador escribiendo sus trece dígitos y el borrado en cascada llevándose valoraciones y favoritos al borrar una cuenta. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
1 month ago |
|
|
c25d432d30 |
Web de Senza Paura: catálogo, taller, blog y tienda
Sitio del letrista y compositor, con SvelteKit 2 y Svelte 5. El catálogo es contenido, no base de datos: estilos, álbumes, canciones y capítulos son Markdown en `src/content/`, validados al compilar y compilados dentro del bundle, así que recorrer la música no hace ni una consulta. La base de datos guarda solo lo que generan las personas: cuentas, carritos, pedidos, compras, valoraciones y favoritos. Lo que hay: - Música agrupada por estilos, con página propia para cada estilo, álbum y tema, buscador y tabla ordenable. - Reproductor persistente que sobrevive a la navegación. Suena un fragmento; el archivo completo solo se obtiene comprando. - Taller de letras: los principios del oficio y una ficha de creación por estilo hispanoamericano. - Blog con feed RSS, y etiquetas que cruzan blog y catálogo. - Tienda con cuentas por código de correo, carrito, Stripe y descargas protegidas. - Panel de contenido en `/admin` que escribe los mismos Markdown. - Tema oscuro de marca y su inversión clara, aplicado antes de pintar. Lo que falta y no es código: las claves de Stripe y de correo, el tratamiento del IVA, las páginas de condiciones y privacidad, y el material real. Las canciones de ejemplo son inventadas y están para sustituirse; el audio son marcadores salvo un tema. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
1 month ago |