qorstack-report-sdk 0.1.1 → 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/README.md +159 -116
- package/dist/QorstackApi.d.ts +64 -13
- package/dist/async.d.ts +41 -0
- package/dist/async.js +59 -0
- package/dist/index.d.ts +11 -1
- package/dist/index.js +30 -2
- package/package.json +45 -38
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ This SDK simplifies integration into your **Node.js** and **.NET** applications.
|
|
|
6
6
|
## Features
|
|
7
7
|
|
|
8
8
|
- **Word template rendering** — generate PDF or DOCX from a stored DOCX template.
|
|
9
|
-
- **Excel template rendering** — generate XLSX
|
|
9
|
+
- **Excel template rendering** — generate XLSX from a stored XLSX template (Excel in, Excel out), with auto-filter, freeze header, table styles, and conditional formatting.
|
|
10
10
|
- **Sandbox rendering** — render a template using its configured sample payload (great for previews).
|
|
11
11
|
- **Render job history** — list and inspect past render jobs.
|
|
12
12
|
- **Rich data binding:**
|
|
@@ -14,10 +14,11 @@ This SDK simplifies integration into your **Node.js** and **.NET** applications.
|
|
|
14
14
|
- Dynamic tables (with vertical merge / column collapse on Word; auto-filter / freeze / split-to-sheets on Excel)
|
|
15
15
|
- Images (URL or Base64)
|
|
16
16
|
- QR codes and barcodes
|
|
17
|
-
- **Pro features** *(
|
|
17
|
+
- **Pro features** *(require a Pro license on your Qorstack Report server)*:
|
|
18
18
|
- PDF password protection
|
|
19
19
|
- PDF watermarking
|
|
20
|
-
-
|
|
20
|
+
- Async renders with webhooks (`async: true`, then poll or `waitForJob`)
|
|
21
|
+
- Zip output — the rendered file inside a `.zip`
|
|
21
22
|
- **Cross-platform** — fully typed clients for TypeScript/Node.js and C#/.NET 8.0+.
|
|
22
23
|
|
|
23
24
|
---
|
|
@@ -29,7 +30,7 @@ This SDK simplifies integration into your **Node.js** and **.NET** applications.
|
|
|
29
30
|
```bash
|
|
30
31
|
npm install qorstack-report-sdk
|
|
31
32
|
# or
|
|
32
|
-
|
|
33
|
+
pnpm add qorstack-report-sdk
|
|
33
34
|
```
|
|
34
35
|
|
|
35
36
|
### .NET (C#)
|
|
@@ -42,11 +43,11 @@ dotnet add package Qorstack.Report.Sdk
|
|
|
42
43
|
|
|
43
44
|
## Authentication
|
|
44
45
|
|
|
45
|
-
You need an **API
|
|
46
|
+
You need an **API key** from your Qorstack Report server (one per project).
|
|
46
47
|
|
|
47
|
-
1.
|
|
48
|
-
2.
|
|
49
|
-
3.
|
|
48
|
+
1. Sign in to the web UI of your Qorstack Report server.
|
|
49
|
+
2. Open the project, then **API key** in the sidebar.
|
|
50
|
+
3. Copy the key and send it as the `X-API-Key` header.
|
|
50
51
|
|
|
51
52
|
---
|
|
52
53
|
|
|
@@ -61,17 +62,17 @@ import { QorstackApi } from "qorstack-report-sdk";
|
|
|
61
62
|
const qorstackApi = new QorstackApi({
|
|
62
63
|
securityData: {
|
|
63
64
|
headers: {
|
|
64
|
-
"X-API-
|
|
65
|
+
"X-API-Key": "YOUR_API_KEY_HERE",
|
|
65
66
|
},
|
|
66
67
|
},
|
|
67
68
|
});
|
|
68
69
|
|
|
69
70
|
// OR: with custom Base URL (e.g. self-hosted instance)
|
|
70
71
|
const qorstackApiCustom = new QorstackApi({
|
|
71
|
-
baseUrl: "https://your-
|
|
72
|
+
baseUrl: "https://your-host/api", // v2: the API lives under /api (appended automatically if missing)
|
|
72
73
|
securityData: {
|
|
73
74
|
headers: {
|
|
74
|
-
"X-API-
|
|
75
|
+
"X-API-Key": "YOUR_API_KEY_HERE",
|
|
75
76
|
},
|
|
76
77
|
},
|
|
77
78
|
});
|
|
@@ -86,7 +87,7 @@ using Qorstack.Report.Sdk;
|
|
|
86
87
|
var qorstackApi = new QorstackApi(apiKey: "YOUR_API_KEY_HERE");
|
|
87
88
|
|
|
88
89
|
// OR: with custom Base URL
|
|
89
|
-
var qorstackApiCustom = new QorstackApi("https://your-
|
|
90
|
+
var qorstackApiCustom = new QorstackApi("https://your-host/api", "YOUR_API_KEY_HERE"); // /api appended if missing
|
|
90
91
|
```
|
|
91
92
|
|
|
92
93
|
---
|
|
@@ -104,52 +105,38 @@ Use `fileType: "pdf"` (default) to get a PDF, or `"docx"` to get the rendered Wo
|
|
|
104
105
|
|
|
105
106
|
```typescript
|
|
106
107
|
// Node.js (TypeScript)
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
unit_price: item.price,
|
|
113
|
-
amount: item.total,
|
|
114
|
-
}));
|
|
108
|
+
// Column names ({{row:<name>}} markers) are listed once; each row holds the values in that order
|
|
109
|
+
const columns = ["no", "category", "description", "qty", "unit_price", "amount"];
|
|
110
|
+
const tableRows = data.map((item, index) => [
|
|
111
|
+
index + 1, item.category, item.description, item.quantity, item.price, item.total,
|
|
112
|
+
]);
|
|
115
113
|
|
|
116
114
|
const response = await qorstackApi.render.postRenderWordTemplate({
|
|
117
115
|
templateKey: "RENDOX-7326-758-974-2067",
|
|
118
116
|
fileName: "Full_Report_Example",
|
|
119
117
|
fileType: "pdf", // "pdf" | "docx"
|
|
120
|
-
replace:
|
|
121
|
-
customer_name: "Example Company Ltd.",
|
|
122
|
-
document_date: new Date().toLocaleDateString("en-GB"),
|
|
123
|
-
total_amount: "1,500.00",
|
|
124
|
-
|
|
118
|
+
replace: [
|
|
119
|
+
{ key: "customer_name", value: "Example Company Ltd." },
|
|
120
|
+
{ key: "document_date", value: new Date().toLocaleDateString("en-GB") },
|
|
121
|
+
{ key: "total_amount", value: "1,500.00" },
|
|
122
|
+
],
|
|
125
123
|
table: [
|
|
126
124
|
{
|
|
125
|
+
columns,
|
|
127
126
|
rows: tableRows,
|
|
128
127
|
verticalMerge: ["category"],
|
|
129
128
|
collapse: ["description"],
|
|
130
129
|
},
|
|
131
130
|
],
|
|
132
|
-
image:
|
|
133
|
-
header_logo:
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
text: "https://payment.link/123456",
|
|
142
|
-
size: 150,
|
|
143
|
-
},
|
|
144
|
-
},
|
|
145
|
-
barcode: {
|
|
146
|
-
product_barcode: {
|
|
147
|
-
text: "PROD-123456789",
|
|
148
|
-
format: "Code128",
|
|
149
|
-
width: 300,
|
|
150
|
-
height: 80,
|
|
151
|
-
},
|
|
152
|
-
},
|
|
131
|
+
image: [
|
|
132
|
+
{ key: "header_logo", src: "https://example.com/logo.png", width: 200, fit: "contain" },
|
|
133
|
+
],
|
|
134
|
+
qrcode: [
|
|
135
|
+
{ key: "payment_qr", text: "https://payment.link/123456", size: 150 },
|
|
136
|
+
],
|
|
137
|
+
barcode: [
|
|
138
|
+
{ key: "product_barcode", text: "PROD-123456789", format: "Code128", width: 300, height: 80 },
|
|
139
|
+
],
|
|
153
140
|
});
|
|
154
141
|
|
|
155
142
|
console.log("Download URL:", response.data.downloadUrl);
|
|
@@ -159,47 +146,45 @@ console.log("Download URL:", response.data.downloadUrl);
|
|
|
159
146
|
// C# (.NET)
|
|
160
147
|
var data = await _exampleService.GetDataAsync(id);
|
|
161
148
|
|
|
162
|
-
|
|
149
|
+
// Column names ({{row:<name>}} markers) are listed once; each row holds the values in that order
|
|
150
|
+
var columns = new List<string> { "no", "category", "description", "qty", "unit_price", "amount" };
|
|
151
|
+
var tableRows = data.Select((item, index) => (ICollection<object>)new List<object>
|
|
163
152
|
{
|
|
164
|
-
|
|
165
|
-
{ "category", item.Category },
|
|
166
|
-
{ "description", item.Description },
|
|
167
|
-
{ "qty", item.Quantity },
|
|
168
|
-
{ "unit_price", item.Price },
|
|
169
|
-
{ "amount", item.Total }
|
|
153
|
+
index + 1, item.Category, item.Description, item.Quantity, item.Price, item.Total
|
|
170
154
|
}).ToList();
|
|
171
155
|
|
|
172
156
|
var request = new PdfFromTemplateRequest
|
|
173
157
|
{
|
|
174
158
|
TemplateKey = "RENDOX-7326-758-974-2067",
|
|
175
159
|
FileName = "Full_Report_Example",
|
|
176
|
-
FileType =
|
|
177
|
-
Replace = new
|
|
160
|
+
FileType = PdfFromTemplateRequestFileType.Pdf,
|
|
161
|
+
Replace = new List<KeyValueRequest>
|
|
178
162
|
{
|
|
179
|
-
{ "customer_name", "Example Company Ltd." },
|
|
180
|
-
{ "document_date", DateTime.Now.ToString("dd/MM/yyyy") },
|
|
181
|
-
{ "total_amount", "1,500.00" }
|
|
163
|
+
new KeyValueRequest { Key = "customer_name", Value = "Example Company Ltd." },
|
|
164
|
+
new KeyValueRequest { Key = "document_date", Value = DateTime.Now.ToString("dd/MM/yyyy") },
|
|
165
|
+
new KeyValueRequest { Key = "total_amount", Value = "1,500.00" }
|
|
182
166
|
},
|
|
183
167
|
Table = new()
|
|
184
168
|
{
|
|
185
169
|
new()
|
|
186
170
|
{
|
|
187
|
-
|
|
171
|
+
Columns = columns,
|
|
172
|
+
Rows = tableRows,
|
|
188
173
|
VerticalMerge = new() { "category" },
|
|
189
174
|
Collapse = new() { "description" }
|
|
190
175
|
}
|
|
191
176
|
},
|
|
192
|
-
Image = new
|
|
177
|
+
Image = new List<ImageDataRequest>
|
|
193
178
|
{
|
|
194
|
-
{ "header_logo",
|
|
179
|
+
new ImageDataRequest { Key = "header_logo", Src = "https://example.com/logo.png", Width = 200, Fit = ImageDataRequestFit.Contain }
|
|
195
180
|
},
|
|
196
|
-
|
|
181
|
+
Qrcode = new List<QrCodeDataRequest>
|
|
197
182
|
{
|
|
198
|
-
{ "payment_qr",
|
|
183
|
+
new QrCodeDataRequest { Key = "payment_qr", Text = "https://payment.link/123456", Size = 150 }
|
|
199
184
|
},
|
|
200
|
-
Barcode = new
|
|
185
|
+
Barcode = new List<BarcodeDataRequest>
|
|
201
186
|
{
|
|
202
|
-
{ "product_barcode",
|
|
187
|
+
new BarcodeDataRequest { Key = "product_barcode", Text = "PROD-123456789", Format = "Code128", Width = 300, Height = 80 }
|
|
203
188
|
}
|
|
204
189
|
};
|
|
205
190
|
|
|
@@ -219,24 +204,28 @@ Method: `render.postRenderExcelTemplate` (Node) / `PostRenderExcelTemplateAsync`
|
|
|
219
204
|
const response = await qorstackApi.render.postRenderExcelTemplate({
|
|
220
205
|
templateKey: "XLSX-1234-5678",
|
|
221
206
|
fileName: "Sales_Report_Q1",
|
|
222
|
-
replace:
|
|
223
|
-
report_period: "Q1 2026",
|
|
224
|
-
generated_by: "Sales Team",
|
|
225
|
-
|
|
207
|
+
replace: [
|
|
208
|
+
{ key: "report_period", value: "Q1 2026" },
|
|
209
|
+
{ key: "generated_by", value: "Sales Team" },
|
|
210
|
+
],
|
|
226
211
|
table: [
|
|
227
212
|
{
|
|
213
|
+
columns: ['region', 'product', 'qty', 'total'],
|
|
228
214
|
rows: [
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
215
|
+
['North', 'A', 10, 1000],
|
|
216
|
+
['North', 'B', 5, 500],
|
|
217
|
+
['South', 'A', 8, 800]
|
|
232
218
|
],
|
|
233
219
|
autoFilter: true,
|
|
234
220
|
freezeHeader: true,
|
|
235
221
|
autoFitColumns: true,
|
|
236
222
|
asExcelTable: true,
|
|
237
223
|
excelTableStyle: "TableStyleMedium2",
|
|
238
|
-
generateTotals:
|
|
239
|
-
|
|
224
|
+
generateTotals: [
|
|
225
|
+
{ key: "total", value: "SUM" },
|
|
226
|
+
{ key: "qty", value: "SUM" },
|
|
227
|
+
],
|
|
228
|
+
numberFormat: [{ key: "total", value: "#,##0.00" }],
|
|
240
229
|
},
|
|
241
230
|
],
|
|
242
231
|
});
|
|
@@ -250,28 +239,33 @@ var request = new ExcelFromTemplateRequest
|
|
|
250
239
|
{
|
|
251
240
|
TemplateKey = "XLSX-1234-5678",
|
|
252
241
|
FileName = "Sales_Report_Q1",
|
|
253
|
-
Replace = new
|
|
242
|
+
Replace = new List<KeyValueRequest>
|
|
254
243
|
{
|
|
255
|
-
{ "report_period", "Q1 2026" },
|
|
256
|
-
{ "generated_by", "Sales Team" }
|
|
244
|
+
new KeyValueRequest { Key = "report_period", Value = "Q1 2026" },
|
|
245
|
+
new KeyValueRequest { Key = "generated_by", Value = "Sales Team" }
|
|
257
246
|
},
|
|
258
|
-
Table = new
|
|
247
|
+
Table = new List<ExcelTableDataRequest>
|
|
259
248
|
{
|
|
260
|
-
new
|
|
249
|
+
new ExcelTableDataRequest
|
|
261
250
|
{
|
|
262
|
-
|
|
251
|
+
Columns = new List<string> { "region", "product", "qty", "total" },
|
|
252
|
+
Rows = new List<ICollection<object>>
|
|
263
253
|
{
|
|
264
|
-
new
|
|
265
|
-
new
|
|
266
|
-
new
|
|
254
|
+
new List<object> { "North", "A", 10, 1000 },
|
|
255
|
+
new List<object> { "North", "B", 5, 500 },
|
|
256
|
+
new List<object> { "South", "A", 8, 800 }
|
|
267
257
|
},
|
|
268
258
|
AutoFilter = true,
|
|
269
259
|
FreezeHeader = true,
|
|
270
260
|
AutoFitColumns = true,
|
|
271
261
|
AsExcelTable = true,
|
|
272
262
|
ExcelTableStyle = "TableStyleMedium2",
|
|
273
|
-
GenerateTotals = new
|
|
274
|
-
|
|
263
|
+
GenerateTotals = new List<KeyValueRequest>
|
|
264
|
+
{
|
|
265
|
+
new KeyValueRequest { Key = "total", Value = "SUM" },
|
|
266
|
+
new KeyValueRequest { Key = "qty", Value = "SUM" }
|
|
267
|
+
},
|
|
268
|
+
NumberFormat = new List<KeyValueRequest> { new KeyValueRequest { Key = "total", Value = "#,##0.00" } }
|
|
275
269
|
}
|
|
276
270
|
}
|
|
277
271
|
};
|
|
@@ -332,33 +326,72 @@ const response = await qorstackApi.render.postRenderWordTemplateSandbox(
|
|
|
332
326
|
|
|
333
327
|
---
|
|
334
328
|
|
|
335
|
-
### 6.
|
|
329
|
+
### 6. Async renders *(Pro)* and job status
|
|
330
|
+
|
|
331
|
+
With `"async": true` the API queues the render and answers `202` with a job id instead of the file.
|
|
332
|
+
Use the helpers below — they handle both answers, poll the job, and turn `429` (too many pending
|
|
333
|
+
jobs) into a typed error. Without a Pro license the API answers `403`.
|
|
336
334
|
|
|
337
335
|
```typescript
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
});
|
|
336
|
+
import { QorstackApi, RenderQueueFullError } from "qorstack-report-sdk";
|
|
337
|
+
|
|
338
|
+
const outcome = await qorstackApi.renderWord({ templateKey: "invoice", async: true, replace: [/* … */] });
|
|
339
|
+
|
|
340
|
+
if (outcome.kind === "queued") {
|
|
341
|
+
const job = await qorstackApi.waitForJob(outcome.job.jobId!, { intervalMs: 2000, timeoutMs: 600_000 });
|
|
342
|
+
console.log(job.downloadUrl); // fresh link once status is "success"
|
|
343
|
+
} else {
|
|
344
|
+
console.log(outcome.download.downloadUrl); // rendered in this request (async not set)
|
|
345
|
+
}
|
|
344
346
|
|
|
345
|
-
//
|
|
346
|
-
|
|
347
|
+
// const job = await qorstackApi.getRenderJob(jobId); // single status check
|
|
348
|
+
// catch (e) { if (e instanceof RenderQueueFullError) retry after e.retryAfterSeconds }
|
|
347
349
|
```
|
|
348
350
|
|
|
351
|
+
```csharp
|
|
352
|
+
var outcome = await qorstackApi.RenderWordAsync(new PdfFromTemplateRequest { TemplateKey = "invoice", Async = true });
|
|
353
|
+
|
|
354
|
+
var link = outcome.IsQueued
|
|
355
|
+
? (await qorstackApi.WaitForJobAsync(outcome.Job!.JobId, TimeSpan.FromSeconds(2), TimeSpan.FromMinutes(10))).DownloadUrl
|
|
356
|
+
: outcome.Download!.DownloadUrl;
|
|
357
|
+
|
|
358
|
+
// RenderQueueFullException.RetryAfter on 429 · RenderJobFailedException when the job fails
|
|
359
|
+
// var job = await qorstackApi.GetRenderJobAsync(jobId);
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
`renderWord` / `renderExcel` (`RenderWordAsync` / `RenderExcelAsync`) also work without `async` —
|
|
363
|
+
they then always return the download. The generated `render.postRender…` methods stay available.
|
|
364
|
+
|
|
349
365
|
---
|
|
350
366
|
|
|
351
367
|
## Data Binding Reference
|
|
352
368
|
|
|
369
|
+
`replace`, `image`, `qrcode` and `barcode` are **keyed arrays**: every item names the
|
|
370
|
+
template marker it fills via `key` (SDK types: `KeyValueRequest`, `ImageDataRequest`,
|
|
371
|
+
`QrCodeDataRequest`, `BarcodeDataRequest`). Tables list their column names once in
|
|
372
|
+
`columns`; each row in `rows` holds the values in that order.
|
|
373
|
+
|
|
374
|
+
The API validates the whole request first and returns every problem in one `400`,
|
|
375
|
+
keyed by JSON path (`replace[3].key`, `table[0].rows[12]`, `image[0].fit`).
|
|
376
|
+
|
|
377
|
+
Set `templateVersion` to render an older stored version of the template (omit it for the
|
|
378
|
+
current version; an unknown version returns `404`).
|
|
379
|
+
|
|
380
|
+
> **SDK 1.0 / API v2.0:** the v1 forms were removed and now return `400`: object maps
|
|
381
|
+
> (`"replace": { "customerName": "..." }`), rows as objects or `{ key, value }` cells,
|
|
382
|
+
> single-string `sort` / `verticalMerge` / `collapse`, and `fileType` on the Excel endpoint.
|
|
383
|
+
> Upgrading from SDK 0.x? See
|
|
384
|
+
> [Migrating from v1](../backend/documents/10_RENDER-API-SUMMARY.md#migrating-from-v1).
|
|
385
|
+
|
|
353
386
|
### Text replacement (`replace`)
|
|
354
387
|
|
|
355
|
-
Replaces `{{variable}}` placeholders with strings.
|
|
388
|
+
Replaces `{{variable}}` placeholders with strings (`KeyValueRequest[]`).
|
|
356
389
|
|
|
357
390
|
```json
|
|
358
|
-
"replace":
|
|
359
|
-
"customerName": "Acme Corp",
|
|
360
|
-
"totalAmount": "$1,050.00"
|
|
361
|
-
|
|
391
|
+
"replace": [
|
|
392
|
+
{ "key": "customerName", "value": "Acme Corp" },
|
|
393
|
+
{ "key": "totalAmount", "value": "$1,050.00" }
|
|
394
|
+
]
|
|
362
395
|
```
|
|
363
396
|
|
|
364
397
|
### Word table (`table` — `WordTableDataRequest[]`)
|
|
@@ -366,9 +399,10 @@ Replaces `{{variable}}` placeholders with strings.
|
|
|
366
399
|
```json
|
|
367
400
|
"table": [
|
|
368
401
|
{
|
|
402
|
+
"columns": ["category", "description", "amount"],
|
|
369
403
|
"rows": [
|
|
370
|
-
|
|
371
|
-
|
|
404
|
+
["Hardware", "Laptop", 1200],
|
|
405
|
+
["Hardware", "Monitor", 300]
|
|
372
406
|
],
|
|
373
407
|
"verticalMerge": ["category"],
|
|
374
408
|
"collapse": ["description"]
|
|
@@ -378,7 +412,9 @@ Replaces `{{variable}}` placeholders with strings.
|
|
|
378
412
|
|
|
379
413
|
| Field | Description |
|
|
380
414
|
| --- | --- |
|
|
381
|
-
| `
|
|
415
|
+
| `columns` | Column names, listed once — each matches a `{{row:<name>}}` placeholder. Unique; required when there are rows. |
|
|
416
|
+
| `rows` | Array of rows; each row is an array of values in `columns` order (string, number, boolean or null). A row with the wrong number of values returns `400` naming the row. |
|
|
417
|
+
| `sort` | `[{ "field": column, "direction": "asc" \| "desc" }]`. |
|
|
382
418
|
| `verticalMerge` | Columns whose adjacent identical values are merged vertically. |
|
|
383
419
|
| `collapse` | Columns whose empty/duplicate values collapse to keep layout tidy. |
|
|
384
420
|
|
|
@@ -387,14 +423,17 @@ Replaces `{{variable}}` placeholders with strings.
|
|
|
387
423
|
```json
|
|
388
424
|
"table": [
|
|
389
425
|
{
|
|
390
|
-
"
|
|
426
|
+
"columns": ["region", "qty"],
|
|
427
|
+
"rows": [
|
|
428
|
+
["North", 10]
|
|
429
|
+
],
|
|
391
430
|
"autoFilter": true,
|
|
392
431
|
"freezeHeader": true,
|
|
393
432
|
"autoFitColumns": true,
|
|
394
433
|
"asExcelTable": true,
|
|
395
434
|
"excelTableStyle": "TableStyleMedium2",
|
|
396
|
-
"generateTotals": { "qty": "
|
|
397
|
-
"numberFormat": { "qty": "#,##0" }
|
|
435
|
+
"generateTotals": [ { "key": "qty", "value": "SUM" } ],
|
|
436
|
+
"numberFormat": [ { "key": "qty", "value": "#,##0" } ]
|
|
398
437
|
}
|
|
399
438
|
]
|
|
400
439
|
```
|
|
@@ -405,8 +444,8 @@ Replaces `{{variable}}` placeholders with strings.
|
|
|
405
444
|
| `freezeHeader` | Freeze the header row. |
|
|
406
445
|
| `autoFitColumns` | Auto-fit column widths to content. |
|
|
407
446
|
| `asExcelTable` / `excelTableStyle` | Render as native Excel table with style. |
|
|
408
|
-
| `generateTotals` | Per-column aggregation (`
|
|
409
|
-
| `numberFormat` | Excel number format
|
|
447
|
+
| `generateTotals` | Per-column aggregation: `[{ "key": column, "value": "SUM" }]` (`SUM`, `AVERAGE`, `COUNT`, `MIN`, `MAX`). |
|
|
448
|
+
| `numberFormat` | Excel number format per column: `[{ "key": column, "value": "#,##0.00" }]`. |
|
|
410
449
|
| `conditionalFormat` | Conditional formatting rules. |
|
|
411
450
|
| `splitToSheets` | Split rows into multiple sheets by a column value. |
|
|
412
451
|
| `sort` | Sort rows before render. |
|
|
@@ -416,14 +455,15 @@ Replaces `{{variable}}` placeholders with strings.
|
|
|
416
455
|
Replaces `{{image:logo}}`. Accepts URL or Base64.
|
|
417
456
|
|
|
418
457
|
```json
|
|
419
|
-
"image":
|
|
420
|
-
|
|
458
|
+
"image": [
|
|
459
|
+
{
|
|
460
|
+
"key": "logo",
|
|
421
461
|
"src": "https://example.com/logo.png",
|
|
422
462
|
"width": 150,
|
|
423
463
|
"height": 50,
|
|
424
464
|
"fit": "contain"
|
|
425
465
|
}
|
|
426
|
-
|
|
466
|
+
]
|
|
427
467
|
```
|
|
428
468
|
|
|
429
469
|
### QR codes (`qrcode`)
|
|
@@ -431,13 +471,14 @@ Replaces `{{image:logo}}`. Accepts URL or Base64.
|
|
|
431
471
|
Replaces `{{qr:website}}`.
|
|
432
472
|
|
|
433
473
|
```json
|
|
434
|
-
"qrcode":
|
|
435
|
-
|
|
474
|
+
"qrcode": [
|
|
475
|
+
{
|
|
476
|
+
"key": "website",
|
|
436
477
|
"text": "https://qorstack.dev",
|
|
437
478
|
"size": 200,
|
|
438
479
|
"color": "#000000"
|
|
439
480
|
}
|
|
440
|
-
|
|
481
|
+
]
|
|
441
482
|
```
|
|
442
483
|
|
|
443
484
|
### Barcodes (`barcode`)
|
|
@@ -445,15 +486,16 @@ Replaces `{{qr:website}}`.
|
|
|
445
486
|
Replaces `{{barcode:productId}}`. Default format: `Code128`.
|
|
446
487
|
|
|
447
488
|
```json
|
|
448
|
-
"barcode":
|
|
449
|
-
|
|
489
|
+
"barcode": [
|
|
490
|
+
{
|
|
491
|
+
"key": "productId",
|
|
450
492
|
"text": "1234567890",
|
|
451
493
|
"format": "Code128",
|
|
452
494
|
"includeText": true,
|
|
453
495
|
"width": 300,
|
|
454
496
|
"height": 80
|
|
455
497
|
}
|
|
456
|
-
|
|
498
|
+
]
|
|
457
499
|
```
|
|
458
500
|
|
|
459
501
|
### Output options
|
|
@@ -461,6 +503,7 @@ Replaces `{{barcode:productId}}`. Default format: `Code128`.
|
|
|
461
503
|
| Field | Type | Description |
|
|
462
504
|
| --- | --- | --- |
|
|
463
505
|
| `fileName` | string | Output file name (without extension). |
|
|
506
|
+
| `templateVersion` | integer | Render this stored version of the template; omit for the current version (unknown → `404`). |
|
|
464
507
|
| `fileType` | string | Word only: `"pdf"` (default) or `"docx"`. |
|
|
465
508
|
| `zipOutput` | boolean | If `true`, bundle the output into a `.zip` archive. |
|
|
466
509
|
| `pdfPassword` | object | *(Pro)* Encrypt the PDF and set permissions. |
|
package/dist/QorstackApi.d.ts
CHANGED
|
@@ -370,41 +370,68 @@ export interface RenderResponse {
|
|
|
370
370
|
}
|
|
371
371
|
export type PdfFromTemplateRequest = DocumentProcessingRequestBase & {
|
|
372
372
|
templateKey?: string;
|
|
373
|
+
/**
|
|
374
|
+
* Render this stored version of the template. Omit for the current version.
|
|
375
|
+
* @format int32
|
|
376
|
+
* @min 1
|
|
377
|
+
*/
|
|
378
|
+
templateVersion?: number | null;
|
|
373
379
|
fileName?: string | null;
|
|
374
|
-
|
|
380
|
+
/** Output format: pdf (default) or docx */
|
|
381
|
+
fileType?: "pdf" | "docx";
|
|
375
382
|
table?: WordTableDataRequest[] | null;
|
|
376
383
|
};
|
|
377
384
|
export type WordTableDataRequest = TableDataRequest & {
|
|
378
385
|
repeatHeader?: boolean;
|
|
379
386
|
};
|
|
380
387
|
export interface TableDataRequest {
|
|
381
|
-
|
|
388
|
+
/** Column marker names ({{row:name}}), listed once, in the order of the row values. Unique. */
|
|
389
|
+
columns: string[];
|
|
390
|
+
/** Rows; each row is an array of cell values in the order of columns, e.g. [["A", 2]] */
|
|
391
|
+
rows: (string | number | boolean | null)[][];
|
|
382
392
|
sort?: SortDefinition[] | null;
|
|
383
393
|
verticalMerge?: string[] | null;
|
|
384
394
|
collapse?: string[] | null;
|
|
385
395
|
}
|
|
386
396
|
export interface SortDefinition {
|
|
387
397
|
field?: string;
|
|
388
|
-
direction?:
|
|
398
|
+
direction?: "asc" | "desc";
|
|
389
399
|
}
|
|
390
400
|
export interface DocumentProcessingRequestBase {
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
401
|
+
/** Variable replacements ({{variable}}): [{ "key": "customer_name", "value": "ACME" }] */
|
|
402
|
+
replace?: KeyValueRequest[] | null;
|
|
403
|
+
/** Images for {{image:key}} markers */
|
|
404
|
+
image?: ImageDataRequest[] | null;
|
|
405
|
+
/** QR codes for {{qr:key}} markers */
|
|
406
|
+
qrcode?: QrCodeDataRequest[] | null;
|
|
407
|
+
/** Barcodes for {{barcode:key}} markers */
|
|
408
|
+
barcode?: BarcodeDataRequest[] | null;
|
|
395
409
|
pdfPassword?: PdfPasswordRequest | null;
|
|
396
410
|
watermark?: PdfWatermarkRequest | null;
|
|
397
411
|
zipOutput?: boolean;
|
|
412
|
+
/** Pro: queue the render and return 202 with a job id instead of the file. Poll GET /render/jobs/{jobId} or set webhookUrl. */
|
|
413
|
+
async?: boolean;
|
|
414
|
+
/**
|
|
415
|
+
* With async: true, receives a POST {jobId, status, downloadUrl?, expiresIn?, error?} when the job finishes. Signed with X-Qorstack-Signature when the server has a signing secret.
|
|
416
|
+
* @format uri
|
|
417
|
+
* @maxLength 2048
|
|
418
|
+
*/
|
|
419
|
+
webhookUrl?: string | null;
|
|
398
420
|
}
|
|
399
421
|
export interface ImageDataRequest {
|
|
422
|
+
/** Image marker name ({{image:key}}) */
|
|
423
|
+
key?: string;
|
|
400
424
|
src?: string;
|
|
401
425
|
/** @format int32 */
|
|
402
426
|
width?: number | null;
|
|
403
427
|
/** @format int32 */
|
|
404
428
|
height?: number | null;
|
|
405
|
-
|
|
429
|
+
/** How the image fills its box: cover, contain (default) or fill */
|
|
430
|
+
fit?: "cover" | "contain" | "fill" | null;
|
|
406
431
|
}
|
|
407
432
|
export interface QrCodeDataRequest {
|
|
433
|
+
/** QR marker name ({{qr:key}}) */
|
|
434
|
+
key?: string;
|
|
408
435
|
text?: string;
|
|
409
436
|
/** @format int32 */
|
|
410
437
|
size?: number;
|
|
@@ -413,6 +440,8 @@ export interface QrCodeDataRequest {
|
|
|
413
440
|
logo?: string | null;
|
|
414
441
|
}
|
|
415
442
|
export interface BarcodeDataRequest {
|
|
443
|
+
/** Barcode marker name ({{barcode:key}}) */
|
|
444
|
+
key?: string;
|
|
416
445
|
text?: string;
|
|
417
446
|
format?: string;
|
|
418
447
|
/** @format int32 */
|
|
@@ -455,7 +484,7 @@ export interface KeyValuePairOfStringAndJsonNode {
|
|
|
455
484
|
value?: JsonNode | null;
|
|
456
485
|
}
|
|
457
486
|
export interface JsonNode {
|
|
458
|
-
underlyingElement?: any;
|
|
487
|
+
underlyingElement?: any | null;
|
|
459
488
|
options?: JsonNodeOptions | null;
|
|
460
489
|
parent?: JsonNode | null;
|
|
461
490
|
root?: JsonNode;
|
|
@@ -475,8 +504,13 @@ export interface ExportExcelUrlResponse {
|
|
|
475
504
|
}
|
|
476
505
|
export type ExcelFromTemplateRequest = DocumentProcessingRequestBase & {
|
|
477
506
|
templateKey?: string;
|
|
507
|
+
/**
|
|
508
|
+
* Render this stored version of the template. Omit for the current version.
|
|
509
|
+
* @format int32
|
|
510
|
+
* @min 1
|
|
511
|
+
*/
|
|
512
|
+
templateVersion?: number | null;
|
|
478
513
|
fileName?: string | null;
|
|
479
|
-
fileType?: string;
|
|
480
514
|
table?: ExcelTableDataRequest[] | null;
|
|
481
515
|
};
|
|
482
516
|
export type ExcelTableDataRequest = TableDataRequest & {
|
|
@@ -486,8 +520,10 @@ export type ExcelTableDataRequest = TableDataRequest & {
|
|
|
486
520
|
asExcelTable?: boolean;
|
|
487
521
|
excelTableStyle?: string | null;
|
|
488
522
|
outline?: boolean;
|
|
489
|
-
|
|
490
|
-
|
|
523
|
+
/** Totals per column: [{ "key": "amount", "value": "sum" }] */
|
|
524
|
+
generateTotals?: KeyValueRequest[] | null;
|
|
525
|
+
/** Number format per column: [{ "key": "amount", "value": "#,##0.00" }] */
|
|
526
|
+
numberFormat?: KeyValueRequest[] | null;
|
|
491
527
|
conditionalFormat?: ConditionalFormatConfigRequest[] | null;
|
|
492
528
|
splitToSheets?: SplitToSheetsConfigRequest | null;
|
|
493
529
|
};
|
|
@@ -496,7 +532,7 @@ export interface ConditionalFormatConfigRequest {
|
|
|
496
532
|
rules?: ConditionalFormatRuleRequest[];
|
|
497
533
|
}
|
|
498
534
|
export interface ConditionalFormatRuleRequest {
|
|
499
|
-
value?: any;
|
|
535
|
+
value?: any | null;
|
|
500
536
|
operator?: string | null;
|
|
501
537
|
fontColor?: string | null;
|
|
502
538
|
backgroundColor?: string | null;
|
|
@@ -751,6 +787,21 @@ export interface ApiKeyDto {
|
|
|
751
787
|
/** @format date-time */
|
|
752
788
|
updatedDatetime?: string | null;
|
|
753
789
|
}
|
|
790
|
+
/** Marker name with a text value (replace / generateTotals / numberFormat) */
|
|
791
|
+
export interface KeyValueRequest {
|
|
792
|
+
/** Marker / column name */
|
|
793
|
+
key?: string;
|
|
794
|
+
/** Text value */
|
|
795
|
+
value?: string;
|
|
796
|
+
}
|
|
797
|
+
export interface ExportPdfAsyncResponse {
|
|
798
|
+
/** @format uuid */
|
|
799
|
+
jobId?: string;
|
|
800
|
+
/** pending → processing → success | failed */
|
|
801
|
+
status?: string;
|
|
802
|
+
/** GET this URL to poll the job; it returns a fresh downloadUrl once status is success. */
|
|
803
|
+
statusUrl?: string | null;
|
|
804
|
+
}
|
|
754
805
|
export type QueryParamsType = Record<string | number, any>;
|
|
755
806
|
export type ResponseFormat = keyof Omit<Body, "body" | "bodyUsed">;
|
|
756
807
|
export interface FullRequestParams extends Omit<RequestInit, "body"> {
|
package/dist/async.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ExportPdfAsyncResponse, HttpResponse } from "./QorstackApi";
|
|
2
|
+
/** Result of a render call: the finished file, or a queued job (async: true, Pro). */
|
|
3
|
+
export type RenderOutcome<TDownload> = {
|
|
4
|
+
kind: "download";
|
|
5
|
+
download: TDownload;
|
|
6
|
+
} | {
|
|
7
|
+
kind: "queued";
|
|
8
|
+
job: ExportPdfAsyncResponse;
|
|
9
|
+
};
|
|
10
|
+
/** Status of an async render job (GET /render/jobs/{id}). */
|
|
11
|
+
export interface RenderJobStatus {
|
|
12
|
+
id: string;
|
|
13
|
+
/** pending → processing → success | failed */
|
|
14
|
+
status: "pending" | "processing" | "success" | "failed" | string;
|
|
15
|
+
/** Fresh download link once status is "success". */
|
|
16
|
+
downloadUrl?: string | null;
|
|
17
|
+
downloadUrlExpiresAt?: string | null;
|
|
18
|
+
errorMessage?: string | null;
|
|
19
|
+
durationMs?: number | null;
|
|
20
|
+
fileSizeBytes?: number | null;
|
|
21
|
+
}
|
|
22
|
+
/** HTTP 429: too many unfinished async renders for this user. */
|
|
23
|
+
export declare class RenderQueueFullError extends Error {
|
|
24
|
+
readonly retryAfterSeconds: number | null;
|
|
25
|
+
constructor(retryAfterSeconds: number | null);
|
|
26
|
+
}
|
|
27
|
+
/** The async render job finished with status "failed". */
|
|
28
|
+
export declare class RenderJobFailedError extends Error {
|
|
29
|
+
readonly job: RenderJobStatus;
|
|
30
|
+
constructor(job: RenderJobStatus);
|
|
31
|
+
}
|
|
32
|
+
export interface WaitForJobOptions {
|
|
33
|
+
/** Poll interval in ms (default 2000). */
|
|
34
|
+
intervalMs?: number;
|
|
35
|
+
/** Give up after this many ms (default 600000 = 10 min). */
|
|
36
|
+
timeoutMs?: number;
|
|
37
|
+
}
|
|
38
|
+
/** Turns a raw render response into a RenderOutcome; maps 429 to RenderQueueFullError. */
|
|
39
|
+
export declare function toRenderOutcome<T>(call: Promise<HttpResponse<T, unknown>>): Promise<RenderOutcome<T>>;
|
|
40
|
+
/** Polls until the job finishes; resolves with the job (downloadUrl set) or rejects on failure / timeout. */
|
|
41
|
+
export declare function waitForJob(getJob: (jobId: string) => Promise<RenderJobStatus>, jobId: string, { intervalMs, timeoutMs }?: WaitForJobOptions): Promise<RenderJobStatus>;
|
package/dist/async.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RenderJobFailedError = exports.RenderQueueFullError = void 0;
|
|
4
|
+
exports.toRenderOutcome = toRenderOutcome;
|
|
5
|
+
exports.waitForJob = waitForJob;
|
|
6
|
+
/** HTTP 429: too many unfinished async renders for this user. */
|
|
7
|
+
class RenderQueueFullError extends Error {
|
|
8
|
+
constructor(retryAfterSeconds) {
|
|
9
|
+
super("Too many async renders are pending. Retry later.");
|
|
10
|
+
this.retryAfterSeconds = retryAfterSeconds;
|
|
11
|
+
this.name = "RenderQueueFullError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
exports.RenderQueueFullError = RenderQueueFullError;
|
|
15
|
+
/** The async render job finished with status "failed". */
|
|
16
|
+
class RenderJobFailedError extends Error {
|
|
17
|
+
constructor(job) {
|
|
18
|
+
var _a;
|
|
19
|
+
super(`Render job ${job.id} failed: ${(_a = job.errorMessage) !== null && _a !== void 0 ? _a : "unknown error"}`);
|
|
20
|
+
this.job = job;
|
|
21
|
+
this.name = "RenderJobFailedError";
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.RenderJobFailedError = RenderJobFailedError;
|
|
25
|
+
/** Turns a raw render response into a RenderOutcome; maps 429 to RenderQueueFullError. */
|
|
26
|
+
async function toRenderOutcome(call) {
|
|
27
|
+
var _a, _b;
|
|
28
|
+
try {
|
|
29
|
+
const res = await call;
|
|
30
|
+
if (res.status === 202) {
|
|
31
|
+
return { kind: "queued", job: res.data };
|
|
32
|
+
}
|
|
33
|
+
return { kind: "download", download: res.data };
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
const res = err;
|
|
37
|
+
if (res && res.status === 429) {
|
|
38
|
+
const header = (_b = (_a = res.headers) === null || _a === void 0 ? void 0 : _a.get) === null || _b === void 0 ? void 0 : _b.call(_a, "Retry-After");
|
|
39
|
+
const seconds = header ? Number.parseInt(header, 10) : NaN;
|
|
40
|
+
throw new RenderQueueFullError(Number.isFinite(seconds) ? seconds : null);
|
|
41
|
+
}
|
|
42
|
+
throw err;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/** Polls until the job finishes; resolves with the job (downloadUrl set) or rejects on failure / timeout. */
|
|
46
|
+
async function waitForJob(getJob, jobId, { intervalMs = 2000, timeoutMs = 600000 } = {}) {
|
|
47
|
+
const deadline = Date.now() + timeoutMs;
|
|
48
|
+
for (;;) {
|
|
49
|
+
const job = await getJob(jobId);
|
|
50
|
+
if (job.status === "success")
|
|
51
|
+
return job;
|
|
52
|
+
if (job.status === "failed")
|
|
53
|
+
throw new RenderJobFailedError(job);
|
|
54
|
+
if (Date.now() + intervalMs > deadline) {
|
|
55
|
+
throw new Error(`Render job ${jobId} did not finish in time (last status: ${job.status}).`);
|
|
56
|
+
}
|
|
57
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
58
|
+
}
|
|
59
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { Api } from "./QorstackApi";
|
|
1
|
+
import { Api, ExcelFromTemplateRequest, ExportExcelUrlResponse, ExportPdfUrlResponse, PdfFromTemplateRequest, RequestParams } from "./QorstackApi";
|
|
2
|
+
import { RenderJobStatus, RenderOutcome, WaitForJobOptions } from "./async";
|
|
2
3
|
export * from "./QorstackApi";
|
|
4
|
+
export * from "./async";
|
|
3
5
|
export declare class QorstackApi<SecurityDataType = unknown> extends Api<SecurityDataType> {
|
|
4
6
|
/**
|
|
5
7
|
* Creates a new instance of QorstackApi.
|
|
@@ -9,4 +11,12 @@ export declare class QorstackApi<SecurityDataType = unknown> extends Api<Securit
|
|
|
9
11
|
baseUrl?: string;
|
|
10
12
|
securityData?: SecurityDataType;
|
|
11
13
|
});
|
|
14
|
+
/** Render from a Word template: the file link, or (with async: true, Pro) the queued job. */
|
|
15
|
+
renderWord(data: PdfFromTemplateRequest, params?: RequestParams): Promise<RenderOutcome<ExportPdfUrlResponse>>;
|
|
16
|
+
/** Render from an Excel template: the file link, or (with async: true, Pro) the queued job. */
|
|
17
|
+
renderExcel(data: ExcelFromTemplateRequest, params?: RequestParams): Promise<RenderOutcome<ExportExcelUrlResponse>>;
|
|
18
|
+
/** GET /render/jobs/{jobId}. */
|
|
19
|
+
getRenderJob(jobId: string, params?: RequestParams): Promise<RenderJobStatus>;
|
|
20
|
+
/** Polls an async job until it finishes; resolves with downloadUrl set, rejects on failure or timeout. */
|
|
21
|
+
waitForJob(jobId: string, options?: WaitForJobOptions): Promise<RenderJobStatus>;
|
|
12
22
|
}
|
package/dist/index.js
CHANGED
|
@@ -16,7 +16,9 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
exports.QorstackApi = void 0;
|
|
18
18
|
const QorstackApi_1 = require("./QorstackApi");
|
|
19
|
+
const async_1 = require("./async");
|
|
19
20
|
__exportStar(require("./QorstackApi"), exports);
|
|
21
|
+
__exportStar(require("./async"), exports);
|
|
20
22
|
class QorstackApi extends QorstackApi_1.Api {
|
|
21
23
|
/**
|
|
22
24
|
* Creates a new instance of QorstackApi.
|
|
@@ -36,8 +38,11 @@ class QorstackApi extends QorstackApi_1.Api {
|
|
|
36
38
|
// Ignore errors accessing process.env
|
|
37
39
|
}
|
|
38
40
|
}
|
|
39
|
-
// Fallback to default if still not found
|
|
40
|
-
|
|
41
|
+
// Fallback to the self-host default if still not found. Since v2 the API lives under /api on the
|
|
42
|
+
// same port as the web UI; a v1-style URL without it (http://host:8080) gets /api appended.
|
|
43
|
+
baseUrl = (baseUrl || "http://localhost:8080/api").replace(/\/+$/, "");
|
|
44
|
+
if (!/\/api$/.test(baseUrl))
|
|
45
|
+
baseUrl = `${baseUrl}/api`;
|
|
41
46
|
super({
|
|
42
47
|
baseUrl,
|
|
43
48
|
securityWorker: (securityData) => {
|
|
@@ -51,5 +56,28 @@ class QorstackApi extends QorstackApi_1.Api {
|
|
|
51
56
|
this.setSecurityData(config.securityData);
|
|
52
57
|
}
|
|
53
58
|
}
|
|
59
|
+
/** Render from a Word template: the file link, or (with async: true, Pro) the queued job. */
|
|
60
|
+
renderWord(data, params = {}) {
|
|
61
|
+
return (0, async_1.toRenderOutcome)(this.render.postRenderWordTemplate(data, params));
|
|
62
|
+
}
|
|
63
|
+
/** Render from an Excel template: the file link, or (with async: true, Pro) the queued job. */
|
|
64
|
+
renderExcel(data, params = {}) {
|
|
65
|
+
return (0, async_1.toRenderOutcome)(this.render.postRenderExcelTemplate(data, params));
|
|
66
|
+
}
|
|
67
|
+
/** GET /render/jobs/{jobId}. */
|
|
68
|
+
async getRenderJob(jobId, params = {}) {
|
|
69
|
+
const res = await this.request({
|
|
70
|
+
path: `/render/jobs/${encodeURIComponent(jobId)}`,
|
|
71
|
+
method: "GET",
|
|
72
|
+
secure: true,
|
|
73
|
+
format: "json",
|
|
74
|
+
...params,
|
|
75
|
+
});
|
|
76
|
+
return res.data;
|
|
77
|
+
}
|
|
78
|
+
/** Polls an async job until it finishes; resolves with downloadUrl set, rejects on failure or timeout. */
|
|
79
|
+
waitForJob(jobId, options) {
|
|
80
|
+
return (0, async_1.waitForJob)((id) => this.getRenderJob(id), jobId, options);
|
|
81
|
+
}
|
|
54
82
|
}
|
|
55
83
|
exports.QorstackApi = QorstackApi;
|
package/package.json
CHANGED
|
@@ -1,38 +1,45 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "qorstack-report-sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"main": "dist/index.js",
|
|
5
|
-
"types": "dist/index.d.ts",
|
|
6
|
-
"files": [
|
|
7
|
-
"dist"
|
|
8
|
-
],
|
|
9
|
-
"scripts": {
|
|
10
|
-
"sync-config": "node scripts/sync-config.js",
|
|
11
|
-
"prebuild": "
|
|
12
|
-
"build": "tsc",
|
|
13
|
-
"test": "echo \"Error: no test specified\" && exit 1",
|
|
14
|
-
"gen:nodejs": "swagger-typescript-api generate -p ../backend/swagger-sdk.json -o ./src/nodejs --name QorstackApi.ts",
|
|
15
|
-
"gen:dotnet": "nswag openapi2csclient /input:../backend/swagger-sdk.json /output:src/dotnet/QorstackApi.cs /namespace:Qorstack.Report.Sdk /ClassName:QorstackApi",
|
|
16
|
-
"gen:all": "
|
|
17
|
-
},
|
|
18
|
-
"keywords": [
|
|
19
|
-
"qorstack",
|
|
20
|
-
"report",
|
|
21
|
-
"sdk",
|
|
22
|
-
"pdf",
|
|
23
|
-
"word"
|
|
24
|
-
],
|
|
25
|
-
"author": "Qorstack",
|
|
26
|
-
"license": "MIT",
|
|
27
|
-
"description": "Qorstack Report SDK for Node.js and .NET",
|
|
28
|
-
"repository": {
|
|
29
|
-
"type": "git",
|
|
30
|
-
"url": "https://github.com/qorstack/qorstack-report"
|
|
31
|
-
},
|
|
32
|
-
"devDependencies": {
|
|
33
|
-
"@types/node": "^25.2.1",
|
|
34
|
-
"nswag": "^14.6.3",
|
|
35
|
-
"swagger-typescript-api": "
|
|
36
|
-
"typescript": "^5.9.3"
|
|
37
|
-
}
|
|
38
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "qorstack-report-sdk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"types": "dist/index.d.ts",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist"
|
|
8
|
+
],
|
|
9
|
+
"scripts": {
|
|
10
|
+
"sync-config": "node scripts/sync-config.js",
|
|
11
|
+
"prebuild": "pnpm sync-config",
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
14
|
+
"gen:nodejs": "swagger-typescript-api generate -p ../backend/swagger-sdk.json -o ./src/nodejs --name QorstackApi.ts",
|
|
15
|
+
"gen:dotnet": "nswag openapi2csclient /input:../backend/swagger-sdk.json /output:src/dotnet/QorstackApi.cs /namespace:Qorstack.Report.Sdk /ClassName:QorstackApi",
|
|
16
|
+
"gen:all": "pnpm gen:nodejs && pnpm gen:dotnet"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"qorstack",
|
|
20
|
+
"report",
|
|
21
|
+
"sdk",
|
|
22
|
+
"pdf",
|
|
23
|
+
"word"
|
|
24
|
+
],
|
|
25
|
+
"author": "Qorstack",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"description": "Qorstack Report SDK for Node.js and .NET",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/qorstack/qorstack-report"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^25.2.1",
|
|
34
|
+
"nswag": "^14.6.3",
|
|
35
|
+
"swagger-typescript-api": "13.12.2",
|
|
36
|
+
"typescript": "^5.9.3"
|
|
37
|
+
},
|
|
38
|
+
"packageManager": "pnpm@9.15.9",
|
|
39
|
+
"pnpm": {
|
|
40
|
+
"overrides": {
|
|
41
|
+
"axios": "1.20.0",
|
|
42
|
+
"defu": "6.1.5"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|