@tecsom/apipos-js 0.0.0-stage → 2.0.4
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/LICENSE +21 -0
- package/README.md +409 -2
- package/dist/apipos.d.ts +67 -0
- package/dist/apipos.js +291 -0
- package/dist/http.d.ts +36 -0
- package/dist/http.js +113 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +9 -0
- package/dist/printer.d.ts +61 -0
- package/dist/printer.js +189 -0
- package/dist/strip-accents.d.ts +21 -0
- package/dist/strip-accents.js +57 -0
- package/dist/ticket-preview.d.ts +33 -0
- package/dist/ticket-preview.js +115 -0
- package/dist/types.d.ts +506 -0
- package/dist/types.js +14 -0
- package/dist/version-check.d.ts +22 -0
- package/dist/version-check.js +72 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +4 -0
- package/package.json +56 -4
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
/** Alineación horizontal de un renglón o de una imagen. */
|
|
2
|
+
export type Align = 'left' | 'center' | 'right';
|
|
3
|
+
/** Tamaños con nombre: `normal` = ×1, `md` = ×2 (doble), `lg` = ×3 (triple). */
|
|
4
|
+
export type FontSizeKeyword = 'normal' | 'md' | 'lg';
|
|
5
|
+
/**
|
|
6
|
+
* Tamaño de letra: un tamaño con nombre o un número en puntos. El número solo
|
|
7
|
+
* lo honra el renderizador PDF (se acota a 4–72); en ESC/POS imprime `normal`.
|
|
8
|
+
*/
|
|
9
|
+
export type FontSize = FontSizeKeyword | number;
|
|
10
|
+
/** Peso de la letra. En PDF, una familia sin cara bold usa negrita sintética. */
|
|
11
|
+
export type FontWeight = 'normal' | 'bold';
|
|
12
|
+
/** Estilo de la letra (solo renderizador PDF). */
|
|
13
|
+
export type FontStyle = 'normal' | 'italic';
|
|
14
|
+
/**
|
|
15
|
+
* Tipografía interna de la impresora (comando `ESC M`; solo ESC/POS): `a`
|
|
16
|
+
* 12×24 (universal), `b` 9×17, `c` 8×16 (soporte limitado de firmware). Cambia
|
|
17
|
+
* las columnas del renglón: 48 / 64 / 72 en papel de 80 mm.
|
|
18
|
+
*/
|
|
19
|
+
export type PrinterTypeface = 'a' | 'b' | 'c';
|
|
20
|
+
/** Trazo de un `separator` (solo renderizador PDF; ESC/POS siempre usa guiones). */
|
|
21
|
+
export type SeparatorStyle = 'dashed' | 'solid' | 'dotted' | 'double';
|
|
22
|
+
/** Campos de estilo que comparten `text` y `special_text`. */
|
|
23
|
+
export interface TextStyle {
|
|
24
|
+
/** Default `normal`. Número = puntos (solo PDF). */
|
|
25
|
+
font_size?: FontSize;
|
|
26
|
+
/** Default `normal`. */
|
|
27
|
+
font_weight?: FontWeight;
|
|
28
|
+
/** Fondo negro y texto blanco (default `false`). En PDF es un bloque de ancho completo. */
|
|
29
|
+
high_contrast?: boolean;
|
|
30
|
+
/** Tipografía de la impresora (solo ESC/POS; default `a`). */
|
|
31
|
+
font?: PrinterTypeface;
|
|
32
|
+
/** Familia de `GET /fonts` o de `builtin` (solo PDF; default la de `settings.font`). */
|
|
33
|
+
font_family?: string;
|
|
34
|
+
/** Solo PDF. */
|
|
35
|
+
font_style?: FontStyle;
|
|
36
|
+
/** Solo PDF. */
|
|
37
|
+
underline?: boolean;
|
|
38
|
+
/** Multiplicador de interlineado 0.8–3.0 (solo PDF; sobrescribe el de `settings.font`). */
|
|
39
|
+
line_height?: number;
|
|
40
|
+
}
|
|
41
|
+
/** Renglón de texto. */
|
|
42
|
+
export interface TextItem extends TextStyle {
|
|
43
|
+
type: 'text';
|
|
44
|
+
data: string;
|
|
45
|
+
/** Default `left`. */
|
|
46
|
+
align?: Align;
|
|
47
|
+
}
|
|
48
|
+
/** Los dos lados de un `special_text`. */
|
|
49
|
+
export interface SpecialTextData {
|
|
50
|
+
/** Va a la izquierda (p. ej. `TOTAL`). */
|
|
51
|
+
text1: string;
|
|
52
|
+
/** Va a la derecha (p. ej. `$81.50`). */
|
|
53
|
+
text2: string;
|
|
54
|
+
}
|
|
55
|
+
/** Renglón con texto a la izquierda y a la derecha (TOTAL, CAMBIO, Folio...). */
|
|
56
|
+
export interface SpecialTextItem extends TextStyle {
|
|
57
|
+
type: 'special_text';
|
|
58
|
+
data: SpecialTextData;
|
|
59
|
+
}
|
|
60
|
+
/** Celda de una fila de tabla; se imprime como texto. */
|
|
61
|
+
export type TableCell = string | number;
|
|
62
|
+
/** Columna de una tabla (solo renderizador PDF; ESC/POS siempre usa las 4 clásicas). */
|
|
63
|
+
export interface TableColumn {
|
|
64
|
+
/** Encabezado. Si todas las columnas lo traen vacío no se dibuja encabezado. */
|
|
65
|
+
label?: string;
|
|
66
|
+
/** Porcentaje del ancho. Si no suman 100 se escalan; las faltantes se reparten el resto. */
|
|
67
|
+
width?: number;
|
|
68
|
+
align?: Align;
|
|
69
|
+
}
|
|
70
|
+
/** Contenido de una tabla. */
|
|
71
|
+
export interface TableData {
|
|
72
|
+
/**
|
|
73
|
+
* Filas de celdas. En ESC/POS cada fila son 4 celdas: cantidad, producto,
|
|
74
|
+
* precio e importe. En PDF las faltantes quedan vacías y las sobrantes se ignoran.
|
|
75
|
+
*/
|
|
76
|
+
rows: TableCell[][];
|
|
77
|
+
/** Default (PDF): `Cant./Producto/Precio/Importe` con `15/45/20/20`. */
|
|
78
|
+
columns?: TableColumn[];
|
|
79
|
+
}
|
|
80
|
+
/** Tabla de productos. */
|
|
81
|
+
export interface TableItem {
|
|
82
|
+
type: 'table';
|
|
83
|
+
data: TableData;
|
|
84
|
+
/** Solo PDF. */
|
|
85
|
+
font_family?: string;
|
|
86
|
+
/** Solo PDF. */
|
|
87
|
+
font_style?: FontStyle;
|
|
88
|
+
/** Solo PDF. */
|
|
89
|
+
line_height?: number;
|
|
90
|
+
}
|
|
91
|
+
/** Línea divisoria. */
|
|
92
|
+
export interface SeparatorItem {
|
|
93
|
+
type: 'separator';
|
|
94
|
+
/** Solo PDF; default `dashed` (valor desconocido → `dashed`). */
|
|
95
|
+
style?: SeparatorStyle;
|
|
96
|
+
/** Grosor en pt (solo PDF; default 0.6, se acota a 0.2–3). */
|
|
97
|
+
thickness?: number;
|
|
98
|
+
}
|
|
99
|
+
/** Imagen (logo, QR...). Se convierte a blanco y negro. */
|
|
100
|
+
export interface ImageItem {
|
|
101
|
+
type: 'image';
|
|
102
|
+
/** Imagen PNG/JPG en base64 (puro o data URI). */
|
|
103
|
+
data: string;
|
|
104
|
+
/** Ancho en dots (default 384). */
|
|
105
|
+
width?: number;
|
|
106
|
+
/** Alias de `width`. */
|
|
107
|
+
width_dots?: number;
|
|
108
|
+
/** Ancho en mm (solo PDF; prioridad sobre `width`). */
|
|
109
|
+
width_mm?: number;
|
|
110
|
+
/** Solo PDF (default `center`); ESC/POS siempre la centra. */
|
|
111
|
+
align?: Align;
|
|
112
|
+
}
|
|
113
|
+
/** Espacio vertical en blanco. */
|
|
114
|
+
export interface SpacerItem {
|
|
115
|
+
type: 'spacer';
|
|
116
|
+
/** Entero ≥ 1 (default 1). ESC/POS: saltos de línea, tope 50. */
|
|
117
|
+
lines?: number;
|
|
118
|
+
/** Alto en mm. PDF: prioridad sobre `lines` (0–200). ESC/POS: solo si falta `lines` (~4 mm por línea). */
|
|
119
|
+
height_mm?: number;
|
|
120
|
+
}
|
|
121
|
+
/** Fragmento HTML (solo renderizador PDF; ESC/POS lo ignora). `<img>` no está soportado. */
|
|
122
|
+
export interface HtmlItem {
|
|
123
|
+
type: 'html';
|
|
124
|
+
data: string;
|
|
125
|
+
}
|
|
126
|
+
/** Abre el cajón de dinero al final del ticket. */
|
|
127
|
+
export interface OpenWithdrawerItem {
|
|
128
|
+
type: 'open_withdrawer';
|
|
129
|
+
}
|
|
130
|
+
/** Un elemento del ticket, discriminado por `type`. */
|
|
131
|
+
export type TicketItem = TextItem | SpecialTextItem | TableItem | SeparatorItem | ImageItem | SpacerItem | HtmlItem | OpenWithdrawerItem;
|
|
132
|
+
/** El ticket completo: lo que recibe `printer.print(content)`. */
|
|
133
|
+
export type TicketContent = TicketItem[];
|
|
134
|
+
/** Motor del ticket: `escpos` (default, bytes de siempre) o `pdf` (Apipos maqueta un PDF). */
|
|
135
|
+
export type Renderer = 'escpos' | 'pdf';
|
|
136
|
+
/**
|
|
137
|
+
* Cómo llega un PDF a la impresora: `driver` (lo renderiza el driver del SO;
|
|
138
|
+
* etiqueteras como AiYin) o `raw` (se rasteriza a ESC/POS; tiqueteras).
|
|
139
|
+
* `document` y `raster` son alias aceptados por compatibilidad.
|
|
140
|
+
*/
|
|
141
|
+
export type PdfMode = 'raw' | 'driver' | 'raster' | 'document';
|
|
142
|
+
/** Unidad de `label_settings` (default `mm`). */
|
|
143
|
+
export type LabelUnit = 'mm' | 'inch';
|
|
144
|
+
/** Tamaño de la etiqueta; la página del PDF debe medir lo mismo. */
|
|
145
|
+
export interface LabelSettings {
|
|
146
|
+
width: number;
|
|
147
|
+
height?: number;
|
|
148
|
+
unit?: LabelUnit;
|
|
149
|
+
}
|
|
150
|
+
/** Fuente base del renderizador PDF (default `{ family: 'sans-serif', size: 9, line_height: 1.25 }`). */
|
|
151
|
+
export interface FontSettings {
|
|
152
|
+
/** Familia de `GET /fonts` o de `builtin`. */
|
|
153
|
+
family?: string;
|
|
154
|
+
/** Puntos, 4–72 (fuera de rango es error del backend). */
|
|
155
|
+
size?: number;
|
|
156
|
+
/** Multiplicador 0.8–3.0 (fuera de rango → 1.25 en silencio). */
|
|
157
|
+
line_height?: number;
|
|
158
|
+
}
|
|
159
|
+
/** Márgenes en mm del renderizador PDF (default `{ top: 1, right: 1, bottom: 3, left: 1 }`). */
|
|
160
|
+
export interface MarginsMm {
|
|
161
|
+
top?: number;
|
|
162
|
+
right?: number;
|
|
163
|
+
bottom?: number;
|
|
164
|
+
left?: number;
|
|
165
|
+
}
|
|
166
|
+
/** `settings` de un trabajo de impresión, tal cual viajan a Apipos. */
|
|
167
|
+
export interface PrintSettings {
|
|
168
|
+
/** Papel en mm: 80 (48 columnas) o 58 (32). `ApiPosPrinter` lo exige numérico. */
|
|
169
|
+
paper_size?: number;
|
|
170
|
+
/** Columnas explícitas; prioridad sobre `paper_size`. */
|
|
171
|
+
char_width?: number;
|
|
172
|
+
/** Default `escpos`. */
|
|
173
|
+
renderer?: Renderer;
|
|
174
|
+
/** Default `driver` con `label_settings`, si no `raw`. */
|
|
175
|
+
pdf_mode?: PdfMode;
|
|
176
|
+
/** Solo renderizador PDF. */
|
|
177
|
+
font?: FontSettings;
|
|
178
|
+
/** Solo renderizador PDF. */
|
|
179
|
+
margins_mm?: MarginsMm;
|
|
180
|
+
/** Ancho de la página en mm (solo renderizador PDF). */
|
|
181
|
+
width_mm?: number;
|
|
182
|
+
/** Alto máximo del ticket renderizado (default 1000; solo renderizador PDF). */
|
|
183
|
+
max_height_mm?: number;
|
|
184
|
+
/** Corte de papel; default corta en recibos y no en etiquetas. */
|
|
185
|
+
cut?: boolean;
|
|
186
|
+
/** Ancho de raster explícito (p. ej. 512 en cabezales de 180 DPI). */
|
|
187
|
+
width_dots?: number;
|
|
188
|
+
label_settings?: LabelSettings;
|
|
189
|
+
/** Alto máximo de una imagen en dots (default 600); lo más alto se reduce. */
|
|
190
|
+
max_image_height_dots?: number;
|
|
191
|
+
/** Bytes por banda de imagen `GS v 0` (default 1024; 512 si la impresora se descarrila). */
|
|
192
|
+
max_band_bytes?: number;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* `settings` que además admiten claves que la librería aún no tipa: escape para
|
|
196
|
+
* claves nuevas del backend. Las conocidas conservan su tipo (`renderer: 'zpl'`
|
|
197
|
+
* sigue sin compilar).
|
|
198
|
+
*/
|
|
199
|
+
export type PrintSettingsOverrides = PrintSettings & {
|
|
200
|
+
[key: string]: unknown;
|
|
201
|
+
};
|
|
202
|
+
/**
|
|
203
|
+
* Opciones de `printer.print(content, options)`. Cada una se copia a su clave
|
|
204
|
+
* de `settings` (nunca al nivel raíz del payload); las no definidas no viajan.
|
|
205
|
+
* La mezcla es superficial: `font` y `marginsMm` reemplazan completo el objeto
|
|
206
|
+
* de la impresora, no se combinan clave por clave.
|
|
207
|
+
*/
|
|
208
|
+
export interface PrintOptions {
|
|
209
|
+
/** → `settings.renderer`. */
|
|
210
|
+
renderer?: Renderer;
|
|
211
|
+
/** → `settings.pdf_mode`. */
|
|
212
|
+
pdfMode?: PdfMode;
|
|
213
|
+
/** → `settings.font`. */
|
|
214
|
+
font?: FontSettings;
|
|
215
|
+
/** → `settings.margins_mm`. */
|
|
216
|
+
marginsMm?: MarginsMm;
|
|
217
|
+
/** → `settings.width_mm`. */
|
|
218
|
+
widthMm?: number;
|
|
219
|
+
/** → `settings.max_height_mm`. */
|
|
220
|
+
maxHeightMm?: number;
|
|
221
|
+
/** → `settings.cut`. */
|
|
222
|
+
cut?: boolean;
|
|
223
|
+
/**
|
|
224
|
+
* Se mezcla al final sobre `settings` y gana sobre las opciones de arriba:
|
|
225
|
+
* escape para claves nuevas del backend. Sus claves `undefined` tampoco viajan.
|
|
226
|
+
*/
|
|
227
|
+
settings?: PrintSettingsOverrides;
|
|
228
|
+
}
|
|
229
|
+
/** Opciones de `printer.printPDF(blob, options)`. */
|
|
230
|
+
export interface PrintPdfOptions {
|
|
231
|
+
/** → `settings.pdf_mode`. */
|
|
232
|
+
pdfMode?: PdfMode;
|
|
233
|
+
/** → `settings.label_settings`. */
|
|
234
|
+
labelSettings?: LabelSettings;
|
|
235
|
+
}
|
|
236
|
+
/** Formato de la vista previa de POST /render. */
|
|
237
|
+
export type RenderFormat = 'pdf' | 'png';
|
|
238
|
+
/** Opciones de `apipos.renderTicket(content, options)`. */
|
|
239
|
+
export interface RenderTicketOptions {
|
|
240
|
+
/** `settings` del ticket, como en POST /print (default `{}`). */
|
|
241
|
+
settings?: PrintSettingsOverrides;
|
|
242
|
+
/** Default `pdf`. */
|
|
243
|
+
format?: RenderFormat;
|
|
244
|
+
/** Entero 72–600, solo con `png` (default 203). Con `pdf` Apipos lo ignora. */
|
|
245
|
+
dpi?: number;
|
|
246
|
+
}
|
|
247
|
+
/** Opciones de `renderTicketPreviewPng(apipos, content, options)`. */
|
|
248
|
+
export interface RenderPreviewOptions {
|
|
249
|
+
/** `settings` del ticket, como en POST /print (default `{}`). */
|
|
250
|
+
settings?: PrintSettingsOverrides;
|
|
251
|
+
/** Entero 72–600 (default 150). */
|
|
252
|
+
dpi?: number;
|
|
253
|
+
}
|
|
254
|
+
/** Opciones de `printer.render(content, options)`: las de `print` más formato y dpi. */
|
|
255
|
+
export interface PrinterRenderOptions extends PrintOptions {
|
|
256
|
+
/** Default `png`. */
|
|
257
|
+
format?: RenderFormat;
|
|
258
|
+
/** Default 203. */
|
|
259
|
+
dpi?: number;
|
|
260
|
+
}
|
|
261
|
+
/** Opciones de `apipos.listFonts(options)`. */
|
|
262
|
+
export interface ListFontsOptions {
|
|
263
|
+
/** `true` → `?refresh=1`: Apipos vuelve a escanear las fuentes. */
|
|
264
|
+
refresh?: boolean;
|
|
265
|
+
}
|
|
266
|
+
/** Valor de `status` del sobre de respuesta. */
|
|
267
|
+
export type EnvelopeStatus = 'success' | 'error';
|
|
268
|
+
/** Sobre de TODA respuesta de Apipos: `{ status, data, message }`. */
|
|
269
|
+
export interface ApiposEnvelope<T> {
|
|
270
|
+
status: EnvelopeStatus;
|
|
271
|
+
data: T;
|
|
272
|
+
message: string;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Resultado de comparar versiones cliente/backend: `equal`, `client_older` (la
|
|
276
|
+
* librería es más vieja que Apipos), `client_newer` (la librería es más nueva) o
|
|
277
|
+
* `unknown` (Apipos no reporta una versión comparable).
|
|
278
|
+
*/
|
|
279
|
+
export type VersionStatus = 'equal' | 'client_older' | 'client_newer' | 'unknown';
|
|
280
|
+
/** Versión numérica `[major, minor, patch]` (de `parseVersion`). */
|
|
281
|
+
export type VersionTuple = [major: number, minor: number, patch: number];
|
|
282
|
+
/** `data.client` de GET /health: cómo vio Apipos el header `X-Apipos-Client`. */
|
|
283
|
+
export interface ServerClientStatus {
|
|
284
|
+
/** Nombre del cliente; `null` si el header no se reconoció (`unknown`). */
|
|
285
|
+
name: string | null;
|
|
286
|
+
/** Versión del cliente; `null` si el header no se reconoció (`unknown`). */
|
|
287
|
+
version: string | null;
|
|
288
|
+
/** Versión de Apipos. */
|
|
289
|
+
server: string;
|
|
290
|
+
status: VersionStatus;
|
|
291
|
+
/** Mensaje en inglés del backend (p. ej. `Client and server versions match.`). */
|
|
292
|
+
message: string;
|
|
293
|
+
}
|
|
294
|
+
/** `data` de GET /health. */
|
|
295
|
+
export interface HealthData {
|
|
296
|
+
/** `ok` cuando el servicio está arriba. */
|
|
297
|
+
status: string;
|
|
298
|
+
/** Versión de Apipos (falta en Apipos anteriores a 1.0.6). */
|
|
299
|
+
version?: string;
|
|
300
|
+
/** `Windows` | `Darwin` | `Linux`. */
|
|
301
|
+
os: string;
|
|
302
|
+
/** `win32` | `darwin` | ... */
|
|
303
|
+
platform: string;
|
|
304
|
+
/** Backend de impresoras activo (p. ej. `MacPrinterBackend`). */
|
|
305
|
+
backend: string;
|
|
306
|
+
port: number;
|
|
307
|
+
/** Handshake; `null` sin header `X-Apipos-Client`; falta en Apipos anteriores a 1.0.6. */
|
|
308
|
+
client?: ServerClientStatus | null;
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Comparación de versiones hecha por la librería (`describeVersions`): su
|
|
312
|
+
* `VERSION` contra la que reporta Apipos (header `X-Apipos-Version` o
|
|
313
|
+
* `data.version` de GET /health).
|
|
314
|
+
*/
|
|
315
|
+
export interface VersionInfo {
|
|
316
|
+
/** Versión de esta librería. */
|
|
317
|
+
client: string;
|
|
318
|
+
/** Versión que reporta Apipos, tal cual llegó; `null` si no la reporta. */
|
|
319
|
+
backend: string | null;
|
|
320
|
+
status: VersionStatus;
|
|
321
|
+
/** Aviso EXACTO para la consola (empieza con `[apipos-js]`); `null` cuando las versiones coinciden. */
|
|
322
|
+
message: string | null;
|
|
323
|
+
}
|
|
324
|
+
/** Resultado de `apipos.checkHealth()`: nunca lanza, `ok = false` ante cualquier error. */
|
|
325
|
+
export interface HealthResult {
|
|
326
|
+
ok: boolean;
|
|
327
|
+
data: HealthData | null;
|
|
328
|
+
/**
|
|
329
|
+
* `describeVersions(VERSION, data.version)`: la versión de esta librería
|
|
330
|
+
* contra la de Apipos. `null` cuando no hubo respuesta válida (`ok: false`
|
|
331
|
+
* por error de conexión, sobre de error o forma inesperada).
|
|
332
|
+
*/
|
|
333
|
+
version: VersionInfo | null;
|
|
334
|
+
}
|
|
335
|
+
/** Formato de archivo de una cara de fuente. */
|
|
336
|
+
export type FontFormat = 'ttf' | 'otf' | 'ttc' | 'otc';
|
|
337
|
+
/** Una cara (estilo) de una familia instalada, tal cual la reporta GET /fonts. */
|
|
338
|
+
export interface FontFace {
|
|
339
|
+
style: string;
|
|
340
|
+
bold: boolean;
|
|
341
|
+
italic: boolean;
|
|
342
|
+
/** Peso OpenType: 400 normal, 700 negrita (`bold` desde 600). */
|
|
343
|
+
weight: number;
|
|
344
|
+
full_name: string;
|
|
345
|
+
postscript_name: string | null;
|
|
346
|
+
path: string;
|
|
347
|
+
/** Posición de la cara dentro de un `.ttc`/`.otc` (0 en archivos de una cara). */
|
|
348
|
+
index: number;
|
|
349
|
+
format: FontFormat;
|
|
350
|
+
}
|
|
351
|
+
/** Familia de fuentes instalada. */
|
|
352
|
+
export interface FontFamily {
|
|
353
|
+
family: string;
|
|
354
|
+
/** `true` si todas sus caras son de ancho fijo. */
|
|
355
|
+
monospace: boolean;
|
|
356
|
+
faces: FontFace[];
|
|
357
|
+
}
|
|
358
|
+
/** `data` de GET /fonts tal cual llega del backend. */
|
|
359
|
+
export interface FontCatalogData {
|
|
360
|
+
families: FontFamily[];
|
|
361
|
+
count: number;
|
|
362
|
+
/** Familias genéricas que el renderizador acepta siempre. */
|
|
363
|
+
builtin: string[];
|
|
364
|
+
dirs: string[];
|
|
365
|
+
scanned_at: string;
|
|
366
|
+
}
|
|
367
|
+
/** Catálogo de fuentes que devuelve `apipos.listFonts()`. */
|
|
368
|
+
export interface FontCatalog {
|
|
369
|
+
families: FontFamily[];
|
|
370
|
+
/** Número de familias. */
|
|
371
|
+
count: number;
|
|
372
|
+
/** `sans-serif`, `serif`, `monospace`: existen aunque la máquina no tenga fuentes. */
|
|
373
|
+
builtin: string[];
|
|
374
|
+
/** Directorios escaneados que existen en la máquina. */
|
|
375
|
+
dirs: string[];
|
|
376
|
+
/** Fecha y hora local del último escaneo (ISO 8601). */
|
|
377
|
+
scannedAt: string;
|
|
378
|
+
}
|
|
379
|
+
/** De dónde salió una fuente usada por el renderizador PDF. */
|
|
380
|
+
export type FontSource = 'system' | 'builtin' | 'fallback';
|
|
381
|
+
/** Fuente pedida por el ticket y lo que se usó realmente. */
|
|
382
|
+
export interface RenderedFont {
|
|
383
|
+
family: string;
|
|
384
|
+
resolved: string;
|
|
385
|
+
source: FontSource;
|
|
386
|
+
bold: boolean;
|
|
387
|
+
italic: boolean;
|
|
388
|
+
}
|
|
389
|
+
/** Tamaño de la página renderizada en mm (el alto es el del contenido). */
|
|
390
|
+
export interface PageSizeMm {
|
|
391
|
+
width: number;
|
|
392
|
+
height: number;
|
|
393
|
+
}
|
|
394
|
+
/** `data` de POST /render tal cual llega del backend. */
|
|
395
|
+
export interface RenderData {
|
|
396
|
+
format: RenderFormat;
|
|
397
|
+
/** `null` con `pdf`. */
|
|
398
|
+
dpi: number | null;
|
|
399
|
+
base64: string;
|
|
400
|
+
page_mm: PageSizeMm;
|
|
401
|
+
pages: number;
|
|
402
|
+
fonts: RenderedFont[];
|
|
403
|
+
warnings: string[];
|
|
404
|
+
}
|
|
405
|
+
/** Vista previa que devuelve `apipos.renderTicket()`. */
|
|
406
|
+
export interface RenderResult {
|
|
407
|
+
format: RenderFormat;
|
|
408
|
+
dpi: number | null;
|
|
409
|
+
/** PDF (`JVBERi...`) o PNG (`iVBORw0KGgo...`) en base64. */
|
|
410
|
+
base64: string;
|
|
411
|
+
pageMm: PageSizeMm;
|
|
412
|
+
pages: number;
|
|
413
|
+
fonts: RenderedFont[];
|
|
414
|
+
warnings: string[];
|
|
415
|
+
/** `application/pdf` o `image/png`. */
|
|
416
|
+
blob: Blob;
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* `data` de POST /print. Los campos dependen del tipo de trabajo: ESC/POS →
|
|
420
|
+
* `char_width`; PDF `raw` → `strategy`, `width_dots`, `cut`; PDF `driver` →
|
|
421
|
+
* `strategy`, `media`; `renderer: 'pdf'` agrega `renderer`, `page_mm`, `fonts`,
|
|
422
|
+
* `warnings` y `drawer_opened`.
|
|
423
|
+
*/
|
|
424
|
+
export interface PrintResult {
|
|
425
|
+
printer: string;
|
|
426
|
+
type?: 'RAW' | 'PDF';
|
|
427
|
+
char_width?: number;
|
|
428
|
+
strategy?: 'raw' | 'driver';
|
|
429
|
+
width_dots?: number;
|
|
430
|
+
cut?: boolean;
|
|
431
|
+
media?: string | null;
|
|
432
|
+
renderer?: 'pdf';
|
|
433
|
+
page_mm?: PageSizeMm;
|
|
434
|
+
fonts?: RenderedFont[];
|
|
435
|
+
warnings?: string[];
|
|
436
|
+
drawer_opened?: boolean;
|
|
437
|
+
}
|
|
438
|
+
/** `data` de POST /open-withdrawer: la impresora a la que se mandó el pulso del cajón. */
|
|
439
|
+
export interface OpenDrawerResult {
|
|
440
|
+
printer: string;
|
|
441
|
+
}
|
|
442
|
+
/** Hook que recibe el `content` de cada trabajo y devuelve el que se envía (arreglo nuevo). */
|
|
443
|
+
export type ContentTransform = (content: TicketContent) => TicketContent;
|
|
444
|
+
/** Opciones de `new Apipos(options)`. */
|
|
445
|
+
export interface ApiposOptions {
|
|
446
|
+
/** Default `http://127.0.0.1:50432`. */
|
|
447
|
+
baseUrl?: string;
|
|
448
|
+
/**
|
|
449
|
+
* Se aplica al `content` en CADA trabajo; debe devolver un arreglo nuevo (la
|
|
450
|
+
* librería no muta la entrada). Default: copia superficial. Aquí se
|
|
451
|
+
* puede inyectar una limpieza de texto o el interruptor "sin acentos".
|
|
452
|
+
*/
|
|
453
|
+
transformContent?: ContentTransform;
|
|
454
|
+
/**
|
|
455
|
+
* Reemplaza el aviso por defecto (`console.warn(info.message)`) cuando la
|
|
456
|
+
* versión de Apipos difiere de la de la librería. El aviso por defecto sale
|
|
457
|
+
* una vez por página por cada versión distinta de Apipos (aunque haya varios
|
|
458
|
+
* clientes); este hook, una vez por versión distinta en ESTE cliente (no en
|
|
459
|
+
* cada petición). Si lanza, el error va a `console.error` y la petición sigue.
|
|
460
|
+
*/
|
|
461
|
+
onVersionMismatch?: (info: VersionInfo) => void;
|
|
462
|
+
/** Inyectable para pruebas. Default: el `fetch` global. */
|
|
463
|
+
fetch?: FetchLike;
|
|
464
|
+
}
|
|
465
|
+
/** Métodos HTTP que usa la librería. */
|
|
466
|
+
export type HttpMethod = 'GET' | 'POST';
|
|
467
|
+
/** Lo que la librería le pasa a `fetch` como segundo argumento. */
|
|
468
|
+
export interface RequestInitLike {
|
|
469
|
+
method: HttpMethod;
|
|
470
|
+
headers: Record<string, string>;
|
|
471
|
+
body?: string;
|
|
472
|
+
}
|
|
473
|
+
/** Lo mínimo que se lee de los headers de una respuesta. */
|
|
474
|
+
export interface HeadersLike {
|
|
475
|
+
get(name: string): string | null;
|
|
476
|
+
}
|
|
477
|
+
/** Lo mínimo que se lee de una respuesta de `fetch` (un `Response` real lo cumple). */
|
|
478
|
+
export interface ResponseLike {
|
|
479
|
+
ok: boolean;
|
|
480
|
+
status: number;
|
|
481
|
+
statusText: string;
|
|
482
|
+
/** Opcional: los dobles de `fetch` de las pruebas pueden no traerlos. */
|
|
483
|
+
headers?: HeadersLike;
|
|
484
|
+
json(): Promise<unknown>;
|
|
485
|
+
}
|
|
486
|
+
/** Firma de `fetch` que usa la librería; `globalThis.fetch` es asignable a ella. */
|
|
487
|
+
export type FetchLike = (url: string, init: RequestInitLike) => Promise<ResponseLike>;
|
|
488
|
+
/** Valor de un parámetro de query: `null`/`undefined` no viajan; `true` → `1`, `false` → `0`. */
|
|
489
|
+
export type QueryValue = string | number | boolean | null | undefined;
|
|
490
|
+
/** Tercer argumento de `apipos.request(method, path, options)`. */
|
|
491
|
+
export interface RequestOptions {
|
|
492
|
+
/** Se serializa con `JSON.stringify` y agrega `Content-Type: application/json`. */
|
|
493
|
+
body?: object;
|
|
494
|
+
query?: Readonly<Record<string, QueryValue>>;
|
|
495
|
+
}
|
|
496
|
+
/** Contexto de red de `request()`: a dónde, con qué `fetch` y a quién avisarle la versión. */
|
|
497
|
+
export interface RequestContext {
|
|
498
|
+
baseUrl: string;
|
|
499
|
+
fetch: FetchLike;
|
|
500
|
+
/**
|
|
501
|
+
* Handshake: recibe `describeVersions(VERSION, X-Apipos-Version)` en cada
|
|
502
|
+
* respuesta que trae `headers` (también las de error). El `Apipos` decide si
|
|
503
|
+
* avisa; las respuestas sin `headers` (dobles de prueba) no lo llaman.
|
|
504
|
+
*/
|
|
505
|
+
onVersion?: (info: VersionInfo) => void;
|
|
506
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Tipos públicos de @tecsom/apipos-js.
|
|
3
|
+
*
|
|
4
|
+
* Reflejan el contrato HTTP de Apipos (APIPOS_API.md del repo de Apipos): los
|
|
5
|
+
* items del ticket, los `settings` y las respuestas viajan en el formato del
|
|
6
|
+
* cable (snake_case), tal cual los lee y los escribe el backend. Lo que es de la
|
|
7
|
+
* librería (opciones de métodos, resultados ya adaptados) va en camelCase.
|
|
8
|
+
*
|
|
9
|
+
* Regla de la casa: nada de comodines. Lo que llega de la red entra como
|
|
10
|
+
* `unknown` y se valida con guardas antes de convertirse en uno de estos tipos.
|
|
11
|
+
*
|
|
12
|
+
* Este módulo solo declara tipos: compilado queda vacío.
|
|
13
|
+
*/
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { VersionInfo, VersionStatus, VersionTuple } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* `'1.2.3'` | `'v1.2.3'` → `[1, 2, 3]`; las partes faltantes valen 0
|
|
4
|
+
* (`'v1.2'` → `[1, 2, 0]`). Vacío, `null`, `undefined` o algo que no empiece
|
|
5
|
+
* con un número → `null`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function parseVersion(text: string | null | undefined): VersionTuple | null;
|
|
8
|
+
/**
|
|
9
|
+
* Compara la versión de la librería (`client`) con la de Apipos (`backend`),
|
|
10
|
+
* numéricamente por `major.minor.patch` (`1.0.10` es mayor que `1.0.9`):
|
|
11
|
+
* `'equal'` | `'client_older'` | `'client_newer'`, o `'unknown'` cuando
|
|
12
|
+
* `backend` es `null`/vacío/no parseable (Apipos anterior a 1.0.6, sin header).
|
|
13
|
+
* Un `client` no parseable también da `'unknown'` (no pasa con `VERSION`, que
|
|
14
|
+
* `sync-version` valida).
|
|
15
|
+
*/
|
|
16
|
+
export declare function compareVersions(client: string, backend: string | null | undefined): VersionStatus;
|
|
17
|
+
/**
|
|
18
|
+
* `{ client, backend, status, message }` listo para avisar: `message` es el
|
|
19
|
+
* texto EXACTO que la librería manda a la consola (`null` si `status` es
|
|
20
|
+
* `'equal'`). `backend` se conserva tal cual llegó (`undefined` → `null`).
|
|
21
|
+
*/
|
|
22
|
+
export declare function describeVersions(client: string, backend: string | null | undefined): VersionInfo;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Handshake de versión con Apipos (versionado en lockstep): la librería `X.Y.Z`
|
|
3
|
+
* está hecha para Apipos `X.Y.Z`. Apipos responde SIEMPRE con el header
|
|
4
|
+
* `X-Apipos-Version`; aquí se compara con `VERSION` y se arma el aviso que la
|
|
5
|
+
* librería muestra en la consola cuando no coinciden.
|
|
6
|
+
*
|
|
7
|
+
* Módulo puro (sin DOM ni red): lo usan `request()` en cada respuesta y
|
|
8
|
+
* `checkHealth()` con `data.version`, y se puede usar suelto.
|
|
9
|
+
*/
|
|
10
|
+
// Mismo patrón que `src/services/version_service.py::_VERSION_RE` de Apipos:
|
|
11
|
+
// `v` opcional, major obligatorio, minor y patch opcionales; lo que siga al
|
|
12
|
+
// patch (`-beta`, `+build`) se ignora.
|
|
13
|
+
const VERSION_RE = /^\s*[vV]?(\d+)(?:\.(\d+))?(?:\.(\d+))?/;
|
|
14
|
+
/**
|
|
15
|
+
* `'1.2.3'` | `'v1.2.3'` → `[1, 2, 3]`; las partes faltantes valen 0
|
|
16
|
+
* (`'v1.2'` → `[1, 2, 0]`). Vacío, `null`, `undefined` o algo que no empiece
|
|
17
|
+
* con un número → `null`.
|
|
18
|
+
*/
|
|
19
|
+
export function parseVersion(text) {
|
|
20
|
+
if (!text)
|
|
21
|
+
return null;
|
|
22
|
+
const match = VERSION_RE.exec(text);
|
|
23
|
+
if (!match)
|
|
24
|
+
return null;
|
|
25
|
+
const [, major = '0', minor = '0', patch = '0'] = match;
|
|
26
|
+
return [Number(major), Number(minor), Number(patch)];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Compara la versión de la librería (`client`) con la de Apipos (`backend`),
|
|
30
|
+
* numéricamente por `major.minor.patch` (`1.0.10` es mayor que `1.0.9`):
|
|
31
|
+
* `'equal'` | `'client_older'` | `'client_newer'`, o `'unknown'` cuando
|
|
32
|
+
* `backend` es `null`/vacío/no parseable (Apipos anterior a 1.0.6, sin header).
|
|
33
|
+
* Un `client` no parseable también da `'unknown'` (no pasa con `VERSION`, que
|
|
34
|
+
* `sync-version` valida).
|
|
35
|
+
*/
|
|
36
|
+
export function compareVersions(client, backend) {
|
|
37
|
+
const clientParts = parseVersion(client);
|
|
38
|
+
const backendParts = parseVersion(backend);
|
|
39
|
+
if (!clientParts || !backendParts)
|
|
40
|
+
return 'unknown';
|
|
41
|
+
for (let index = 0; index < clientParts.length; index++) {
|
|
42
|
+
const mine = clientParts[index] ?? 0;
|
|
43
|
+
const theirs = backendParts[index] ?? 0;
|
|
44
|
+
if (mine < theirs)
|
|
45
|
+
return 'client_older';
|
|
46
|
+
if (mine > theirs)
|
|
47
|
+
return 'client_newer';
|
|
48
|
+
}
|
|
49
|
+
return 'equal';
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* `{ client, backend, status, message }` listo para avisar: `message` es el
|
|
53
|
+
* texto EXACTO que la librería manda a la consola (`null` si `status` es
|
|
54
|
+
* `'equal'`). `backend` se conserva tal cual llegó (`undefined` → `null`).
|
|
55
|
+
*/
|
|
56
|
+
export function describeVersions(client, backend) {
|
|
57
|
+
const reported = backend ?? null;
|
|
58
|
+
const status = compareVersions(client, reported);
|
|
59
|
+
return { client, backend: reported, status, message: versionMessage(status, client, reported) };
|
|
60
|
+
}
|
|
61
|
+
function versionMessage(status, client, backend) {
|
|
62
|
+
switch (status) {
|
|
63
|
+
case 'equal':
|
|
64
|
+
return null;
|
|
65
|
+
case 'client_older':
|
|
66
|
+
return `[apipos-js] Apipos ${String(backend)} es más nuevo que la librería apipos-js ${client}. Actualiza la librería en el front: ambas deben ir en la misma versión.`;
|
|
67
|
+
case 'client_newer':
|
|
68
|
+
return `[apipos-js] La librería apipos-js ${client} es más nueva que Apipos ${String(backend)} instalado en esta máquina. Actualiza Apipos (menú de bandeja → Buscar actualizaciones): ambas deben ir en la misma versión.`;
|
|
69
|
+
case 'unknown':
|
|
70
|
+
return `[apipos-js] Apipos no reporta su versión (falta el header X-Apipos-Version): es anterior a la librería apipos-js ${client}. Actualiza Apipos en esta máquina.`;
|
|
71
|
+
}
|
|
72
|
+
}
|
package/dist/version.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,58 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tecsom/apipos-js",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "2.0.4",
|
|
4
|
+
"description": "Cliente JavaScript (ES modules para el navegador, con tipos TypeScript) para Apipos.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./strip-accents": {
|
|
14
|
+
"types": "./dist/strip-accents.d.ts",
|
|
15
|
+
"default": "./dist/strip-accents.js"
|
|
16
|
+
},
|
|
17
|
+
"./ticket-preview": {
|
|
18
|
+
"types": "./dist/ticket-preview.d.ts",
|
|
19
|
+
"default": "./dist/ticket-preview.js"
|
|
20
|
+
},
|
|
21
|
+
"./version": {
|
|
22
|
+
"types": "./dist/version.d.ts",
|
|
23
|
+
"default": "./dist/version.js"
|
|
24
|
+
},
|
|
25
|
+
"./types": {
|
|
26
|
+
"types": "./dist/types.d.ts",
|
|
27
|
+
"default": "./dist/types.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"README.md"
|
|
33
|
+
],
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "tsc -p tsconfig.json",
|
|
36
|
+
"typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.test.json",
|
|
37
|
+
"test": "npm run build && tsc -p tsconfig.test.json && node --test \"test/**/*.test.ts\"",
|
|
38
|
+
"sync-version": "node scripts/sync-version.ts",
|
|
39
|
+
"check-version": "node scripts/sync-version.ts --check",
|
|
40
|
+
"prepare": "npm run build",
|
|
41
|
+
"pack": "node -e \"fs.mkdirSync('release', { recursive: true })\" && npm pack --pack-destination release"
|
|
42
|
+
},
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=22.18"
|
|
45
|
+
},
|
|
46
|
+
"license": "MIT",
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public"
|
|
49
|
+
},
|
|
50
|
+
"repository": {
|
|
51
|
+
"type": "git",
|
|
52
|
+
"url": "git+https://github.com/Tecsom/apipos-js.git"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@types/node": "24.19.2",
|
|
56
|
+
"typescript": "5.9.3"
|
|
57
|
+
}
|
|
58
|
+
}
|