You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
senzapaura_es/package.json

71 lines
2.6 KiB

{
"name": "senzapaura",
"private": true,
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"prepare": "svelte-kit sync || echo ''",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --check . && eslint .",
"format": "prettier --write .",
"test:unit": "vitest",
"test": "npm run test:unit -- --run && npm run test:e2e",
"test:e2e": "playwright install && node scripts/construir.mjs && playwright test",
"db:push": "drizzle-kit push",
"db:generate": "drizzle-kit generate",
"db:migrate": "node --env-file=.env scripts/db/migrar.mjs",
"db:studio": "drizzle-kit studio",
"medios": "node scripts/medios.mjs",
"predev": "node scripts/medios.mjs",
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>
1 month ago
"prebuild": "node scripts/medios.mjs",
"db:guardas": "node --env-file=.env scripts/db/comprobar-guardas.mjs",
La puerta del panel, y una portada que dice qué falta Fase 6. Está lo que todo lo demás necesita: quién entra y por dónde. El rol vive en `usuario.rol`, con un CHECK que impide inventarse uno, y se da con `npm run db:admin`. En una variable de entorno con correos habría sido más rápido, pero entonces revocar a alguien pide un despliegue y a la pregunta «quién administra esto» solo sabe contestar quien pueda leer la configuración del servidor. La guarda está en el `+layout.server.ts` de la carpeta y no en cada ruta: una pantalla nueva queda protegida por el hecho de estar dentro. Sin sesión, a entrar con la vuelta puesta; con sesión y sin el rol, 404 y no 403, porque un 403 confirma que el panel existe y dónde vive. Los tres casos, comprobados contra la aplicación de verdad. La portada contesta «qué falta» y no «cuánto hay». Cada sección trae lo que tiene, lo que está a medias y los nombres de lo que está a medias: decir «1 género sin ficha» obliga a ir a buscar cuál, y esa búsqueda ya la ha hecho la consulta. Dos viajes a la base para las siete filas. Fuera Sveltia CMS y `static/admin/`. Editaba los Markdown de `src/content/`, que desde la fase 4 no lee nadie: guardaba cambios que no salían en pantalla, y además tapaba la ruta nueva. Y de paso, la prueba de contacto que fallaba en la tanda completa y pasaba a solas. No era aislamiento: `page.goto` vuelve al cargar, no al hidratar, y al hidratar Svelte reescribe el valor de cada `input` con el del componente. Lo tecleado antes de ese instante se perdía, y solo le pasaba al primer campo y solo con la máquina cargada. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
4 weeks ago
"db:admin": "node --env-file=.env scripts/db/hacer-admin.mjs",
"db:volcar": "node --experimental-strip-types --import ./scripts/db/resolver-lib.mjs --env-file=.env scripts/db/volcar.mjs",
"db:medios": "node --env-file=.env scripts/db/volcar-medios.mjs",
Ejemplos de versos en la ficha, y dónde escuchar cada obra La ficha decía que el bolero va en octosílabo y endecasílabo alternados sin enseñar nunca uno. Un taller que describe la métrica y no la muestra está pidiendo que se la imaginen. `verso` es la estrofa y no el verso suelto —la rima y la alternancia de medidas no se ven en menos de dos versos— y `verso_linea` lleva cada uno con su escansión: `silabas` es la métrica y no la del diccionario, así que la sinalefa mete dos palabras en una casilla —«za⁀a»—, que es justo lo que en un texto corrido no se ve. `medida` va aparte porque un verso agudo suma una sílaba y uno esdrújulo resta una: «yo respondo con el son» tiene siete casillas y es un octosílabo. Los diez ejemplos son originales, escritos para enseñar, y entran marcados con `fuente = 'ejemplo'`. Ninguno es cita: para eso está `obra_referencia.cita`, y dos CHECK lo sostienen —un verso «cita» sin obra, o uno «propio» sin canción, no entran—. La escansión se comprueba antes de cargarla, contra la ley del acento final, y el sembrador aborta si algo no cuadra. Un ejemplo mal contado enseña a contar mal y eso no lo detecta ningún tipo ni ningún lint: por eso la comprobación está en su propio módulo, con nueve pruebas. `obra_referencia_enlace` sustituye a la columna `enlace`, que era texto suelto y solo daba sitio para uno; una obra está en Spotify **y** en YouTube. Comparte el vocabulario `PLATAFORMAS` con los enlaces de las canciones. De momento son búsquedas y no grabaciones concretas: una URL inventada manda a un 404 con pinta de dato bueno, y los enlaces exactos llegarán en el JSON de la ficha. De paso, dos cosas que se vieron al montarlo: - El orden de la ficha cambia. Primero lo que se usa para escribir —ejemplos, rasgos, temática— y después lo que sitúa el género. Es un taller: quien abre la página viene a escribir una letra. - `repeat(auto-fit, minmax(24rem, 1fr))` desbordaba ochenta píxeles en un móvil de 320, porque la rejilla pone la columna aunque no quepa. Ahora es `min(24rem, 100%)`, en los tres sitios donde estaba. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
"db:generos": "node --env-file=.env scripts/db/sembrar-generos.mjs",
El catálogo de la base ya es idéntico al de los archivos Antes de cambiar veinte rutas de sitio, poder demostrar que sale lo mismo. `npm run db:comparar` pone lado a lado las dieciocho canciones, los cuatro discos y los cuatro estilos, campo a campo, y dice dónde difieren con la ruta exacta —`dorina.registro.reparto[0].porcentaje`—. Confiar en que las pruebas de interfaz noten una diferencia de un año o de un porcentaje sería confiar de más: pintan lo que les den. La primera pasada encontró ciento y pico diferencias, y no eran del comparador: - **Ningún medio estaba enlazado.** El volcado del catálogo y el de archivos se hicieron por separado, así que las veintitrés canciones tenían `audio_id`, `muestra_id` y `portada_id` a nulo. La web habría dejado de reproducir en cuanto leyera de la base. No se vio al volcar porque aquello comprobaba las letras carácter a carácter —lo delicado— y no que las relaciones estuvieran puestas. - **Las etiquetas volvían en orden alfabético.** No es cosmético: la primera etiqueta de un tema dice de qué va y las de después matizan. Ahora `cancion_etiqueta` tiene `orden`. - **La nota de cada versión no tenía columna** donde caer. - El país de edición se guardaba en ninguna parte, y los cuatro discos se quedaron sin él. `scripts/db/completar-catalogo.mjs` ata esos cabos y es idempotente. Los alias de `medio` son de verdad y no la misma tabla dos veces: sin ellos Postgres devuelve la misma fila para el audio y para la portada. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
"db:versos": "node --env-file=.env scripts/db/sembrar-versos.mjs",
El glosario del taller, con su globo en el texto Lo que se pidió: «cada vez que aparezca un término del glosario en el texto, estará marcado para visualizarse con un popup con la explicación». Veintinueve términos con acepciones numeradas —«clave» es el patrón rítmico y el instrumento de percusión, y con una sola definición una de las dos sería falsa—, más las formas con las que cada palabra aparece de verdad: plurales, femeninos, con tilde y sin ella. El marcado va sobre el HTML ya compuesto y no sobre el Markdown: antes habría que esquivar la sintaxis, y un término dentro de un enlace o de un bloque de código es justo donde un reemplazo de texto se equivoca en silencio. Se marca una vez por término y por apartado, no en cada aparición: nueve «compás» subrayados no hay quien los lea. La pieza tiene más casos raros que cualquier otra del sitio y ninguno se ve mirando la página —el fallo es que un globo no salga, y nadie echa en falta lo que no sabe que existía—, así que va separada de la base y con doce pruebas. Dos las escribí creyendo que ya pasaban: - Marcaba «sinalefas» del final del párrafo en vez de la «sinalefa» de la primera línea, porque probaba forma por forma en orden de longitud. - Con las formas sin ordenar marcaba «rima» y no «rima asonante». Salía bien solo porque la consulta las devolvía ordenadas; ahora el desempate está en la función, que no puede depender de cómo se la llame. El globo es un botón y no un `title`: un tooltip nativo no se abre con teclado y en un móvil no existe. Se cierra con Escape devolviendo el foco, y se coloca debajo del término o encima si no cabe, porque tapar lo que se acaba de pulsar es lo peor que podría hacer. El glosario entero tiene además su página, donde sí cabe la ampliación que en un globo taparía el texto. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
"db:glosario": "node --env-file=.env scripts/db/sembrar-glosario.mjs",
El catálogo de la base ya es idéntico al de los archivos Antes de cambiar veinte rutas de sitio, poder demostrar que sale lo mismo. `npm run db:comparar` pone lado a lado las dieciocho canciones, los cuatro discos y los cuatro estilos, campo a campo, y dice dónde difieren con la ruta exacta —`dorina.registro.reparto[0].porcentaje`—. Confiar en que las pruebas de interfaz noten una diferencia de un año o de un porcentaje sería confiar de más: pintan lo que les den. La primera pasada encontró ciento y pico diferencias, y no eran del comparador: - **Ningún medio estaba enlazado.** El volcado del catálogo y el de archivos se hicieron por separado, así que las veintitrés canciones tenían `audio_id`, `muestra_id` y `portada_id` a nulo. La web habría dejado de reproducir en cuanto leyera de la base. No se vio al volcar porque aquello comprobaba las letras carácter a carácter —lo delicado— y no que las relaciones estuvieran puestas. - **Las etiquetas volvían en orden alfabético.** No es cosmético: la primera etiqueta de un tema dice de qué va y las de después matizan. Ahora `cancion_etiqueta` tiene `orden`. - **La nota de cada versión no tenía columna** donde caer. - El país de edición se guardaba en ninguna parte, y los cuatro discos se quedaron sin él. `scripts/db/completar-catalogo.mjs` ata esos cabos y es idempotente. Los alias de `medio` son de verdad y no la misma tabla dos veces: sin ellos Postgres devuelve la misma fila para el audio y para la portada. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
"db:completar": "node --env-file=.env scripts/db/completar-catalogo.mjs",
"db:comparar": "node --experimental-strip-types --import ./scripts/db/resolver-lib.mjs --env-file=.env scripts/db/comparar-catalogo.mjs"
},
"devDependencies": {
"@eslint/js": "^10.0.1",
"@playwright/test": "^1.60.0",
"@sveltejs/adapter-node": "^5.5.4",
"@sveltejs/kit": "^2.63.0",
"@sveltejs/vite-plugin-svelte": "^7.1.2",
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>
1 month ago
"@types/js-yaml": "^4.0.9",
"@types/node": "^22",
"@types/pg": "^8.23.1",
"@vitest/browser-playwright": "^4.1.8",
"drizzle-kit": "^0.31.10",
"eslint": "^10.4.1",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-svelte": "^3.19.0",
"globals": "^17.6.0",
"js-yaml": "^5.4.1",
"mdsvex": "^0.12.7",
"playwright": "^1.60.0",
"prettier": "^3.8.3",
"prettier-plugin-svelte": "^4.1.0",
"remark-breaks": "^4.0.0",
"svelte": "^5.56.1",
"svelte-check": "^4.6.0",
"typescript": "^6.0.3",
"typescript-eslint": "^8.60.1",
"vite": "^8.0.16",
"vitest": "^4.1.8",
"vitest-browser-svelte": "^2.1.1"
},
"dependencies": {
"drizzle-orm": "^0.45.2",
"marked": "^18.0.12",
"pg": "^8.23.0",
"stripe": "^22.6.1"
}
}

Powered by TurnKey Linux.