dikshiprintagent 1.0.0

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/NPM-PACKAGE.md ADDED
@@ -0,0 +1,48 @@
1
+ # dikshiprintagent npm package
2
+
3
+ Install after publishing (or install the generated `.tgz` file locally):
4
+
5
+ ```bash
6
+ npm install dikshiprintagent
7
+ ```
8
+
9
+ The package uses ES modules and works in modern browser build tools and Node.js 18+.
10
+
11
+ ```js
12
+ import DikshiPrintAgent from 'dikshiprintagent';
13
+
14
+ const agent = new DikshiPrintAgent({
15
+ token: 'TOKEN_FROM_THE_WINDOWS_TRAY_APP'
16
+ });
17
+
18
+ const { printers } = await agent.printers();
19
+ const job = await agent.printText(printers[0].name, 'Hello from npm');
20
+ await agent.wait(job.jobId);
21
+ ```
22
+
23
+ ESC/POS receipt example:
24
+
25
+ ```js
26
+ const receipt = DikshiPrintAgent.receiptBuilder({ width: 42 })
27
+ .center().bold().text('DIKSHI STORE\n').bold(false)
28
+ .line()
29
+ .row('Total', 'Rs. 100.00')
30
+ .cut()
31
+ .build();
32
+
33
+ await agent.printEscPos('EPSON TM-T82', receipt);
34
+ ```
35
+
36
+ Create the distributable package:
37
+
38
+ ```bash
39
+ npm test
40
+ npm pack
41
+ ```
42
+
43
+ Publish (requires npm account access and ownership of the package name):
44
+
45
+ ```bash
46
+ npm login
47
+ npm publish
48
+ ```
package/README.md ADDED
@@ -0,0 +1,98 @@
1
+ # Dikshi Print Agent V10
2
+
3
+ Windows local print bridge for websites. Designed around Windows printer queues, so newly installed printers are discovered automatically when Windows has a working driver.
4
+
5
+ Android/iOS remote printing through the optional secure relay is documented in [REMOTE-PRINTING.md](REMOTE-PRINTING.md).
6
+
7
+ ## V10 features
8
+ - Windows tray application
9
+ - Loopback-only HTTP API on `127.0.0.1:8181`
10
+ - First-run 256-bit random API token
11
+ - Origin allow-list/CORS protection
12
+ - Dynamic installed-printer discovery and default-printer detection
13
+ - Queued print jobs + job status/history
14
+ - Text printing through Windows PrintDocument
15
+ - Image printing (base64 PNG/JPG/BMP etc.)
16
+ - RAW printing through Winspool (label languages / printer-native data)
17
+ - ESC/POS uses the RAW path
18
+ - PDF printing through Windows registered PDF `printto` handler
19
+ - Direct PDF URL printing: agent downloads the PDF from an API URL and sends it to the selected printer
20
+ - HTML printing through WebView2 with page size/orientation options
21
+ - Invoice/receipt template printing from plain JSON data
22
+ - QR and CODE128 barcode printing
23
+ - ESC/POS receipt builder in the browser SDK
24
+ - Persistent recent job history under `%APPDATA%\DikshiPrintAgent\job-history.json`
25
+ - Local dashboard with printer list, HTML/text test print, and job history
26
+ - Visual template designer at `/designer` with drag/drop fields and saved JSON layouts
27
+ - Designer validation, version backups, locked layouts, resize handles, undo/redo, zoom, logo upload, and custom table columns
28
+ - Dashboard/designer pages do not embed the API token; paste it from the tray menu before using protected actions
29
+ - Daily logs under `%APPDATA%\DikshiPrintAgent\logs`
30
+ - Start-with-Windows support
31
+ - Browser JavaScript SDK + test page
32
+ - Single-file self-contained `win-x64` publish
33
+ - Inno Setup script included
34
+
35
+ ## Build
36
+ Install the Microsoft .NET 8 SDK on the Windows development PC, then run `build-exe.bat`.
37
+ Output: `DikshiPrintAgent-V10.exe` in the project root.
38
+
39
+ ## First run
40
+ Run the EXE. It creates:
41
+ `%APPDATA%\DikshiPrintAgent\config.json`
42
+
43
+ Right-click the tray icon -> **Copy API Token**.
44
+ Edit `AllowedOrigins` in config.json to include your websites, then restart the agent.
45
+ Example:
46
+ ```json
47
+ "AllowedOrigins": [
48
+ "https://erp.example.com",
49
+ "https://billing.example.com",
50
+ "http://localhost:*"
51
+ ]
52
+ ```
53
+ Do not set `AllowAllOrigins=true` on production PCs unless you fully understand the security risk.
54
+
55
+ ## API
56
+ - `GET /api/v1/health` (no token; health only)
57
+ - `GET /api/v1/printers`
58
+ - `GET /api/v1/jobs`
59
+ - `GET /api/v1/jobs/{id}`
60
+ - `GET /api/v1/templates`
61
+ - `GET /api/v1/templates/{name}`
62
+ - `POST /api/v1/templates`
63
+ - `POST /api/v1/print/text`
64
+ - `POST /api/v1/print/image`
65
+ - `POST /api/v1/print/code`
66
+ - `POST /api/v1/print/raw`
67
+ - `POST /api/v1/print/escpos`
68
+ - `POST /api/v1/print/pdf`
69
+ - `POST /api/v1/print/pdf-url`
70
+ - `POST /api/v1/print/html`
71
+ - `POST /api/v1/print/template`
72
+ - `POST /api/v1/print/designed-template`
73
+ - WebSocket: `/ws?token=...`
74
+
75
+ Authenticated REST requests require `X-Dikshi-Token`.
76
+
77
+ ### Print a PDF directly from an API URL
78
+ Use this when your server API returns `application/pdf` as a downloadable file. The agent downloads the PDF locally, validates it, queues it, and prints through the existing PDF print handler.
79
+
80
+ ```bash
81
+ curl -X POST "http://127.0.0.1:8181/api/v1/print/pdf-url" \
82
+ -H "Content-Type: application/json" \
83
+ -H "X-Dikshi-Token: YOUR_AGENT_TOKEN" \
84
+ -d "{\"printer\":\"YOUR_WINDOWS_PRINTER_NAME\",\"url\":\"https://dikshitech.ddns.net/kingmotorsapi/api/BookingEntry/DownloadBookingPdf/001/B00009AA\",\"documentName\":\"Booking_B00009AA.pdf\"}"
85
+ ```
86
+
87
+ Browser SDK:
88
+ ```js
89
+ await agent.printPdfUrl(printerName, bookingPdfUrl, {
90
+ documentName: 'Booking_B00009AA.pdf'
91
+ });
92
+ ```
93
+
94
+ ## Important compatibility notes
95
+ 1. Generic printing works with printers exposed as valid Windows printer queues. Future printer models normally need only their Windows driver.
96
+ 2. RAW/ESC-POS requires the printer/driver to accept the command language being sent. There is no universal ESC/POS guarantee for every model.
97
+ 3. PDF `printto` relies on a Windows PDF application that implements the `printto` shell verb. For maximum unattended PDF reliability, a dedicated PDF rendering/printing engine should be embedded in a future signed enterprise build.
98
+ 4. An HTTPS website talking to a local agent can be affected by browser mixed-content/private-network policies. For strict enterprise/all-site deployment, use a trusted local TLS certificate or companion Chrome/Edge extension. This source keeps loopback HTTP intentionally simple and auditable.
package/npm/browser.js ADDED
@@ -0,0 +1,7 @@
1
+ export { DikshiPrintAgent, DikshiReceiptBuilder, default } from './index.js';
2
+ import { DikshiPrintAgent, DikshiReceiptBuilder } from './index.js';
3
+
4
+ if (typeof window !== 'undefined') {
5
+ window.DikshiPrintAgent = DikshiPrintAgent;
6
+ window.DikshiReceiptBuilder = DikshiReceiptBuilder;
7
+ }
package/npm/index.d.ts ADDED
@@ -0,0 +1,52 @@
1
+ export interface AgentOptions {
2
+ token: string;
3
+ host?: string;
4
+ port?: number;
5
+ protocol?: 'http' | 'https';
6
+ fetch?: typeof fetch;
7
+ }
8
+
9
+ export type PrintOptions = Record<string, unknown>;
10
+ export interface PrintJob { jobId?: string; status?: string; error?: string; [key: string]: unknown; }
11
+
12
+ export class DikshiPrintAgent {
13
+ constructor(options: AgentOptions);
14
+ token: string;
15
+ base: string;
16
+ request(path: string, options?: RequestInit): Promise<any>;
17
+ health(): Promise<any>;
18
+ printers(): Promise<any>;
19
+ jobs(): Promise<any>;
20
+ job(id: string): Promise<PrintJob>;
21
+ templates(): Promise<any>;
22
+ template(name: string): Promise<any>;
23
+ saveTemplate(template: object): Promise<any>;
24
+ printText(printer: string, text: string, options?: PrintOptions): Promise<PrintJob>;
25
+ printRaw(printer: string, data: Uint8Array | string, options?: PrintOptions): Promise<PrintJob>;
26
+ printEscPos(printer: string, data: Uint8Array | string, options?: PrintOptions): Promise<PrintJob>;
27
+ printImage(printer: string, dataBase64: string, options?: PrintOptions): Promise<PrintJob>;
28
+ printCode(printer: string, value: string, options?: PrintOptions): Promise<PrintJob>;
29
+ printQr(printer: string, value: string, options?: PrintOptions): Promise<PrintJob>;
30
+ printBarcode(printer: string, value: string, options?: PrintOptions): Promise<PrintJob>;
31
+ printPdf(printer: string, dataBase64: string, options?: PrintOptions): Promise<PrintJob>;
32
+ printPdfUrl(printer: string, url: string, options?: PrintOptions): Promise<PrintJob>;
33
+ printHtml(printer: string, html: string, options?: PrintOptions): Promise<PrintJob>;
34
+ printTemplate(printer: string, templateData?: object): Promise<PrintJob>;
35
+ printDesignedTemplate(printer: string, templateName: string, data?: object, options?: PrintOptions): Promise<PrintJob>;
36
+ wait(jobId: string, timeoutMs?: number): Promise<PrintJob>;
37
+ static receiptBuilder(options?: ReceiptBuilderOptions): DikshiReceiptBuilder;
38
+ }
39
+
40
+ export interface ReceiptBuilderOptions { width?: number; encoding?: string; }
41
+ export class DikshiReceiptBuilder {
42
+ constructor(options?: ReceiptBuilderOptions);
43
+ init(): this; bytes(...values: number[]): this; text(value?: unknown): this; line(char?: string): this;
44
+ feed(lines?: number): this; bold(on?: boolean): this; underline(on?: boolean): this;
45
+ size(mode?: 'normal' | 'double' | 'wide' | 'tall'): this; align(value?: 'left' | 'center' | 'right'): this;
46
+ left(): this; center(): this; right(): this; row(left: unknown, right: unknown): this;
47
+ item(name: unknown, qty: number, rate: number, amount?: number): this; qr(value: unknown, size?: number): this;
48
+ barcode(value: unknown, height?: number): this; openCashDrawer(): this; cut(partial?: boolean): this;
49
+ raw(data: Uint8Array | number[]): this; build(): Uint8Array;
50
+ }
51
+
52
+ export default DikshiPrintAgent;
package/npm/index.js ADDED
@@ -0,0 +1,135 @@
1
+ /** JavaScript client for the Dikshi Print Agent HTTP API. */
2
+ export class DikshiPrintAgent {
3
+ constructor({ token, host = '127.0.0.1', port = 8181, protocol = 'http', fetch: fetchImpl } = {}) {
4
+ if (!token) throw new Error('Dikshi Print Agent token is required');
5
+ this.token = token;
6
+ this.base = `${protocol}://${host}:${port}`;
7
+ this.fetch = fetchImpl || globalThis.fetch;
8
+ if (typeof this.fetch !== 'function') throw new Error('A fetch implementation is required');
9
+ }
10
+
11
+ async request(path, options = {}) {
12
+ const response = await this.fetch(this.base + path, {
13
+ ...options,
14
+ headers: {
15
+ 'Content-Type': 'application/json',
16
+ 'X-Dikshi-Token': this.token,
17
+ ...(options.headers || {})
18
+ }
19
+ });
20
+ const data = await response.json().catch(() => ({}));
21
+ if (!response.ok) throw new Error(data.error || `Print Agent HTTP ${response.status}`);
22
+ return data;
23
+ }
24
+
25
+ async health() {
26
+ const response = await this.fetch(this.base + '/api/v1/health');
27
+ const data = await response.json().catch(() => ({}));
28
+ if (!response.ok) throw new Error(data.error || `Print Agent HTTP ${response.status}`);
29
+ return data;
30
+ }
31
+
32
+ printers() { return this.request('/api/v1/printers'); }
33
+ jobs() { return this.request('/api/v1/jobs'); }
34
+ job(id) { return this.request('/api/v1/jobs/' + encodeURIComponent(id)); }
35
+ templates() { return this.request('/api/v1/templates'); }
36
+ template(name) { return this.request('/api/v1/templates/' + encodeURIComponent(name)); }
37
+ saveTemplate(template) { return this.request('/api/v1/templates', this._post(template)); }
38
+ printText(printer, text, options = {}) { return this.request('/api/v1/print/text', this._post({ printer, text, ...options })); }
39
+ printImage(printer, dataBase64, options = {}) { return this.request('/api/v1/print/image', this._post({ printer, dataBase64, ...options })); }
40
+ printCode(printer, value, options = {}) { return this.request('/api/v1/print/code', this._post({ printer, value, ...options })); }
41
+ printQr(printer, value, options = {}) { return this.printCode(printer, value, { type: 'QR', ...options }); }
42
+ printBarcode(printer, value, options = {}) { return this.printCode(printer, value, { type: 'CODE128', ...options }); }
43
+ printPdf(printer, dataBase64, options = {}) { return this.request('/api/v1/print/pdf', this._post({ printer, dataBase64, ...options })); }
44
+ printPdfUrl(printer, url, options = {}) { return this.request('/api/v1/print/pdf-url', this._post({ printer, url, ...options })); }
45
+ printHtml(printer, html, options = {}) { return this.request('/api/v1/print/html', this._post({ printer, html, ...options })); }
46
+ printTemplate(printer, templateData = {}) { return this.request('/api/v1/print/template', this._post({ printer, ...templateData })); }
47
+ printDesignedTemplate(printer, templateName, data = {}, options = {}) {
48
+ return this.request('/api/v1/print/designed-template', this._post({ printer, templateName, data, ...options }));
49
+ }
50
+ printRaw(printer, bytesOrBase64, options = {}) { return this._binary('/api/v1/print/raw', printer, bytesOrBase64, options); }
51
+ printEscPos(printer, bytesOrBase64, options = {}) { return this._binary('/api/v1/print/escpos', printer, bytesOrBase64, options); }
52
+
53
+ async wait(jobId, timeoutMs = 20000) {
54
+ const start = Date.now();
55
+ while (Date.now() - start < timeoutMs) {
56
+ const job = await this.job(jobId);
57
+ if (job.status === 'completed') return job;
58
+ if (job.status === 'failed') throw new Error(job.error || 'Print failed');
59
+ await new Promise(resolve => setTimeout(resolve, 300));
60
+ }
61
+ throw new Error('Print job timeout');
62
+ }
63
+
64
+ _post(value) { return { method: 'POST', body: JSON.stringify(value) }; }
65
+ _binary(path, printer, data, options) {
66
+ let dataBase64 = data;
67
+ if (data instanceof Uint8Array) dataBase64 = bytesToBase64(data);
68
+ return this.request(path, this._post({ printer, dataBase64, ...options }));
69
+ }
70
+
71
+ static receiptBuilder(options = {}) { return new DikshiReceiptBuilder(options); }
72
+ }
73
+
74
+ export class DikshiReceiptBuilder {
75
+ constructor({ width = 42, encoding = 'utf-8' } = {}) {
76
+ this.width = width;
77
+ this.encoding = encoding;
78
+ this.parts = [];
79
+ this.init();
80
+ }
81
+ init() { return this.bytes(0x1b, 0x40); }
82
+ bytes(...values) { this.parts.push(Uint8Array.from(values)); return this; }
83
+ text(value = '') { this.parts.push(new TextEncoder(this.encoding).encode(String(value))); return this; }
84
+ line(char = '-') { return this.text(String(char).repeat(this.width) + '\n'); }
85
+ feed(lines = 1) { return this.text('\n'.repeat(Math.max(1, lines))); }
86
+ bold(on = true) { return this.bytes(0x1b, 0x45, on ? 1 : 0); }
87
+ underline(on = true) { return this.bytes(0x1b, 0x2d, on ? 1 : 0); }
88
+ size(mode = 'normal') { return this.bytes(0x1d, 0x21, mode === 'double' ? 0x11 : mode === 'wide' ? 0x10 : mode === 'tall' ? 0x01 : 0x00); }
89
+ align(value = 'left') { return this.bytes(0x1b, 0x61, value === 'center' ? 1 : value === 'right' ? 2 : 0); }
90
+ left() { return this.align('left'); }
91
+ center() { return this.align('center'); }
92
+ right() { return this.align('right'); }
93
+ row(left, right) {
94
+ const a = String(left ?? '');
95
+ const b = String(right ?? '');
96
+ return this.text(a + ' '.repeat(Math.max(1, this.width - a.length - b.length)) + b + '\n');
97
+ }
98
+ item(name, qty, rate, amount = Number(qty || 0) * Number(rate || 0)) {
99
+ this.text(String(name ?? '') + '\n');
100
+ return this.row(`${qty} x ${Number(rate || 0).toFixed(2)}`, Number(amount || 0).toFixed(2));
101
+ }
102
+ qr(value, size = 6) {
103
+ const data = new TextEncoder(this.encoding).encode(String(value ?? ''));
104
+ const length = data.length + 3;
105
+ return this.align('center')
106
+ .bytes(0x1d, 0x28, 0x6b, 0x04, 0x00, 0x31, 0x41, 0x32, 0x00)
107
+ .bytes(0x1d, 0x28, 0x6b, 0x03, 0x00, 0x31, 0x43, Math.max(1, Math.min(16, size)))
108
+ .bytes(0x1d, 0x28, 0x6b, 0x03, 0x00, 0x31, 0x45, 0x31)
109
+ .bytes(0x1d, 0x28, 0x6b, length & 0xff, (length >> 8) & 0xff, 0x31, 0x50, 0x30)
110
+ .raw(data).bytes(0x1d, 0x28, 0x6b, 0x03, 0x00, 0x31, 0x51, 0x30).align('left');
111
+ }
112
+ barcode(value, height = 70) {
113
+ const data = new TextEncoder(this.encoding).encode(String(value ?? ''));
114
+ return this.align('center').bytes(0x1d, 0x68, Math.max(1, Math.min(255, height)))
115
+ .bytes(0x1d, 0x48, 0x02).bytes(0x1d, 0x6b, 0x49, data.length).raw(data).feed().align('left');
116
+ }
117
+ openCashDrawer() { return this.bytes(0x1b, 0x70, 0x00, 0x19, 0xfa); }
118
+ cut(partial = false) { return this.feed(3).bytes(0x1d, 0x56, partial ? 0x01 : 0x00); }
119
+ raw(data) { this.parts.push(data instanceof Uint8Array ? data : Uint8Array.from(data || [])); return this; }
120
+ build() {
121
+ const output = new Uint8Array(this.parts.reduce((sum, part) => sum + part.length, 0));
122
+ let offset = 0;
123
+ for (const part of this.parts) { output.set(part, offset); offset += part.length; }
124
+ return output;
125
+ }
126
+ }
127
+
128
+ function bytesToBase64(bytes) {
129
+ if (typeof Buffer !== 'undefined') return Buffer.from(bytes).toString('base64');
130
+ let binary = '';
131
+ for (const byte of bytes) binary += String.fromCharCode(byte);
132
+ return btoa(binary);
133
+ }
134
+
135
+ export default DikshiPrintAgent;
@@ -0,0 +1,29 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import DikshiPrintAgent, { DikshiReceiptBuilder } from '../index.js';
4
+
5
+ test('requires a token', () => {
6
+ assert.throws(() => new DikshiPrintAgent(), /token is required/);
7
+ });
8
+
9
+ test('sends authenticated print requests', async () => {
10
+ let request;
11
+ const agent = new DikshiPrintAgent({
12
+ token: 'secret',
13
+ fetch: async (url, options) => {
14
+ request = { url, options };
15
+ return { ok: true, json: async () => ({ jobId: '123' }) };
16
+ }
17
+ });
18
+ const result = await agent.printText('Printer 1', 'Hello');
19
+ assert.equal(result.jobId, '123');
20
+ assert.equal(request.url, 'http://127.0.0.1:8181/api/v1/print/text');
21
+ assert.equal(request.options.headers['X-Dikshi-Token'], 'secret');
22
+ assert.deepEqual(JSON.parse(request.options.body), { printer: 'Printer 1', text: 'Hello' });
23
+ });
24
+
25
+ test('builds ESC/POS bytes', () => {
26
+ const output = new DikshiReceiptBuilder({ width: 10 }).text('Hi').cut().build();
27
+ assert.ok(output instanceof Uint8Array);
28
+ assert.ok(output.length > 5);
29
+ });
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "dikshiprintagent",
3
+ "version": "1.0.0",
4
+ "description": "JavaScript SDK for the Dikshi Print Agent local Windows printing service",
5
+ "keywords": [
6
+ "printing",
7
+ "silent-print",
8
+ "escpos",
9
+ "windows-printer",
10
+ "dikshi"
11
+ ],
12
+ "license": "UNLICENSED",
13
+ "type": "module",
14
+ "main": "./npm/index.js",
15
+ "module": "./npm/index.js",
16
+ "types": "./npm/index.d.ts",
17
+ "browser": "./npm/browser.js",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./npm/index.d.ts",
21
+ "import": "./npm/index.js",
22
+ "default": "./npm/index.js"
23
+ },
24
+ "./browser": "./npm/browser.js"
25
+ },
26
+ "files": [
27
+ "npm",
28
+ "README.md",
29
+ "NPM-PACKAGE.md"
30
+ ],
31
+ "scripts": {
32
+ "test": "node --test npm/test",
33
+ "prepack": "npm test"
34
+ },
35
+ "engines": {
36
+ "node": ">=18"
37
+ },
38
+ "publishConfig": {
39
+ "access": "public"
40
+ }
41
+ }