{"_id":"@alphasoft/alpha-pkg","_rev":"2-2abbe0a379f9396af77ba7615a6063c2","name":"@alphasoft/alpha-pkg","dist-tags":{"latest":"0.1.1"},"versions":{"0.1.0":{"name":"@alphasoft/alpha-pkg","version":"0.1.0","keywords":["alpha","alphasoft","alphaadmin","packages","extensions","cli"],"author":{"name":"AlphaSoft, C.A."},"license":"MIT","_id":"@alphasoft/alpha-pkg@0.1.0","maintainers":[{"name":"alphasoft","email":"alejandro@diosenticonfio.com"}],"bin":{"alpha-pkg":"bin/alpha-pkg.js"},"dist":{"shasum":"50536524d10adef7e14bee87bb7204eeb84ef8e3","tarball":"https://registry.npmjs.org/@alphasoft/alpha-pkg/-/alpha-pkg-0.1.0.tgz","fileCount":44,"integrity":"sha512-ec4P2pOwtMwYx2k0JLpILgmr80m5GNnwkN5WhUONCKeymqL1bT6DStAyTBYSRSHRx97jsXz/SfaZ0O7jPc/hQA==","signatures":[{"sig":"MEUCIGOsPywNYUVxT8qRWzh7OyxwU3Wb3wZlp6l/Stq1/4LgAiEApsfJORdMVMvd/Imi8Sz9jLGSDfq+RtEwxv+ddU+gQ5M=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":112272},"main":"src/index.js","engines":{"node":">=18.0.0"},"gitHead":"2199f53dc7c4e39cc6225fda43d7f69a372cb5a2","scripts":{"test":"node tests/run.js","start":"node bin/alpha-pkg.js"},"_npmUser":{"name":"alphasoft","email":"alejandro@diosenticonfio.com"},"_npmVersion":"10.9.2","description":"CLI para desarrollar, validar, empaquetar y publicar extensiones de Alpha Administrativo","directories":{},"_nodeVersion":"22.14.0","dependencies":{"ora":"^5.4.1","axios":"^1.7.7","chalk":"^4.1.2","semver":"^7.6.3","adm-zip":"^0.5.16","prompts":"^2.4.2","commander":"^12.1.0","form-data":"^4.0.1"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/alpha-pkg_0.1.0_1779852713767_0.7110044450697375","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@alphasoft/alpha-pkg","version":"0.1.1","description":"CLI para desarrollar, validar, empaquetar y publicar extensiones de Alpha Administrativo","bin":{"alpha-pkg":"bin/alpha-pkg.js"},"main":"src/index.js","scripts":{"start":"node bin/alpha-pkg.js","test":"node tests/run.js"},"engines":{"node":">=18.0.0"},"keywords":["alpha","alphasoft","alphaadmin","packages","extensions","cli"],"author":{"name":"AlphaSoft, C.A."},"license":"MIT","dependencies":{"adm-zip":"^0.5.16","axios":"^1.7.7","chalk":"^4.1.2","commander":"^12.1.0","form-data":"^4.0.1","ora":"^5.4.1","prompts":"^2.4.2","semver":"^7.6.3"},"_id":"@alphasoft/alpha-pkg@0.1.1","gitHead":"ba58804973d90ca7a405a6e7783549205be60156","_nodeVersion":"22.14.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-s+TdVZVTNKwrFlFrqR/1rztXTz97HwaTxxV4VidwBQGz5Dm8jLeyVyRj2atVS0+C6ND4+hg5gfuW8bIUndlh7Q==","shasum":"f612b88adce748901cdc756845ad85ec5058b758","tarball":"https://registry.npmjs.org/@alphasoft/alpha-pkg/-/alpha-pkg-0.1.1.tgz","fileCount":44,"unpackedSize":112910,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCICGh2ydvwpqM1y03PGkt7qPfTQbrC6Z8EA0T7/8yoxKkAiAfb7xiRa1DTHcQTxhzLURInObrP3Dp2m+Tjw0IpVIK/A=="}]},"_npmUser":{"name":"alphasoft","email":"alejandro@diosenticonfio.com"},"directories":{},"maintainers":[{"name":"alphasoft","email":"alejandro@diosenticonfio.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/alpha-pkg_0.1.1_1781110401381_0.7199164832676705"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-27T03:31:53.568Z","modified":"2026-06-10T16:53:21.624Z","0.1.0":"2026-05-27T03:31:53.920Z","0.1.1":"2026-06-10T16:53:21.518Z"},"author":{"name":"AlphaSoft, C.A."},"license":"MIT","keywords":["alpha","alphasoft","alphaadmin","packages","extensions","cli"],"description":"CLI para desarrollar, validar, empaquetar y publicar extensiones de Alpha Administrativo","maintainers":[{"name":"alphasoft","email":"alejandro@diosenticonfio.com"}],"readme":"# `@alphasoft/alpha-pkg`\n\nCLI para desarrollar, validar, empaquetar y publicar extensiones de Alpha Administrativo.\n\n## Instalación\n\n```bash\nnpm install -g @alphasoft/alpha-pkg\n```\n\nVerifica:\n\n```bash\nalpha-pkg --version\n```\n\n## Comandos\n\n### `alpha-pkg init <name>`\n\nCrea un nuevo paquete con la estructura estándar Alpha.\n\n```bash\nalpha-pkg init mi-paquete                       # template fullstack (default)\nalpha-pkg init mi-paquete --template=renderer   # solo UI, sin backend\nalpha-pkg init mi-paquete --template=report     # helper de reporte\nalpha-pkg init mi-paquete --author=\"Juan Pérez\" --description=\"...\"\nalpha-pkg init mi-paquete --yes                 # no preguntar, usar defaults\n```\n\n**Convención de nombres**: kebab-case (minúsculas + dígitos + guiones). Ej: `inventario-helper`, `factura-electronica`. Esto es el `keyName`, debe ser único en el store.\n\nTemplates disponibles:\n\n| Template    | Para qué                                                                                                        |\n| ----------- | --------------------------------------------------------------------------------------------------------------- |\n| `fullstack` | Backend (`index.js`) + renderer (`render.js`) + manifest. Default.                                              |\n| `renderer`  | Solo UI, sin backend. Consume APIs estándar del cliente.                                                        |\n| `report`    | Helper de reporte (`report: true`, se monta en `/ReportDataLoad/`).                                             |\n| `extension` | Inyecta campos custom + handlers (`onClienteChange`, etc.) en VenCore/Facturas o ComCore/Compras. Sin renderer. |\n| `crud`      | DataGrid + Dialog + 5 endpoints REST + EmpDbMgr pattern. UI base para módulos de negocio.                       |\n| `importer`  | XLSX/CSV upload + preview + commit. `xlsx` lib + `npmInstall: true`.                                            |\n| `downbar`   | Widget compacto para la barra inferior (`PackageLoaderDownBar`). Badge + popover + polling.                     |\n\n### `alpha-pkg lint [path]`\n\nValida el manifest y los archivos referenciados.\n\n```bash\nalpha-pkg lint              # en cwd\nalpha-pkg lint ./mi-paquete # carpeta específica\n```\n\nReporta:\n\n- **Errores** (bloquean publish): campos requeridos faltantes, semver inválido, archivos referenciados que no existen, schema inválido.\n- **Warnings** (no bloquean): categoría no estándar, `extensions.model` desconocido, sin `main`/`renderer`/`extensions` declarado.\n\n### `alpha-pkg build [path]`\n\nEmpaqueta el zip listo para publicar.\n\n```bash\nalpha-pkg build                  # bumpa patch + zips\nalpha-pkg build --bump=minor\nalpha-pkg build --bump=none      # no bumpear versión\nalpha-pkg build --output=./dist  # custom output\nalpha-pkg build --no-lint        # saltar lint (no recomendado)\n```\n\nProduce `./dist/<keyName>-<version>.zip` excluyendo:\n\n- `node_modules/`, `dist/`, `.git/`, `*.log`, `.DS_Store`\n- `.alpha-git-token`, `.alpha-git-meta.json` (sidecars del cliente)\n\nSincroniza la versión en `package.json` si existe.\n\n### `alpha-pkg link [path]`\n\n**Iteración instantánea** — symlinkea tu carpeta de desarrollo dentro de\n`packages/` de Alpha Admin local. Sin copiar archivos: editas en tu repo y\nel cliente lo ve al instante (con `hotReload: true` el backend se recarga\nsolo al guardar).\n\n```bash\nalpha-pkg link                                 # cwd\nalpha-pkg link ./mi-paquete\nalpha-pkg link --host=http://localhost:4545    # cambiar host de Alpha Admin\n```\n\nRequisitos:\n\n- Alpha Admin local corriendo\n- **DevMode habilitado** en Configuración → DevMode = `true` (o `ALPHA_DEV_MODE=true`)\n- Request desde localhost/LAN (rechaza requests remotas por seguridad)\n\n### `alpha-pkg unlink <keyName>`\n\nRemueve el symlink. **NO toca tu carpeta original** (distinto de `/pkg/uninstall`\nque hace rm -rf en la carpeta real).\n\n```bash\nalpha-pkg unlink mi-paquete\n```\n\n### `alpha-pkg publish [path]`\n\nSube el paquete al store. Hace `build` automáticamente (a menos que `--no-build`).\n\n```bash\nalpha-pkg publish                                       # interactivo\nalpha-pkg publish --bump=minor --changelog=\"Fix bug X\"\nalpha-pkg publish --no-build                            # usa ./dist/ ya construido\nalpha-pkg publish --prerelease                          # marca como pre-release\nalpha-pkg publish --store-url=https://store.alphadministrativo.app \\\n                  --email=admin@example.com --password=...\n```\n\nSi el paquete no existe en el store, ofrece crearlo desde el manifest local.\n\n## Configuración\n\nEn orden de precedencia:\n\n1. **Flags CLI** (`--store-url`, `--email`, `--password`)\n2. **Variables de entorno**: `ALPHA_STORE_URL`, `ALPHA_STORE_EMAIL`, `ALPHA_STORE_PASSWORD`\n3. **`.alpharc`** en cwd\n4. **`.alpharc`** en `$HOME`\n5. Defaults (`storeUrl = https://store.alphadministrativo.app`)\n\nEjemplo `.alpharc`:\n\n```json\n{\n  \"storeUrl\": \"https://store.alphadministrativo.app\",\n  \"email\": \"dev@mi-empresa.com\"\n}\n```\n\n> **No commits el password en `.alpharc`** si vives en repo público. Mejor `ALPHA_STORE_PASSWORD` en tu shell o keychain del SO.\n\n## Flujo recomendado\n\n```bash\n# Día 1: nuevo paquete\nalpha-pkg init mi-paquete --template=fullstack\ncd mi-paquete\nnpm install                  # si necesitas deps\ngit init && git add -A && git commit -m \"init\"\n\n# Iteración\n# ...editas index.js / renderer/render.js...\nalpha-pkg lint               # antes de cada commit\nalpha-pkg build --bump=none  # probar el zip localmente\n\n# Probar en local (DevMode)\n# 1. activa LocalConfig.DevMode = \"true\" en tu AlphaAdmin\n# 2. en la tab \"Paquetes Personalizados\" del store, clona/symlink este repo\n# 3. abre el módulo desde el sidebar\n\n# Publicar al store\nalpha-pkg publish --bump=patch --changelog=\"Bugfix descripción\"\n```\n\n## Estructura generada\n\n```\nmi-paquete/\n├── mi-paquete.appkg     # manifest (DENTRO de la carpeta, convención v1+)\n├── index.js             # backend Express router (omitir si renderer-only)\n├── renderer/\n│   └── render.js        # frontend (function __Module)\n├── package.json         # si tienes deps npm\n├── .encryptIgnore       # patrones a no encriptar al publicar (opcional)\n├── .gitignore\n└── README.md\n```\n\n---\n\n# Reference — Escribiendo paquetes Alpha\n\nEsta sección contiene lo crítico que necesitas para escribir un paquete que\ncargue correctamente. Si algo no está aquí, consulta el doc completo en el\nrepo del cliente: `docs/Contexto - Extensiones JS.md`.\n\n## El manifest `.appkg`\n\nVive **dentro** de la carpeta (`mi-paquete/mi-paquete.appkg`). Es JSON puro.\n\n```json\n{\n  \"name\": \"MiPaquete\",\n  \"version\": \"1.0.0\",\n  \"author\": \"Tu Nombre\",\n  \"description\": \"Hace X y Y\",\n  \"category\": \"utils\",\n  \"minVersion\": \"0.9.10\",\n  \"requiresCores\": [\"VenCore\"],\n  \"npmInstall\": false,\n\n  \"main\": [{ \"routeName\": \"api\", \"routePath\": \"index.js\", \"hotReload\": true }],\n  \"renderer\": [\n    {\n      \"opcName\": \"MiPaquete\",\n      \"opcPath\": \"renderer\",\n      \"opcMain\": \"index\",\n      \"opcFile\": \"render\",\n      \"opcType\": \"page\",\n      \"Icon\": \"ph:cube\"\n    }\n  ],\n  \"downbar\": [\n    /* widgets en barra inferior, opcional */\n  ],\n  \"extensions\": [\n    /* inject de campos en Facturas/Compras, opcional */\n  ]\n}\n```\n\n| Campo           | Significado                                                            |\n| --------------- | ---------------------------------------------------------------------- |\n| `routeName`     | URL pública será `/pkgs/<keyName>/<routeName>/...`                     |\n| `routePath`     | Archivo `.js` relativo al folder del paquete                           |\n| `hotReload`     | `true` → chokidar recarga el archivo al guardar (solo en `.js` planos) |\n| `opcName`       | nombre visible en sidebar                                              |\n| `opcFile`       | nombre del archivo del renderer (sin `.js`)                            |\n| `Icon`          | iconify id (recomendado `ph:*` Phosphor)                               |\n| `requiresCores` | el cliente solo descarga si `Empresa.Cores` los incluye                |\n| `npmInstall`    | si `true`, el cliente corre `npm install` tras descomprimir            |\n\n`routePath` y `opcFile` declaran `.js` aunque el store los encripte a `.js-alpcrt`\n— el desktop resuelve ambos automáticamente. **No declares `.js-alpcrt`**.\n\n## Backend (`main`) — API del paquete\n\nEl archivo `routePath` (típicamente `index.js`) exporta `{ main }` que devuelve\nun `express.Router`.\n\n```js\nconst main = (AlphaRequire) => {\n  const express = AlphaRequire(\"express\");\n  const router = express.Router();\n\n  router.get(\"/items\", async (req, res) => {\n    const empresaId = req.headers[\"empresa\"]; // ← inyectado automáticamente\n    res.json({ empresaId, items: [] });\n  });\n\n  return router;\n};\n\nmodule.exports = { main };\n```\n\n**`AlphaRequire(name)`** te da acceso a `require()` desde el contexto del\ncliente Alpha — incluye `express`, `mongoose`, y todo lo que tenga en su\n`node_modules` (puedes pedir libs del core de Alpha como `EmpDbMgr`).\n\n### Multi-empresa: `EmpDbMgr`\n\nCada empresa vive en su propia BD MongoDB (`Alp_<EmpresaId>`). Para escribir/leer\ndatos de una empresa:\n\n```js\nconst main = (AlphaRequire) => {\n  const EmpDbMgr = AlphaRequire(\"../EmpDbMgr\");\n  const Schema = AlphaRequire(\"../../models/InvCore/ProductosGeneral\");\n\n  router.get(\"/productos\", async (req, res) => {\n    const conn = await EmpDbMgr.ConnectToEmpDb(req.headers[\"empresa\"]);\n    const Productos = conn.db.model(\"ProductosGenerals\", Schema);\n    res.json(await Productos.find({}).limit(50).lean());\n  });\n  // ...\n};\n```\n\n| Path desde `controllers/StoragePkg/`     | Acceso                 |\n| ---------------------------------------- | ---------------------- |\n| `AlphaRequire(\"../EmpDbMgr\")`            | Conexiones por empresa |\n| `AlphaRequire(\"../../models/InvCore/X\")` | Schemas de inventario  |\n| `AlphaRequire(\"../../models/VenCore/X\")` | Schemas de ventas      |\n| `AlphaRequire(\"../../utils/X\")`          | Utilidades del core    |\n\n### Hot-reload y timers\n\n`hotReload: true` re-ejecuta `main()` al guardar el `.js`. **Si arrancas un\n`setInterval`, guarda el id a nivel de módulo** y limpia antes de re-crear,\nsi no acumulas timers duplicados:\n\n```js\nlet _intervalId = null; // top-level, sobrevive a re-requires\n\nconst main = (AlphaRequire) => {\n  if (_intervalId) clearInterval(_intervalId);\n  _intervalId = setInterval(() => syncCycle(), 5 * 60 * 1000);\n  return router;\n};\n```\n\n## Renderer — gotchas críticos\n\n### 1. Una sola `function __Module` por archivo\n\nEl `PackageLoader` del cliente transforma así:\n\n```js\nconst code = cd\n  .replace('\"use strict\";', \"\")\n  .replace(\"function __Module\", \"(function\") // ← solo la PRIMERA ocurrencia\n  .trim();\n```\n\nImplica:\n\n- ❌ NO declares helpers / sub-componentes / constantes **fuera** de `__Module`\n- ❌ NO uses `import` / `export` en el renderer\n- ✅ TODO vive dentro de `function __Module`\n\n### 2. Sub-componentes con estado → `useMemo` para fijar referencia\n\nSi declaras `function MiSub()` adentro de `__Module` y lo usas como `<MiSub />`,\ncada render crea una nueva referencia → React la trata como tipo distinto →\ndesmonta + monta → `useEffect` dispara cada vez → si hace `setState` en el\npadre → **loop infinito**.\n\n```js\nfunction __Module({ MUI, Deps }) {\n  const { useState, useMemo } = Deps.React;\n\n  // La fábrica corre UNA vez; la referencia es estable entre renders.\n  const MiSub = useMemo(\n    () =>\n      function MiSub({ items, onAdd }) {\n        const [q, setQ] = useState(\"\");\n        return (\n          <MUI.TextField value={q} onChange={(e) => setQ(e.target.value)} />\n        );\n      },\n    [],\n  );\n\n  return <MiSub items={items} onAdd={handleAdd} />;\n}\n```\n\nSi el sub NO necesita estado, una función pura que devuelve JSX es OK\n(`const renderHeader = () => <MUI.AppBar />`).\n\n### 3. Callbacks a sub-componentes → `useCallback`\n\nSin memoizar, cada render crea una nueva fn → si el sub la usa como dep de\n`useEffect`, se re-corre cada render. `setState` en la callback → loop.\n\n```js\nconst handleAdd = useCallback(async (id) => { ... }, [refrescar]);\nreturn <MiSub onAdd={handleAdd} />;\n```\n\n### 4. El ErrorBoundary enmascara errores con \"process is not defined\"\n\nEl `ErrorBoundary` del cliente referencia `process.env.*` que en el browser\nno existe. Cuando tu renderer lanza CUALQUIER error y entra al boundary, ESE\nse rompe y muestra `ReferenceError: process is not defined`. El error real\nqueda oculto.\n\nCuando veas ese mensaje, **NO lo tomes literal**. Mira la consola del browser\npara el `console.log` que `PackageLoader` imprime con el código FINAL\npost-transform — ahí está el error real.\n\n### 5. JSX `&&` con `0` renderiza \"0\"\n\n```jsx\n{\n  count && <Badge>{count}</Badge>;\n} // ❌ si count===0 renderiza \"0\"\n{\n  count > 0 && <Badge>{count}</Badge>;\n} // ✅\n```\n\n## Renderer — props inyectadas\n\n```js\nfunction __Module({ MUI, Deps, Utils, Contexts }) {\n  // MUI: componentes estándar @mui/material\n  //   MUI.Button, MUI.Card, MUI.Stack, MUI.TextField, ...\n\n  // Deps: utilidades varias\n  const {\n    React, // React puro (usar React.useState etc.)\n    xDataGrid, // @mui/x-data-grid\n    ALPHA, // Componentes nativos Alpha (CardArchivo, SelectBy, etc.)\n    AlphaApi_Fetch, // axios wrapper que incluye token + header empresa\n    NiceModal, // @ebay/nice-modal-react\n    toast, // react-toastify\n    moment, // moment.js\n    Icon, // @iconify/react\n    ReportViewer, // visor de PDFs/.mrt\n    useFormulas, // hook de cálculos numéricos Alpha\n    Recharts, // { LineChart, BarChart, PieChart, ... }\n  } = Deps;\n\n  // Utils\n  const { useRecover, fCurrency, navigate } = Utils;\n\n  // Contexts (usar con React.useContext)\n  const {\n    UserContext, // sesión + permisos del usuario\n    DivisaContext, // moneda activa (Local / Extranjera)\n    ConfigAlphaContext, // settings core\n    ConfigAllContext, // todas las configuraciones del cliente\n  } = Contexts;\n\n  const user = React.useContext(UserContext);\n  // ...\n}\n```\n\n## Llamadas al API\n\nTodo HTTP desde el renderer pasa por `Deps.AlphaApi_Fetch` — un wrapper de\naxios que automáticamente inyecta el token de sesión y el header `empresa`:\n\n```js\nDeps.AlphaApi_Fetch(\"/pkgs/mi-paquete/api/items\", \"GET\")\n  .then((res) => {\n    // res.status — código HTTP\n    // res.data   — body parseado\n  });\n\nDeps.AlphaApi_Fetch(\"/pkgs/mi-paquete/api/items\", \"POST\", { Codp: \"X\", ... });\nDeps.AlphaApi_Fetch(\"/pkgs/mi-paquete/api/items/X\", \"DELETE\");\n```\n\n## Componentes Alpha reusables (`Deps.ALPHA`)\n\n| Componente           | Uso                                                                             |\n| -------------------- | ------------------------------------------------------------------------------- |\n| `ALPHA.CardArchivo`  | Patrón archivo maestro (selector + tabs + form + buscar) — usar template `crud` |\n| `ALPHA.SelectBy`     | Dropdown con datasource HTTP, búsqueda, OtherFields                             |\n| `ALPHA.Searchcm`     | Diálogo de búsqueda avanzada con columnas custom                                |\n| `ALPHA.CodpSelector` | Input de código con autocomplete + Nuevo libre                                  |\n\nEl template `crud` muestra el uso de `CardArchivo` end-to-end con backend wireado.\n\n## Inyectar campos en Facturas / Compras (`extensions[]`)\n\nSin renderer propio — solo agrega el bloque `extensions[]` al manifest. Los\ncampos capturados se guardan automáticamente en `Factura.Totalizar.ExtraData`\n(o `Compra.Totalizar.ExtraData`) sin tocar schema.\n\n```json\n\"extensions\": [\n  {\n    \"model\": \"VenCore/Facturas\",\n    \"fields\": [\n      {\n        \"name\": \"MedicoTratante\",\n        \"label\": \"Médico Tratante\",\n        \"type\": \"String\",\n        \"ui\": \"TextField\"\n      },\n      {\n        \"name\": \"PacienteCodigo\",\n        \"label\": \"Paciente\",\n        \"type\": \"String\",\n        \"ui\": \"SelectBy\",\n        \"selectByProps\": {\n          \"Url\": \"/pkgs/mi-paquete/api/pacientes\",\n          \"OtherFields\": [\"Nombre\", \"Cedula\"]\n        }\n      }\n    ],\n    \"events\": [\n      {\n        \"on\": \"onClienteChange\",\n        \"action\": \"Factura.Totalizar.ExtraData = Factura.Totalizar.ExtraData || {}; if (sourceValue && sourceValue.Nombre) { Factura.Totalizar.ExtraData.ClienteNombre = sourceValue.Nombre; } return Factura;\"\n      }\n    ]\n  }\n]\n```\n\n| `ui`        | Props extra                               |\n| ----------- | ----------------------------------------- |\n| `TextField` | —                                         |\n| `SelectBy`  | `selectByProps: { Url, OtherFields }`     |\n| `SearchCM`  | `searchcmProps: { titulo, Url, columns }` |\n\n**`events[].action`** es código JS evaluado con `new Function()` en el browser.\nMantenlo corto y puro. Para lógica compleja expón un endpoint en `main` y\nllámalo con `AlphaApi_Fetch`. Es **obligatorio** retornar el objeto principal\nmodificado (`return Factura;` o `return Compra;`).\n\nEventos disponibles (subset): `onClienteChange`, `onProveedorChange`, `onItemAdd`.\n\n## Reportes — AlphaReport Engine (`engine: \"arpt\"`)\n\nUsa el template `report`. El `.arpt` exporta:\n\n- `metadata` — objeto o función async `(ctx) => metadata`\n- `generatePDF(ctx)` → `<Document>` de `@react-pdf/renderer` (declarativo)\n- `generatePdfKit(ctx, outputPath)` → imperativo, **60x más rápido** para datasets grandes\n- `generateExcel(ctx)` → `await workbook.xlsx.writeBuffer()`\n\n```js\nexports.metadata = {\n  name: \"Mi Reporte\",\n  parameters: [\n    { name: \"Titulo\", type: \"text\", label: \"Título\", required: true },\n    { name: \"FechaInicio\", type: \"date\", label: \"Desde\", required: true },\n    { name: \"Tipo\", type: \"select\", options: [\"A\", \"B\"], default: \"A\" },\n  ],\n};\n\nexports.generatePDF = async function (ctx) {\n  const { params, getEmpresaInfo, models, empresaId } = ctx;\n  const empresa = await getEmpresaInfo();\n  // ...return <Document>...\n};\n```\n\n`ctx` incluye: `params`, `empresaId`, `models.{Facturas, ProductosGeneral, ...}`,\n`getEmpresaInfo()`, `moneda`, `log/warn/error`. Para `generatePdfKit` también\n`utilsPath` con helpers `createPdf`, `Table`, `drawHeader`, `drawFooter`.\n\nTipos de parámetros: `text` · `textarea` · `number` · `date` · `checkbox` ·\n`select` · `multiselect`. Todos soportan `required`, `validate(value, allValues)`.\n\n## Encriptacion (`.js-alpcrt` y `.appkg-cr`)\n\nAl subir un paquete con `encryptOnDownload: true`, el store re-encripta los\n`.js` y el manifest **al momento de cada descarga** usando una clave\nderivada de `(Serial, ActCode, keyName)` del comprador. Un paquete copiado\nde otra máquina no puede ejecutarse.\n\n### `.encryptIgnore`\n\nArchivo opcional con globs simples (`*` wildcard). Lo que liste NO se\nencripta. `package.json` y `node_modules/` se ignoran siempre.\n\n```\n# .encryptIgnore\ndocs/*.md\nsecrets.json\ndist/*.bundle.js\n```\n\n### Reglas para devs\n\n- **No declares `.js-alpcrt`** en el manifest — declara `.js`. El cliente\n  resuelve automáticamente.\n- **Hot-reload no funciona con encriptados** — los `.js-alpcrt` son inmutables\n  en producción.\n- El plaintext **nunca toca disco** del cliente: se compila en memoria vía\n  `Module._compile()`. Si tu paquete tiene timers o estado top-level, asegúrate\n  de que sobrevivan a re-imports.\n\n## DevMode — iteración instantánea\n\nPara iterar sin re-publicar tras cada cambio:\n\n1. **En Alpha Admin**: Configuración → `DevMode = true` (o `ALPHA_DEV_MODE=true`)\n2. **En tu repo**: `alpha-pkg link` — symlinkea tu carpeta en `packages/`\n3. Editas archivos → el backend recarga al guardar (con `hotReload: true`)\n4. Renderer: clic \"Refrescar\" arriba de la página (no hay hot-reload del frontend)\n5. Cuando termines: `alpha-pkg unlink mi-paquete`\n\n## Checklist antes de publicar\n\n- [ ] `alpha-pkg lint` pasa sin errores\n- [ ] Una sola `function __Module` por archivo, todo adentro\n- [ ] Sub-componentes con estado en `useMemo`\n- [ ] Callbacks a subs con `useCallback`\n- [ ] `setInterval` en backend protegido con `clearInterval` (id top-level)\n- [ ] `requiresCores` declara los cores que usas (`VenCore`, `InvCore`, etc.)\n- [ ] `minVersion` está a la altura de la API que usas\n- [ ] `.encryptIgnore` si tienes assets que no deben encriptarse\n- [ ] Cerrado y reabierto la página tras editar el renderer\n\n---\n\n## Troubleshooting\n\n### CLI\n\n| Error                                   | Causa probable                                                                                                |\n| --------------------------------------- | ------------------------------------------------------------------------------------------------------------- |\n| `No se encontró <keyName>.appkg`        | El manifest no vive dentro de la carpeta. Mover.                                                              |\n| `version no es semver`                  | Editar manifest manualmente con un formato válido (ej. `1.0.0`).                                              |\n| `Login falló (401)`                     | Credenciales admin incorrectas o el store no tiene admins seeded.                                             |\n| `Buscar package falló (401)`            | Token expiró. Vuelve a correr `publish`.                                                                      |\n| `Upload falló (400) zip inválido`       | El zip no contiene `<keyName>.appkg` adentro. Verifica con `unzip -l dist/...`.                               |\n| `No se pudo contactar Alpha Admin en X` | El cliente no está corriendo, o `--host` apunta a un puerto erróneo (default 4545).                           |\n| `DevMode no está habilitado`            | Activa `DevMode = true` en Configuración del cliente, o `export ALPHA_DEV_MODE=true` antes de arrancar Alpha. |\n| `EPERM al crear symlink` (Windows)      | Habilita \"Developer Mode\" del SO o corre Alpha Admin como administrador.                                      |\n\n### Renderer\n\n| Síntoma                                                         | Posible causa                                                                                                                            |\n| --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |\n| `ReferenceError: process is not defined`                        | Tu renderer lanza un error — el ErrorBoundary del cliente lo enmascara. Mira la consola del browser para el código FINAL post-transform. |\n| Página en blanco / pantalla gris                                | Probablemente declaraste algo fuera de `function __Module`. Mueve TODO adentro.                                                          |\n| Loop infinito de re-renders / fetches                           | Sub-componente declarado sin `useMemo` o callback sin `useCallback` — gotchas 2 y 3 arriba.                                              |\n| `Cannot read properties of undefined (reading 'X')` en `Deps.X` | Esa dependencia no está inyectada. Revisa \"Renderer — props inyectadas\".                                                                 |\n| `0` aparece donde esperabas nada                                | `{count && <X/>}` con count=0. Usar `{count > 0 && <X/>}`.                                                                               |\n\n### Backend (`main`)\n\n| Síntoma                                                | Posible causa                                                                                                                                     |\n| ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Cambios al guardar `index.js` no se reflejan           | `hotReload: true` no está en el manifest, o el paquete está encriptado.                                                                           |\n| `setInterval` corre 2x, 3x... tras cada save           | No estás haciendo `clearInterval` con id a nivel de módulo — ver \"Hot-reload y timers\".                                                           |\n| `mongoose.model \"Schema hasn't been registered\"`       | Estás usando el modelo del core con `require()` normal en vez de via `EmpDbMgr.ConnectToEmpDb()`.                                                 |\n| 404 al pegarle a `/pkgs/<kn>/api/...` recién instalado | El paquete quedó después del catch-all SPA. Reinicia Alpha Admin (o ya está fixed: el hoister lo reordena automáticamente — si pasa, abre issue). |\n\n## Licencia\n\nMIT — AlphaSoft, C.A.\n","readmeFilename":"README.md"}