servicios-linea-once / primevuekit
Kit de componentes PrimeVue para aplicaciones Laravel.
Package info
github.com/servicios-linea-once/primevuekit
pkg:composer/servicios-linea-once/primevuekit
Requires
- php: ^8.3
- bacon/bacon-qr-code: ^3.0
- illuminate/console: ^12.0 || ^13.0
- illuminate/contracts: ^12.0 || ^13.0
- illuminate/database: ^12.0 || ^13.0
- illuminate/notifications: ^12.0 || ^13.0
- illuminate/process: ^12.0 || ^13.0
- illuminate/support: ^12.0 || ^13.0
- pragmarx/google2fa: ^9.0
Requires (Dev)
- inertiajs/inertia-laravel: ^3.3
- larastan/larastan: ^3.10
- laravel/pint: ^1.27
- orchestra/testbench: ^10.0 || ^11.0
- pestphp/pest: ^4.0 || ^5.0
- pestphp/pest-plugin-laravel: ^4.0 || ^5.0
Suggests
- inertiajs/inertia-laravel: ^3.3 — necesario para las páginas de autenticación (lo instala primevuekit:install)
- laravel/fortify: ^1.39 — sólo si eliges la estrategia fortify en primevuekit:auth
Provides
None
Conflicts
None
Replaces
None
README
Paquete local de la aplicación, instalado mediante un repositorio path de Composer
(servicios-linea-once/primevuekit). Su código se autocarga bajo el namespace PrimeVueKit\.
Estado
Service provider con auto-discovery, configuración publicable, el comando
primevuekit:install para verificar e instalar el stack de frontend, y suite de pruebas con
Pest + Testbench. Todavía no expone componentes propios de PrimeVue.
Instalación en la aplicación
Ya está declarado en el composer.json de la raíz:
"repositories": [ { "type": "path", "url": "servicios-linea-once/primevuekit" } ], "require": { "primevuekit/primevuekit": "@dev" }
Composer crea un enlace simbólico, así que cualquier cambio en servicios-linea-once/primevuekit/src
se refleja de inmediato en la aplicación.
primevuekit:install
Verifica e instala el stack de frontend (Vue 3 + Inertia + PrimeVue + Ziggy) y aplica el wiring mínimo sobre la aplicación.
# Diagnóstico: no escribe nada y sale con código 1 si falta algo (apto para CI) php artisan primevuekit:install --check # Instalación completa php artisan primevuekit:install
| Opción | Efecto |
|---|---|
--check |
Sólo diagnostica. Sin efectos secundarios. Salida 1 si falta algo |
--primevue=4|5 |
Línea de PrimeVue. 4 (por defecto) es MIT; 5 usa la licencia PrimeUI |
--composer=<ruta> |
Ruta absoluta al binario de Composer (por defecto, el composer del PATH) |
--force |
Sobrescribe los archivos de wiring ya existentes |
--no-scaffold |
Instala dependencias sin tocar archivos de la aplicación |
--no-demo |
Omite la ruta y la página Inertia de demostración |
Qué instala
Composer: inertiajs/inertia-laravel:^3.3 y tightenco/ziggy:^2.6.
npm: vue, @inertiajs/vue3, ziggy-js, primevue, @primeuix/themes, primeicons y,
como dependencias de desarrollo, @vitejs/plugin-vue y tailwindcss-primeui.
Las versiones viven en un único sitio,
src/Installer/DependencySet.php; es ahí donde se suben.
Las dos líneas de PrimeVue
npm install primevue resuelve hoy a la 5.x, que ya no es MIT: se distribuye bajo la
licencia PrimeUI y exige una clave de licencia. La última línea MIT es la 4.x. El comando fija
siempre la versión y mantiene coherente la tripleta, porque primevue y @primeuix/themes
comparten @primeuix/styled y mezclar majors rompe el modo styled en silencio:
| Línea | primevue |
@primeuix/themes |
primeicons |
Licencia |
|---|---|---|---|---|
--primevue=4 (por defecto) |
^4.5.5 |
^2.0.3 |
^7.0.0 |
MIT |
--primevue=5 |
^5.0.1 |
^3.0.0 |
^8.0.0 |
PrimeUI, clave obligatoria |
Con --primevue=5 el comando avisa de las condiciones de la licencia y pide confirmación,
pero no configura la clave.
Wiring que aplica
Todas las operaciones son idempotentes; si un archivo no encaja con el patrón esperado el comando avisa con la instrucción manual en lugar de dejarlo a medias.
| Archivo | Cambio |
|---|---|
vite.config.js |
Registra @vitejs/plugin-vue |
resources/js/app.js |
Bootstrap de Inertia + PrimeVue (preset Aura) + ZiggyVue, conservando el import './echo' |
resources/css/app.css |
Añade @import 'tailwindcss-primeui'; |
resources/views/app.blade.php |
Vista raíz con @routes, @vite y los componentes <x-inertia::head /> / <x-inertia::app /> |
bootstrap/app.php |
Añade HandleInertiaRequests al grupo web |
routes/web.php |
Ruta primevuekit.demo en /primevuekit |
resources/js/Pages/PrimeVueKit/Welcome.vue |
Página Inertia de demostración |
bootstrap/app.php sólo se toca si app/Http/Middleware/HandleInertiaRequests.php existe:
referenciar una clase inexistente rompería todas las peticiones.
Node dentro de Docker
El contenedor app (php:8.4-fpm) no trae Node. Cuando el comando no encuentra el binario
no falla: imprime el comando equivalente para el servicio vite.
docker compose run --rm vite npm install vue@^3.5.42 ... --ignore-scripts
Como node_modules vive en un volumen de Docker, el diagnóstico distingue declarada (está
en package.json / composer.json) de instalada (presente en node_modules / vendor), y
sólo considera un problema lo primero.
CORS del dev server de Vite
La aplicación se sirve en :8000 y el dev server de Vite en :5173, así que cargar
resources/js/app.js es una petición cross-origin. Vite 8 endureció esto a raíz de
CVE-2025-24010: sólo responde con Access-Control-Allow-Origin a orígenes que conoce. Si el
navegador muestra has been blocked by CORS policy al cargar app.js, hay que declarar el
origen de la aplicación en vite.config.js:
server: { cors: { origin: [env.APP_URL, 'http://localhost:8000'] }, }
Caso aparte: si el origen del error es null, la página no está en una pestaña normal sino
dentro de un documento embebido (about:srcdoc: el panel de vista previa del IDE, un iframe en
sandbox, file://). Lo sano es abrirla en una pestaña de verdad; si necesitas la vista previa,
añade 'null' a la lista de orígenes, detrás de una variable de entorno y sólo en desarrollo:
permite que cualquier contexto aislado lea el código fuente del dev server.
primevuekit:auth
Instala la autenticación. Menú interactivo (Laravel Prompts) para elegir el backend; el segundo factor por correo (OTP) y el de aplicación (TOTP) se instalan siempre, en las tres estrategias.
php artisan primevuekit:auth # pregunta la estrategia php artisan primevuekit:auth --check # diagnóstico, salida 1 si falta algo php artisan primevuekit:auth --strategy=kit php artisan migrate
| Estrategia | Backend | Dónde vive el código |
|---|---|---|
fortify |
laravel/fortify ^1.39 (login, registro, reset, verificación y TOTP) |
app/Actions + provider publicados por fortify:install |
kit |
Rutas, controladores y páginas del paquete | En el paquete, se actualiza con él |
manual |
El mismo código, publicado | En tu aplicación, para editarlo |
Exige que primevuekit:install se haya ejecutado antes: sin Inertia y Vue no hay dónde
montar las páginas.
Por qué no hay opción de Breeze
laravel/breeze se declara a sí mismo legacy — su README en la rama 2.x dice "This starter
kit is for Laravel 11.x and prior" — y laravel.com/docs/13.x/starter-kits ya no lo menciona.
Además breeze:install vue fija inertiajs/inertia-laravel:^2.0, @inertiajs/vue3:^2.0.0 y
tailwindcss:^3.2.1, y sobrescribe resources/js/app.js, vite.config.js,
resources/css/app.css, routes/web.php, app.blade.php y HandleInertiaRequests.php: es
decir, destruiría el stack que instaló primevuekit:install. El preset kit cubre el mismo
terreno sin degradar nada.
Segundo factor
- TOTP (RFC 6238) con
pragmarx/google2fay QR en SVG conbacon/bacon-qr-code, las mismas librerías que usa Fortify, así que no hay conflicto de versiones. El secreto se guarda cifrado y el factor no se activa hasta confirmarlo con un código válido. Hay 8 códigos de recuperación cifrados que se consumen de uno en uno. - OTP por correo: código de 6 dígitos generado con
random_int, guardado hasheado, válido 5 minutos, 5 intentos como máximo, reenvío limitado a 1 por minuto y 5 por hora. Emitir uno nuevo invalida el anterior. La notificación va en cola. - Columnas: las tres de Fortify (
two_factor_secret,two_factor_recovery_codes,two_factor_confirmed_at) mástwo_factor_last_used_counteryemail_otp_enabled, y la tablaemail_otp_codes. La migración es condicional, así que convive con la de Fortify. - Todo es configurable en
config/primevuekit.php, secciónauth.
bacon/bacon-qr-code y su dependencia dasprid/enum son BSD-2-Clause (permisiva y compatible
con MIT, pero no MIT); el resto de dependencias del kit son MIT.
Rutas y páginas
Las rutas se enganchan desde routes/web.php de la aplicación, como hace Breeze, y no desde
el service provider: así una app que sólo quiera los componentes de PrimeVue no acaba con un
/login que no ha pedido. El comando añade \PrimeVueKit\Auth\AuthRoutes::register(); (preset
kit) o require __DIR__.'/auth.php'; (modo manual). Si detecta la línea de la otra
estrategia avisa en vez de registrar las rutas dos veces.
| Ruta | Nombre | Página |
|---|---|---|
GET/POST /login, POST /logout |
login, logout |
Auth/Login |
GET/POST /register |
register |
Auth/Register |
GET/POST /forgot-password, /reset-password/{token} |
password.* |
Auth/ForgotPassword, Auth/ResetPassword |
GET /verify-email, /verify-email/{id}/{hash} |
verification.* |
Auth/VerifyEmail |
GET/POST /confirm-password |
password.confirm |
Auth/ConfirmPassword |
GET/POST /two-factor-challenge |
two-factor.challenge |
Auth/TwoFactorChallenge |
GET/POST /otp-challenge, POST /otp-challenge/resend |
otp.challenge, otp.resend |
Auth/EmailOtpChallenge |
GET/POST/DELETE /user/two-factor, /user/email-otp |
two-factor.*, email-otp.* |
Auth/TwoFactor |
Las pantallas de reto están protegidas por EnsureChallengeIsPending: sin credenciales
validadas redirigen al login. El enrolamiento va detrás de password.confirm.
El modelo de usuario recibe los traits y los contratos SupportsEmailOtp y SupportsTotp,
de forma que los controladores comprueban capacidades con instanceof en lugar de
method_exists. Con --strategy=fortify no se añade SupportsTotp: ese factor es de Fortify.
Panel y perfil
Al terminar, el comando copia en la aplicación un punto de partida para la zona
autenticada. Esto se publica siempre, en las tres estrategias: un panel y un perfil son
de cada proyecto y se editan desde el primer día, así que no tiene sentido servirlos desde el
paquete. Los stubs viven en stubs/dashboard/ y ya traen el namespace
App\, no hay nada que reescribir. Con --no-dashboard se omite.
app/Http/Controllers/DashboardController.php
app/Http/Controllers/Profile/{ProfileController,PasswordController}.php
app/Http/Requests/Profile/{UpdateProfileRequest,UpdatePasswordRequest}.php
routes/dashboard.php → require desde routes/web.php
resources/js/Layouts/AppLayout.vue → Menubar + menú de usuario
resources/js/Pages/Dashboard.vue → estado de la cuenta y de los factores
resources/js/Pages/Profile/Edit.vue
resources/js/Pages/Profile/Partials/{UpdateProfileForm,UpdatePasswordForm,DeleteAccountForm}.vue
Rutas: dashboard (tras auth + verified), profile.edit, profile.update,
profile.destroy y profile.password.update.
El publicador también añade las props compartidas que el layout necesita en
HandleInertiaRequests::share(). Comparte sólo cuatro atributos del usuario:
'user' => $request->user()?->only('id', 'name', 'email', 'email_verified_at'),
Compartir el modelo entero enviaría two_factor_secret y two_factor_recovery_codes al
navegador, porque no están en $hidden.
Cambiar el correo en el perfil invalida la verificación y reenvía el enlace. Eliminar la cuenta exige la contraseña actual, cierra la sesión y la invalida.
Para que el login lleve al panel en vez de a /, pon PRIMEVUEKIT_HOME=/dashboard en .env.
Aspecto de las pantallas
Las 9 páginas usan layouts/AuthCard.vue: una tarjeta partida en dos, con
panel de marca en degradado a la izquierda —oculto por debajo de lg— y el formulario a la derecha. El degradado
sale de los tokens del tema (--p-primary-500/700/900), así que cambiar la paleta cambia toda la UI sin tocar
ninguna página.
components/AuthIllustration.vue— escudo, cubos y esferas en SVG inline con animación de flotación, que se desactiva conprefers-reduced-motion. Sin assets binarios.components/AuthSubmit.vue— el botón principal en degradado, centralizado para no repetir la cadena de clases nueve veces.- Los campos usan
IftaLabel(etiqueta dentro de la caja) másIconField+InputIconpara el icono a la derecha. Las contraseñas usanPassword toggle-mask, que ya trae el ojo, y los códigosInputOtp.
La paleta va en el preset de PrimeVue, no en clases fijas. El stub
stubs/app.js.stub la define con definePreset(Aura, …); para cambiarla, edita los tonos
primary ahí (o en el resources/js/app.js ya generado).
Publicar la configuración
php artisan vendor:publish --tag=primevuekit-config