Skip to content

Verificar y auditar ​

larapack:verify mira hacia dentro: que lo generado siga en su sitio y diga lo mismo que el contrato. larapack:audit mira hacia fuera: que el paquete encaje con los demás del ecosistema. Los dos salen con código distinto de cero cuando algo falla, así que sirven de puerta en la CI y de comprobación para un agente.

Ninguno intenta adivinar si una clase «tiene demasiada lógica»: esa clase de comprobación produce falsos positivos y acaba desactivándose. Sólo comprueban lo que es determinista.

larapack:verify ​

bash
php vendor/bin/builder larapack:verify
php vendor/bin/builder larapack:verify --strict          # los avisos cuentan como fallo
php vendor/bin/builder larapack:verify --format=json

Lee .larapack/manifest.json y los archivos. No recibe el laraimport: la forma de cada modelo (sus acciones, si es inmutable, sus secretos) la saca de la declaration que el manifiesto guarda al generar.

ComprobaciónNivelQué detecta
empty-manifestavisoEl manifiesto no registra ningún modelo. O no se ha generado nada, o .larapack/manifest.json no está en la raíz que se está verificando. Es el único hallazgo en ese caso.
missing-fileerrorUn archivo que se generó y ya no existe.
customisedinfoUn archivo editado a mano desde que se generó: --force no lo tocará.
inconsistent-entityavisoA un modelo le falta un componente que tienen todos los demás modelos de su misma forma.
route-prefixerrorEl API_ROUTE_PREFIX del contrato JS no coincide con el nombre que registra el RouteServiceProvider, o no está declarado.
route-not-declarederrorUna ruta, un método del controlador, un request o un componente de interfaz de una acción que el modelo no declara.
immutable-writeerrorUn camino de escritura en un modelo immutable, o su modelo sin la guarda que rechaza modificaciones.
secret-exposederrorUna columna secret que vuelve a salir.

Cada comprobación, en detalle ​

missing-file recorre todos los archivos del manifiesto, también los del paquete. Regenera con --force o retira la entrada del manifiesto.

customised es informativo: editar lo generado es lo normal en los huecos. Lo que hay que mirar es qué archivo aparece: un trait, una política o un listener están bien; un controlador, un archivo de rutas o el modelo son deriva.

inconsistent-entity sólo se evalúa con dos modelos o más, y compara cada modelo con los que declararon su misma forma: las mismas acciones y el mismo immutable. Un componente es la primera carpeta de la plantilla (Policy, Requests, ModelView…), y sólo cuenta el que tienen todos los demás de esa forma. Las metas no cuentan: que un modelo las tenga y otro no es una decisión.

route-prefix lee el index.js de cada modelo y espera api. + el namespace en minúsculas con puntos + el modelo en snake_case + .: api.acme.catalogo.order_line., o api.app.order_line. en una aplicación. Si no coincide, el front llama a rutas que no existen sin que nada falle al compilar.

route-not-declared busca, para cada acción que el modelo no declara:

  • en el archivo de rutas, un ->name('<nombre de ruta>');
  • en el controlador, un método público con el nombre de la acción;
  • el archivo Http/Requests/<Model>/<Acción>Request.php;
  • en cada módulo de interfaz, sus componentes: views/AdminView y widgets/DataTable para index, views/ShowView para show, forms/CreateForm y views/CreateView para create, y forms/EditForm y views/EditView para update.

Existen porque alguien los escribió a mano. Declara la acción en el laraimport, o retíralos. En un modelo inmutable, las escrituras las cuenta immutable-write.

immutable-write busca esas mismas huellas para update, delete, restore, forceDelete, bulkUpdate y bulkDelete, y comprueba que el modelo conserve static::updating( y static::deleting( en booted(). Sin esa guarda, cualquier save() o delete() del propio código modifica una fila que no debería cambiar.

secret-exposed, para cada columna secret, falla si:

  • no está en $hidden del modelo;
  • está en $export_cols;
  • el recurso la nombra (->token_hash, makeVisible('token_hash') o una clave 'token_hash' =>), aunque esté en $hidden;
  • es columna de la tabla en el contrato JS (id: 'token_hash').

Qué hacer con el resultado ​

  • Sale con 1 si hay algún error, o algún aviso con --strict. customised nunca hace fallar.
  • La salida JSON tiene la forma de la salida de larapack:verify.
  • Un agente ha terminado cuando verify sale con 0 y sólo marca como customised los huecos.

larapack:audit ​

bash
php vendor/bin/builder larapack:audit                     # el paquete actual
php vendor/bin/builder larapack:audit packages --all      # cada paquete de un directorio
php vendor/bin/builder larapack:audit --format=json --strict

Compara el paquete con la línea base: el ecosystem.json del LaraPack que ejecuta el comando. Actualizar LaraPack actualiza la línea base.

json
{
    "php": { "require": "^8.3", "matrix": ["8.3", "8.4"] },
    "laravel": { "illuminate": "^13.0", "testbench": "^11.0" },
    "dev": { "phpunit/phpunit": "^12.0 || ^13.0", "laravel/pint": "^1.18", "larastan/larastan": "^3.0" },
    "node": { "engines": ">=20", "ci": "22" },
    "js": { "vite": "^7.1.0", "vitest": "^3.0.0", "vue": "^3.5.0", "react": "^19.0.0" },
    "internal": {
        "innoboxrr/larapack-generator": "^7.10",
        "innoboxrr/traits": "^2.1",
        "innoboxrr/support": "^2.1",
        "innoboxrr/search-surge": "^3.0"
    },
    "internalNpm": { "innoboxrr-form-core": "^2.8.0", "…": "…" },
    "generated": { "require": { "laravel/sanctum": "^4.3", "maatwebsite/excel": "^4.0" } },
    "required": {
        "composer": ["name", "description", "license", "autoload"],
        "npm": ["name", "version", "license", "type", "exports", "files", "sideEffects"]
    },
    "workflows": { "composer": ["tests.yml", "release.yml"], "npm": ["tests.yml", "release.yml"] }
}

(Extracto; internalNpm son las versiones que declara el módulo generado, y generated, lo que larapack:new añade a require.)

El audit detecta solo qué es el directorio: si tiene composer.json lo audita como paquete de Composer, si tiene package.json como paquete npm, y si tiene los dos, de las dos formas.

Comprobaciones de Composer ​

CódigoNivelQué detecta
composer-fieldavisoFalta name, description, license o autoload.
composer-versionerrorcomposer.json fija version. Composer descarta cada tag que no coincide con ella, así que una versión nueva quedaría invisible.
php-missingerrorrequire no declara php.
php-versionsegún la reglaLa restricción de php frente a ^8.3.
illuminate-missingerrorEl paquete usa Laravel y no declara ningún illuminate/* ni laravel/framework. Usa Laravel si algún src/*.php menciona Illuminate\, o si tiene src/Providers, database/migrations o routes.
illuminate-versionsegún la reglaCada illuminate/* o laravel/framework, en require o require-dev, frente a ^13.0.
testbench-versionsegún la reglaorchestra/testbench, si se declara, frente a ^11.0.
internal-versionsegún la reglaCada dependencia de internal que se declare, en require o require-dev.
phpunit-configerrorNo hay phpunit.xml ni phpunit.xml.dist.
no-testserrorNo hay ningún *Test.php en tests/.
workflow-missingerrorFalta .github/workflows/tests.yml o release.yml.
bump-patcherror o avisoQueda bump-patch.yml. Es error si publica sin esperar a los tests, y aviso si espera a ellos con workflow_run o needs: tests: tiene puerta, pero deriva la versión del último tag en vez de declararla.

Comprobaciones de npm ​

CódigoNivelQué detecta
npm-fieldavisoFalta name, version, license, type, exports, files o sideEffects. sideEffects: false cuenta como declarado.
node-enginesavisoengines.node no es exactamente >=20.
js-versionsegún la reglavite, vitest, vue o react en dependencies o devDependencies. Las peerDependencies no se comprueban: dicen contra qué puede funcionar la librería, y exigirles la línea base las estrecharía sin ganar nada.
workflow-missingerrorFalta tests.yml o release.yml.
bump-patcherror o avisoIgual que en Composer.

Un directorio sin composer.json ni package.json da not-a-package (aviso).

El módulo generado no se audita solo

El audit lee el composer.json y el package.json de la ruta que recibe. El package.json de resources/vue o resources/react sólo se audita si pasas esa ruta; sus versiones innoboxrr-* son las de internalNpm, y pide Vite 8, mientras que la línea base js es la de los paquetes npm del ecosistema, que siguen en Vite 7.

La regla de las restricciones ​

Lo que importa no es que el texto coincida, sino qué versiones admite la restricción. Hay tres casos:

CasoQué significaResultado
ExactaAdmite justo la línea base.Nada.
AnchaAdmite la línea base y además versiones fuera de ella.Aviso: es legítimo, pero la matriz de CI tiene que cubrir de verdad lo que promete.
EstrechaNo admite toda la línea base.Error: el paquete no se podría instalar donde el ecosistema dice que funciona.

Con la línea base de php en ^8.3: ^8.3 es exacta, ^8.2 es ancha y ^8.4 es estrecha. Con illuminate/support en ^13.0, una librería que declara ^12.0 || ^13.0 es ancha. Una restricción que no se puede leer da un aviso con el mismo código.

Pedir un parche concreto: el patrón conflict ​

A veces un paquete necesita un arreglo de un parche, por ejemplo LaraPack 7.10.2. Subir require a ^7.10.2 es estrecho frente a la línea base ^7.10 y bloquea la publicación. Lo correcto es mantener require en la línea base y excluir lo anterior con conflict, que el audit no compara:

json
{
    "require-dev": {
        "innoboxrr/larapack-generator": "^7.10"
    },
    "conflict": {
        "innoboxrr/larapack-generator": "<7.10.2"
    }
}

Composer no instalará una versión anterior al arreglo y el audit sigue en verde.

En la CI ​

  • Sale con 1 si hay algún error, o algún aviso con --strict, y también si la ruta no existe.
  • tests.yml del ecosistema lo ejecuta antes de la suite, así que un paquete que no cumple la línea base no llega a publicarse. Ver La línea base y Los workflows.
  • Antes de empujar, ejecútalo en local: php vendor/innoboxrr/larapack-generator/builder larapack:audit .
  • La salida JSON tiene la forma de la salida de larapack:audit.