ksef-client-ts 0.6.0 → 0.6.2
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 +1 -1
- package/dist/cli.js +308 -54
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +380 -58
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +149 -10
- package/dist/index.d.ts +149 -10
- package/dist/index.js +370 -57
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -97,12 +97,14 @@ var init_ksef_unauthorized_error = __esm({
|
|
|
97
97
|
detail;
|
|
98
98
|
traceId;
|
|
99
99
|
instance;
|
|
100
|
+
timestamp;
|
|
100
101
|
constructor(problemDetails) {
|
|
101
102
|
super(problemDetails.detail || "Unauthorized");
|
|
102
103
|
this.name = "KSeFUnauthorizedError";
|
|
103
104
|
this.detail = problemDetails.detail;
|
|
104
105
|
this.traceId = problemDetails.traceId;
|
|
105
106
|
this.instance = problemDetails.instance;
|
|
107
|
+
this.timestamp = problemDetails.timestamp;
|
|
106
108
|
}
|
|
107
109
|
};
|
|
108
110
|
}
|
|
@@ -121,6 +123,7 @@ var init_ksef_forbidden_error = __esm({
|
|
|
121
123
|
instance;
|
|
122
124
|
security;
|
|
123
125
|
traceId;
|
|
126
|
+
timestamp;
|
|
124
127
|
constructor(problemDetails) {
|
|
125
128
|
super(problemDetails.detail || "Forbidden");
|
|
126
129
|
this.name = "KSeFForbiddenError";
|
|
@@ -129,6 +132,7 @@ var init_ksef_forbidden_error = __esm({
|
|
|
129
132
|
this.instance = problemDetails.instance;
|
|
130
133
|
this.security = problemDetails.security;
|
|
131
134
|
this.traceId = problemDetails.traceId;
|
|
135
|
+
this.timestamp = problemDetails.timestamp;
|
|
132
136
|
}
|
|
133
137
|
};
|
|
134
138
|
}
|
|
@@ -219,6 +223,69 @@ var init_config = __esm({
|
|
|
219
223
|
}
|
|
220
224
|
});
|
|
221
225
|
|
|
226
|
+
// src/errors/ksef-gone-error.ts
|
|
227
|
+
var KSeFGoneError;
|
|
228
|
+
var init_ksef_gone_error = __esm({
|
|
229
|
+
"src/errors/ksef-gone-error.ts"() {
|
|
230
|
+
"use strict";
|
|
231
|
+
init_ksef_error();
|
|
232
|
+
KSeFGoneError = class extends KSeFError {
|
|
233
|
+
statusCode = 410;
|
|
234
|
+
detail;
|
|
235
|
+
instance;
|
|
236
|
+
traceId;
|
|
237
|
+
timestamp;
|
|
238
|
+
constructor(problemDetails) {
|
|
239
|
+
super(problemDetails.detail || "Operation status no longer available (retention expired)");
|
|
240
|
+
this.name = "KSeFGoneError";
|
|
241
|
+
this.detail = problemDetails.detail;
|
|
242
|
+
this.instance = problemDetails.instance;
|
|
243
|
+
this.traceId = problemDetails.traceId;
|
|
244
|
+
this.timestamp = problemDetails.timestamp;
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
// src/errors/error-codes.ts
|
|
251
|
+
function hasErrorCode(body, code) {
|
|
252
|
+
return !!body?.exception?.exceptionDetailList?.some((d) => d.exceptionCode === code);
|
|
253
|
+
}
|
|
254
|
+
var KSeFErrorCode;
|
|
255
|
+
var init_error_codes = __esm({
|
|
256
|
+
"src/errors/error-codes.ts"() {
|
|
257
|
+
"use strict";
|
|
258
|
+
KSeFErrorCode = {
|
|
259
|
+
BatchTimeout: 21208,
|
|
260
|
+
DuplicateInvoice: 440
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
// src/errors/ksef-batch-timeout-error.ts
|
|
266
|
+
var KSeFBatchTimeoutError;
|
|
267
|
+
var init_ksef_batch_timeout_error = __esm({
|
|
268
|
+
"src/errors/ksef-batch-timeout-error.ts"() {
|
|
269
|
+
"use strict";
|
|
270
|
+
init_ksef_api_error();
|
|
271
|
+
init_error_codes();
|
|
272
|
+
KSeFBatchTimeoutError = class _KSeFBatchTimeoutError extends KSeFApiError {
|
|
273
|
+
errorCode = KSeFErrorCode.BatchTimeout;
|
|
274
|
+
constructor(message, statusCode, errorResponse) {
|
|
275
|
+
super(message, statusCode, errorResponse);
|
|
276
|
+
this.name = "KSeFBatchTimeoutError";
|
|
277
|
+
}
|
|
278
|
+
static fromResponse(statusCode, body) {
|
|
279
|
+
const detail = body?.exception?.exceptionDetailList?.find(
|
|
280
|
+
(d) => d.exceptionCode === KSeFErrorCode.BatchTimeout
|
|
281
|
+
);
|
|
282
|
+
const message = detail?.exceptionDescription?.trim() || "Batch session timed out before the server completed processing (KSeF 21208).";
|
|
283
|
+
return new _KSeFBatchTimeoutError(message, statusCode, body);
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
|
|
222
289
|
// src/http/route-builder.ts
|
|
223
290
|
var RouteBuilder;
|
|
224
291
|
var init_route_builder = __esm({
|
|
@@ -396,6 +463,9 @@ var init_rest_client = __esm({
|
|
|
396
463
|
init_ksef_rate_limit_error();
|
|
397
464
|
init_ksef_unauthorized_error();
|
|
398
465
|
init_ksef_forbidden_error();
|
|
466
|
+
init_ksef_gone_error();
|
|
467
|
+
init_ksef_batch_timeout_error();
|
|
468
|
+
init_error_codes();
|
|
399
469
|
init_route_builder();
|
|
400
470
|
init_transport();
|
|
401
471
|
init_retry_policy();
|
|
@@ -536,18 +606,33 @@ var init_rest_client = __esm({
|
|
|
536
606
|
);
|
|
537
607
|
}
|
|
538
608
|
if (response.status === 401) {
|
|
539
|
-
const
|
|
540
|
-
if (
|
|
541
|
-
throw new KSeFUnauthorizedError(
|
|
609
|
+
const body2 = parseJson();
|
|
610
|
+
if (body2?.detail) {
|
|
611
|
+
throw new KSeFUnauthorizedError(body2);
|
|
542
612
|
}
|
|
543
613
|
}
|
|
544
614
|
if (response.status === 403) {
|
|
545
|
-
const
|
|
546
|
-
if (
|
|
547
|
-
throw new KSeFForbiddenError(
|
|
615
|
+
const body2 = parseJson();
|
|
616
|
+
if (body2?.reasonCode) {
|
|
617
|
+
throw new KSeFForbiddenError(body2);
|
|
548
618
|
}
|
|
549
619
|
}
|
|
550
|
-
|
|
620
|
+
if (response.status === 410) {
|
|
621
|
+
const body2 = parseJson();
|
|
622
|
+
throw new KSeFGoneError({
|
|
623
|
+
title: body2?.title || "Gone",
|
|
624
|
+
status: body2?.status || 410,
|
|
625
|
+
detail: body2?.detail || "Operation status no longer available (retention expired)",
|
|
626
|
+
instance: body2?.instance,
|
|
627
|
+
traceId: body2?.traceId,
|
|
628
|
+
timestamp: body2?.timestamp
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
const body = parseJson();
|
|
632
|
+
if (hasErrorCode(body, KSeFErrorCode.BatchTimeout)) {
|
|
633
|
+
throw KSeFBatchTimeoutError.fromResponse(response.status, body);
|
|
634
|
+
}
|
|
635
|
+
throw KSeFApiError.fromResponse(response.status, body);
|
|
551
636
|
}
|
|
552
637
|
};
|
|
553
638
|
}
|
|
@@ -988,6 +1073,32 @@ var init_online_session = __esm({
|
|
|
988
1073
|
}
|
|
989
1074
|
});
|
|
990
1075
|
|
|
1076
|
+
// src/utils/concurrency.ts
|
|
1077
|
+
async function runWithConcurrency(tasks, parallelism) {
|
|
1078
|
+
if (tasks.length === 0) return;
|
|
1079
|
+
const limit = Math.max(1, Math.min(parallelism, tasks.length));
|
|
1080
|
+
const controller = new AbortController();
|
|
1081
|
+
let index = 0;
|
|
1082
|
+
const workers = Array.from({ length: limit }, async () => {
|
|
1083
|
+
while (index < tasks.length && !controller.signal.aborted) {
|
|
1084
|
+
const current = index;
|
|
1085
|
+
index += 1;
|
|
1086
|
+
try {
|
|
1087
|
+
await tasks[current](controller.signal);
|
|
1088
|
+
} catch (err) {
|
|
1089
|
+
controller.abort();
|
|
1090
|
+
throw err;
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
});
|
|
1094
|
+
await Promise.all(workers);
|
|
1095
|
+
}
|
|
1096
|
+
var init_concurrency = __esm({
|
|
1097
|
+
"src/utils/concurrency.ts"() {
|
|
1098
|
+
"use strict";
|
|
1099
|
+
}
|
|
1100
|
+
});
|
|
1101
|
+
|
|
991
1102
|
// src/services/batch-session.ts
|
|
992
1103
|
var BatchSessionService;
|
|
993
1104
|
var init_batch_session = __esm({
|
|
@@ -996,6 +1107,7 @@ var init_batch_session = __esm({
|
|
|
996
1107
|
init_ksef_feature();
|
|
997
1108
|
init_rest_request();
|
|
998
1109
|
init_routes();
|
|
1110
|
+
init_concurrency();
|
|
999
1111
|
BatchSessionService = class {
|
|
1000
1112
|
restClient;
|
|
1001
1113
|
constructor(restClient) {
|
|
@@ -1009,12 +1121,10 @@ var init_batch_session = __esm({
|
|
|
1009
1121
|
const response = await this.restClient.execute(req);
|
|
1010
1122
|
return response.body;
|
|
1011
1123
|
}
|
|
1012
|
-
async sendParts(openResponse, parts) {
|
|
1013
|
-
const
|
|
1014
|
-
const tasks = parts.map(async (
|
|
1015
|
-
const uploadReq =
|
|
1016
|
-
(r) => r.ordinalNumber === part.ordinalNumber
|
|
1017
|
-
);
|
|
1124
|
+
async sendParts(openResponse, parts, parallelism) {
|
|
1125
|
+
const uploadMap = new Map(openResponse.partUploadRequests.map((r) => [r.ordinalNumber, r]));
|
|
1126
|
+
const tasks = parts.map((part) => async (signal) => {
|
|
1127
|
+
const uploadReq = uploadMap.get(part.ordinalNumber);
|
|
1018
1128
|
if (!uploadReq) {
|
|
1019
1129
|
throw new Error(`No upload request found for part ${part.ordinalNumber}`);
|
|
1020
1130
|
}
|
|
@@ -1022,25 +1132,38 @@ var init_batch_session = __esm({
|
|
|
1022
1132
|
for (const [k, v] of Object.entries(uploadReq.headers)) {
|
|
1023
1133
|
if (v != null) headers[k] = v;
|
|
1024
1134
|
}
|
|
1025
|
-
await fetch(uploadReq.url, {
|
|
1135
|
+
const resp = await fetch(uploadReq.url, {
|
|
1026
1136
|
method: uploadReq.method,
|
|
1027
1137
|
headers,
|
|
1028
|
-
body: part.data
|
|
1138
|
+
body: part.data,
|
|
1139
|
+
signal
|
|
1029
1140
|
});
|
|
1141
|
+
if (!resp.ok) {
|
|
1142
|
+
const body = await resp.text().catch(() => "");
|
|
1143
|
+
throw new Error(
|
|
1144
|
+
`Upload failed for part ${part.ordinalNumber}: HTTP ${resp.status}${body ? ` \u2014 ${body}` : ""}`
|
|
1145
|
+
);
|
|
1146
|
+
}
|
|
1030
1147
|
});
|
|
1031
|
-
|
|
1148
|
+
if (parallelism !== void 0) {
|
|
1149
|
+
await runWithConcurrency(tasks, parallelism);
|
|
1150
|
+
} else {
|
|
1151
|
+
const ac = new AbortController();
|
|
1152
|
+
await Promise.all(tasks.map((t) => t(ac.signal).catch((err) => {
|
|
1153
|
+
ac.abort();
|
|
1154
|
+
throw err;
|
|
1155
|
+
})));
|
|
1156
|
+
}
|
|
1032
1157
|
}
|
|
1033
1158
|
/**
|
|
1034
|
-
* Upload parts
|
|
1035
|
-
*
|
|
1036
|
-
*
|
|
1159
|
+
* Upload parts using streaming bodies (`duplex: 'half'`).
|
|
1160
|
+
* By default uploads sequentially to avoid backpressure issues.
|
|
1161
|
+
* Pass `parallelism` to enable bounded concurrent uploads.
|
|
1037
1162
|
*/
|
|
1038
|
-
async sendPartsWithStream(openResponse, parts) {
|
|
1039
|
-
const
|
|
1040
|
-
|
|
1041
|
-
const uploadReq =
|
|
1042
|
-
(r) => r.ordinalNumber === part.ordinalNumber
|
|
1043
|
-
);
|
|
1163
|
+
async sendPartsWithStream(openResponse, parts, parallelism) {
|
|
1164
|
+
const uploadMap = new Map(openResponse.partUploadRequests.map((r) => [r.ordinalNumber, r]));
|
|
1165
|
+
const uploadPart = async (part, signal) => {
|
|
1166
|
+
const uploadReq = uploadMap.get(part.ordinalNumber);
|
|
1044
1167
|
if (!uploadReq) {
|
|
1045
1168
|
throw new Error(`No upload request found for part ${part.ordinalNumber}`);
|
|
1046
1169
|
}
|
|
@@ -1052,11 +1175,23 @@ var init_batch_session = __esm({
|
|
|
1052
1175
|
method: uploadReq.method,
|
|
1053
1176
|
headers,
|
|
1054
1177
|
body: part.dataStream,
|
|
1178
|
+
signal,
|
|
1055
1179
|
// @ts-expect-error -- Node 18+ undici supports duplex for streaming body
|
|
1056
1180
|
duplex: "half"
|
|
1057
1181
|
});
|
|
1058
1182
|
if (!resp.ok) {
|
|
1059
|
-
|
|
1183
|
+
const body = await resp.text().catch(() => "");
|
|
1184
|
+
throw new Error(
|
|
1185
|
+
`Upload failed for part ${part.ordinalNumber}: HTTP ${resp.status}${body ? ` \u2014 ${body}` : ""}`
|
|
1186
|
+
);
|
|
1187
|
+
}
|
|
1188
|
+
};
|
|
1189
|
+
if (parallelism !== void 0) {
|
|
1190
|
+
await runWithConcurrency(parts.map((p) => (signal) => uploadPart(p, signal)), parallelism);
|
|
1191
|
+
} else {
|
|
1192
|
+
const { signal } = new AbortController();
|
|
1193
|
+
for (const part of parts) {
|
|
1194
|
+
await uploadPart(part, signal);
|
|
1060
1195
|
}
|
|
1061
1196
|
}
|
|
1062
1197
|
}
|
|
@@ -1169,7 +1304,10 @@ var init_invoice_download = __esm({
|
|
|
1169
1304
|
async getInvoice(ksefNumber) {
|
|
1170
1305
|
const req = RestRequest.get(Routes.Invoices.byKsefNumber(ksefNumber));
|
|
1171
1306
|
const response = await this.restClient.executeRaw(req);
|
|
1172
|
-
return
|
|
1307
|
+
return {
|
|
1308
|
+
xml: new TextDecoder().decode(response.body),
|
|
1309
|
+
hash: response.headers.get("x-ms-meta-hash") ?? void 0
|
|
1310
|
+
};
|
|
1173
1311
|
}
|
|
1174
1312
|
async queryInvoiceMetadata(filters, pageOffset, pageSize, sortOrder) {
|
|
1175
1313
|
const req = RestRequest.post(Routes.Invoices.queryMetadata).body(filters);
|
|
@@ -1450,9 +1588,12 @@ var init_errors = __esm({
|
|
|
1450
1588
|
init_ksef_rate_limit_error();
|
|
1451
1589
|
init_ksef_unauthorized_error();
|
|
1452
1590
|
init_ksef_forbidden_error();
|
|
1591
|
+
init_ksef_gone_error();
|
|
1453
1592
|
init_ksef_auth_status_error();
|
|
1454
1593
|
init_ksef_session_expired_error();
|
|
1455
1594
|
init_ksef_validation_error();
|
|
1595
|
+
init_ksef_batch_timeout_error();
|
|
1596
|
+
init_error_codes();
|
|
1456
1597
|
}
|
|
1457
1598
|
});
|
|
1458
1599
|
|
|
@@ -2087,6 +2228,72 @@ var init_auth_xml_builder = __esm({
|
|
|
2087
2228
|
}
|
|
2088
2229
|
});
|
|
2089
2230
|
|
|
2231
|
+
// src/offline/holidays.ts
|
|
2232
|
+
function toDateKey(d) {
|
|
2233
|
+
return d.toISOString().slice(0, 10);
|
|
2234
|
+
}
|
|
2235
|
+
function dateKey(year, month, day) {
|
|
2236
|
+
return toDateKey(new Date(Date.UTC(year, month - 1, day)));
|
|
2237
|
+
}
|
|
2238
|
+
function addDays(d, n) {
|
|
2239
|
+
const result = new Date(d);
|
|
2240
|
+
result.setUTCDate(result.getUTCDate() + n);
|
|
2241
|
+
return result;
|
|
2242
|
+
}
|
|
2243
|
+
function computeEasterSunday(year) {
|
|
2244
|
+
const a = year % 19;
|
|
2245
|
+
const b = Math.floor(year / 100);
|
|
2246
|
+
const c = year % 100;
|
|
2247
|
+
const d = Math.floor(b / 4);
|
|
2248
|
+
const e = b % 4;
|
|
2249
|
+
const f = Math.floor((b + 8) / 25);
|
|
2250
|
+
const g = Math.floor((b - f + 1) / 3);
|
|
2251
|
+
const h = (19 * a + b - d - g + 15) % 30;
|
|
2252
|
+
const i = Math.floor(c / 4);
|
|
2253
|
+
const k = c % 4;
|
|
2254
|
+
const l = (32 + 2 * e + 2 * i - h - k) % 7;
|
|
2255
|
+
const m = Math.floor((a + 11 * h + 22 * l) / 451);
|
|
2256
|
+
const month = Math.floor((h + l - 7 * m + 114) / 31);
|
|
2257
|
+
const day = (h + l - 7 * m + 114) % 31 + 1;
|
|
2258
|
+
return new Date(Date.UTC(year, month - 1, day));
|
|
2259
|
+
}
|
|
2260
|
+
function getPolishHolidays(year) {
|
|
2261
|
+
const cached = holidayCache.get(year);
|
|
2262
|
+
if (cached) return cached;
|
|
2263
|
+
const easter = computeEasterSunday(year);
|
|
2264
|
+
const holidays = /* @__PURE__ */ new Set([
|
|
2265
|
+
// Fixed
|
|
2266
|
+
dateKey(year, 1, 1),
|
|
2267
|
+
dateKey(year, 1, 6),
|
|
2268
|
+
dateKey(year, 5, 1),
|
|
2269
|
+
dateKey(year, 5, 3),
|
|
2270
|
+
dateKey(year, 8, 15),
|
|
2271
|
+
dateKey(year, 11, 1),
|
|
2272
|
+
dateKey(year, 11, 11),
|
|
2273
|
+
dateKey(year, 12, 25),
|
|
2274
|
+
dateKey(year, 12, 26),
|
|
2275
|
+
// Wigilia — added by Dz.U. 2024 poz. 1965, effective from 2025
|
|
2276
|
+
...year >= 2025 ? [dateKey(year, 12, 24)] : [],
|
|
2277
|
+
// Moveable
|
|
2278
|
+
toDateKey(easter),
|
|
2279
|
+
toDateKey(addDays(easter, 1)),
|
|
2280
|
+
toDateKey(addDays(easter, 49)),
|
|
2281
|
+
toDateKey(addDays(easter, 60))
|
|
2282
|
+
]);
|
|
2283
|
+
holidayCache.set(year, holidays);
|
|
2284
|
+
return holidays;
|
|
2285
|
+
}
|
|
2286
|
+
function isPolishHoliday(date) {
|
|
2287
|
+
return getPolishHolidays(date.getUTCFullYear()).has(toDateKey(date));
|
|
2288
|
+
}
|
|
2289
|
+
var holidayCache;
|
|
2290
|
+
var init_holidays = __esm({
|
|
2291
|
+
"src/offline/holidays.ts"() {
|
|
2292
|
+
"use strict";
|
|
2293
|
+
holidayCache = /* @__PURE__ */ new Map();
|
|
2294
|
+
}
|
|
2295
|
+
});
|
|
2296
|
+
|
|
2090
2297
|
// src/offline/deadline.ts
|
|
2091
2298
|
function getDefaultReason(mode) {
|
|
2092
2299
|
switch (mode) {
|
|
@@ -2103,7 +2310,7 @@ function getDefaultReason(mode) {
|
|
|
2103
2310
|
function nextBusinessDay(from) {
|
|
2104
2311
|
const d = new Date(from);
|
|
2105
2312
|
d.setUTCDate(d.getUTCDate() + 1);
|
|
2106
|
-
while (d.getUTCDay() === 0 || d.getUTCDay() === 6) {
|
|
2313
|
+
while (d.getUTCDay() === 0 || d.getUTCDay() === 6 || isPolishHoliday(d)) {
|
|
2107
2314
|
d.setUTCDate(d.getUTCDate() + 1);
|
|
2108
2315
|
}
|
|
2109
2316
|
return d;
|
|
@@ -2116,7 +2323,7 @@ function addBusinessDays(from, days) {
|
|
|
2116
2323
|
let remaining = days;
|
|
2117
2324
|
while (remaining > 0) {
|
|
2118
2325
|
d.setUTCDate(d.getUTCDate() + 1);
|
|
2119
|
-
if (d.getUTCDay() !== 0 && d.getUTCDay() !== 6) {
|
|
2326
|
+
if (d.getUTCDay() !== 0 && d.getUTCDay() !== 6 && !isPolishHoliday(d)) {
|
|
2120
2327
|
remaining--;
|
|
2121
2328
|
}
|
|
2122
2329
|
}
|
|
@@ -2162,6 +2369,7 @@ var FAR_FUTURE;
|
|
|
2162
2369
|
var init_deadline = __esm({
|
|
2163
2370
|
"src/offline/deadline.ts"() {
|
|
2164
2371
|
"use strict";
|
|
2372
|
+
init_holidays();
|
|
2165
2373
|
FAR_FUTURE = /* @__PURE__ */ new Date("9999-12-31T23:59:59Z");
|
|
2166
2374
|
}
|
|
2167
2375
|
});
|
|
@@ -2884,6 +3092,7 @@ var init_client = __esm({
|
|
|
2884
3092
|
qr;
|
|
2885
3093
|
options;
|
|
2886
3094
|
authManager;
|
|
3095
|
+
_baseRestClientConfig;
|
|
2887
3096
|
_offline;
|
|
2888
3097
|
constructor(options) {
|
|
2889
3098
|
this.options = resolveOptions(options);
|
|
@@ -2895,6 +3104,8 @@ var init_client = __esm({
|
|
|
2895
3104
|
});
|
|
2896
3105
|
this.authManager = authManager;
|
|
2897
3106
|
const restClientConfig = buildRestClientConfig(options, authManager);
|
|
3107
|
+
const { authManager: _am, ...baseConfig } = restClientConfig;
|
|
3108
|
+
this._baseRestClientConfig = baseConfig;
|
|
2898
3109
|
const restClient = new RestClient(this.options, restClientConfig);
|
|
2899
3110
|
const fetcher = new CertificateFetcher(restClient);
|
|
2900
3111
|
this.crypto = new CryptographyService(fetcher);
|
|
@@ -2919,6 +3130,10 @@ var init_client = __esm({
|
|
|
2919
3130
|
}
|
|
2920
3131
|
return this._offline;
|
|
2921
3132
|
}
|
|
3133
|
+
/** @internal Create a RestClient with a different AuthManager, preserving transport/retry/rateLimit config. */
|
|
3134
|
+
createScopedRestClient(authManager) {
|
|
3135
|
+
return new RestClient(this.options, { ...this._baseRestClientConfig, authManager });
|
|
3136
|
+
}
|
|
2922
3137
|
async loginWithToken(token, nip) {
|
|
2923
3138
|
const challenge2 = await this.auth.getChallenge();
|
|
2924
3139
|
await this.crypto.init();
|
|
@@ -2933,6 +3148,7 @@ var init_client = __esm({
|
|
|
2933
3148
|
const tokens = await this.auth.getAccessToken(authToken);
|
|
2934
3149
|
this.authManager.setAccessToken(tokens.accessToken.token);
|
|
2935
3150
|
this.authManager.setRefreshToken(tokens.refreshToken.token);
|
|
3151
|
+
return { clientIp: challenge2.clientIp };
|
|
2936
3152
|
}
|
|
2937
3153
|
async loginWithCertificate(certPem, keyPem, nip, keyPassword) {
|
|
2938
3154
|
const challenge2 = await this.auth.getChallenge();
|
|
@@ -2945,11 +3161,12 @@ var init_client = __esm({
|
|
|
2945
3161
|
const tokens = await this.auth.getAccessToken(authToken);
|
|
2946
3162
|
this.authManager.setAccessToken(tokens.accessToken.token);
|
|
2947
3163
|
this.authManager.setRefreshToken(tokens.refreshToken.token);
|
|
3164
|
+
return { clientIp: challenge2.clientIp };
|
|
2948
3165
|
}
|
|
2949
3166
|
async loginWithPkcs12(p12, password, nip) {
|
|
2950
3167
|
const { Pkcs12Loader: Pkcs12Loader2 } = await Promise.resolve().then(() => (init_pkcs12_loader(), pkcs12_loader_exports));
|
|
2951
3168
|
const { certificatePem, privateKeyPem } = Pkcs12Loader2.load(p12, password);
|
|
2952
|
-
|
|
3169
|
+
return this.loginWithCertificate(certificatePem, privateKeyPem, nip);
|
|
2953
3170
|
}
|
|
2954
3171
|
async awaitAuthReady(referenceNumber, authToken) {
|
|
2955
3172
|
for (let i = 0; i < 30; i++) {
|
|
@@ -5034,7 +5251,7 @@ var init_invoice_validator = __esm({
|
|
|
5034
5251
|
});
|
|
5035
5252
|
|
|
5036
5253
|
// src/builders/batch-file.ts
|
|
5037
|
-
import * as
|
|
5254
|
+
import * as crypto6 from "crypto";
|
|
5038
5255
|
function splitBuffer(data, maxPartSize) {
|
|
5039
5256
|
if (data.length <= maxPartSize) {
|
|
5040
5257
|
return [data];
|
|
@@ -5045,8 +5262,8 @@ function splitBuffer(data, maxPartSize) {
|
|
|
5045
5262
|
}
|
|
5046
5263
|
return parts;
|
|
5047
5264
|
}
|
|
5048
|
-
function
|
|
5049
|
-
return
|
|
5265
|
+
function sha256Base642(data) {
|
|
5266
|
+
return crypto6.createHash("sha256").update(data).digest("base64");
|
|
5050
5267
|
}
|
|
5051
5268
|
var BATCH_MAX_PART_SIZE, BATCH_MAX_TOTAL_SIZE, BATCH_MAX_PARTS, BatchFileBuilder;
|
|
5052
5269
|
var init_batch_file = __esm({
|
|
@@ -5083,7 +5300,7 @@ var init_batch_file = __esm({
|
|
|
5083
5300
|
`Data requires ${rawParts.length} parts, exceeding maximum of ${BATCH_MAX_PARTS}`
|
|
5084
5301
|
);
|
|
5085
5302
|
}
|
|
5086
|
-
const zipHash =
|
|
5303
|
+
const zipHash = sha256Base642(zipBytes);
|
|
5087
5304
|
const encryptedParts = [];
|
|
5088
5305
|
const fileParts = rawParts.map((raw, i) => {
|
|
5089
5306
|
const encrypted = encryptFn(raw);
|
|
@@ -5091,7 +5308,7 @@ var init_batch_file = __esm({
|
|
|
5091
5308
|
return {
|
|
5092
5309
|
ordinalNumber: i + 1,
|
|
5093
5310
|
fileSize: encrypted.length,
|
|
5094
|
-
fileHash:
|
|
5311
|
+
fileHash: sha256Base642(encrypted)
|
|
5095
5312
|
};
|
|
5096
5313
|
});
|
|
5097
5314
|
return {
|
|
@@ -5174,7 +5391,7 @@ var init_batch_file = __esm({
|
|
|
5174
5391
|
encryptedChunks.push(value);
|
|
5175
5392
|
}
|
|
5176
5393
|
const encryptedData = new Uint8Array(Buffer.concat(encryptedChunks));
|
|
5177
|
-
const encryptedMeta =
|
|
5394
|
+
const encryptedMeta = sha256Base642(encryptedData);
|
|
5178
5395
|
fileParts.push({
|
|
5179
5396
|
ordinalNumber: partIndex + 1,
|
|
5180
5397
|
fileSize: encryptedData.byteLength,
|
|
@@ -5218,6 +5435,9 @@ __export(batch_session_workflow_exports, {
|
|
|
5218
5435
|
uploadBatchStreamParsed: () => uploadBatchStreamParsed
|
|
5219
5436
|
});
|
|
5220
5437
|
async function uploadBatch(client, zipData, options) {
|
|
5438
|
+
if (options?.parallelism !== void 0 && (!Number.isInteger(options.parallelism) || options.parallelism < 1)) {
|
|
5439
|
+
throw new Error("parallelism must be a positive integer");
|
|
5440
|
+
}
|
|
5221
5441
|
await client.crypto.init();
|
|
5222
5442
|
if (options?.validate) {
|
|
5223
5443
|
const { unzip: unzip2 } = await Promise.resolve().then(() => (init_zip(), zip_exports));
|
|
@@ -5260,7 +5480,7 @@ async function uploadBatch(client, zipData, options) {
|
|
|
5260
5480
|
},
|
|
5261
5481
|
ordinalNumber: i + 1
|
|
5262
5482
|
}));
|
|
5263
|
-
await client.batchSession.sendParts(openResp, sendingParts);
|
|
5483
|
+
await client.batchSession.sendParts(openResp, sendingParts, options?.parallelism);
|
|
5264
5484
|
await client.batchSession.closeSession(openResp.referenceNumber);
|
|
5265
5485
|
const result = await pollUntil(
|
|
5266
5486
|
() => client.sessionStatus.getSessionStatus(openResp.referenceNumber),
|
|
@@ -5281,6 +5501,9 @@ async function uploadBatch(client, zipData, options) {
|
|
|
5281
5501
|
};
|
|
5282
5502
|
}
|
|
5283
5503
|
async function uploadBatchStream(client, zipStreamFactory, zipSize, options) {
|
|
5504
|
+
if (options?.parallelism !== void 0 && (!Number.isInteger(options.parallelism) || options.parallelism < 1)) {
|
|
5505
|
+
throw new Error("parallelism must be a positive integer");
|
|
5506
|
+
}
|
|
5284
5507
|
await client.crypto.init();
|
|
5285
5508
|
const encData = client.crypto.getEncryptionData();
|
|
5286
5509
|
const formCode = options?.formCode ?? DEFAULT_FORM_CODE;
|
|
@@ -5302,7 +5525,7 @@ async function uploadBatchStream(client, zipStreamFactory, zipSize, options) {
|
|
|
5302
5525
|
},
|
|
5303
5526
|
options?.upoVersion
|
|
5304
5527
|
);
|
|
5305
|
-
await client.batchSession.sendPartsWithStream(openResp, streamParts);
|
|
5528
|
+
await client.batchSession.sendPartsWithStream(openResp, streamParts, options?.parallelism);
|
|
5306
5529
|
await client.batchSession.closeSession(openResp.referenceNumber);
|
|
5307
5530
|
const result = await pollUntil(
|
|
5308
5531
|
() => client.sessionStatus.getSessionStatus(openResp.referenceNumber),
|
|
@@ -5923,17 +6146,18 @@ var login = defineCommand2({
|
|
|
5923
6146
|
throw new Error("NIP is required. Provide --nip or set it via `ksef config set --nip <nip>`.");
|
|
5924
6147
|
}
|
|
5925
6148
|
const token = args.token ?? loadCredentials()?.token;
|
|
6149
|
+
let loginResult;
|
|
5926
6150
|
if (token) {
|
|
5927
|
-
await client.loginWithToken(token, nip);
|
|
6151
|
+
loginResult = await client.loginWithToken(token, nip);
|
|
5928
6152
|
} else if (args.p12) {
|
|
5929
6153
|
const fs15 = await import("fs");
|
|
5930
6154
|
const p12Buffer = fs15.readFileSync(args.p12);
|
|
5931
|
-
await client.loginWithPkcs12(p12Buffer, args["p12-password"] ?? "", nip);
|
|
6155
|
+
loginResult = await client.loginWithPkcs12(p12Buffer, args["p12-password"] ?? "", nip);
|
|
5932
6156
|
} else if (args.cert && args.key) {
|
|
5933
6157
|
const fs15 = await import("fs");
|
|
5934
6158
|
const certPem = fs15.readFileSync(args.cert, "utf-8");
|
|
5935
6159
|
const keyPem = fs15.readFileSync(args.key, "utf-8");
|
|
5936
|
-
await client.loginWithCertificate(certPem, keyPem, nip, args["key-password"]);
|
|
6160
|
+
loginResult = await client.loginWithCertificate(certPem, keyPem, nip, args["key-password"]);
|
|
5937
6161
|
} else {
|
|
5938
6162
|
throw new Error("Provide --token, --p12, or both --cert and --key for authentication.");
|
|
5939
6163
|
}
|
|
@@ -5948,7 +6172,12 @@ var login = defineCommand2({
|
|
|
5948
6172
|
if (args.env && args.env !== config.environment) {
|
|
5949
6173
|
saveConfig({ ...config, environment: session.environment });
|
|
5950
6174
|
}
|
|
5951
|
-
|
|
6175
|
+
if (args.json) {
|
|
6176
|
+
console.log(JSON.stringify({ status: "ok", clientIp: loginResult.clientIp }, null, 2));
|
|
6177
|
+
} else {
|
|
6178
|
+
consola6.info(`Seen client IP: ${loginResult.clientIp}`);
|
|
6179
|
+
outputSuccess("Logged in successfully.");
|
|
6180
|
+
}
|
|
5952
6181
|
});
|
|
5953
6182
|
}
|
|
5954
6183
|
});
|
|
@@ -6111,6 +6340,8 @@ var loginExternal = defineCommand2({
|
|
|
6111
6340
|
process.stderr.write(`Challenge: ${challengeResult.challenge}
|
|
6112
6341
|
`);
|
|
6113
6342
|
process.stderr.write(`Timestamp: ${challengeResult.timestamp}
|
|
6343
|
+
`);
|
|
6344
|
+
process.stderr.write(`Seen client IP: ${challengeResult.clientIp}
|
|
6114
6345
|
`);
|
|
6115
6346
|
process.stderr.write(`Note: Sign this XML and submit with --submit before the challenge expires.
|
|
6116
6347
|
`);
|
|
@@ -6311,6 +6542,7 @@ var list = defineCommand3({
|
|
|
6311
6542
|
reference: s.referenceNumber,
|
|
6312
6543
|
status: `${s.status.code} \u2014 ${s.status.description}`,
|
|
6313
6544
|
created: s.dateCreated,
|
|
6545
|
+
updated: s.dateUpdated,
|
|
6314
6546
|
total: s.totalInvoiceCount,
|
|
6315
6547
|
success: s.successfulInvoiceCount,
|
|
6316
6548
|
failed: s.failedInvoiceCount
|
|
@@ -6319,6 +6551,7 @@ var list = defineCommand3({
|
|
|
6319
6551
|
{ key: "reference", label: "Reference" },
|
|
6320
6552
|
{ key: "status", label: "Status" },
|
|
6321
6553
|
{ key: "created", label: "Created" },
|
|
6554
|
+
{ key: "updated", label: "Updated" },
|
|
6322
6555
|
{ key: "total", label: "Total" },
|
|
6323
6556
|
{ key: "success", label: "Success" },
|
|
6324
6557
|
{ key: "failed", label: "Failed" }
|
|
@@ -6646,6 +6879,17 @@ var FileHwmStore = class {
|
|
|
6646
6879
|
// src/workflows/invoice-export-workflow.ts
|
|
6647
6880
|
init_zip();
|
|
6648
6881
|
init_polling();
|
|
6882
|
+
|
|
6883
|
+
// src/utils/hash.ts
|
|
6884
|
+
import crypto5 from "crypto";
|
|
6885
|
+
function sha256Base64(data) {
|
|
6886
|
+
return crypto5.createHash("sha256").update(data).digest("base64");
|
|
6887
|
+
}
|
|
6888
|
+
function verifyHash(data, expectedHash) {
|
|
6889
|
+
return sha256Base64(data) === expectedHash;
|
|
6890
|
+
}
|
|
6891
|
+
|
|
6892
|
+
// src/workflows/invoice-export-workflow.ts
|
|
6649
6893
|
async function doExport(client, filters, options) {
|
|
6650
6894
|
await client.crypto.init();
|
|
6651
6895
|
const encData = client.crypto.getEncryptionData();
|
|
@@ -6674,6 +6918,7 @@ async function doExport(client, filters, options) {
|
|
|
6674
6918
|
url: p.url,
|
|
6675
6919
|
method: p.method,
|
|
6676
6920
|
partSize: p.partSize,
|
|
6921
|
+
partHash: p.partHash,
|
|
6677
6922
|
encryptedPartSize: p.encryptedPartSize,
|
|
6678
6923
|
encryptedPartHash: p.encryptedPartHash,
|
|
6679
6924
|
expirationDate: p.expirationDate
|
|
@@ -6735,6 +6980,9 @@ async function incrementalExportAndDownload(client, options) {
|
|
|
6735
6980
|
throw new Error(`Download failed for part ${part.ordinalNumber}: HTTP ${resp.status}`);
|
|
6736
6981
|
}
|
|
6737
6982
|
const encryptedData = new Uint8Array(await resp.arrayBuffer());
|
|
6983
|
+
if (options.verifyHash !== false && !verifyHash(encryptedData, part.encryptedPartHash)) {
|
|
6984
|
+
throw new Error(`Hash mismatch for export part ${part.ordinalNumber}`);
|
|
6985
|
+
}
|
|
6738
6986
|
const decrypted = client.crypto.decryptAES256(encryptedData, encData.cipherKey, encData.cipherIv);
|
|
6739
6987
|
decryptedParts.push(decrypted);
|
|
6740
6988
|
}
|
|
@@ -6930,8 +7178,8 @@ var QUERY_FILTER_ARGS = {
|
|
|
6930
7178
|
dateType: { type: "string", description: "Date type: Issue|Invoicing|PermanentStorage (default: Invoicing)" },
|
|
6931
7179
|
sellerNip: { type: "string", description: "Filter by seller NIP" },
|
|
6932
7180
|
buyerNip: { type: "string", description: "Filter by buyer NIP" },
|
|
6933
|
-
amountFrom: { type: "string", description: "Minimum amount" },
|
|
6934
|
-
amountTo: { type: "string", description: "Maximum amount" },
|
|
7181
|
+
amountFrom: { type: "string", description: "Minimum amount (negative values allowed)" },
|
|
7182
|
+
amountTo: { type: "string", description: "Maximum amount (negative values allowed)" },
|
|
6935
7183
|
amountType: { type: "string", description: "Amount type: Brutto|Netto|Vat (default: Brutto)" },
|
|
6936
7184
|
currency: { type: "string", description: "Currency code (e.g. PLN, EUR)" }
|
|
6937
7185
|
};
|
|
@@ -6943,6 +7191,7 @@ var send = defineCommand5({
|
|
|
6943
7191
|
stream: { type: "boolean", description: "Use stream-based batch upload (for ZIP files, reduces memory usage)" },
|
|
6944
7192
|
formCode: { type: "string", description: "Document type: FA2, FA3, PEF3, PEFKOR3, FARR1 (default: FA3)" },
|
|
6945
7193
|
validate: { type: "boolean", description: "Validate XML before sending" },
|
|
7194
|
+
parallelism: { type: "string", description: "Number of concurrent part uploads (batch mode)" },
|
|
6946
7195
|
env: { type: "string", description: "Environment (test/demo/prod)" },
|
|
6947
7196
|
json: { type: "boolean", description: "Output as JSON" },
|
|
6948
7197
|
verbose: { type: "boolean", description: "Show HTTP request/response details" },
|
|
@@ -6956,6 +7205,10 @@ var send = defineCommand5({
|
|
|
6956
7205
|
const config = loadConfig();
|
|
6957
7206
|
const nip = args.nip ?? config.nip;
|
|
6958
7207
|
const filePath = args.path;
|
|
7208
|
+
const parallelism = args.parallelism ? Number(args.parallelism) : void 0;
|
|
7209
|
+
if (parallelism !== void 0 && (!Number.isInteger(parallelism) || parallelism < 1)) {
|
|
7210
|
+
throw new Error("--parallelism must be a positive integer");
|
|
7211
|
+
}
|
|
6959
7212
|
const formCodeKey = args.formCode;
|
|
6960
7213
|
let formCode = DEFAULT_FORM_CODE;
|
|
6961
7214
|
if (formCodeKey) {
|
|
@@ -6988,7 +7241,8 @@ var send = defineCommand5({
|
|
|
6988
7241
|
if (!args.json) consola9.start(`Sending batch via stream (${(zipSize / 1e6).toFixed(1)} MB)...`);
|
|
6989
7242
|
const result = await uploadBatchStream2(client, zipStreamFactory, zipSize, {
|
|
6990
7243
|
formCode,
|
|
6991
|
-
pollOptions: { intervalMs: 3e3 }
|
|
7244
|
+
pollOptions: { intervalMs: 3e3 },
|
|
7245
|
+
parallelism
|
|
6992
7246
|
});
|
|
6993
7247
|
if (args.json) {
|
|
6994
7248
|
outputResult(result, { json: true });
|
|
@@ -7052,7 +7306,7 @@ var send = defineCommand5({
|
|
|
7052
7306
|
{ formCode, batchFile: batchFileInfo, encryption: encryptionData.encryptionInfo }
|
|
7053
7307
|
);
|
|
7054
7308
|
saveOnlineSessionRef(openResult.referenceNumber);
|
|
7055
|
-
await client.batchSession.sendParts(openResult, parts);
|
|
7309
|
+
await client.batchSession.sendParts(openResult, parts, parallelism);
|
|
7056
7310
|
await client.batchSession.closeSession(openResult.referenceNumber);
|
|
7057
7311
|
clearOnlineSessionRef();
|
|
7058
7312
|
if (args.json) {
|
|
@@ -7117,9 +7371,9 @@ var get = defineCommand5({
|
|
|
7117
7371
|
return withErrorHandler(async () => {
|
|
7118
7372
|
const globalOpts = getGlobalOpts4(args);
|
|
7119
7373
|
const { client } = await requireSession(globalOpts);
|
|
7120
|
-
const xml = await client.invoices.getInvoice(args.ksefNumber);
|
|
7374
|
+
const { xml, hash } = await client.invoices.getInvoice(args.ksefNumber);
|
|
7121
7375
|
if (args.json) {
|
|
7122
|
-
outputResult({ ksefNumber: args.ksefNumber, xml }, { json: true });
|
|
7376
|
+
outputResult({ ksefNumber: args.ksefNumber, xml, hash }, { json: true });
|
|
7123
7377
|
return;
|
|
7124
7378
|
}
|
|
7125
7379
|
if (args.o) {
|
|
@@ -8036,12 +8290,12 @@ import fs10 from "fs";
|
|
|
8036
8290
|
import path7 from "path";
|
|
8037
8291
|
|
|
8038
8292
|
// src/crypto/certificate-service.ts
|
|
8039
|
-
import * as
|
|
8293
|
+
import * as crypto7 from "crypto";
|
|
8040
8294
|
import * as x5092 from "@peculiar/x509";
|
|
8041
8295
|
var CertificateService = class {
|
|
8042
8296
|
static getSha256Fingerprint(certPem) {
|
|
8043
8297
|
const der = extractDerFromPem2(certPem);
|
|
8044
|
-
return
|
|
8298
|
+
return crypto7.createHash("sha256").update(der).digest("hex").toUpperCase();
|
|
8045
8299
|
}
|
|
8046
8300
|
static async generatePersonalCertificate(givenName, surname, serialNumber, commonName, method = "RSA") {
|
|
8047
8301
|
const nameParts = [];
|
|
@@ -8064,7 +8318,7 @@ var CertificateService = class {
|
|
|
8064
8318
|
}
|
|
8065
8319
|
};
|
|
8066
8320
|
async function generateSelfSigned(subject2, method) {
|
|
8067
|
-
x5092.cryptoProvider.set(
|
|
8321
|
+
x5092.cryptoProvider.set(crypto7.webcrypto);
|
|
8068
8322
|
let algorithm;
|
|
8069
8323
|
let signingAlgorithm;
|
|
8070
8324
|
if (method === "ECDSA") {
|
|
@@ -8079,7 +8333,7 @@ async function generateSelfSigned(subject2, method) {
|
|
|
8079
8333
|
};
|
|
8080
8334
|
signingAlgorithm = algorithm;
|
|
8081
8335
|
}
|
|
8082
|
-
const keys = await
|
|
8336
|
+
const keys = await crypto7.webcrypto.subtle.generateKey(
|
|
8083
8337
|
algorithm,
|
|
8084
8338
|
true,
|
|
8085
8339
|
["sign", "verify"]
|
|
@@ -8096,9 +8350,9 @@ async function generateSelfSigned(subject2, method) {
|
|
|
8096
8350
|
serialNumber: Date.now().toString(16)
|
|
8097
8351
|
});
|
|
8098
8352
|
const certPem = cert.toString("pem");
|
|
8099
|
-
const pkcs8 = await
|
|
8353
|
+
const pkcs8 = await crypto7.webcrypto.subtle.exportKey("pkcs8", keys.privateKey);
|
|
8100
8354
|
const privateKeyPem = pemEncode2(new Uint8Array(pkcs8), "PRIVATE KEY");
|
|
8101
|
-
const fingerprint =
|
|
8355
|
+
const fingerprint = crypto7.createHash("sha256").update(Buffer.from(cert.rawData)).digest("hex").toUpperCase();
|
|
8102
8356
|
return { certificatePem: certPem, privateKeyPem, fingerprint };
|
|
8103
8357
|
}
|
|
8104
8358
|
function extractDerFromPem2(pem) {
|