labelinn 1.1.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/CHANGELOG.md +36 -0
- package/LICENSE +21 -0
- package/README.md +458 -0
- package/bin/cli.js +793 -0
- package/lib/index.d.ts +327 -0
- package/lib/index.js +556 -0
- package/package.json +61 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the LabelInn Node.js SDK will be documented in this file.
|
|
4
|
+
|
|
5
|
+
This project adheres to [Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [1.1.0] - 2026-03-18
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `ConnectResource` — universal data ingestion (XML, CSV, JSON, TSV, NDJSON)
|
|
12
|
+
- `connect.ingest()` — push data in any format
|
|
13
|
+
- `connect.testParse()` — dry-run parse without storing
|
|
14
|
+
- `connect.sources.create/list/get/update/delete()` — manage connector sources
|
|
15
|
+
- `connect.sources.records()` — list ingested records
|
|
16
|
+
- `connect.sources.schema()` — get auto-discovered schema
|
|
17
|
+
- `connect.sources.getMappings/updateMappings()` — field→label mappings
|
|
18
|
+
- `connect.sources.print()` — print labels from connector data
|
|
19
|
+
- CLI: `npx labelinn connect sources`, `connect ingest`, `connect parse`
|
|
20
|
+
|
|
21
|
+
## [1.0.0] - 2026-03-30
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- Initial public release
|
|
26
|
+
- `PrintResource` — create, list, get, cancel, reprint print jobs
|
|
27
|
+
- `FleetResource` — list printers, get details, check status
|
|
28
|
+
- `DesignsResource` — full design CRUD, element management, variables, clone, render, print
|
|
29
|
+
- `WebhooksResource` — create, list, delete, test webhook subscriptions
|
|
30
|
+
- `LabelInnError` and `LabelInnRateLimitError` error classes
|
|
31
|
+
- Idempotency support via `X-Idempotency-Key` header
|
|
32
|
+
- Test mode detection (`sk_test_*` keys)
|
|
33
|
+
- Rate limit info on every response (`_rateLimit`)
|
|
34
|
+
- CLI tool: `npx labelinn test|printers|print|jobs|designs|webhooks`
|
|
35
|
+
- Full TypeScript type definitions
|
|
36
|
+
- Zero external dependencies (uses Node.js 18+ built-in `fetch`)
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 LabelInn
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
# LabelInn Node.js SDK
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/labelinn)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://nodejs.org/)
|
|
6
|
+
|
|
7
|
+
Official Node.js SDK for the [LabelInn](https://labelinn.com) Cloud Print API. Send labels to thermal printers (Zebra, TSC, Honeywell), manage your fleet, and build print automation workflows — all from code.
|
|
8
|
+
|
|
9
|
+
**Zero dependencies.** Uses Node.js 18+ built-in `fetch`.
|
|
10
|
+
|
|
11
|
+
## Table of Contents
|
|
12
|
+
|
|
13
|
+
- [Installation](#installation)
|
|
14
|
+
- [Quick Start](#quick-start)
|
|
15
|
+
- [Authentication](#authentication)
|
|
16
|
+
- [Print Jobs](#print-jobs)
|
|
17
|
+
- [Fleet Management](#fleet-management)
|
|
18
|
+
- [Designs (Templates)](#designs-templates)
|
|
19
|
+
- [Webhooks](#webhooks)
|
|
20
|
+
- [Data Connect](#data-connect)
|
|
21
|
+
- [Rate Limits](#rate-limits)
|
|
22
|
+
- [Error Handling](#error-handling)
|
|
23
|
+
- [CLI Tool](#cli-tool)
|
|
24
|
+
- [Configuration](#configuration)
|
|
25
|
+
- [Requirements](#requirements)
|
|
26
|
+
- [Contributing](#contributing)
|
|
27
|
+
- [License](#license)
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install labelinn
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
```js
|
|
38
|
+
const LabelInn = require('labelinn');
|
|
39
|
+
|
|
40
|
+
// Use a test key for development (no real prints, no quota cost)
|
|
41
|
+
const client = new LabelInn('sk_test_xxxxx');
|
|
42
|
+
|
|
43
|
+
// Print a ZPL label
|
|
44
|
+
const job = await client.print.create({
|
|
45
|
+
printer_id: 'prt_abc123',
|
|
46
|
+
payload_type: 'zpl',
|
|
47
|
+
payload_data: '^XA^FO50,50^ADN,36,20^FDHello World^FS^XZ',
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
console.log(`Job ${job.id} → ${job.status}`);
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Authentication
|
|
54
|
+
|
|
55
|
+
Get your API key from **LabelInn → Settings → API Keys**.
|
|
56
|
+
|
|
57
|
+
| Key prefix | Mode | Description |
|
|
58
|
+
|---------------|---------|------------------------------------|
|
|
59
|
+
| `sk_test_xxx` | Sandbox | No real prints, separate quota |
|
|
60
|
+
| `sk_live_xxx` | Live | Sends jobs to physical printers |
|
|
61
|
+
|
|
62
|
+
```js
|
|
63
|
+
// Test mode — safe for development
|
|
64
|
+
const dev = new LabelInn('sk_test_xxxxx');
|
|
65
|
+
console.log(dev.isTestMode); // true
|
|
66
|
+
|
|
67
|
+
// Live mode — real prints
|
|
68
|
+
const prod = new LabelInn('sk_live_xxxxx');
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Print Jobs
|
|
72
|
+
|
|
73
|
+
### Send a ZPL label
|
|
74
|
+
```js
|
|
75
|
+
const job = await client.print.create({
|
|
76
|
+
printer_id: 'prt_abc123',
|
|
77
|
+
payload_type: 'zpl',
|
|
78
|
+
payload_data: '^XA^FO50,50^ADN,36,20^FDShipping Label^FS^XZ',
|
|
79
|
+
copies: 2,
|
|
80
|
+
job_name: 'Order #1234',
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Send an image label
|
|
85
|
+
```js
|
|
86
|
+
// From a URL (HTTPS only, max 500KB)
|
|
87
|
+
const job = await client.print.create({
|
|
88
|
+
printer_id: 'prt_abc123',
|
|
89
|
+
payload_type: 'image',
|
|
90
|
+
image_url: 'https://example.com/labels/shipping.png',
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Print from a template
|
|
95
|
+
```js
|
|
96
|
+
const job = await client.print.create({
|
|
97
|
+
printer_id: 'prt_abc123',
|
|
98
|
+
payload_type: 'template',
|
|
99
|
+
design_id: 'dsg_shipping_v2',
|
|
100
|
+
data: {
|
|
101
|
+
order_id: 'ORD-9876',
|
|
102
|
+
customer: 'Ali Yilmaz',
|
|
103
|
+
barcode: 'TR123456789',
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Idempotency (prevent duplicate prints)
|
|
109
|
+
```js
|
|
110
|
+
const job = await client.print.create(
|
|
111
|
+
{ printer_id: 'prt_abc123', payload_type: 'zpl', payload_data: '...' },
|
|
112
|
+
{ idempotencyKey: 'order-9876-label' }
|
|
113
|
+
);
|
|
114
|
+
// Calling again with the same key returns the original job
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### List & manage jobs
|
|
118
|
+
```js
|
|
119
|
+
// List recent jobs
|
|
120
|
+
const { jobs } = await client.print.list({ limit: 10 });
|
|
121
|
+
|
|
122
|
+
// Filter by status
|
|
123
|
+
const { jobs: failed } = await client.print.list({ status: 'failed' });
|
|
124
|
+
|
|
125
|
+
// Get job details
|
|
126
|
+
const job = await client.print.get('job_abc123');
|
|
127
|
+
|
|
128
|
+
// Cancel a queued job
|
|
129
|
+
await client.print.cancel('job_abc123');
|
|
130
|
+
|
|
131
|
+
// Reprint with different printer
|
|
132
|
+
await client.print.reprint('job_abc123', { printer_id: 'prt_backup' });
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Fleet Management
|
|
136
|
+
|
|
137
|
+
```js
|
|
138
|
+
// List all printers
|
|
139
|
+
const { printers } = await client.fleet.list();
|
|
140
|
+
|
|
141
|
+
// Filter by status
|
|
142
|
+
const { printers: online } = await client.fleet.list({ status: 'online' });
|
|
143
|
+
|
|
144
|
+
// Get printer details
|
|
145
|
+
const printer = await client.fleet.get('prt_abc123');
|
|
146
|
+
|
|
147
|
+
// Quick status check
|
|
148
|
+
const status = await client.fleet.status('prt_abc123');
|
|
149
|
+
console.log(`${status.status} — Paper: ${status.paper_status}`);
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Designs (Templates)
|
|
153
|
+
|
|
154
|
+
```js
|
|
155
|
+
// List designs
|
|
156
|
+
const { designs } = await client.designs.list();
|
|
157
|
+
|
|
158
|
+
// Get design with elements
|
|
159
|
+
const design = await client.designs.get('dsg_abc123');
|
|
160
|
+
|
|
161
|
+
// Create a design
|
|
162
|
+
const newDesign = await client.designs.create({
|
|
163
|
+
name: 'Shipping Label v3',
|
|
164
|
+
width_mm: 100,
|
|
165
|
+
height_mm: 60,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// Add elements
|
|
169
|
+
await client.designs.addElement('dsg_abc123', {
|
|
170
|
+
type: 'text',
|
|
171
|
+
x: 10, y: 10,
|
|
172
|
+
text: '{{order_id}}',
|
|
173
|
+
font_size: 14,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
await client.designs.addElement('dsg_abc123', {
|
|
177
|
+
type: 'barcode',
|
|
178
|
+
x: 10, y: 40,
|
|
179
|
+
barcode_type: 'code128',
|
|
180
|
+
data: '{{tracking_number}}',
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Clone a design
|
|
184
|
+
const clone = await client.designs.clone('dsg_abc123');
|
|
185
|
+
|
|
186
|
+
// Print directly from a design
|
|
187
|
+
await client.designs.print('dsg_abc123', {
|
|
188
|
+
printer_id: 'prt_abc123',
|
|
189
|
+
data: { order_id: 'ORD-5555', tracking_number: 'TR999' },
|
|
190
|
+
copies: 1,
|
|
191
|
+
});
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Webhooks
|
|
195
|
+
|
|
196
|
+
```js
|
|
197
|
+
// Subscribe to events
|
|
198
|
+
const webhook = await client.webhooks.create({
|
|
199
|
+
url: 'https://yourapp.com/webhooks/labelinn',
|
|
200
|
+
events: ['print_job.completed', 'print_job.failed', 'printer.offline'],
|
|
201
|
+
description: 'Production monitoring',
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// Save the signing secret!
|
|
205
|
+
console.log('Signing secret:', webhook.signing_secret);
|
|
206
|
+
|
|
207
|
+
// List subscriptions
|
|
208
|
+
const { webhooks } = await client.webhooks.list();
|
|
209
|
+
|
|
210
|
+
// Send a test ping
|
|
211
|
+
await client.webhooks.test(webhook.id);
|
|
212
|
+
|
|
213
|
+
// Unsubscribe
|
|
214
|
+
await client.webhooks.delete(webhook.id);
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Verifying webhook signatures
|
|
218
|
+
|
|
219
|
+
```js
|
|
220
|
+
const crypto = require('crypto');
|
|
221
|
+
|
|
222
|
+
function verifyWebhook(payload, signature, secret) {
|
|
223
|
+
const expected = crypto
|
|
224
|
+
.createHmac('sha256', secret)
|
|
225
|
+
.update(payload)
|
|
226
|
+
.digest('hex');
|
|
227
|
+
return crypto.timingSafeEqual(
|
|
228
|
+
Buffer.from(signature),
|
|
229
|
+
Buffer.from(expected)
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// In your Express handler:
|
|
234
|
+
app.post('/webhooks/labelinn', (req, res) => {
|
|
235
|
+
const signature = req.headers['x-labelinn-signature'];
|
|
236
|
+
const valid = verifyWebhook(JSON.stringify(req.body), signature, WEBHOOK_SECRET);
|
|
237
|
+
if (!valid) return res.status(401).send('Invalid signature');
|
|
238
|
+
|
|
239
|
+
const { event, data } = req.body;
|
|
240
|
+
console.log(`Event: ${event}`, data);
|
|
241
|
+
res.sendStatus(200);
|
|
242
|
+
});
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Data Connect
|
|
246
|
+
|
|
247
|
+
Data Connect lets you ingest data from any enterprise system (SAP, Oracle, CSV exports, custom APIs) and print labels from it. Supports XML/IDoc, CSV/TSV, and JSON/NDJSON formats with auto-detection.
|
|
248
|
+
|
|
249
|
+
### Ingest Data
|
|
250
|
+
|
|
251
|
+
```js
|
|
252
|
+
// From a JSON payload
|
|
253
|
+
const result = await client.connect.ingest({
|
|
254
|
+
source_id: 'src_abc123',
|
|
255
|
+
payload: JSON.stringify([
|
|
256
|
+
{ sku: 'A001', name: 'Widget', qty: 10 },
|
|
257
|
+
{ sku: 'A002', name: 'Gadget', qty: 5 },
|
|
258
|
+
]),
|
|
259
|
+
format: 'json',
|
|
260
|
+
});
|
|
261
|
+
console.log(`Ingested ${result.records_count} records`);
|
|
262
|
+
|
|
263
|
+
// From XML/IDoc
|
|
264
|
+
const xmlResult = await client.connect.ingest({
|
|
265
|
+
source_id: 'src_sap',
|
|
266
|
+
payload: xmlString,
|
|
267
|
+
format: 'xml',
|
|
268
|
+
});
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Test Parse (Dry Run)
|
|
272
|
+
|
|
273
|
+
```js
|
|
274
|
+
const parsed = await client.connect.testParse({
|
|
275
|
+
payload: csvString,
|
|
276
|
+
format: 'csv',
|
|
277
|
+
});
|
|
278
|
+
console.log(`Detected ${parsed.records.length} records`);
|
|
279
|
+
console.log('Schema:', parsed.schema.map(f => f.path));
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Manage Sources
|
|
283
|
+
|
|
284
|
+
```js
|
|
285
|
+
// Create a source
|
|
286
|
+
const source = await client.connect.createSource({
|
|
287
|
+
name: 'SAP Orders',
|
|
288
|
+
format: 'xml',
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
// List all sources
|
|
292
|
+
const sources = await client.connect.listSources();
|
|
293
|
+
|
|
294
|
+
// Get source details
|
|
295
|
+
const detail = await client.connect.getSource('src_abc123');
|
|
296
|
+
|
|
297
|
+
// Update a source
|
|
298
|
+
await client.connect.updateSource('src_abc123', { name: 'SAP Orders v2' });
|
|
299
|
+
|
|
300
|
+
// Delete a source
|
|
301
|
+
await client.connect.deleteSource('src_abc123');
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### Schema & Records
|
|
305
|
+
|
|
306
|
+
```js
|
|
307
|
+
// Get detected schema
|
|
308
|
+
const schema = await client.connect.getSchema('src_abc123');
|
|
309
|
+
schema.fields.forEach(f => console.log(`${f.path} (${f.type}): ${f.sample}`));
|
|
310
|
+
|
|
311
|
+
// List ingested records
|
|
312
|
+
const records = await client.connect.listRecords('src_abc123', 50);
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Field Mappings
|
|
316
|
+
|
|
317
|
+
```js
|
|
318
|
+
// Get current mappings
|
|
319
|
+
const mappings = await client.connect.getMappings('src_abc123');
|
|
320
|
+
|
|
321
|
+
// Update mappings
|
|
322
|
+
await client.connect.updateMappings('src_abc123', {
|
|
323
|
+
sku: 'product_code',
|
|
324
|
+
name: 'product_name',
|
|
325
|
+
});
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Print from Connector
|
|
329
|
+
|
|
330
|
+
```js
|
|
331
|
+
const printResult = await client.connect.print({
|
|
332
|
+
source_id: 'src_abc123',
|
|
333
|
+
design_id: 'dsg_shipping',
|
|
334
|
+
printer_id: 'prt_warehouse1',
|
|
335
|
+
copies: 1,
|
|
336
|
+
});
|
|
337
|
+
console.log(`Created ${printResult.count} print jobs`);
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## Rate Limits
|
|
341
|
+
|
|
342
|
+
Every response includes rate limit info:
|
|
343
|
+
|
|
344
|
+
```js
|
|
345
|
+
const result = await client.print.list();
|
|
346
|
+
console.log(result._rateLimit);
|
|
347
|
+
// { limit: 2000, remaining: 1847, reset: 3600 }
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
| Plan | Daily Limit |
|
|
351
|
+
|------------|-------------|
|
|
352
|
+
| Pro | 2,000 |
|
|
353
|
+
| Enterprise | 50,000 |
|
|
354
|
+
| Test keys | 500 minimum |
|
|
355
|
+
|
|
356
|
+
When rate-limited, the SDK throws `LabelInnRateLimitError`:
|
|
357
|
+
|
|
358
|
+
```js
|
|
359
|
+
const { LabelInnRateLimitError } = require('labelinn');
|
|
360
|
+
|
|
361
|
+
try {
|
|
362
|
+
await client.print.create({ ... });
|
|
363
|
+
} catch (err) {
|
|
364
|
+
if (err instanceof LabelInnRateLimitError) {
|
|
365
|
+
console.log(`Rate limited. Retry in ${err.retryAfter}s`);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
## Error Handling
|
|
371
|
+
|
|
372
|
+
```js
|
|
373
|
+
const { LabelInnError } = require('labelinn');
|
|
374
|
+
|
|
375
|
+
try {
|
|
376
|
+
await client.print.create({ ... });
|
|
377
|
+
} catch (err) {
|
|
378
|
+
if (err instanceof LabelInnError) {
|
|
379
|
+
console.error(`[${err.status}] ${err.code}: ${err.message}`);
|
|
380
|
+
// err.raw contains the full response body
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
## CLI Tool
|
|
386
|
+
|
|
387
|
+
The SDK includes a CLI for quick testing:
|
|
388
|
+
|
|
389
|
+
```bash
|
|
390
|
+
# Set your API key
|
|
391
|
+
export LABELINN_API_KEY=sk_test_xxxxx
|
|
392
|
+
|
|
393
|
+
# Verify connectivity
|
|
394
|
+
npx labelinn test
|
|
395
|
+
|
|
396
|
+
# List printers
|
|
397
|
+
npx labelinn printers
|
|
398
|
+
|
|
399
|
+
# Print a ZPL label
|
|
400
|
+
npx labelinn print prt_abc123 --zpl "^XA^FO50,50^ADN,36,20^FDHello^FS^XZ"
|
|
401
|
+
|
|
402
|
+
# Print from a template
|
|
403
|
+
npx labelinn print prt_abc123 --design dsg_shipping --data '{"name":"Ali"}'
|
|
404
|
+
|
|
405
|
+
# List jobs
|
|
406
|
+
npx labelinn jobs
|
|
407
|
+
|
|
408
|
+
# Get raw JSON output
|
|
409
|
+
npx labelinn printers --json
|
|
410
|
+
|
|
411
|
+
# --- Data Connect ---
|
|
412
|
+
# List connected data sources
|
|
413
|
+
npx labelinn connect sources
|
|
414
|
+
|
|
415
|
+
# Ingest data from a file
|
|
416
|
+
npx labelinn connect ingest conn_abc123 --file data.csv
|
|
417
|
+
|
|
418
|
+
# Test-parse a local file
|
|
419
|
+
npx labelinn connect parse --file data.csv --format csv
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
Run `npx labelinn --help` for all commands.
|
|
423
|
+
|
|
424
|
+
## Configuration
|
|
425
|
+
|
|
426
|
+
```js
|
|
427
|
+
const client = new LabelInn('sk_test_xxxxx', {
|
|
428
|
+
baseUrl: 'https://labelinn.com/v1', // default
|
|
429
|
+
timeout: 30000, // 30s default
|
|
430
|
+
});
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
## Requirements
|
|
434
|
+
|
|
435
|
+
- Node.js 18+ (uses built-in `fetch`)
|
|
436
|
+
- No external dependencies
|
|
437
|
+
|
|
438
|
+
## Contributing
|
|
439
|
+
|
|
440
|
+
Contributions are welcome! Please open an issue or submit a pull request on [GitHub](https://github.com/ArisHub/labelinn-node).
|
|
441
|
+
|
|
442
|
+
1. Fork the repository
|
|
443
|
+
2. Create your feature branch (`git checkout -b feature/my-feature`)
|
|
444
|
+
3. Commit your changes (`git commit -am 'Add my feature'`)
|
|
445
|
+
4. Push to the branch (`git push origin feature/my-feature`)
|
|
446
|
+
5. Open a Pull Request
|
|
447
|
+
|
|
448
|
+
## Links
|
|
449
|
+
|
|
450
|
+
- [LabelInn Website](https://labelinn.com)
|
|
451
|
+
- [API Documentation](https://labelinn.com/developers)
|
|
452
|
+
- [Python SDK](https://github.com/ArisHub/labelinn-python)
|
|
453
|
+
- [Changelog](./CHANGELOG.md)
|
|
454
|
+
- [Report a Bug](https://github.com/ArisHub/labelinn-node/issues)
|
|
455
|
+
|
|
456
|
+
## License
|
|
457
|
+
|
|
458
|
+
MIT — see [LICENSE](./LICENSE) for details.
|