nexabase-report 0.28.0 → 0.28.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/NEXAREPORT.md ADDED
@@ -0,0 +1,291 @@
1
+ <!-- nexabase-report@0.28.3 — generado por scripts/gen-ai-context.mjs, no editar a mano -->
2
+ # NexaReport — referencia para agentes de IA
3
+
4
+ > Este archivo se genera desde los tipos TypeScript reales del paquete (`src/lib/types/report.ts`
5
+ > vía `schema/nexareport.schema.json`), no a mano — así nunca queda desactualizado respecto al
6
+ > código. Si este archivo dice una versión de `nexabase-report` distinta a la que tienes
7
+ > instalada en `package.json`, hay una más reciente en
8
+ > `https://unpkg.com/nexabase-report@latest/NEXAREPORT.md` — tráela de ahí antes de confiar
9
+ > en este archivo (o corre `npm run build` en el repo de nexabase-report para regenerarla).
10
+
11
+ ## Qué es
12
+
13
+ NexaReport es un motor de reportes (diseñador + visor + exportación PDF/Excel/Word) basado en
14
+ **bandas** apiladas verticalmente, al estilo Stimulsoft/FastReport. Un reporte es un único objeto
15
+ JSON: `NexaReportDefinition`.
16
+
17
+ ## Estructura mínima de un reporte
18
+
19
+ ```json
20
+ {
21
+ "metadata": { "version": "1.0", "name": "Nombre del reporte", "createdAt": "2026-01-01" },
22
+ "layout": {
23
+ "page": { "format": "A4", "orientation": "portrait", "margins": { "top": 60, "right": 60, "bottom": 60, "left": 60 } },
24
+ "bands": [ /* ver "Tipos de banda" abajo */ ]
25
+ },
26
+ "dataSources": [ { "id": "ds1", "alias": "items", "collection": "items", "enabled": true } ]
27
+ }
28
+ ```
29
+
30
+ `layout.page.format` es `"A4" | "Letter" | "Ticket" | "Dashboard"`. `orientation` es
31
+ `"portrait" | "landscape"`.
32
+
33
+ ## Tipos de banda (`NexaReportBand.type`)
34
+
35
+ - **ReportHeader** — Se imprime una sola vez, al inicio (o en cada página si repeatOnEveryPage: true). El título/membrete del reporte.
36
+ - **PageHeader** — Se repite en la parte superior de CADA página.
37
+ - **DataBand** — La banda de datos principal — una instancia por fila (o una sola con una Table que lista todas las filas). Requiere dataSource.
38
+ - **GroupHeader** — Se imprime al iniciar un grupo (requiere masterField). Va antes de las filas de ese grupo.
39
+ - **GroupFooter** — Se imprime al cerrar un grupo (requiere masterField). Ideal para subtotales.
40
+ - **PageFooter** — Se repite en la parte inferior de CADA página.
41
+ - **ReportFooter** — Se imprime una sola vez, al final de todo el reporte (totales generales, firma).
42
+ - **DetailBand** — Sub-banda para master-detail: una fila hija por cada fila del DataBand padre (parentDataSource + masterField + childField).
43
+ - **StaticPage** — Contenido FIJO de una página completa, independiente de los datos. Repetible: una StaticPage por cada página fija que necesites (ej. una carta de 2 hojas). Puede tener dataSource igual que cualquier otra banda (ej. para el nombre del destinatario). Nace con pageBreakBefore: true.
44
+
45
+ Toda banda puede tener: `pageBreakBefore` / `pageBreakAfter` (forzar salto de página),
46
+ `backgroundColor`, `dataSource` (alias de `dataSources` a usar). Las coordenadas de los
47
+ elementos dentro de una banda (`x`, `y`) son relativas a la banda, en píxeles.
48
+
49
+ ## Tipos de elemento (`NexaReportElement.type`)
50
+
51
+ - **Text** — Texto simple. `binding` para un campo ({{campo}} implícito) o `content` con placeholders {{campo}} / expresiones {[expr]}.
52
+ - **Image** — `options.imageSource`: "url" (URL fija en options.imageUrl) o "binding" (campo de datos con la URL/base64).
53
+ - **Barcode** — Código de barras. `binding` o `content` con el valor; `options.barcodeFormat` (ej. "CODE128").
54
+ - **Table** — Tabla tabular — usar dentro de un DataBand. `tableColumns` (id/title/binding/width/format), tableShowHeader/Footer/GroupHeader/Footer, tableGroupBy.
55
+ - **Chart** — `chartConfig` (tipo, series, ejes).
56
+ - **Crosstab** — Tabla dinámica. `crosstabConfig`.
57
+ - **SubReport** — Reporte embebido. `subReportDefinition` (definición completa inline) + `subReportDataSource` (alias en el dataMap del padre) + `subReportBindings` ([{masterField, childField}]) para filtrar por la fila actual del padre.
58
+ - **Line** — Forma. `shapeType` implícito ("Line").
59
+ - **Rectangle** — Forma. `options.fillColor`, `options.cornerRadius`.
60
+ - **Ellipse** — Forma.
61
+ - **Arrow** — Forma.
62
+ - **QRCode** — Código QR. Igual que Barcode pero sin formato.
63
+ - **DrillDown** — Navega a otro reporte al hacer clic. `drillDownConfig`.
64
+ - **Gauge** — `gaugeConfig` (valueField, min, max, ranges).
65
+ - **Indicator** — KPI simple. `indicatorConfig` (binding, aggregation, label).
66
+ - **RichText** — Texto con formato (negrita, listas, etc.). El HTML autorizado va en `richText` (NO en `content`).
67
+
68
+ Todo elemento tiene `id`, `type`, `x`, `y`, `width`, `height`, `style` (obligatorios).
69
+
70
+ ## Binding de datos y expresiones
71
+
72
+ - `{{campo}}` dentro de `content` — inserta el valor de ese campo de la fila actual.
73
+ - `{[expresión]}` — evalúa una expresión, incluye funciones de agregación: `sum(campo)`,
74
+ `count(campo)`, `avg(campo)`, `min(campo)`, `max(campo)`, `first(campo)`, `last(campo)`.
75
+ - Variables de sistema: `{{Page}}`, `{{TotalPages}}`, `{{Today}}`, `{{Now}}`, `{{RowNumber}}`,
76
+ `{{TotalRows}}`, `{{ReportName}}`, y más — ver `docs/VIEWER_API.md` para la lista completa.
77
+ - Un elemento con `binding` (en vez de `content`) lee ese campo directamente — usado para
78
+ Barcode/QRCode/Image; para Text es más común usar `content` con `{{...}}`.
79
+
80
+ ## Ejemplo 1 — Carta fija de 2 páginas (StaticPage)
81
+
82
+ ```json
83
+ {
84
+ "metadata": {
85
+ "version": "1.0",
86
+ "name": "Carta",
87
+ "createdAt": "2026-01-01"
88
+ },
89
+ "layout": {
90
+ "page": {
91
+ "format": "A4",
92
+ "orientation": "portrait",
93
+ "margins": {
94
+ "top": 60,
95
+ "right": 60,
96
+ "bottom": 60,
97
+ "left": 60
98
+ }
99
+ },
100
+ "bands": [
101
+ {
102
+ "id": "pagina1",
103
+ "type": "StaticPage",
104
+ "height": 900,
105
+ "dataSource": "destinatario",
106
+ "elements": [
107
+ {
108
+ "id": "saludo",
109
+ "type": "Text",
110
+ "x": 0,
111
+ "y": 0,
112
+ "width": 500,
113
+ "height": 30,
114
+ "style": {
115
+ "fontSize": "14px"
116
+ },
117
+ "binding": "nombre",
118
+ "content": "Estimado {{nombre}}:"
119
+ },
120
+ {
121
+ "id": "cuerpo",
122
+ "type": "Text",
123
+ "x": 0,
124
+ "y": 40,
125
+ "width": 500,
126
+ "height": 400,
127
+ "style": {
128
+ "fontSize": "12px"
129
+ },
130
+ "content": "Cuerpo de la carta..."
131
+ }
132
+ ]
133
+ },
134
+ {
135
+ "id": "pagina2",
136
+ "type": "StaticPage",
137
+ "height": 900,
138
+ "pageBreakBefore": true,
139
+ "elements": [
140
+ {
141
+ "id": "firma",
142
+ "type": "Text",
143
+ "x": 0,
144
+ "y": 700,
145
+ "width": 300,
146
+ "height": 30,
147
+ "style": {
148
+ "fontSize": "12px"
149
+ },
150
+ "content": "Atentamente,\nEl equipo"
151
+ }
152
+ ]
153
+ }
154
+ ]
155
+ },
156
+ "dataSources": [
157
+ {
158
+ "id": "ds1",
159
+ "alias": "destinatario",
160
+ "collection": "destinatario",
161
+ "enabled": true
162
+ }
163
+ ]
164
+ }
165
+ ```
166
+
167
+ ## Ejemplo 2 — Factura con tabla y totales
168
+
169
+ ```json
170
+ {
171
+ "metadata": {
172
+ "version": "1.0",
173
+ "name": "Factura",
174
+ "createdAt": "2026-01-01"
175
+ },
176
+ "layout": {
177
+ "page": {
178
+ "format": "A4",
179
+ "orientation": "portrait"
180
+ },
181
+ "bands": [
182
+ {
183
+ "id": "rh",
184
+ "type": "ReportHeader",
185
+ "height": 80,
186
+ "elements": [
187
+ {
188
+ "id": "titulo",
189
+ "type": "Text",
190
+ "x": 0,
191
+ "y": 0,
192
+ "width": 300,
193
+ "height": 30,
194
+ "style": {
195
+ "fontSize": "20px",
196
+ "fontWeight": "bold"
197
+ },
198
+ "content": "Factura {{numero}}"
199
+ }
200
+ ]
201
+ },
202
+ {
203
+ "id": "db",
204
+ "type": "DataBand",
205
+ "height": 200,
206
+ "dataSource": "items",
207
+ "elements": [
208
+ {
209
+ "id": "tabla",
210
+ "type": "Table",
211
+ "x": 0,
212
+ "y": 0,
213
+ "width": 500,
214
+ "height": 150,
215
+ "style": {},
216
+ "tableShowFooter": true,
217
+ "tableColumns": [
218
+ {
219
+ "id": "c1",
220
+ "title": "Producto",
221
+ "binding": "nombre",
222
+ "width": 250
223
+ },
224
+ {
225
+ "id": "c2",
226
+ "title": "Cantidad",
227
+ "binding": "cantidad",
228
+ "width": 100,
229
+ "alignment": "right"
230
+ },
231
+ {
232
+ "id": "c3",
233
+ "title": "Subtotal",
234
+ "binding": "subtotal",
235
+ "width": 150,
236
+ "alignment": "right",
237
+ "format": "$#,##0.00",
238
+ "footerText": "Total: {[sum(subtotal)]}"
239
+ }
240
+ ]
241
+ }
242
+ ]
243
+ },
244
+ {
245
+ "id": "pf",
246
+ "type": "PageFooter",
247
+ "height": 30,
248
+ "elements": [
249
+ {
250
+ "id": "pag",
251
+ "type": "Text",
252
+ "x": 400,
253
+ "y": 0,
254
+ "width": 100,
255
+ "height": 20,
256
+ "style": {
257
+ "fontSize": "9px",
258
+ "textAlign": "right"
259
+ },
260
+ "content": "Página {{Page}} de {{TotalPages}}"
261
+ }
262
+ ]
263
+ }
264
+ ]
265
+ },
266
+ "dataSources": [
267
+ {
268
+ "id": "ds1",
269
+ "alias": "items",
270
+ "collection": "items",
271
+ "enabled": true
272
+ }
273
+ ]
274
+ }
275
+ ```
276
+
277
+ ## Validación
278
+
279
+ El schema formal completo (todas las interfaces, no solo bandas/elementos) está en
280
+ `schema/nexareport.schema.json` dentro del paquete — también publicado como export
281
+ `nexabase-report/schema`, así que es accesible sin instalar nada vía:
282
+
283
+ ```
284
+ https://unpkg.com/nexabase-report@0.28.3/schema/nexareport.schema.json
285
+ ```
286
+
287
+ (cambia `@0.28.3` por `@latest` para la versión más reciente). Valida el JSON generado
288
+ contra ese schema antes de darlo por bueno — evita que un campo inventado llegue al usuario.
289
+
290
+ Más ejemplos reales (subreportes, crosstab, master-detail, códigos de barras) en `examples/*.json`
291
+ dentro del paquete publicado.
package/README.md CHANGED
@@ -241,6 +241,29 @@ Reports use a banded structure:
241
241
 
242
242
  `Text`, `Image`, `Barcode`, `QRCode`, `Rectangle`, `Ellipse`, `Line`, `Arrow`, `Table`, `Chart`, `Crosstab`, `SubReport`, `DrillDown`
243
243
 
244
+ ## AI-Assisted Report Generation
245
+
246
+ Let your AI coding assistant (Claude Code, Gemini CLI, OpenCode, Cursor...) build report JSON for
247
+ you — one command, run once per project:
248
+
249
+ ```bash
250
+ npx nexabase-report-ai
251
+ ```
252
+
253
+ This detects your project's AI instructions file (`CLAUDE.md`, `AGENTS.md`, `GEMINI.md`,
254
+ `.cursorrules`) — or creates `AGENTS.md` if none exists — and adds a pointer to the always-current
255
+ report reference and JSON Schema (served straight from the published package, so it's never
256
+ stale):
257
+
258
+ - `https://unpkg.com/nexabase-report@latest/NEXAREPORT.md` — band/element types, binding syntax,
259
+ worked examples
260
+ - `https://unpkg.com/nexabase-report@latest/schema/nexareport.schema.json` — the formal schema, to
261
+ validate generated JSON before trusting it
262
+
263
+ From then on, any assistant that reads your project's instructions file already knows the current
264
+ report format — no manual copy-pasting, and it can't drift out of date since both files are
265
+ regenerated from the real TypeScript types on every published version.
266
+
244
267
  ## Expression Engine
245
268
 
246
269
  Safe evaluation without `eval`. Supports:
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env node
2
+ // npx nexabase-report-ai -> agrega, UNA vez, el puntero a NEXAREPORT.md/schema en el archivo
3
+ // de instrucciones de IA que ya tenga el proyecto (CLAUDE.md/AGENTS.md/GEMINI.md/.cursorrules).
4
+ // Idempotente: si ya está, no duplica nada.
5
+ import { readFile, writeFile, appendFile } from 'node:fs/promises';
6
+ import { existsSync } from 'node:fs';
7
+ import { join } from 'node:path';
8
+
9
+ const cwd = process.cwd();
10
+ const MARKER = '<!-- nexabase-report:ai-context -->';
11
+
12
+ const BLOCK = `
13
+ ${MARKER}
14
+ ## NexaReport
15
+
16
+ Antes de crear o modificar un reporte de NexaReport (JSON \`NexaReportDefinition\`), lee
17
+ https://unpkg.com/nexabase-report@latest/NEXAREPORT.md y valida el JSON generado contra
18
+ https://unpkg.com/nexabase-report@latest/schema/nexareport.schema.json antes de entregarlo.
19
+ `;
20
+
21
+ const candidates = ['CLAUDE.md', 'AGENTS.md', 'GEMINI.md', '.cursorrules'];
22
+ const found = candidates.filter(f => existsSync(join(cwd, f)));
23
+
24
+ async function addTo(file) {
25
+ const path = join(cwd, file);
26
+ const current = existsSync(path) ? await readFile(path, 'utf-8') : '';
27
+ if (current.includes(MARKER)) {
28
+ console.log(` = ${file} ya tiene la sección de NexaReport, sin cambios`);
29
+ return;
30
+ }
31
+ if (current) {
32
+ await appendFile(path, '\n' + BLOCK);
33
+ } else {
34
+ await writeFile(path, BLOCK.trimStart() + '\n');
35
+ }
36
+ console.log(` + ${file} actualizado`);
37
+ }
38
+
39
+ if (found.length > 0) {
40
+ for (const f of found) await addTo(f);
41
+ } else {
42
+ // Ningún archivo de instrucciones detectado: crea AGENTS.md, la convención más genérica
43
+ // (la leen Claude Code, Gemini CLI, OpenCode, Cursor y otros).
44
+ await addTo('AGENTS.md');
45
+ }
46
+
47
+ console.log('\nListo. Cualquier IA que lea ese archivo ya sabe consultar el formato actual de NexaReport.');
@@ -859,6 +859,36 @@ promedio…). No dependen de la banda en la que estén, porque no hay bandas.</l
859
859
  <p>Los formatos pensados para documentos —Word, PPTX— y los de datos —Excel, CSV— están orientados a
860
860
  reportes paginados. Si necesitas repartir cifras periódicamente por correo o en un archivo, el
861
861
  formato adecuado es un <strong>reporte</strong>, no un dashboard.</p>
862
+ `
863
+ },
864
+ {
865
+ id: "ia-generacion",
866
+ title: "Generar reportes con IA",
867
+ search: "ia, ai, claude, gemini, copilot, cursor, prompt, generar, automatizar, npx generar reportes con ia si integras nexareport en tu app, no necesitas armar el json del reporte a mano ni empezar desde cero en el diseñador: un asistente de ia puede generarlo por ti. configuración (un comando, una sola vez) desde la carpeta de tu proyecto: npx nexabase report ai ese comando detecta el archivo de instrucciones que tu asistente ya lee solo ( claude.md , agents.md , gemini.md , .cursorrules ) —o crea agents.md si no encuentra ninguno— y le agrega el puntero al formato actual de nexareport. no hay nada que copiar ni pegar a mano, y si lo corres de nuevo no duplica nada. de ahí en adelante, cualquier desarrollador del equipo , en cualquier sesión , con cualquier reporte que pida, la ia ya sabe consultar sola el formato actual — sin volver a tocar nada. por qué siempre está al día el comando apunta a nexabase report@latest en npm, no a una copia local: el documento de referencia y el schema formal se regeneran automáticamente en cada versión publicada, directo desde el código. nunca vas a recibir un json con un campo que ya no existe, o le falte uno nuevo (como staticpage , para reportes de páginas fijas). después de generarlo pega el json resultante en el diseñador ( importar json ) para revisarlo visualmente y ajustar posiciones, colores o binding fino — la ia arma la estructura, el diseñador es donde lo dejas exacto.",
868
+ element: "",
869
+ html: `<h1>Generar reportes con IA</h1>
870
+ <p>Si integras NexaReport en tu app, no necesitas armar el JSON del reporte a mano ni empezar desde
871
+ cero en el diseñador: un asistente de IA puede generarlo por ti.</p>
872
+ <h2>Configuración (un comando, una sola vez)</h2>
873
+ <p>Desde la carpeta de tu proyecto:</p>
874
+ <pre><code>npx nexabase-report-ai
875
+ </code></pre>
876
+ <p>Ese comando detecta el archivo de instrucciones que tu asistente ya lee solo (<code>CLAUDE.md</code>,
877
+ <code>AGENTS.md</code>, <code>GEMINI.md</code>, <code>.cursorrules</code>) —o crea <code>AGENTS.md</code> si no encuentra ninguno— y le agrega
878
+ el puntero al formato actual de NexaReport. No hay nada que copiar ni pegar a mano, y si lo
879
+ corres de nuevo no duplica nada.</p>
880
+ <p>De ahí en adelante, <strong>cualquier desarrollador del equipo</strong>, en <strong>cualquier sesión</strong>, con
881
+ <strong>cualquier reporte</strong> que pida, la IA ya sabe consultar sola el formato actual — sin volver a
882
+ tocar nada.</p>
883
+ <h2>Por qué siempre está al día</h2>
884
+ <p>El comando apunta a <code>nexabase-report@latest</code> en npm, no a una copia local: el documento de
885
+ referencia y el schema formal se regeneran automáticamente en cada versión publicada, directo
886
+ desde el código. Nunca vas a recibir un JSON con un campo que ya no existe, o le falte uno nuevo
887
+ (como <code>StaticPage</code>, para reportes de páginas fijas).</p>
888
+ <h2>Después de generarlo</h2>
889
+ <p>Pega el JSON resultante en el diseñador (<strong>Importar JSON</strong>) para revisarlo visualmente y ajustar
890
+ posiciones, colores o binding fino — la IA arma la estructura, el diseñador es donde lo dejas
891
+ exacto.</p>
862
892
  `
863
893
  }
864
894
  ];
@@ -1,5 +1,5 @@
1
- import { g as Nl, c as ut, d as Vl } from "./index-Cn1BXocE.js";
2
- import { j as Xl } from "./jspdf.es.min-CQvy1ivX.js";
1
+ import { g as Nl, c as ut, d as Vl } from "./index-Dc1HlUG3.js";
2
+ import { j as Xl } from "./jspdf.es.min-Ce1kkozr.js";
3
3
  function Jl(Pe, br) {
4
4
  for (var fe = 0; fe < br.length; fe++) {
5
5
  const HA = br[fe];