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/index.js
CHANGED
|
@@ -153,12 +153,14 @@ var init_ksef_unauthorized_error = __esm({
|
|
|
153
153
|
detail;
|
|
154
154
|
traceId;
|
|
155
155
|
instance;
|
|
156
|
+
timestamp;
|
|
156
157
|
constructor(problemDetails) {
|
|
157
158
|
super(problemDetails.detail || "Unauthorized");
|
|
158
159
|
this.name = "KSeFUnauthorizedError";
|
|
159
160
|
this.detail = problemDetails.detail;
|
|
160
161
|
this.traceId = problemDetails.traceId;
|
|
161
162
|
this.instance = problemDetails.instance;
|
|
163
|
+
this.timestamp = problemDetails.timestamp;
|
|
162
164
|
}
|
|
163
165
|
};
|
|
164
166
|
}
|
|
@@ -177,6 +179,7 @@ var init_ksef_forbidden_error = __esm({
|
|
|
177
179
|
instance;
|
|
178
180
|
security;
|
|
179
181
|
traceId;
|
|
182
|
+
timestamp;
|
|
180
183
|
constructor(problemDetails) {
|
|
181
184
|
super(problemDetails.detail || "Forbidden");
|
|
182
185
|
this.name = "KSeFForbiddenError";
|
|
@@ -185,6 +188,31 @@ var init_ksef_forbidden_error = __esm({
|
|
|
185
188
|
this.instance = problemDetails.instance;
|
|
186
189
|
this.security = problemDetails.security;
|
|
187
190
|
this.traceId = problemDetails.traceId;
|
|
191
|
+
this.timestamp = problemDetails.timestamp;
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// src/errors/ksef-gone-error.ts
|
|
198
|
+
var KSeFGoneError;
|
|
199
|
+
var init_ksef_gone_error = __esm({
|
|
200
|
+
"src/errors/ksef-gone-error.ts"() {
|
|
201
|
+
"use strict";
|
|
202
|
+
init_ksef_error();
|
|
203
|
+
KSeFGoneError = class extends KSeFError {
|
|
204
|
+
statusCode = 410;
|
|
205
|
+
detail;
|
|
206
|
+
instance;
|
|
207
|
+
traceId;
|
|
208
|
+
timestamp;
|
|
209
|
+
constructor(problemDetails) {
|
|
210
|
+
super(problemDetails.detail || "Operation status no longer available (retention expired)");
|
|
211
|
+
this.name = "KSeFGoneError";
|
|
212
|
+
this.detail = problemDetails.detail;
|
|
213
|
+
this.instance = problemDetails.instance;
|
|
214
|
+
this.traceId = problemDetails.traceId;
|
|
215
|
+
this.timestamp = problemDetails.timestamp;
|
|
188
216
|
}
|
|
189
217
|
};
|
|
190
218
|
}
|
|
@@ -252,6 +280,45 @@ var init_ksef_validation_error = __esm({
|
|
|
252
280
|
}
|
|
253
281
|
});
|
|
254
282
|
|
|
283
|
+
// src/errors/error-codes.ts
|
|
284
|
+
function hasErrorCode(body, code) {
|
|
285
|
+
return !!body?.exception?.exceptionDetailList?.some((d) => d.exceptionCode === code);
|
|
286
|
+
}
|
|
287
|
+
var KSeFErrorCode;
|
|
288
|
+
var init_error_codes = __esm({
|
|
289
|
+
"src/errors/error-codes.ts"() {
|
|
290
|
+
"use strict";
|
|
291
|
+
KSeFErrorCode = {
|
|
292
|
+
BatchTimeout: 21208,
|
|
293
|
+
DuplicateInvoice: 440
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
// src/errors/ksef-batch-timeout-error.ts
|
|
299
|
+
var KSeFBatchTimeoutError;
|
|
300
|
+
var init_ksef_batch_timeout_error = __esm({
|
|
301
|
+
"src/errors/ksef-batch-timeout-error.ts"() {
|
|
302
|
+
"use strict";
|
|
303
|
+
init_ksef_api_error();
|
|
304
|
+
init_error_codes();
|
|
305
|
+
KSeFBatchTimeoutError = class _KSeFBatchTimeoutError extends KSeFApiError {
|
|
306
|
+
errorCode = KSeFErrorCode.BatchTimeout;
|
|
307
|
+
constructor(message, statusCode, errorResponse) {
|
|
308
|
+
super(message, statusCode, errorResponse);
|
|
309
|
+
this.name = "KSeFBatchTimeoutError";
|
|
310
|
+
}
|
|
311
|
+
static fromResponse(statusCode, body) {
|
|
312
|
+
const detail = body?.exception?.exceptionDetailList?.find(
|
|
313
|
+
(d) => d.exceptionCode === KSeFErrorCode.BatchTimeout
|
|
314
|
+
);
|
|
315
|
+
const message = detail?.exceptionDescription?.trim() || "Batch session timed out before the server completed processing (KSeF 21208).";
|
|
316
|
+
return new _KSeFBatchTimeoutError(message, statusCode, body);
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
|
|
255
322
|
// src/errors/index.ts
|
|
256
323
|
var init_errors = __esm({
|
|
257
324
|
"src/errors/index.ts"() {
|
|
@@ -261,9 +328,12 @@ var init_errors = __esm({
|
|
|
261
328
|
init_ksef_rate_limit_error();
|
|
262
329
|
init_ksef_unauthorized_error();
|
|
263
330
|
init_ksef_forbidden_error();
|
|
331
|
+
init_ksef_gone_error();
|
|
264
332
|
init_ksef_auth_status_error();
|
|
265
333
|
init_ksef_session_expired_error();
|
|
266
334
|
init_ksef_validation_error();
|
|
335
|
+
init_ksef_batch_timeout_error();
|
|
336
|
+
init_error_codes();
|
|
267
337
|
}
|
|
268
338
|
});
|
|
269
339
|
|
|
@@ -520,6 +590,9 @@ var init_rest_client = __esm({
|
|
|
520
590
|
init_ksef_rate_limit_error();
|
|
521
591
|
init_ksef_unauthorized_error();
|
|
522
592
|
init_ksef_forbidden_error();
|
|
593
|
+
init_ksef_gone_error();
|
|
594
|
+
init_ksef_batch_timeout_error();
|
|
595
|
+
init_error_codes();
|
|
523
596
|
init_route_builder();
|
|
524
597
|
init_transport();
|
|
525
598
|
init_retry_policy();
|
|
@@ -660,18 +733,33 @@ var init_rest_client = __esm({
|
|
|
660
733
|
);
|
|
661
734
|
}
|
|
662
735
|
if (response.status === 401) {
|
|
663
|
-
const
|
|
664
|
-
if (
|
|
665
|
-
throw new KSeFUnauthorizedError(
|
|
736
|
+
const body2 = parseJson();
|
|
737
|
+
if (body2?.detail) {
|
|
738
|
+
throw new KSeFUnauthorizedError(body2);
|
|
666
739
|
}
|
|
667
740
|
}
|
|
668
741
|
if (response.status === 403) {
|
|
669
|
-
const
|
|
670
|
-
if (
|
|
671
|
-
throw new KSeFForbiddenError(
|
|
742
|
+
const body2 = parseJson();
|
|
743
|
+
if (body2?.reasonCode) {
|
|
744
|
+
throw new KSeFForbiddenError(body2);
|
|
672
745
|
}
|
|
673
746
|
}
|
|
674
|
-
|
|
747
|
+
if (response.status === 410) {
|
|
748
|
+
const body2 = parseJson();
|
|
749
|
+
throw new KSeFGoneError({
|
|
750
|
+
title: body2?.title || "Gone",
|
|
751
|
+
status: body2?.status || 410,
|
|
752
|
+
detail: body2?.detail || "Operation status no longer available (retention expired)",
|
|
753
|
+
instance: body2?.instance,
|
|
754
|
+
traceId: body2?.traceId,
|
|
755
|
+
timestamp: body2?.timestamp
|
|
756
|
+
});
|
|
757
|
+
}
|
|
758
|
+
const body = parseJson();
|
|
759
|
+
if (hasErrorCode(body, KSeFErrorCode.BatchTimeout)) {
|
|
760
|
+
throw KSeFBatchTimeoutError.fromResponse(response.status, body);
|
|
761
|
+
}
|
|
762
|
+
throw KSeFApiError.fromResponse(response.status, body);
|
|
675
763
|
}
|
|
676
764
|
};
|
|
677
765
|
}
|
|
@@ -955,7 +1043,9 @@ function isValidVatUe(value) {
|
|
|
955
1043
|
return VatUe.test(value);
|
|
956
1044
|
}
|
|
957
1045
|
function isValidNipVatUe(value) {
|
|
958
|
-
|
|
1046
|
+
if (!NipVatUe.test(value)) return false;
|
|
1047
|
+
const nip = value.split("-")[0];
|
|
1048
|
+
return isValidNip(nip);
|
|
959
1049
|
}
|
|
960
1050
|
function isValidInternalId(value) {
|
|
961
1051
|
return InternalId.test(value);
|
|
@@ -2967,6 +3057,32 @@ var init_online_session = __esm({
|
|
|
2967
3057
|
}
|
|
2968
3058
|
});
|
|
2969
3059
|
|
|
3060
|
+
// src/utils/concurrency.ts
|
|
3061
|
+
async function runWithConcurrency(tasks, parallelism) {
|
|
3062
|
+
if (tasks.length === 0) return;
|
|
3063
|
+
const limit = Math.max(1, Math.min(parallelism, tasks.length));
|
|
3064
|
+
const controller = new AbortController();
|
|
3065
|
+
let index = 0;
|
|
3066
|
+
const workers = Array.from({ length: limit }, async () => {
|
|
3067
|
+
while (index < tasks.length && !controller.signal.aborted) {
|
|
3068
|
+
const current = index;
|
|
3069
|
+
index += 1;
|
|
3070
|
+
try {
|
|
3071
|
+
await tasks[current](controller.signal);
|
|
3072
|
+
} catch (err) {
|
|
3073
|
+
controller.abort();
|
|
3074
|
+
throw err;
|
|
3075
|
+
}
|
|
3076
|
+
}
|
|
3077
|
+
});
|
|
3078
|
+
await Promise.all(workers);
|
|
3079
|
+
}
|
|
3080
|
+
var init_concurrency = __esm({
|
|
3081
|
+
"src/utils/concurrency.ts"() {
|
|
3082
|
+
"use strict";
|
|
3083
|
+
}
|
|
3084
|
+
});
|
|
3085
|
+
|
|
2970
3086
|
// src/services/batch-session.ts
|
|
2971
3087
|
var BatchSessionService;
|
|
2972
3088
|
var init_batch_session = __esm({
|
|
@@ -2975,6 +3091,7 @@ var init_batch_session = __esm({
|
|
|
2975
3091
|
init_ksef_feature();
|
|
2976
3092
|
init_rest_request();
|
|
2977
3093
|
init_routes();
|
|
3094
|
+
init_concurrency();
|
|
2978
3095
|
BatchSessionService = class {
|
|
2979
3096
|
restClient;
|
|
2980
3097
|
constructor(restClient) {
|
|
@@ -2988,12 +3105,10 @@ var init_batch_session = __esm({
|
|
|
2988
3105
|
const response = await this.restClient.execute(req);
|
|
2989
3106
|
return response.body;
|
|
2990
3107
|
}
|
|
2991
|
-
async sendParts(openResponse, parts) {
|
|
2992
|
-
const
|
|
2993
|
-
const tasks = parts.map(async (
|
|
2994
|
-
const uploadReq =
|
|
2995
|
-
(r) => r.ordinalNumber === part.ordinalNumber
|
|
2996
|
-
);
|
|
3108
|
+
async sendParts(openResponse, parts, parallelism) {
|
|
3109
|
+
const uploadMap = new Map(openResponse.partUploadRequests.map((r) => [r.ordinalNumber, r]));
|
|
3110
|
+
const tasks = parts.map((part) => async (signal) => {
|
|
3111
|
+
const uploadReq = uploadMap.get(part.ordinalNumber);
|
|
2997
3112
|
if (!uploadReq) {
|
|
2998
3113
|
throw new Error(`No upload request found for part ${part.ordinalNumber}`);
|
|
2999
3114
|
}
|
|
@@ -3001,25 +3116,38 @@ var init_batch_session = __esm({
|
|
|
3001
3116
|
for (const [k, v] of Object.entries(uploadReq.headers)) {
|
|
3002
3117
|
if (v != null) headers[k] = v;
|
|
3003
3118
|
}
|
|
3004
|
-
await fetch(uploadReq.url, {
|
|
3119
|
+
const resp = await fetch(uploadReq.url, {
|
|
3005
3120
|
method: uploadReq.method,
|
|
3006
3121
|
headers,
|
|
3007
|
-
body: part.data
|
|
3122
|
+
body: part.data,
|
|
3123
|
+
signal
|
|
3008
3124
|
});
|
|
3125
|
+
if (!resp.ok) {
|
|
3126
|
+
const body = await resp.text().catch(() => "");
|
|
3127
|
+
throw new Error(
|
|
3128
|
+
`Upload failed for part ${part.ordinalNumber}: HTTP ${resp.status}${body ? ` \u2014 ${body}` : ""}`
|
|
3129
|
+
);
|
|
3130
|
+
}
|
|
3009
3131
|
});
|
|
3010
|
-
|
|
3132
|
+
if (parallelism !== void 0) {
|
|
3133
|
+
await runWithConcurrency(tasks, parallelism);
|
|
3134
|
+
} else {
|
|
3135
|
+
const ac = new AbortController();
|
|
3136
|
+
await Promise.all(tasks.map((t) => t(ac.signal).catch((err) => {
|
|
3137
|
+
ac.abort();
|
|
3138
|
+
throw err;
|
|
3139
|
+
})));
|
|
3140
|
+
}
|
|
3011
3141
|
}
|
|
3012
3142
|
/**
|
|
3013
|
-
* Upload parts
|
|
3014
|
-
*
|
|
3015
|
-
*
|
|
3143
|
+
* Upload parts using streaming bodies (`duplex: 'half'`).
|
|
3144
|
+
* By default uploads sequentially to avoid backpressure issues.
|
|
3145
|
+
* Pass `parallelism` to enable bounded concurrent uploads.
|
|
3016
3146
|
*/
|
|
3017
|
-
async sendPartsWithStream(openResponse, parts) {
|
|
3018
|
-
const
|
|
3019
|
-
|
|
3020
|
-
const uploadReq =
|
|
3021
|
-
(r) => r.ordinalNumber === part.ordinalNumber
|
|
3022
|
-
);
|
|
3147
|
+
async sendPartsWithStream(openResponse, parts, parallelism) {
|
|
3148
|
+
const uploadMap = new Map(openResponse.partUploadRequests.map((r) => [r.ordinalNumber, r]));
|
|
3149
|
+
const uploadPart = async (part, signal) => {
|
|
3150
|
+
const uploadReq = uploadMap.get(part.ordinalNumber);
|
|
3023
3151
|
if (!uploadReq) {
|
|
3024
3152
|
throw new Error(`No upload request found for part ${part.ordinalNumber}`);
|
|
3025
3153
|
}
|
|
@@ -3031,11 +3159,23 @@ var init_batch_session = __esm({
|
|
|
3031
3159
|
method: uploadReq.method,
|
|
3032
3160
|
headers,
|
|
3033
3161
|
body: part.dataStream,
|
|
3162
|
+
signal,
|
|
3034
3163
|
// @ts-expect-error -- Node 18+ undici supports duplex for streaming body
|
|
3035
3164
|
duplex: "half"
|
|
3036
3165
|
});
|
|
3037
3166
|
if (!resp.ok) {
|
|
3038
|
-
|
|
3167
|
+
const body = await resp.text().catch(() => "");
|
|
3168
|
+
throw new Error(
|
|
3169
|
+
`Upload failed for part ${part.ordinalNumber}: HTTP ${resp.status}${body ? ` \u2014 ${body}` : ""}`
|
|
3170
|
+
);
|
|
3171
|
+
}
|
|
3172
|
+
};
|
|
3173
|
+
if (parallelism !== void 0) {
|
|
3174
|
+
await runWithConcurrency(parts.map((p) => (signal) => uploadPart(p, signal)), parallelism);
|
|
3175
|
+
} else {
|
|
3176
|
+
const { signal } = new AbortController();
|
|
3177
|
+
for (const part of parts) {
|
|
3178
|
+
await uploadPart(part, signal);
|
|
3039
3179
|
}
|
|
3040
3180
|
}
|
|
3041
3181
|
}
|
|
@@ -3148,7 +3288,10 @@ var init_invoice_download = __esm({
|
|
|
3148
3288
|
async getInvoice(ksefNumber) {
|
|
3149
3289
|
const req = RestRequest.get(Routes.Invoices.byKsefNumber(ksefNumber));
|
|
3150
3290
|
const response = await this.restClient.executeRaw(req);
|
|
3151
|
-
return
|
|
3291
|
+
return {
|
|
3292
|
+
xml: new TextDecoder().decode(response.body),
|
|
3293
|
+
hash: response.headers.get("x-ms-meta-hash") ?? void 0
|
|
3294
|
+
};
|
|
3152
3295
|
}
|
|
3153
3296
|
async queryInvoiceMetadata(filters, pageOffset, pageSize, sortOrder) {
|
|
3154
3297
|
const req = RestRequest.post(Routes.Invoices.queryMetadata).body(filters);
|
|
@@ -4410,6 +4553,72 @@ var init_zip = __esm({
|
|
|
4410
4553
|
}
|
|
4411
4554
|
});
|
|
4412
4555
|
|
|
4556
|
+
// src/offline/holidays.ts
|
|
4557
|
+
function toDateKey(d) {
|
|
4558
|
+
return d.toISOString().slice(0, 10);
|
|
4559
|
+
}
|
|
4560
|
+
function dateKey(year, month, day) {
|
|
4561
|
+
return toDateKey(new Date(Date.UTC(year, month - 1, day)));
|
|
4562
|
+
}
|
|
4563
|
+
function addDays(d, n) {
|
|
4564
|
+
const result = new Date(d);
|
|
4565
|
+
result.setUTCDate(result.getUTCDate() + n);
|
|
4566
|
+
return result;
|
|
4567
|
+
}
|
|
4568
|
+
function computeEasterSunday(year) {
|
|
4569
|
+
const a = year % 19;
|
|
4570
|
+
const b = Math.floor(year / 100);
|
|
4571
|
+
const c = year % 100;
|
|
4572
|
+
const d = Math.floor(b / 4);
|
|
4573
|
+
const e = b % 4;
|
|
4574
|
+
const f = Math.floor((b + 8) / 25);
|
|
4575
|
+
const g = Math.floor((b - f + 1) / 3);
|
|
4576
|
+
const h = (19 * a + b - d - g + 15) % 30;
|
|
4577
|
+
const i = Math.floor(c / 4);
|
|
4578
|
+
const k = c % 4;
|
|
4579
|
+
const l = (32 + 2 * e + 2 * i - h - k) % 7;
|
|
4580
|
+
const m = Math.floor((a + 11 * h + 22 * l) / 451);
|
|
4581
|
+
const month = Math.floor((h + l - 7 * m + 114) / 31);
|
|
4582
|
+
const day = (h + l - 7 * m + 114) % 31 + 1;
|
|
4583
|
+
return new Date(Date.UTC(year, month - 1, day));
|
|
4584
|
+
}
|
|
4585
|
+
function getPolishHolidays(year) {
|
|
4586
|
+
const cached = holidayCache.get(year);
|
|
4587
|
+
if (cached) return cached;
|
|
4588
|
+
const easter = computeEasterSunday(year);
|
|
4589
|
+
const holidays = /* @__PURE__ */ new Set([
|
|
4590
|
+
// Fixed
|
|
4591
|
+
dateKey(year, 1, 1),
|
|
4592
|
+
dateKey(year, 1, 6),
|
|
4593
|
+
dateKey(year, 5, 1),
|
|
4594
|
+
dateKey(year, 5, 3),
|
|
4595
|
+
dateKey(year, 8, 15),
|
|
4596
|
+
dateKey(year, 11, 1),
|
|
4597
|
+
dateKey(year, 11, 11),
|
|
4598
|
+
dateKey(year, 12, 25),
|
|
4599
|
+
dateKey(year, 12, 26),
|
|
4600
|
+
// Wigilia — added by Dz.U. 2024 poz. 1965, effective from 2025
|
|
4601
|
+
...year >= 2025 ? [dateKey(year, 12, 24)] : [],
|
|
4602
|
+
// Moveable
|
|
4603
|
+
toDateKey(easter),
|
|
4604
|
+
toDateKey(addDays(easter, 1)),
|
|
4605
|
+
toDateKey(addDays(easter, 49)),
|
|
4606
|
+
toDateKey(addDays(easter, 60))
|
|
4607
|
+
]);
|
|
4608
|
+
holidayCache.set(year, holidays);
|
|
4609
|
+
return holidays;
|
|
4610
|
+
}
|
|
4611
|
+
function isPolishHoliday(date) {
|
|
4612
|
+
return getPolishHolidays(date.getUTCFullYear()).has(toDateKey(date));
|
|
4613
|
+
}
|
|
4614
|
+
var holidayCache;
|
|
4615
|
+
var init_holidays = __esm({
|
|
4616
|
+
"src/offline/holidays.ts"() {
|
|
4617
|
+
"use strict";
|
|
4618
|
+
holidayCache = /* @__PURE__ */ new Map();
|
|
4619
|
+
}
|
|
4620
|
+
});
|
|
4621
|
+
|
|
4413
4622
|
// src/offline/deadline.ts
|
|
4414
4623
|
function getDefaultReason(mode) {
|
|
4415
4624
|
switch (mode) {
|
|
@@ -4426,7 +4635,7 @@ function getDefaultReason(mode) {
|
|
|
4426
4635
|
function nextBusinessDay(from) {
|
|
4427
4636
|
const d = new Date(from);
|
|
4428
4637
|
d.setUTCDate(d.getUTCDate() + 1);
|
|
4429
|
-
while (d.getUTCDay() === 0 || d.getUTCDay() === 6) {
|
|
4638
|
+
while (d.getUTCDay() === 0 || d.getUTCDay() === 6 || isPolishHoliday(d)) {
|
|
4430
4639
|
d.setUTCDate(d.getUTCDate() + 1);
|
|
4431
4640
|
}
|
|
4432
4641
|
return d;
|
|
@@ -4439,7 +4648,7 @@ function addBusinessDays(from, days) {
|
|
|
4439
4648
|
let remaining = days;
|
|
4440
4649
|
while (remaining > 0) {
|
|
4441
4650
|
d.setUTCDate(d.getUTCDate() + 1);
|
|
4442
|
-
if (d.getUTCDay() !== 0 && d.getUTCDay() !== 6) {
|
|
4651
|
+
if (d.getUTCDay() !== 0 && d.getUTCDay() !== 6 && !isPolishHoliday(d)) {
|
|
4443
4652
|
remaining--;
|
|
4444
4653
|
}
|
|
4445
4654
|
}
|
|
@@ -4505,12 +4714,13 @@ var FAR_FUTURE;
|
|
|
4505
4714
|
var init_deadline = __esm({
|
|
4506
4715
|
"src/offline/deadline.ts"() {
|
|
4507
4716
|
"use strict";
|
|
4717
|
+
init_holidays();
|
|
4508
4718
|
FAR_FUTURE = /* @__PURE__ */ new Date("9999-12-31T23:59:59Z");
|
|
4509
4719
|
}
|
|
4510
4720
|
});
|
|
4511
4721
|
|
|
4512
4722
|
// src/workflows/offline-invoice-workflow.ts
|
|
4513
|
-
import
|
|
4723
|
+
import crypto7 from "crypto";
|
|
4514
4724
|
var OfflineInvoiceWorkflow;
|
|
4515
4725
|
var init_offline_invoice_workflow = __esm({
|
|
4516
4726
|
"src/workflows/offline-invoice-workflow.ts"() {
|
|
@@ -4534,7 +4744,7 @@ var init_offline_invoice_workflow = __esm({
|
|
|
4534
4744
|
}
|
|
4535
4745
|
const mode = options?.mode ?? "offline24";
|
|
4536
4746
|
const reason = getDefaultReason(mode);
|
|
4537
|
-
const invoiceHashBase64 =
|
|
4747
|
+
const invoiceHashBase64 = crypto7.createHash("sha256").update(input.invoiceXml).digest("base64");
|
|
4538
4748
|
const kod1Url = this.qrService.buildInvoiceVerificationUrl(
|
|
4539
4749
|
input.sellerNip,
|
|
4540
4750
|
input.invoiceDate,
|
|
@@ -4553,7 +4763,7 @@ var init_offline_invoice_workflow = __esm({
|
|
|
4553
4763
|
}
|
|
4554
4764
|
const submitBy = options?.customDeadline ? typeof options.customDeadline === "string" ? options.customDeadline : options.customDeadline.toISOString() : calculateOfflineDeadline(mode, input.invoiceDate, options?.maintenanceWindow).toISOString();
|
|
4555
4765
|
const metadata = {
|
|
4556
|
-
id:
|
|
4766
|
+
id: crypto7.randomUUID(),
|
|
4557
4767
|
mode,
|
|
4558
4768
|
reason,
|
|
4559
4769
|
status: "GENERATED",
|
|
@@ -4709,7 +4919,7 @@ var init_offline_invoice_workflow = __esm({
|
|
|
4709
4919
|
original.status === "CORRECTED" ? `Invoice ${rejectedInvoiceId} has already been corrected (by ${original.correctedBy})` : `Only rejected invoices can be corrected (current status: ${original.status})`
|
|
4710
4920
|
);
|
|
4711
4921
|
}
|
|
4712
|
-
const originalHash =
|
|
4922
|
+
const originalHash = crypto7.createHash("sha256").update(original.invoiceXml).digest("base64");
|
|
4713
4923
|
await client.crypto.init();
|
|
4714
4924
|
const encData = client.crypto.getEncryptionData();
|
|
4715
4925
|
const formCode = options.formCode ?? DEFAULT_FORM_CODE;
|
|
@@ -4722,9 +4932,9 @@ var init_offline_invoice_workflow = __esm({
|
|
|
4722
4932
|
const plainMeta = client.crypto.getFileMetadata(data);
|
|
4723
4933
|
const encrypted = client.crypto.encryptAES256(data, encData.cipherKey, encData.cipherIv);
|
|
4724
4934
|
const encMeta = client.crypto.getFileMetadata(encrypted);
|
|
4725
|
-
const correctionId =
|
|
4935
|
+
const correctionId = crypto7.randomUUID();
|
|
4726
4936
|
const submittedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4727
|
-
const correctedHashBase64 =
|
|
4937
|
+
const correctedHashBase64 = crypto7.createHash("sha256").update(correctedInvoiceXml).digest("base64");
|
|
4728
4938
|
const kod1Url = this.qrService.buildInvoiceVerificationUrl(
|
|
4729
4939
|
original.sellerNip,
|
|
4730
4940
|
original.invoiceDate,
|
|
@@ -4879,6 +5089,7 @@ var init_client = __esm({
|
|
|
4879
5089
|
qr;
|
|
4880
5090
|
options;
|
|
4881
5091
|
authManager;
|
|
5092
|
+
_baseRestClientConfig;
|
|
4882
5093
|
_offline;
|
|
4883
5094
|
constructor(options) {
|
|
4884
5095
|
this.options = resolveOptions(options);
|
|
@@ -4890,6 +5101,8 @@ var init_client = __esm({
|
|
|
4890
5101
|
});
|
|
4891
5102
|
this.authManager = authManager;
|
|
4892
5103
|
const restClientConfig = buildRestClientConfig(options, authManager);
|
|
5104
|
+
const { authManager: _am, ...baseConfig } = restClientConfig;
|
|
5105
|
+
this._baseRestClientConfig = baseConfig;
|
|
4893
5106
|
const restClient = new RestClient(this.options, restClientConfig);
|
|
4894
5107
|
const fetcher = new CertificateFetcher(restClient);
|
|
4895
5108
|
this.crypto = new CryptographyService(fetcher);
|
|
@@ -4914,6 +5127,10 @@ var init_client = __esm({
|
|
|
4914
5127
|
}
|
|
4915
5128
|
return this._offline;
|
|
4916
5129
|
}
|
|
5130
|
+
/** @internal Create a RestClient with a different AuthManager, preserving transport/retry/rateLimit config. */
|
|
5131
|
+
createScopedRestClient(authManager) {
|
|
5132
|
+
return new RestClient(this.options, { ...this._baseRestClientConfig, authManager });
|
|
5133
|
+
}
|
|
4917
5134
|
async loginWithToken(token, nip) {
|
|
4918
5135
|
const challenge = await this.auth.getChallenge();
|
|
4919
5136
|
await this.crypto.init();
|
|
@@ -4928,6 +5145,7 @@ var init_client = __esm({
|
|
|
4928
5145
|
const tokens = await this.auth.getAccessToken(authToken);
|
|
4929
5146
|
this.authManager.setAccessToken(tokens.accessToken.token);
|
|
4930
5147
|
this.authManager.setRefreshToken(tokens.refreshToken.token);
|
|
5148
|
+
return { clientIp: challenge.clientIp };
|
|
4931
5149
|
}
|
|
4932
5150
|
async loginWithCertificate(certPem, keyPem, nip, keyPassword) {
|
|
4933
5151
|
const challenge = await this.auth.getChallenge();
|
|
@@ -4940,11 +5158,12 @@ var init_client = __esm({
|
|
|
4940
5158
|
const tokens = await this.auth.getAccessToken(authToken);
|
|
4941
5159
|
this.authManager.setAccessToken(tokens.accessToken.token);
|
|
4942
5160
|
this.authManager.setRefreshToken(tokens.refreshToken.token);
|
|
5161
|
+
return { clientIp: challenge.clientIp };
|
|
4943
5162
|
}
|
|
4944
5163
|
async loginWithPkcs12(p12, password, nip) {
|
|
4945
5164
|
const { Pkcs12Loader: Pkcs12Loader2 } = await Promise.resolve().then(() => (init_pkcs12_loader(), pkcs12_loader_exports));
|
|
4946
5165
|
const { certificatePem, privateKeyPem } = Pkcs12Loader2.load(p12, password);
|
|
4947
|
-
|
|
5166
|
+
return this.loginWithCertificate(certificatePem, privateKeyPem, nip);
|
|
4948
5167
|
}
|
|
4949
5168
|
async awaitAuthReady(referenceNumber, authToken) {
|
|
4950
5169
|
for (let i = 0; i < 30; i++) {
|
|
@@ -5733,6 +5952,18 @@ function parseKSeFTokenContext(token) {
|
|
|
5733
5952
|
};
|
|
5734
5953
|
}
|
|
5735
5954
|
|
|
5955
|
+
// src/utils/hash.ts
|
|
5956
|
+
import crypto6 from "crypto";
|
|
5957
|
+
function sha256Base642(data) {
|
|
5958
|
+
return crypto6.createHash("sha256").update(data).digest("base64");
|
|
5959
|
+
}
|
|
5960
|
+
function verifyHash(data, expectedHash) {
|
|
5961
|
+
return sha256Base642(data) === expectedHash;
|
|
5962
|
+
}
|
|
5963
|
+
|
|
5964
|
+
// src/utils/index.ts
|
|
5965
|
+
init_concurrency();
|
|
5966
|
+
|
|
5736
5967
|
// src/workflows/polling.ts
|
|
5737
5968
|
async function pollUntil(action, condition, options) {
|
|
5738
5969
|
const intervalMs = options?.intervalMs ?? 2e3;
|
|
@@ -5751,6 +5982,10 @@ async function pollUntil(action, condition, options) {
|
|
|
5751
5982
|
}
|
|
5752
5983
|
|
|
5753
5984
|
// src/workflows/online-session-workflow.ts
|
|
5985
|
+
init_ksef_session_expired_error();
|
|
5986
|
+
init_auth_manager();
|
|
5987
|
+
init_online_session();
|
|
5988
|
+
init_session_status();
|
|
5754
5989
|
init_document_structures();
|
|
5755
5990
|
|
|
5756
5991
|
// src/xml/upo-parser.ts
|
|
@@ -5914,18 +6149,11 @@ function nonEmptyString(value) {
|
|
|
5914
6149
|
// src/workflows/online-session-workflow.ts
|
|
5915
6150
|
init_invoice_validator();
|
|
5916
6151
|
init_ksef_validation_error();
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
const encData = client.crypto.getEncryptionData();
|
|
5920
|
-
const formCode = options?.formCode ?? DEFAULT_FORM_CODE;
|
|
5921
|
-
const openResp = await client.onlineSession.openSession(
|
|
5922
|
-
{ formCode, encryption: encData.encryptionInfo },
|
|
5923
|
-
options?.upoVersion
|
|
5924
|
-
);
|
|
5925
|
-
const sessionRef = openResp.referenceNumber;
|
|
6152
|
+
function buildSessionHandle(params) {
|
|
6153
|
+
const { deps, sessionRef, validUntil, cipherKey, cipherIv, formCode, validate: validate2 } = params;
|
|
5926
6154
|
async function fetchUpo(pollOpts) {
|
|
5927
6155
|
const result = await pollUntil(
|
|
5928
|
-
() =>
|
|
6156
|
+
() => deps.sessionStatus.getSessionStatus(sessionRef),
|
|
5929
6157
|
(s) => s.status.code === 200 || s.status.code >= 400,
|
|
5930
6158
|
{ ...pollOpts, description: `UPO for session ${sessionRef}` }
|
|
5931
6159
|
);
|
|
@@ -5941,9 +6169,9 @@ async function openOnlineSession(client, options) {
|
|
|
5941
6169
|
}
|
|
5942
6170
|
return {
|
|
5943
6171
|
sessionRef,
|
|
5944
|
-
validUntil
|
|
6172
|
+
validUntil,
|
|
5945
6173
|
async sendInvoice(invoiceXml) {
|
|
5946
|
-
if (
|
|
6174
|
+
if (validate2) {
|
|
5947
6175
|
const xmlStr = typeof invoiceXml === "string" ? invoiceXml : new TextDecoder().decode(invoiceXml);
|
|
5948
6176
|
const vResult = await validate(xmlStr);
|
|
5949
6177
|
if (!vResult.valid) {
|
|
@@ -5954,10 +6182,10 @@ async function openOnlineSession(client, options) {
|
|
|
5954
6182
|
}
|
|
5955
6183
|
}
|
|
5956
6184
|
const data = typeof invoiceXml === "string" ? new TextEncoder().encode(invoiceXml) : invoiceXml;
|
|
5957
|
-
const plainMeta =
|
|
5958
|
-
const encrypted =
|
|
5959
|
-
const encMeta =
|
|
5960
|
-
const resp = await
|
|
6185
|
+
const plainMeta = deps.crypto.getFileMetadata(data);
|
|
6186
|
+
const encrypted = deps.crypto.encryptAES256(data, cipherKey, cipherIv);
|
|
6187
|
+
const encMeta = deps.crypto.getFileMetadata(encrypted);
|
|
6188
|
+
const resp = await deps.onlineSession.sendInvoice(sessionRef, {
|
|
5961
6189
|
invoiceHash: plainMeta.hashSHA,
|
|
5962
6190
|
invoiceSize: plainMeta.fileSize,
|
|
5963
6191
|
encryptedInvoiceHash: encMeta.hashSHA,
|
|
@@ -5967,7 +6195,7 @@ async function openOnlineSession(client, options) {
|
|
|
5967
6195
|
return resp.referenceNumber;
|
|
5968
6196
|
},
|
|
5969
6197
|
async close() {
|
|
5970
|
-
await
|
|
6198
|
+
await deps.onlineSession.closeSession(sessionRef);
|
|
5971
6199
|
},
|
|
5972
6200
|
async waitForUpo(pollOpts) {
|
|
5973
6201
|
return fetchUpo(pollOpts);
|
|
@@ -5976,13 +6204,75 @@ async function openOnlineSession(client, options) {
|
|
|
5976
6204
|
const upoInfo = await fetchUpo(pollOpts);
|
|
5977
6205
|
const parsed = [];
|
|
5978
6206
|
for (const page of upoInfo.pages) {
|
|
5979
|
-
const result = await
|
|
6207
|
+
const result = await deps.sessionStatus.getSessionUpo(sessionRef, page.referenceNumber);
|
|
5980
6208
|
parsed.push(parseUpoXml(result.upo));
|
|
5981
6209
|
}
|
|
5982
6210
|
return { ...upoInfo, parsed };
|
|
6211
|
+
},
|
|
6212
|
+
getState() {
|
|
6213
|
+
const token = deps.getAccessToken();
|
|
6214
|
+
if (!token) {
|
|
6215
|
+
throw new Error("Cannot serialize session state: no access token available");
|
|
6216
|
+
}
|
|
6217
|
+
return {
|
|
6218
|
+
referenceNumber: sessionRef,
|
|
6219
|
+
aesKey: Buffer.from(cipherKey).toString("base64"),
|
|
6220
|
+
iv: Buffer.from(cipherIv).toString("base64"),
|
|
6221
|
+
accessToken: token,
|
|
6222
|
+
formCode,
|
|
6223
|
+
validUntil,
|
|
6224
|
+
...validate2 ? { validate: validate2 } : {}
|
|
6225
|
+
};
|
|
5983
6226
|
}
|
|
5984
6227
|
};
|
|
5985
6228
|
}
|
|
6229
|
+
async function openOnlineSession(client, options) {
|
|
6230
|
+
await client.crypto.init();
|
|
6231
|
+
const encData = client.crypto.getEncryptionData();
|
|
6232
|
+
const formCode = options?.formCode ?? DEFAULT_FORM_CODE;
|
|
6233
|
+
const openResp = await client.onlineSession.openSession(
|
|
6234
|
+
{ formCode, encryption: encData.encryptionInfo },
|
|
6235
|
+
options?.upoVersion
|
|
6236
|
+
);
|
|
6237
|
+
return buildSessionHandle({
|
|
6238
|
+
deps: {
|
|
6239
|
+
crypto: client.crypto,
|
|
6240
|
+
onlineSession: client.onlineSession,
|
|
6241
|
+
sessionStatus: client.sessionStatus,
|
|
6242
|
+
getAccessToken: () => client.authManager.getAccessToken()
|
|
6243
|
+
},
|
|
6244
|
+
sessionRef: openResp.referenceNumber,
|
|
6245
|
+
validUntil: openResp.validUntil,
|
|
6246
|
+
cipherKey: encData.cipherKey,
|
|
6247
|
+
cipherIv: encData.cipherIv,
|
|
6248
|
+
formCode,
|
|
6249
|
+
validate: options?.validate
|
|
6250
|
+
});
|
|
6251
|
+
}
|
|
6252
|
+
function resumeOnlineSession(client, state, options) {
|
|
6253
|
+
const expiry = new Date(state.validUntil);
|
|
6254
|
+
if (expiry.getTime() <= Date.now()) {
|
|
6255
|
+
throw new KSeFSessionExpiredError(
|
|
6256
|
+
`Cannot resume session: expired at ${state.validUntil}`
|
|
6257
|
+
);
|
|
6258
|
+
}
|
|
6259
|
+
const scopedAuth = new DefaultAuthManager(() => Promise.resolve(null), state.accessToken);
|
|
6260
|
+
const scopedRestClient = client.createScopedRestClient(scopedAuth);
|
|
6261
|
+
return buildSessionHandle({
|
|
6262
|
+
deps: {
|
|
6263
|
+
crypto: client.crypto,
|
|
6264
|
+
onlineSession: new OnlineSessionService(scopedRestClient),
|
|
6265
|
+
sessionStatus: new SessionStatusService(scopedRestClient),
|
|
6266
|
+
getAccessToken: () => scopedAuth.getAccessToken()
|
|
6267
|
+
},
|
|
6268
|
+
sessionRef: state.referenceNumber,
|
|
6269
|
+
validUntil: state.validUntil,
|
|
6270
|
+
cipherKey: new Uint8Array(Buffer.from(state.aesKey, "base64")),
|
|
6271
|
+
cipherIv: new Uint8Array(Buffer.from(state.iv, "base64")),
|
|
6272
|
+
formCode: state.formCode,
|
|
6273
|
+
validate: options?.validate ?? state.validate
|
|
6274
|
+
});
|
|
6275
|
+
}
|
|
5986
6276
|
async function openSendAndClose(client, invoices, options) {
|
|
5987
6277
|
const handle = await openOnlineSession(client, options);
|
|
5988
6278
|
for (const inv of invoices) {
|
|
@@ -5995,6 +6285,9 @@ async function openSendAndClose(client, invoices, options) {
|
|
|
5995
6285
|
// src/workflows/batch-session-workflow.ts
|
|
5996
6286
|
init_document_structures();
|
|
5997
6287
|
async function uploadBatch(client, zipData, options) {
|
|
6288
|
+
if (options?.parallelism !== void 0 && (!Number.isInteger(options.parallelism) || options.parallelism < 1)) {
|
|
6289
|
+
throw new Error("parallelism must be a positive integer");
|
|
6290
|
+
}
|
|
5998
6291
|
await client.crypto.init();
|
|
5999
6292
|
if (options?.validate) {
|
|
6000
6293
|
const { unzip: unzip2 } = await Promise.resolve().then(() => (init_zip(), zip_exports));
|
|
@@ -6037,7 +6330,7 @@ async function uploadBatch(client, zipData, options) {
|
|
|
6037
6330
|
},
|
|
6038
6331
|
ordinalNumber: i + 1
|
|
6039
6332
|
}));
|
|
6040
|
-
await client.batchSession.sendParts(openResp, sendingParts);
|
|
6333
|
+
await client.batchSession.sendParts(openResp, sendingParts, options?.parallelism);
|
|
6041
6334
|
await client.batchSession.closeSession(openResp.referenceNumber);
|
|
6042
6335
|
const result = await pollUntil(
|
|
6043
6336
|
() => client.sessionStatus.getSessionStatus(openResp.referenceNumber),
|
|
@@ -6058,6 +6351,9 @@ async function uploadBatch(client, zipData, options) {
|
|
|
6058
6351
|
};
|
|
6059
6352
|
}
|
|
6060
6353
|
async function uploadBatchStream(client, zipStreamFactory, zipSize, options) {
|
|
6354
|
+
if (options?.parallelism !== void 0 && (!Number.isInteger(options.parallelism) || options.parallelism < 1)) {
|
|
6355
|
+
throw new Error("parallelism must be a positive integer");
|
|
6356
|
+
}
|
|
6061
6357
|
await client.crypto.init();
|
|
6062
6358
|
const encData = client.crypto.getEncryptionData();
|
|
6063
6359
|
const formCode = options?.formCode ?? DEFAULT_FORM_CODE;
|
|
@@ -6079,7 +6375,7 @@ async function uploadBatchStream(client, zipStreamFactory, zipSize, options) {
|
|
|
6079
6375
|
},
|
|
6080
6376
|
options?.upoVersion
|
|
6081
6377
|
);
|
|
6082
|
-
await client.batchSession.sendPartsWithStream(openResp, streamParts);
|
|
6378
|
+
await client.batchSession.sendPartsWithStream(openResp, streamParts, options?.parallelism);
|
|
6083
6379
|
await client.batchSession.closeSession(openResp.referenceNumber);
|
|
6084
6380
|
const result = await pollUntil(
|
|
6085
6381
|
() => client.sessionStatus.getSessionStatus(openResp.referenceNumber),
|
|
@@ -6154,6 +6450,7 @@ async function doExport(client, filters, options) {
|
|
|
6154
6450
|
url: p.url,
|
|
6155
6451
|
method: p.method,
|
|
6156
6452
|
partSize: p.partSize,
|
|
6453
|
+
partHash: p.partHash,
|
|
6157
6454
|
encryptedPartSize: p.encryptedPartSize,
|
|
6158
6455
|
encryptedPartHash: p.encryptedPartHash,
|
|
6159
6456
|
expirationDate: p.expirationDate
|
|
@@ -6179,6 +6476,9 @@ async function exportAndDownload(client, filters, options) {
|
|
|
6179
6476
|
throw new Error(`Download failed for part ${part.ordinalNumber}: HTTP ${resp.status}`);
|
|
6180
6477
|
}
|
|
6181
6478
|
const encryptedData = new Uint8Array(await resp.arrayBuffer());
|
|
6479
|
+
if (options?.verifyHash !== false && !verifyHash(encryptedData, part.encryptedPartHash)) {
|
|
6480
|
+
throw new Error(`Hash mismatch for export part ${part.ordinalNumber}`);
|
|
6481
|
+
}
|
|
6182
6482
|
const decrypted = client.crypto.decryptAES256(encryptedData, encData.cipherKey, encData.cipherIv);
|
|
6183
6483
|
decryptedParts.push(decrypted);
|
|
6184
6484
|
}
|
|
@@ -6251,6 +6551,9 @@ async function incrementalExportAndDownload(client, options) {
|
|
|
6251
6551
|
throw new Error(`Download failed for part ${part.ordinalNumber}: HTTP ${resp.status}`);
|
|
6252
6552
|
}
|
|
6253
6553
|
const encryptedData = new Uint8Array(await resp.arrayBuffer());
|
|
6554
|
+
if (options.verifyHash !== false && !verifyHash(encryptedData, part.encryptedPartHash)) {
|
|
6555
|
+
throw new Error(`Hash mismatch for export part ${part.ordinalNumber}`);
|
|
6556
|
+
}
|
|
6254
6557
|
const decrypted = client.crypto.decryptAES256(encryptedData, encData.cipherKey, encData.cipherIv);
|
|
6255
6558
|
decryptedParts.push(decrypted);
|
|
6256
6559
|
}
|
|
@@ -6416,6 +6719,7 @@ async function authenticateWithPkcs12(client, options) {
|
|
|
6416
6719
|
|
|
6417
6720
|
// src/offline/index.ts
|
|
6418
6721
|
init_deadline();
|
|
6722
|
+
init_holidays();
|
|
6419
6723
|
|
|
6420
6724
|
// src/offline/storage.ts
|
|
6421
6725
|
function matchesFilter(invoice, filter) {
|
|
@@ -6590,9 +6894,12 @@ export {
|
|
|
6590
6894
|
KSEF_FEATURE_HEADER,
|
|
6591
6895
|
KSeFApiError,
|
|
6592
6896
|
KSeFAuthStatusError,
|
|
6897
|
+
KSeFBatchTimeoutError,
|
|
6593
6898
|
KSeFClient,
|
|
6594
6899
|
KSeFError,
|
|
6900
|
+
KSeFErrorCode,
|
|
6595
6901
|
KSeFForbiddenError,
|
|
6902
|
+
KSeFGoneError,
|
|
6596
6903
|
KSeFRateLimitError,
|
|
6597
6904
|
KSeFSessionExpiredError,
|
|
6598
6905
|
KSeFUnauthorizedError,
|
|
@@ -6657,9 +6964,11 @@ export {
|
|
|
6657
6964
|
getDefaultReason,
|
|
6658
6965
|
getEffectiveStartDate,
|
|
6659
6966
|
getFormCode,
|
|
6967
|
+
getPolishHolidays,
|
|
6660
6968
|
getTimeUntilDeadline,
|
|
6661
6969
|
incrementalExportAndDownload,
|
|
6662
6970
|
isExpired,
|
|
6971
|
+
isPolishHoliday,
|
|
6663
6972
|
isRetryableError,
|
|
6664
6973
|
isRetryableStatus,
|
|
6665
6974
|
isValidBase64,
|
|
@@ -6686,6 +6995,9 @@ export {
|
|
|
6686
6995
|
parseUpoXml,
|
|
6687
6996
|
pollUntil,
|
|
6688
6997
|
resolveOptions,
|
|
6998
|
+
resumeOnlineSession,
|
|
6999
|
+
runWithConcurrency,
|
|
7000
|
+
sha256Base642 as sha256Base64,
|
|
6689
7001
|
sleep,
|
|
6690
7002
|
unzip,
|
|
6691
7003
|
updateContinuationPoint,
|
|
@@ -6700,6 +7012,7 @@ export {
|
|
|
6700
7012
|
validatePresignedUrl,
|
|
6701
7013
|
validateSchema,
|
|
6702
7014
|
validateWellFormedness,
|
|
7015
|
+
verifyHash,
|
|
6703
7016
|
xmlToObject
|
|
6704
7017
|
};
|
|
6705
7018
|
//# sourceMappingURL=index.js.map
|