ksef-client-ts 0.5.2 → 0.6.1
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 +2 -1
- package/dist/cli.js +1198 -158
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +962 -119
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +307 -7
- package/dist/index.d.ts +307 -7
- package/dist/index.js +945 -119
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -298,21 +298,21 @@ var init_rest_request = __esm({
|
|
|
298
298
|
_query = [];
|
|
299
299
|
_presigned = false;
|
|
300
300
|
_skipAuthRetry = false;
|
|
301
|
-
constructor(method,
|
|
301
|
+
constructor(method, path2) {
|
|
302
302
|
this.method = method;
|
|
303
|
-
this.path =
|
|
303
|
+
this.path = path2;
|
|
304
304
|
}
|
|
305
|
-
static get(
|
|
306
|
-
return new _RestRequest("GET",
|
|
305
|
+
static get(path2) {
|
|
306
|
+
return new _RestRequest("GET", path2);
|
|
307
307
|
}
|
|
308
|
-
static post(
|
|
309
|
-
return new _RestRequest("POST",
|
|
308
|
+
static post(path2) {
|
|
309
|
+
return new _RestRequest("POST", path2);
|
|
310
310
|
}
|
|
311
|
-
static put(
|
|
312
|
-
return new _RestRequest("PUT",
|
|
311
|
+
static put(path2) {
|
|
312
|
+
return new _RestRequest("PUT", path2);
|
|
313
313
|
}
|
|
314
|
-
static delete(
|
|
315
|
-
return new _RestRequest("DELETE",
|
|
314
|
+
static delete(path2) {
|
|
315
|
+
return new _RestRequest("DELETE", path2);
|
|
316
316
|
}
|
|
317
317
|
body(data) {
|
|
318
318
|
this._body = data;
|
|
@@ -632,9 +632,9 @@ var init_rest_client = __esm({
|
|
|
632
632
|
return response;
|
|
633
633
|
}
|
|
634
634
|
buildUrl(request) {
|
|
635
|
-
const
|
|
635
|
+
const path2 = this.routeBuilder.build(request.path);
|
|
636
636
|
const base = this.options.baseUrl;
|
|
637
|
-
const url = new URL(`${base}${
|
|
637
|
+
const url = new URL(`${base}${path2}`);
|
|
638
638
|
const query = request.getQuery();
|
|
639
639
|
for (const [key, value] of query) {
|
|
640
640
|
url.searchParams.append(key, value);
|
|
@@ -955,7 +955,9 @@ function isValidVatUe(value) {
|
|
|
955
955
|
return VatUe.test(value);
|
|
956
956
|
}
|
|
957
957
|
function isValidNipVatUe(value) {
|
|
958
|
-
|
|
958
|
+
if (!NipVatUe.test(value)) return false;
|
|
959
|
+
const nip = value.split("-")[0];
|
|
960
|
+
return isValidNip(nip);
|
|
959
961
|
}
|
|
960
962
|
function isValidInternalId(value) {
|
|
961
963
|
return InternalId.test(value);
|
|
@@ -2645,11 +2647,11 @@ async function validateSchema(xml, options, _parsed) {
|
|
|
2645
2647
|
const prefix = rootElement ? `/${rootElement}/` : "/";
|
|
2646
2648
|
const validationErrors = result.error.issues.map((issue) => {
|
|
2647
2649
|
const zodPath = issue.path.join("/");
|
|
2648
|
-
const
|
|
2650
|
+
const path2 = zodPath ? `${prefix}${zodPath}` : rootElement ? `/${rootElement}` : void 0;
|
|
2649
2651
|
return {
|
|
2650
2652
|
code: mapZodErrorCode(issue),
|
|
2651
2653
|
message: issue.message,
|
|
2652
|
-
path
|
|
2654
|
+
path: path2
|
|
2653
2655
|
};
|
|
2654
2656
|
});
|
|
2655
2657
|
return { valid: false, schemaType, errors: validationErrors };
|
|
@@ -2689,9 +2691,9 @@ function validateBusinessRules(xml, _parsed) {
|
|
|
2689
2691
|
collectDateErrors(object, rootElement, errors);
|
|
2690
2692
|
return { valid: errors.length === 0, schemaType, errors };
|
|
2691
2693
|
}
|
|
2692
|
-
function collectNipPeselErrors(obj,
|
|
2694
|
+
function collectNipPeselErrors(obj, path2, errors) {
|
|
2693
2695
|
for (const [key, value] of Object.entries(obj)) {
|
|
2694
|
-
const currentPath =
|
|
2696
|
+
const currentPath = path2 ? `${path2}/${key}` : key;
|
|
2695
2697
|
if (key === "NIP" && typeof value === "string") {
|
|
2696
2698
|
if (!isValidNip(value)) {
|
|
2697
2699
|
errors.push({
|
|
@@ -2770,6 +2772,88 @@ var init_invoice_validator = __esm({
|
|
|
2770
2772
|
}
|
|
2771
2773
|
});
|
|
2772
2774
|
|
|
2775
|
+
// src/models/document-structures/types.ts
|
|
2776
|
+
var SystemCode, FORM_CODES, DEFAULT_FORM_CODE, INVOICE_TYPES_BY_SYSTEM_CODE, FORM_CODE_KEYS;
|
|
2777
|
+
var init_types = __esm({
|
|
2778
|
+
"src/models/document-structures/types.ts"() {
|
|
2779
|
+
"use strict";
|
|
2780
|
+
SystemCode = {
|
|
2781
|
+
FA_2: "FA (2)",
|
|
2782
|
+
FA_3: "FA (3)",
|
|
2783
|
+
PEF_3: "PEF (3)",
|
|
2784
|
+
PEF_KOR_3: "PEF_KOR (3)",
|
|
2785
|
+
FA_RR_1: "FA_RR (1)"
|
|
2786
|
+
};
|
|
2787
|
+
FORM_CODES = {
|
|
2788
|
+
FA_2: { systemCode: "FA (2)", schemaVersion: "1-0E", value: "FA" },
|
|
2789
|
+
FA_3: { systemCode: "FA (3)", schemaVersion: "1-0E", value: "FA" },
|
|
2790
|
+
PEF_3: { systemCode: "PEF (3)", schemaVersion: "2-1", value: "PEF" },
|
|
2791
|
+
PEF_KOR_3: { systemCode: "PEF_KOR (3)", schemaVersion: "2-1", value: "PEF" },
|
|
2792
|
+
FA_RR_1_LEGACY: { systemCode: "FA_RR (1)", schemaVersion: "1-0E", value: "RR" },
|
|
2793
|
+
FA_RR_1_TRANSITION: { systemCode: "FA_RR (1)", schemaVersion: "1-1E", value: "RR" },
|
|
2794
|
+
FA_RR_1: { systemCode: "FA_RR (1)", schemaVersion: "1-1E", value: "FA_RR" }
|
|
2795
|
+
};
|
|
2796
|
+
DEFAULT_FORM_CODE = FORM_CODES.FA_3;
|
|
2797
|
+
INVOICE_TYPES_BY_SYSTEM_CODE = {
|
|
2798
|
+
[SystemCode.FA_2]: ["Vat", "Zal", "Kor", "Roz", "Upr", "KorZal", "KorRoz"],
|
|
2799
|
+
[SystemCode.FA_3]: ["Vat", "Zal", "Kor", "Roz", "Upr", "KorZal", "KorRoz"],
|
|
2800
|
+
[SystemCode.PEF_3]: ["VatPef", "VatPefSp", "KorPef"],
|
|
2801
|
+
[SystemCode.PEF_KOR_3]: ["KorPef"],
|
|
2802
|
+
[SystemCode.FA_RR_1]: ["VatRr", "KorVatRr"]
|
|
2803
|
+
};
|
|
2804
|
+
FORM_CODE_KEYS = {
|
|
2805
|
+
FA2: FORM_CODES.FA_2,
|
|
2806
|
+
FA3: FORM_CODES.FA_3,
|
|
2807
|
+
PEF3: FORM_CODES.PEF_3,
|
|
2808
|
+
PEFKOR3: FORM_CODES.PEF_KOR_3,
|
|
2809
|
+
FARR1: FORM_CODES.FA_RR_1
|
|
2810
|
+
};
|
|
2811
|
+
}
|
|
2812
|
+
});
|
|
2813
|
+
|
|
2814
|
+
// src/models/document-structures/helpers.ts
|
|
2815
|
+
function getFormCode(systemCode) {
|
|
2816
|
+
return SYSTEM_CODE_TO_FORM_CODE[systemCode];
|
|
2817
|
+
}
|
|
2818
|
+
function parseFormCode(raw) {
|
|
2819
|
+
const match = ALL_FORM_CODES.find(
|
|
2820
|
+
(fc) => fc.systemCode === raw.systemCode && fc.schemaVersion === raw.schemaVersion && fc.value === raw.value
|
|
2821
|
+
);
|
|
2822
|
+
return match ?? raw;
|
|
2823
|
+
}
|
|
2824
|
+
function validateFormCodeForSession(formCode, sessionType) {
|
|
2825
|
+
if (sessionType === "online") return true;
|
|
2826
|
+
return !BATCH_DISALLOWED_SYSTEM_CODES.has(formCode.systemCode);
|
|
2827
|
+
}
|
|
2828
|
+
var SYSTEM_CODE_TO_FORM_CODE, ALL_FORM_CODES, BATCH_DISALLOWED_SYSTEM_CODES;
|
|
2829
|
+
var init_helpers = __esm({
|
|
2830
|
+
"src/models/document-structures/helpers.ts"() {
|
|
2831
|
+
"use strict";
|
|
2832
|
+
init_types();
|
|
2833
|
+
SYSTEM_CODE_TO_FORM_CODE = {
|
|
2834
|
+
[SystemCode.FA_2]: FORM_CODES.FA_2,
|
|
2835
|
+
[SystemCode.FA_3]: FORM_CODES.FA_3,
|
|
2836
|
+
[SystemCode.PEF_3]: FORM_CODES.PEF_3,
|
|
2837
|
+
[SystemCode.PEF_KOR_3]: FORM_CODES.PEF_KOR_3,
|
|
2838
|
+
[SystemCode.FA_RR_1]: FORM_CODES.FA_RR_1
|
|
2839
|
+
};
|
|
2840
|
+
ALL_FORM_CODES = Object.values(FORM_CODES);
|
|
2841
|
+
BATCH_DISALLOWED_SYSTEM_CODES = /* @__PURE__ */ new Set([
|
|
2842
|
+
SystemCode.PEF_3,
|
|
2843
|
+
SystemCode.PEF_KOR_3
|
|
2844
|
+
]);
|
|
2845
|
+
}
|
|
2846
|
+
});
|
|
2847
|
+
|
|
2848
|
+
// src/models/document-structures/index.ts
|
|
2849
|
+
var init_document_structures = __esm({
|
|
2850
|
+
"src/models/document-structures/index.ts"() {
|
|
2851
|
+
"use strict";
|
|
2852
|
+
init_types();
|
|
2853
|
+
init_helpers();
|
|
2854
|
+
}
|
|
2855
|
+
});
|
|
2856
|
+
|
|
2773
2857
|
// src/services/auth.ts
|
|
2774
2858
|
var AuthService;
|
|
2775
2859
|
var init_auth = __esm({
|
|
@@ -2885,6 +2969,32 @@ var init_online_session = __esm({
|
|
|
2885
2969
|
}
|
|
2886
2970
|
});
|
|
2887
2971
|
|
|
2972
|
+
// src/utils/concurrency.ts
|
|
2973
|
+
async function runWithConcurrency(tasks, parallelism) {
|
|
2974
|
+
if (tasks.length === 0) return;
|
|
2975
|
+
const limit = Math.max(1, Math.min(parallelism, tasks.length));
|
|
2976
|
+
const controller = new AbortController();
|
|
2977
|
+
let index = 0;
|
|
2978
|
+
const workers = Array.from({ length: limit }, async () => {
|
|
2979
|
+
while (index < tasks.length && !controller.signal.aborted) {
|
|
2980
|
+
const current = index;
|
|
2981
|
+
index += 1;
|
|
2982
|
+
try {
|
|
2983
|
+
await tasks[current](controller.signal);
|
|
2984
|
+
} catch (err) {
|
|
2985
|
+
controller.abort();
|
|
2986
|
+
throw err;
|
|
2987
|
+
}
|
|
2988
|
+
}
|
|
2989
|
+
});
|
|
2990
|
+
await Promise.all(workers);
|
|
2991
|
+
}
|
|
2992
|
+
var init_concurrency = __esm({
|
|
2993
|
+
"src/utils/concurrency.ts"() {
|
|
2994
|
+
"use strict";
|
|
2995
|
+
}
|
|
2996
|
+
});
|
|
2997
|
+
|
|
2888
2998
|
// src/services/batch-session.ts
|
|
2889
2999
|
var BatchSessionService;
|
|
2890
3000
|
var init_batch_session = __esm({
|
|
@@ -2893,6 +3003,7 @@ var init_batch_session = __esm({
|
|
|
2893
3003
|
init_ksef_feature();
|
|
2894
3004
|
init_rest_request();
|
|
2895
3005
|
init_routes();
|
|
3006
|
+
init_concurrency();
|
|
2896
3007
|
BatchSessionService = class {
|
|
2897
3008
|
restClient;
|
|
2898
3009
|
constructor(restClient) {
|
|
@@ -2906,12 +3017,10 @@ var init_batch_session = __esm({
|
|
|
2906
3017
|
const response = await this.restClient.execute(req);
|
|
2907
3018
|
return response.body;
|
|
2908
3019
|
}
|
|
2909
|
-
async sendParts(openResponse, parts) {
|
|
2910
|
-
const
|
|
2911
|
-
const tasks = parts.map(async (
|
|
2912
|
-
const uploadReq =
|
|
2913
|
-
(r) => r.ordinalNumber === part.ordinalNumber
|
|
2914
|
-
);
|
|
3020
|
+
async sendParts(openResponse, parts, parallelism) {
|
|
3021
|
+
const uploadMap = new Map(openResponse.partUploadRequests.map((r) => [r.ordinalNumber, r]));
|
|
3022
|
+
const tasks = parts.map((part) => async (signal) => {
|
|
3023
|
+
const uploadReq = uploadMap.get(part.ordinalNumber);
|
|
2915
3024
|
if (!uploadReq) {
|
|
2916
3025
|
throw new Error(`No upload request found for part ${part.ordinalNumber}`);
|
|
2917
3026
|
}
|
|
@@ -2919,25 +3028,38 @@ var init_batch_session = __esm({
|
|
|
2919
3028
|
for (const [k, v] of Object.entries(uploadReq.headers)) {
|
|
2920
3029
|
if (v != null) headers[k] = v;
|
|
2921
3030
|
}
|
|
2922
|
-
await fetch(uploadReq.url, {
|
|
3031
|
+
const resp = await fetch(uploadReq.url, {
|
|
2923
3032
|
method: uploadReq.method,
|
|
2924
3033
|
headers,
|
|
2925
|
-
body: part.data
|
|
3034
|
+
body: part.data,
|
|
3035
|
+
signal
|
|
2926
3036
|
});
|
|
3037
|
+
if (!resp.ok) {
|
|
3038
|
+
const body = await resp.text().catch(() => "");
|
|
3039
|
+
throw new Error(
|
|
3040
|
+
`Upload failed for part ${part.ordinalNumber}: HTTP ${resp.status}${body ? ` \u2014 ${body}` : ""}`
|
|
3041
|
+
);
|
|
3042
|
+
}
|
|
2927
3043
|
});
|
|
2928
|
-
|
|
3044
|
+
if (parallelism !== void 0) {
|
|
3045
|
+
await runWithConcurrency(tasks, parallelism);
|
|
3046
|
+
} else {
|
|
3047
|
+
const ac = new AbortController();
|
|
3048
|
+
await Promise.all(tasks.map((t) => t(ac.signal).catch((err) => {
|
|
3049
|
+
ac.abort();
|
|
3050
|
+
throw err;
|
|
3051
|
+
})));
|
|
3052
|
+
}
|
|
2929
3053
|
}
|
|
2930
3054
|
/**
|
|
2931
|
-
* Upload parts
|
|
2932
|
-
*
|
|
2933
|
-
*
|
|
3055
|
+
* Upload parts using streaming bodies (`duplex: 'half'`).
|
|
3056
|
+
* By default uploads sequentially to avoid backpressure issues.
|
|
3057
|
+
* Pass `parallelism` to enable bounded concurrent uploads.
|
|
2934
3058
|
*/
|
|
2935
|
-
async sendPartsWithStream(openResponse, parts) {
|
|
2936
|
-
const
|
|
2937
|
-
|
|
2938
|
-
const uploadReq =
|
|
2939
|
-
(r) => r.ordinalNumber === part.ordinalNumber
|
|
2940
|
-
);
|
|
3059
|
+
async sendPartsWithStream(openResponse, parts, parallelism) {
|
|
3060
|
+
const uploadMap = new Map(openResponse.partUploadRequests.map((r) => [r.ordinalNumber, r]));
|
|
3061
|
+
const uploadPart = async (part, signal) => {
|
|
3062
|
+
const uploadReq = uploadMap.get(part.ordinalNumber);
|
|
2941
3063
|
if (!uploadReq) {
|
|
2942
3064
|
throw new Error(`No upload request found for part ${part.ordinalNumber}`);
|
|
2943
3065
|
}
|
|
@@ -2949,11 +3071,23 @@ var init_batch_session = __esm({
|
|
|
2949
3071
|
method: uploadReq.method,
|
|
2950
3072
|
headers,
|
|
2951
3073
|
body: part.dataStream,
|
|
3074
|
+
signal,
|
|
2952
3075
|
// @ts-expect-error -- Node 18+ undici supports duplex for streaming body
|
|
2953
3076
|
duplex: "half"
|
|
2954
3077
|
});
|
|
2955
3078
|
if (!resp.ok) {
|
|
2956
|
-
|
|
3079
|
+
const body = await resp.text().catch(() => "");
|
|
3080
|
+
throw new Error(
|
|
3081
|
+
`Upload failed for part ${part.ordinalNumber}: HTTP ${resp.status}${body ? ` \u2014 ${body}` : ""}`
|
|
3082
|
+
);
|
|
3083
|
+
}
|
|
3084
|
+
};
|
|
3085
|
+
if (parallelism !== void 0) {
|
|
3086
|
+
await runWithConcurrency(parts.map((p) => (signal) => uploadPart(p, signal)), parallelism);
|
|
3087
|
+
} else {
|
|
3088
|
+
const { signal } = new AbortController();
|
|
3089
|
+
for (const part of parts) {
|
|
3090
|
+
await uploadPart(part, signal);
|
|
2957
3091
|
}
|
|
2958
3092
|
}
|
|
2959
3093
|
}
|
|
@@ -3066,7 +3200,10 @@ var init_invoice_download = __esm({
|
|
|
3066
3200
|
async getInvoice(ksefNumber) {
|
|
3067
3201
|
const req = RestRequest.get(Routes.Invoices.byKsefNumber(ksefNumber));
|
|
3068
3202
|
const response = await this.restClient.executeRaw(req);
|
|
3069
|
-
return
|
|
3203
|
+
return {
|
|
3204
|
+
xml: new TextDecoder().decode(response.body),
|
|
3205
|
+
hash: response.headers.get("x-ms-meta-hash") ?? void 0
|
|
3206
|
+
};
|
|
3070
3207
|
}
|
|
3071
3208
|
async queryInvoiceMetadata(filters, pageOffset, pageSize, sortOrder) {
|
|
3072
3209
|
const req = RestRequest.post(Routes.Invoices.queryMetadata).body(filters);
|
|
@@ -3335,20 +3472,20 @@ var init_lighthouse = __esm({
|
|
|
3335
3472
|
this.lighthouseUrl = options.lighthouseUrl;
|
|
3336
3473
|
this.timeout = options.timeout;
|
|
3337
3474
|
}
|
|
3338
|
-
async fetchJson(
|
|
3475
|
+
async fetchJson(path2) {
|
|
3339
3476
|
if (!this.lighthouseUrl) {
|
|
3340
3477
|
throw new KSeFError(
|
|
3341
3478
|
"Lighthouse API is not available for the DEMO environment. Use TEST or PROD instead."
|
|
3342
3479
|
);
|
|
3343
3480
|
}
|
|
3344
|
-
const response = await fetch(`${this.lighthouseUrl}${
|
|
3481
|
+
const response = await fetch(`${this.lighthouseUrl}${path2}`, {
|
|
3345
3482
|
headers: { Accept: "application/json" },
|
|
3346
3483
|
signal: AbortSignal.timeout(this.timeout)
|
|
3347
3484
|
});
|
|
3348
3485
|
if (!response.ok) {
|
|
3349
3486
|
const body = await response.text();
|
|
3350
3487
|
throw new KSeFError(
|
|
3351
|
-
`Lighthouse ${
|
|
3488
|
+
`Lighthouse ${path2} failed: HTTP ${response.status} \u2014 ${body}`
|
|
3352
3489
|
);
|
|
3353
3490
|
}
|
|
3354
3491
|
return await response.json();
|
|
@@ -4328,6 +4465,458 @@ var init_zip = __esm({
|
|
|
4328
4465
|
}
|
|
4329
4466
|
});
|
|
4330
4467
|
|
|
4468
|
+
// src/offline/holidays.ts
|
|
4469
|
+
function toDateKey(d) {
|
|
4470
|
+
return d.toISOString().slice(0, 10);
|
|
4471
|
+
}
|
|
4472
|
+
function dateKey(year, month, day) {
|
|
4473
|
+
return toDateKey(new Date(Date.UTC(year, month - 1, day)));
|
|
4474
|
+
}
|
|
4475
|
+
function addDays(d, n) {
|
|
4476
|
+
const result = new Date(d);
|
|
4477
|
+
result.setUTCDate(result.getUTCDate() + n);
|
|
4478
|
+
return result;
|
|
4479
|
+
}
|
|
4480
|
+
function computeEasterSunday(year) {
|
|
4481
|
+
const a = year % 19;
|
|
4482
|
+
const b = Math.floor(year / 100);
|
|
4483
|
+
const c = year % 100;
|
|
4484
|
+
const d = Math.floor(b / 4);
|
|
4485
|
+
const e = b % 4;
|
|
4486
|
+
const f = Math.floor((b + 8) / 25);
|
|
4487
|
+
const g = Math.floor((b - f + 1) / 3);
|
|
4488
|
+
const h = (19 * a + b - d - g + 15) % 30;
|
|
4489
|
+
const i = Math.floor(c / 4);
|
|
4490
|
+
const k = c % 4;
|
|
4491
|
+
const l = (32 + 2 * e + 2 * i - h - k) % 7;
|
|
4492
|
+
const m = Math.floor((a + 11 * h + 22 * l) / 451);
|
|
4493
|
+
const month = Math.floor((h + l - 7 * m + 114) / 31);
|
|
4494
|
+
const day = (h + l - 7 * m + 114) % 31 + 1;
|
|
4495
|
+
return new Date(Date.UTC(year, month - 1, day));
|
|
4496
|
+
}
|
|
4497
|
+
function getPolishHolidays(year) {
|
|
4498
|
+
const cached = holidayCache.get(year);
|
|
4499
|
+
if (cached) return cached;
|
|
4500
|
+
const easter = computeEasterSunday(year);
|
|
4501
|
+
const holidays = /* @__PURE__ */ new Set([
|
|
4502
|
+
// Fixed
|
|
4503
|
+
dateKey(year, 1, 1),
|
|
4504
|
+
dateKey(year, 1, 6),
|
|
4505
|
+
dateKey(year, 5, 1),
|
|
4506
|
+
dateKey(year, 5, 3),
|
|
4507
|
+
dateKey(year, 8, 15),
|
|
4508
|
+
dateKey(year, 11, 1),
|
|
4509
|
+
dateKey(year, 11, 11),
|
|
4510
|
+
dateKey(year, 12, 25),
|
|
4511
|
+
dateKey(year, 12, 26),
|
|
4512
|
+
// Wigilia — added by Dz.U. 2024 poz. 1965, effective from 2025
|
|
4513
|
+
...year >= 2025 ? [dateKey(year, 12, 24)] : [],
|
|
4514
|
+
// Moveable
|
|
4515
|
+
toDateKey(easter),
|
|
4516
|
+
toDateKey(addDays(easter, 1)),
|
|
4517
|
+
toDateKey(addDays(easter, 49)),
|
|
4518
|
+
toDateKey(addDays(easter, 60))
|
|
4519
|
+
]);
|
|
4520
|
+
holidayCache.set(year, holidays);
|
|
4521
|
+
return holidays;
|
|
4522
|
+
}
|
|
4523
|
+
function isPolishHoliday(date) {
|
|
4524
|
+
return getPolishHolidays(date.getUTCFullYear()).has(toDateKey(date));
|
|
4525
|
+
}
|
|
4526
|
+
var holidayCache;
|
|
4527
|
+
var init_holidays = __esm({
|
|
4528
|
+
"src/offline/holidays.ts"() {
|
|
4529
|
+
"use strict";
|
|
4530
|
+
holidayCache = /* @__PURE__ */ new Map();
|
|
4531
|
+
}
|
|
4532
|
+
});
|
|
4533
|
+
|
|
4534
|
+
// src/offline/deadline.ts
|
|
4535
|
+
function getDefaultReason(mode) {
|
|
4536
|
+
switch (mode) {
|
|
4537
|
+
case "offline24":
|
|
4538
|
+
return "PLANNED";
|
|
4539
|
+
case "offline":
|
|
4540
|
+
return "SYSTEM_UNAVAILABLE";
|
|
4541
|
+
case "awaryjny":
|
|
4542
|
+
return "EMERGENCY";
|
|
4543
|
+
case "awaria_calkowita":
|
|
4544
|
+
return "TOTAL_FAILURE";
|
|
4545
|
+
}
|
|
4546
|
+
}
|
|
4547
|
+
function nextBusinessDay(from) {
|
|
4548
|
+
const d = new Date(from);
|
|
4549
|
+
d.setUTCDate(d.getUTCDate() + 1);
|
|
4550
|
+
while (d.getUTCDay() === 0 || d.getUTCDay() === 6 || isPolishHoliday(d)) {
|
|
4551
|
+
d.setUTCDate(d.getUTCDate() + 1);
|
|
4552
|
+
}
|
|
4553
|
+
return d;
|
|
4554
|
+
}
|
|
4555
|
+
function addBusinessDays(from, days) {
|
|
4556
|
+
if (!Number.isInteger(days) || days < 0) {
|
|
4557
|
+
throw new Error(`days must be a non-negative integer, got ${days}`);
|
|
4558
|
+
}
|
|
4559
|
+
const d = new Date(from);
|
|
4560
|
+
let remaining = days;
|
|
4561
|
+
while (remaining > 0) {
|
|
4562
|
+
d.setUTCDate(d.getUTCDate() + 1);
|
|
4563
|
+
if (d.getUTCDay() !== 0 && d.getUTCDay() !== 6 && !isPolishHoliday(d)) {
|
|
4564
|
+
remaining--;
|
|
4565
|
+
}
|
|
4566
|
+
}
|
|
4567
|
+
return d;
|
|
4568
|
+
}
|
|
4569
|
+
function endOfDay(d) {
|
|
4570
|
+
const result = new Date(d);
|
|
4571
|
+
result.setUTCHours(23, 59, 59, 999);
|
|
4572
|
+
return result;
|
|
4573
|
+
}
|
|
4574
|
+
function getMaintenanceEndFallback(mw) {
|
|
4575
|
+
if (mw.endTime) {
|
|
4576
|
+
return new Date(mw.endTime);
|
|
4577
|
+
}
|
|
4578
|
+
const start = new Date(mw.startTime);
|
|
4579
|
+
start.setUTCDate(start.getUTCDate() + 7);
|
|
4580
|
+
return start;
|
|
4581
|
+
}
|
|
4582
|
+
function calculateOfflineDeadline(mode, invoiceDate, maintenanceWindow) {
|
|
4583
|
+
const date = typeof invoiceDate === "string" ? new Date(invoiceDate) : invoiceDate;
|
|
4584
|
+
switch (mode) {
|
|
4585
|
+
case "offline24": {
|
|
4586
|
+
return endOfDay(nextBusinessDay(date));
|
|
4587
|
+
}
|
|
4588
|
+
case "offline": {
|
|
4589
|
+
const base = maintenanceWindow ? getMaintenanceEndFallback(maintenanceWindow) : date;
|
|
4590
|
+
return endOfDay(nextBusinessDay(base));
|
|
4591
|
+
}
|
|
4592
|
+
case "awaryjny": {
|
|
4593
|
+
const base = maintenanceWindow ? getMaintenanceEndFallback(maintenanceWindow) : date;
|
|
4594
|
+
return endOfDay(addBusinessDays(base, 7));
|
|
4595
|
+
}
|
|
4596
|
+
case "awaria_calkowita": {
|
|
4597
|
+
return new Date(FAR_FUTURE);
|
|
4598
|
+
}
|
|
4599
|
+
}
|
|
4600
|
+
}
|
|
4601
|
+
function extendDeadlineForMaintenance(currentDeadline, maintenanceWindow, mode = "awaryjny") {
|
|
4602
|
+
const current = typeof currentDeadline === "string" ? new Date(currentDeadline) : new Date(currentDeadline);
|
|
4603
|
+
const mwEnd = getMaintenanceEndFallback(maintenanceWindow);
|
|
4604
|
+
if (mwEnd.getTime() > current.getTime()) {
|
|
4605
|
+
switch (mode) {
|
|
4606
|
+
case "offline24":
|
|
4607
|
+
case "offline":
|
|
4608
|
+
return endOfDay(nextBusinessDay(mwEnd));
|
|
4609
|
+
case "awaryjny":
|
|
4610
|
+
return endOfDay(addBusinessDays(mwEnd, 7));
|
|
4611
|
+
case "awaria_calkowita":
|
|
4612
|
+
return new Date(FAR_FUTURE);
|
|
4613
|
+
}
|
|
4614
|
+
}
|
|
4615
|
+
return current;
|
|
4616
|
+
}
|
|
4617
|
+
function isExpired(submitBy) {
|
|
4618
|
+
const deadline = typeof submitBy === "string" ? new Date(submitBy) : submitBy;
|
|
4619
|
+
return Date.now() > deadline.getTime();
|
|
4620
|
+
}
|
|
4621
|
+
function getTimeUntilDeadline(submitBy) {
|
|
4622
|
+
const deadline = typeof submitBy === "string" ? new Date(submitBy) : submitBy;
|
|
4623
|
+
return Math.max(0, deadline.getTime() - Date.now());
|
|
4624
|
+
}
|
|
4625
|
+
var FAR_FUTURE;
|
|
4626
|
+
var init_deadline = __esm({
|
|
4627
|
+
"src/offline/deadline.ts"() {
|
|
4628
|
+
"use strict";
|
|
4629
|
+
init_holidays();
|
|
4630
|
+
FAR_FUTURE = /* @__PURE__ */ new Date("9999-12-31T23:59:59Z");
|
|
4631
|
+
}
|
|
4632
|
+
});
|
|
4633
|
+
|
|
4634
|
+
// src/workflows/offline-invoice-workflow.ts
|
|
4635
|
+
import crypto7 from "crypto";
|
|
4636
|
+
var OfflineInvoiceWorkflow;
|
|
4637
|
+
var init_offline_invoice_workflow = __esm({
|
|
4638
|
+
"src/workflows/offline-invoice-workflow.ts"() {
|
|
4639
|
+
"use strict";
|
|
4640
|
+
init_ksef_api_error();
|
|
4641
|
+
init_deadline();
|
|
4642
|
+
init_document_structures();
|
|
4643
|
+
OfflineInvoiceWorkflow = class {
|
|
4644
|
+
constructor(qrService) {
|
|
4645
|
+
this.qrService = qrService;
|
|
4646
|
+
}
|
|
4647
|
+
async generate(input, options) {
|
|
4648
|
+
if (!input.invoiceXml || input.invoiceXml.trim().length === 0) {
|
|
4649
|
+
throw new Error("invoiceXml must not be empty");
|
|
4650
|
+
}
|
|
4651
|
+
if (!input.invoiceNumber) {
|
|
4652
|
+
throw new Error("invoiceNumber is required");
|
|
4653
|
+
}
|
|
4654
|
+
if (!input.sellerNip) {
|
|
4655
|
+
throw new Error("sellerNip is required");
|
|
4656
|
+
}
|
|
4657
|
+
const mode = options?.mode ?? "offline24";
|
|
4658
|
+
const reason = getDefaultReason(mode);
|
|
4659
|
+
const invoiceHashBase64 = crypto7.createHash("sha256").update(input.invoiceXml).digest("base64");
|
|
4660
|
+
const kod1Url = this.qrService.buildInvoiceVerificationUrl(
|
|
4661
|
+
input.sellerNip,
|
|
4662
|
+
input.invoiceDate,
|
|
4663
|
+
invoiceHashBase64
|
|
4664
|
+
);
|
|
4665
|
+
let kod2Url;
|
|
4666
|
+
if (options?.certificate) {
|
|
4667
|
+
kod2Url = this.qrService.buildCertificateVerificationUrl(
|
|
4668
|
+
input.sellerIdentifier.type,
|
|
4669
|
+
input.sellerIdentifier.value,
|
|
4670
|
+
input.sellerNip,
|
|
4671
|
+
options.certificate.certificateSerial,
|
|
4672
|
+
invoiceHashBase64,
|
|
4673
|
+
options.certificate.privateKeyPem
|
|
4674
|
+
);
|
|
4675
|
+
}
|
|
4676
|
+
const submitBy = options?.customDeadline ? typeof options.customDeadline === "string" ? options.customDeadline : options.customDeadline.toISOString() : calculateOfflineDeadline(mode, input.invoiceDate, options?.maintenanceWindow).toISOString();
|
|
4677
|
+
const metadata = {
|
|
4678
|
+
id: crypto7.randomUUID(),
|
|
4679
|
+
mode,
|
|
4680
|
+
reason,
|
|
4681
|
+
status: "GENERATED",
|
|
4682
|
+
invoiceNumber: input.invoiceNumber,
|
|
4683
|
+
invoiceDate: input.invoiceDate,
|
|
4684
|
+
invoiceXml: input.invoiceXml,
|
|
4685
|
+
sellerNip: input.sellerNip,
|
|
4686
|
+
sellerIdentifier: input.sellerIdentifier,
|
|
4687
|
+
buyerIdentifier: input.buyerIdentifier,
|
|
4688
|
+
totalAmount: input.totalAmount,
|
|
4689
|
+
currency: input.currency,
|
|
4690
|
+
kod1Url,
|
|
4691
|
+
kod2Url,
|
|
4692
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4693
|
+
submitBy,
|
|
4694
|
+
maintenanceWindowId: options?.maintenanceWindow?.id
|
|
4695
|
+
};
|
|
4696
|
+
if (options?.storage) {
|
|
4697
|
+
await options.storage.save(metadata);
|
|
4698
|
+
}
|
|
4699
|
+
return metadata;
|
|
4700
|
+
}
|
|
4701
|
+
async submit(client, options) {
|
|
4702
|
+
const { storage, checkExpiry = true } = options;
|
|
4703
|
+
let invoices;
|
|
4704
|
+
if (options.invoiceIds) {
|
|
4705
|
+
invoices = [];
|
|
4706
|
+
const notFound = [];
|
|
4707
|
+
for (const id of options.invoiceIds) {
|
|
4708
|
+
const inv = await storage.get(id);
|
|
4709
|
+
if (inv) {
|
|
4710
|
+
invoices.push(inv);
|
|
4711
|
+
} else {
|
|
4712
|
+
notFound.push(id);
|
|
4713
|
+
}
|
|
4714
|
+
}
|
|
4715
|
+
if (notFound.length > 0) {
|
|
4716
|
+
throw new Error(`Offline invoice(s) not found: ${notFound.join(", ")}`);
|
|
4717
|
+
}
|
|
4718
|
+
} else {
|
|
4719
|
+
invoices = await storage.list({ status: ["GENERATED", "QUEUED", "SUBMITTED"] });
|
|
4720
|
+
}
|
|
4721
|
+
const result = {
|
|
4722
|
+
total: invoices.length,
|
|
4723
|
+
submitted: 0,
|
|
4724
|
+
accepted: 0,
|
|
4725
|
+
rejected: 0,
|
|
4726
|
+
failed: 0,
|
|
4727
|
+
expired: 0,
|
|
4728
|
+
results: []
|
|
4729
|
+
};
|
|
4730
|
+
if (invoices.length === 0) return result;
|
|
4731
|
+
const pending = [];
|
|
4732
|
+
for (const inv of invoices) {
|
|
4733
|
+
if (checkExpiry && isExpired(inv.submitBy)) {
|
|
4734
|
+
await storage.update(inv.id, { status: "EXPIRED" });
|
|
4735
|
+
result.expired++;
|
|
4736
|
+
result.results.push({
|
|
4737
|
+
invoiceId: inv.id,
|
|
4738
|
+
invoiceNumber: inv.invoiceNumber,
|
|
4739
|
+
status: "EXPIRED"
|
|
4740
|
+
});
|
|
4741
|
+
} else {
|
|
4742
|
+
pending.push(inv);
|
|
4743
|
+
}
|
|
4744
|
+
}
|
|
4745
|
+
if (pending.length === 0) return result;
|
|
4746
|
+
await client.crypto.init();
|
|
4747
|
+
const encData = client.crypto.getEncryptionData();
|
|
4748
|
+
const formCode = options.formCode ?? DEFAULT_FORM_CODE;
|
|
4749
|
+
const openResp = await client.onlineSession.openSession(
|
|
4750
|
+
{ formCode, encryption: encData.encryptionInfo }
|
|
4751
|
+
);
|
|
4752
|
+
const sessionRef = openResp.referenceNumber;
|
|
4753
|
+
try {
|
|
4754
|
+
for (const inv of pending) {
|
|
4755
|
+
await storage.update(inv.id, { status: "QUEUED" });
|
|
4756
|
+
try {
|
|
4757
|
+
const data = new TextEncoder().encode(inv.invoiceXml);
|
|
4758
|
+
const plainMeta = client.crypto.getFileMetadata(data);
|
|
4759
|
+
const encrypted = client.crypto.encryptAES256(data, encData.cipherKey, encData.cipherIv);
|
|
4760
|
+
const encMeta = client.crypto.getFileMetadata(encrypted);
|
|
4761
|
+
await storage.update(inv.id, { status: "SUBMITTED", submittedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
4762
|
+
const resp = await client.onlineSession.sendInvoice(sessionRef, {
|
|
4763
|
+
invoiceHash: plainMeta.hashSHA,
|
|
4764
|
+
invoiceSize: plainMeta.fileSize,
|
|
4765
|
+
encryptedInvoiceHash: encMeta.hashSHA,
|
|
4766
|
+
encryptedInvoiceSize: encMeta.fileSize,
|
|
4767
|
+
encryptedInvoiceContent: Buffer.from(encrypted).toString("base64"),
|
|
4768
|
+
offlineMode: true
|
|
4769
|
+
});
|
|
4770
|
+
await storage.update(inv.id, {
|
|
4771
|
+
status: "ACCEPTED",
|
|
4772
|
+
acceptedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4773
|
+
ksefReferenceNumber: resp.referenceNumber
|
|
4774
|
+
});
|
|
4775
|
+
result.submitted++;
|
|
4776
|
+
result.accepted++;
|
|
4777
|
+
result.results.push({
|
|
4778
|
+
invoiceId: inv.id,
|
|
4779
|
+
invoiceNumber: inv.invoiceNumber,
|
|
4780
|
+
status: "ACCEPTED",
|
|
4781
|
+
ksefReferenceNumber: resp.referenceNumber
|
|
4782
|
+
});
|
|
4783
|
+
} catch (err) {
|
|
4784
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
4785
|
+
if (err instanceof KSeFApiError) {
|
|
4786
|
+
await storage.update(inv.id, {
|
|
4787
|
+
status: "REJECTED",
|
|
4788
|
+
error: { code: err.statusCode, message }
|
|
4789
|
+
});
|
|
4790
|
+
result.submitted++;
|
|
4791
|
+
result.rejected++;
|
|
4792
|
+
result.results.push({
|
|
4793
|
+
invoiceId: inv.id,
|
|
4794
|
+
invoiceNumber: inv.invoiceNumber,
|
|
4795
|
+
status: "REJECTED",
|
|
4796
|
+
error: { code: err.statusCode, message }
|
|
4797
|
+
});
|
|
4798
|
+
} else {
|
|
4799
|
+
await storage.update(inv.id, {
|
|
4800
|
+
status: "QUEUED",
|
|
4801
|
+
error: { code: 0, message }
|
|
4802
|
+
});
|
|
4803
|
+
result.failed++;
|
|
4804
|
+
result.results.push({
|
|
4805
|
+
invoiceId: inv.id,
|
|
4806
|
+
invoiceNumber: inv.invoiceNumber,
|
|
4807
|
+
status: "QUEUED",
|
|
4808
|
+
error: { code: 0, message }
|
|
4809
|
+
});
|
|
4810
|
+
}
|
|
4811
|
+
}
|
|
4812
|
+
}
|
|
4813
|
+
} finally {
|
|
4814
|
+
try {
|
|
4815
|
+
await client.onlineSession.closeSession(sessionRef);
|
|
4816
|
+
} catch {
|
|
4817
|
+
}
|
|
4818
|
+
}
|
|
4819
|
+
return result;
|
|
4820
|
+
}
|
|
4821
|
+
// TODO(perf): Each correction opens a separate KSeF session.
|
|
4822
|
+
// For batch corrections, consider a correctBatch() sharing one session (like submit()).
|
|
4823
|
+
async correct(client, options) {
|
|
4824
|
+
const { storage, rejectedInvoiceId, correctedInvoiceXml } = options;
|
|
4825
|
+
const original = await storage.get(rejectedInvoiceId);
|
|
4826
|
+
if (!original) {
|
|
4827
|
+
throw new Error(`Offline invoice not found: ${rejectedInvoiceId}`);
|
|
4828
|
+
}
|
|
4829
|
+
if (original.status !== "REJECTED") {
|
|
4830
|
+
throw new Error(
|
|
4831
|
+
original.status === "CORRECTED" ? `Invoice ${rejectedInvoiceId} has already been corrected (by ${original.correctedBy})` : `Only rejected invoices can be corrected (current status: ${original.status})`
|
|
4832
|
+
);
|
|
4833
|
+
}
|
|
4834
|
+
const originalHash = crypto7.createHash("sha256").update(original.invoiceXml).digest("base64");
|
|
4835
|
+
await client.crypto.init();
|
|
4836
|
+
const encData = client.crypto.getEncryptionData();
|
|
4837
|
+
const formCode = options.formCode ?? DEFAULT_FORM_CODE;
|
|
4838
|
+
const openResp = await client.onlineSession.openSession(
|
|
4839
|
+
{ formCode, encryption: encData.encryptionInfo }
|
|
4840
|
+
);
|
|
4841
|
+
const sessionRef = openResp.referenceNumber;
|
|
4842
|
+
try {
|
|
4843
|
+
const data = new TextEncoder().encode(correctedInvoiceXml);
|
|
4844
|
+
const plainMeta = client.crypto.getFileMetadata(data);
|
|
4845
|
+
const encrypted = client.crypto.encryptAES256(data, encData.cipherKey, encData.cipherIv);
|
|
4846
|
+
const encMeta = client.crypto.getFileMetadata(encrypted);
|
|
4847
|
+
const correctionId = crypto7.randomUUID();
|
|
4848
|
+
const submittedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4849
|
+
const correctedHashBase64 = crypto7.createHash("sha256").update(correctedInvoiceXml).digest("base64");
|
|
4850
|
+
const kod1Url = this.qrService.buildInvoiceVerificationUrl(
|
|
4851
|
+
original.sellerNip,
|
|
4852
|
+
original.invoiceDate,
|
|
4853
|
+
correctedHashBase64
|
|
4854
|
+
);
|
|
4855
|
+
let kod2Url;
|
|
4856
|
+
if (options.certificate) {
|
|
4857
|
+
kod2Url = this.qrService.buildCertificateVerificationUrl(
|
|
4858
|
+
original.sellerIdentifier.type,
|
|
4859
|
+
original.sellerIdentifier.value,
|
|
4860
|
+
original.sellerNip,
|
|
4861
|
+
options.certificate.certificateSerial,
|
|
4862
|
+
correctedHashBase64,
|
|
4863
|
+
options.certificate.privateKeyPem
|
|
4864
|
+
);
|
|
4865
|
+
}
|
|
4866
|
+
const correctionMetadata = {
|
|
4867
|
+
id: correctionId,
|
|
4868
|
+
mode: original.mode,
|
|
4869
|
+
reason: original.reason,
|
|
4870
|
+
status: "SUBMITTED",
|
|
4871
|
+
invoiceNumber: original.invoiceNumber,
|
|
4872
|
+
invoiceDate: original.invoiceDate,
|
|
4873
|
+
invoiceXml: correctedInvoiceXml,
|
|
4874
|
+
sellerNip: original.sellerNip,
|
|
4875
|
+
sellerIdentifier: original.sellerIdentifier,
|
|
4876
|
+
buyerIdentifier: original.buyerIdentifier,
|
|
4877
|
+
kod1Url,
|
|
4878
|
+
kod2Url,
|
|
4879
|
+
generatedAt: submittedAt,
|
|
4880
|
+
submitBy: original.submitBy,
|
|
4881
|
+
submittedAt,
|
|
4882
|
+
correctedInvoiceId: rejectedInvoiceId
|
|
4883
|
+
};
|
|
4884
|
+
await storage.save(correctionMetadata);
|
|
4885
|
+
const resp = await client.onlineSession.sendInvoice(sessionRef, {
|
|
4886
|
+
invoiceHash: plainMeta.hashSHA,
|
|
4887
|
+
invoiceSize: plainMeta.fileSize,
|
|
4888
|
+
encryptedInvoiceHash: encMeta.hashSHA,
|
|
4889
|
+
encryptedInvoiceSize: encMeta.fileSize,
|
|
4890
|
+
encryptedInvoiceContent: Buffer.from(encrypted).toString("base64"),
|
|
4891
|
+
offlineMode: true,
|
|
4892
|
+
hashOfCorrectedInvoice: originalHash
|
|
4893
|
+
});
|
|
4894
|
+
await storage.update(correctionId, {
|
|
4895
|
+
status: "ACCEPTED",
|
|
4896
|
+
acceptedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4897
|
+
ksefReferenceNumber: resp.referenceNumber
|
|
4898
|
+
});
|
|
4899
|
+
await storage.update(rejectedInvoiceId, {
|
|
4900
|
+
status: "CORRECTED",
|
|
4901
|
+
correctedBy: correctionId
|
|
4902
|
+
});
|
|
4903
|
+
return {
|
|
4904
|
+
invoiceId: correctionId,
|
|
4905
|
+
invoiceNumber: correctionMetadata.invoiceNumber,
|
|
4906
|
+
status: "ACCEPTED",
|
|
4907
|
+
ksefReferenceNumber: resp.referenceNumber
|
|
4908
|
+
};
|
|
4909
|
+
} finally {
|
|
4910
|
+
try {
|
|
4911
|
+
await client.onlineSession.closeSession(sessionRef);
|
|
4912
|
+
} catch {
|
|
4913
|
+
}
|
|
4914
|
+
}
|
|
4915
|
+
}
|
|
4916
|
+
};
|
|
4917
|
+
}
|
|
4918
|
+
});
|
|
4919
|
+
|
|
4331
4920
|
// src/client.ts
|
|
4332
4921
|
var client_exports = {};
|
|
4333
4922
|
__export(client_exports, {
|
|
@@ -4393,6 +4982,7 @@ var init_client = __esm({
|
|
|
4393
4982
|
init_cryptography_service();
|
|
4394
4983
|
init_verification_link_service();
|
|
4395
4984
|
init_auth_xml_builder();
|
|
4985
|
+
init_offline_invoice_workflow();
|
|
4396
4986
|
KSeFClient = class {
|
|
4397
4987
|
auth;
|
|
4398
4988
|
activeSessions;
|
|
@@ -4411,6 +5001,8 @@ var init_client = __esm({
|
|
|
4411
5001
|
qr;
|
|
4412
5002
|
options;
|
|
4413
5003
|
authManager;
|
|
5004
|
+
_baseRestClientConfig;
|
|
5005
|
+
_offline;
|
|
4414
5006
|
constructor(options) {
|
|
4415
5007
|
this.options = resolveOptions(options);
|
|
4416
5008
|
const authManager = options?.authManager ?? new DefaultAuthManager(async () => {
|
|
@@ -4421,6 +5013,8 @@ var init_client = __esm({
|
|
|
4421
5013
|
});
|
|
4422
5014
|
this.authManager = authManager;
|
|
4423
5015
|
const restClientConfig = buildRestClientConfig(options, authManager);
|
|
5016
|
+
const { authManager: _am, ...baseConfig } = restClientConfig;
|
|
5017
|
+
this._baseRestClientConfig = baseConfig;
|
|
4424
5018
|
const restClient = new RestClient(this.options, restClientConfig);
|
|
4425
5019
|
const fetcher = new CertificateFetcher(restClient);
|
|
4426
5020
|
this.crypto = new CryptographyService(fetcher);
|
|
@@ -4439,6 +5033,16 @@ var init_client = __esm({
|
|
|
4439
5033
|
this.testData = new TestDataService(restClient, this.options.environmentName);
|
|
4440
5034
|
this.qr = new VerificationLinkService(this.options.baseQrUrl);
|
|
4441
5035
|
}
|
|
5036
|
+
get offline() {
|
|
5037
|
+
if (!this._offline) {
|
|
5038
|
+
this._offline = new OfflineInvoiceWorkflow(this.qr);
|
|
5039
|
+
}
|
|
5040
|
+
return this._offline;
|
|
5041
|
+
}
|
|
5042
|
+
/** @internal Create a RestClient with a different AuthManager, preserving transport/retry/rateLimit config. */
|
|
5043
|
+
createScopedRestClient(authManager) {
|
|
5044
|
+
return new RestClient(this.options, { ...this._baseRestClientConfig, authManager });
|
|
5045
|
+
}
|
|
4442
5046
|
async loginWithToken(token, nip) {
|
|
4443
5047
|
const challenge = await this.auth.getChallenge();
|
|
4444
5048
|
await this.crypto.init();
|
|
@@ -4522,65 +5126,8 @@ init_xml_to_object();
|
|
|
4522
5126
|
init_schema_registry();
|
|
4523
5127
|
init_invoice_validator();
|
|
4524
5128
|
|
|
4525
|
-
// src/models/
|
|
4526
|
-
|
|
4527
|
-
FA_2: "FA (2)",
|
|
4528
|
-
FA_3: "FA (3)",
|
|
4529
|
-
PEF_3: "PEF (3)",
|
|
4530
|
-
PEF_KOR_3: "PEF_KOR (3)",
|
|
4531
|
-
FA_RR_1: "FA_RR (1)"
|
|
4532
|
-
};
|
|
4533
|
-
var FORM_CODES = {
|
|
4534
|
-
FA_2: { systemCode: "FA (2)", schemaVersion: "1-0E", value: "FA" },
|
|
4535
|
-
FA_3: { systemCode: "FA (3)", schemaVersion: "1-0E", value: "FA" },
|
|
4536
|
-
PEF_3: { systemCode: "PEF (3)", schemaVersion: "2-1", value: "PEF" },
|
|
4537
|
-
PEF_KOR_3: { systemCode: "PEF_KOR (3)", schemaVersion: "2-1", value: "PEF" },
|
|
4538
|
-
FA_RR_1_LEGACY: { systemCode: "FA_RR (1)", schemaVersion: "1-0E", value: "RR" },
|
|
4539
|
-
FA_RR_1_TRANSITION: { systemCode: "FA_RR (1)", schemaVersion: "1-1E", value: "RR" },
|
|
4540
|
-
FA_RR_1: { systemCode: "FA_RR (1)", schemaVersion: "1-1E", value: "FA_RR" }
|
|
4541
|
-
};
|
|
4542
|
-
var DEFAULT_FORM_CODE = FORM_CODES.FA_3;
|
|
4543
|
-
var INVOICE_TYPES_BY_SYSTEM_CODE = {
|
|
4544
|
-
[SystemCode.FA_2]: ["Vat", "Zal", "Kor", "Roz", "Upr", "KorZal", "KorRoz"],
|
|
4545
|
-
[SystemCode.FA_3]: ["Vat", "Zal", "Kor", "Roz", "Upr", "KorZal", "KorRoz"],
|
|
4546
|
-
[SystemCode.PEF_3]: ["VatPef", "VatPefSp", "KorPef"],
|
|
4547
|
-
[SystemCode.PEF_KOR_3]: ["KorPef"],
|
|
4548
|
-
[SystemCode.FA_RR_1]: ["VatRr", "KorVatRr"]
|
|
4549
|
-
};
|
|
4550
|
-
var FORM_CODE_KEYS = {
|
|
4551
|
-
FA2: FORM_CODES.FA_2,
|
|
4552
|
-
FA3: FORM_CODES.FA_3,
|
|
4553
|
-
PEF3: FORM_CODES.PEF_3,
|
|
4554
|
-
PEFKOR3: FORM_CODES.PEF_KOR_3,
|
|
4555
|
-
FARR1: FORM_CODES.FA_RR_1
|
|
4556
|
-
};
|
|
4557
|
-
|
|
4558
|
-
// src/models/document-structures/helpers.ts
|
|
4559
|
-
var SYSTEM_CODE_TO_FORM_CODE = {
|
|
4560
|
-
[SystemCode.FA_2]: FORM_CODES.FA_2,
|
|
4561
|
-
[SystemCode.FA_3]: FORM_CODES.FA_3,
|
|
4562
|
-
[SystemCode.PEF_3]: FORM_CODES.PEF_3,
|
|
4563
|
-
[SystemCode.PEF_KOR_3]: FORM_CODES.PEF_KOR_3,
|
|
4564
|
-
[SystemCode.FA_RR_1]: FORM_CODES.FA_RR_1
|
|
4565
|
-
};
|
|
4566
|
-
var ALL_FORM_CODES = Object.values(FORM_CODES);
|
|
4567
|
-
var BATCH_DISALLOWED_SYSTEM_CODES = /* @__PURE__ */ new Set([
|
|
4568
|
-
SystemCode.PEF_3,
|
|
4569
|
-
SystemCode.PEF_KOR_3
|
|
4570
|
-
]);
|
|
4571
|
-
function getFormCode(systemCode) {
|
|
4572
|
-
return SYSTEM_CODE_TO_FORM_CODE[systemCode];
|
|
4573
|
-
}
|
|
4574
|
-
function parseFormCode(raw) {
|
|
4575
|
-
const match = ALL_FORM_CODES.find(
|
|
4576
|
-
(fc) => fc.systemCode === raw.systemCode && fc.schemaVersion === raw.schemaVersion && fc.value === raw.value
|
|
4577
|
-
);
|
|
4578
|
-
return match ?? raw;
|
|
4579
|
-
}
|
|
4580
|
-
function validateFormCodeForSession(formCode, sessionType) {
|
|
4581
|
-
if (sessionType === "online") return true;
|
|
4582
|
-
return !BATCH_DISALLOWED_SYSTEM_CODES.has(formCode.systemCode);
|
|
4583
|
-
}
|
|
5129
|
+
// src/models/index.ts
|
|
5130
|
+
init_document_structures();
|
|
4584
5131
|
|
|
4585
5132
|
// src/services/index.ts
|
|
4586
5133
|
init_auth();
|
|
@@ -5315,6 +5862,18 @@ function parseKSeFTokenContext(token) {
|
|
|
5315
5862
|
};
|
|
5316
5863
|
}
|
|
5317
5864
|
|
|
5865
|
+
// src/utils/hash.ts
|
|
5866
|
+
import crypto6 from "crypto";
|
|
5867
|
+
function sha256Base642(data) {
|
|
5868
|
+
return crypto6.createHash("sha256").update(data).digest("base64");
|
|
5869
|
+
}
|
|
5870
|
+
function verifyHash(data, expectedHash) {
|
|
5871
|
+
return sha256Base642(data) === expectedHash;
|
|
5872
|
+
}
|
|
5873
|
+
|
|
5874
|
+
// src/utils/index.ts
|
|
5875
|
+
init_concurrency();
|
|
5876
|
+
|
|
5318
5877
|
// src/workflows/polling.ts
|
|
5319
5878
|
async function pollUntil(action, condition, options) {
|
|
5320
5879
|
const intervalMs = options?.intervalMs ?? 2e3;
|
|
@@ -5332,6 +5891,13 @@ async function pollUntil(action, condition, options) {
|
|
|
5332
5891
|
);
|
|
5333
5892
|
}
|
|
5334
5893
|
|
|
5894
|
+
// src/workflows/online-session-workflow.ts
|
|
5895
|
+
init_ksef_session_expired_error();
|
|
5896
|
+
init_auth_manager();
|
|
5897
|
+
init_online_session();
|
|
5898
|
+
init_session_status();
|
|
5899
|
+
init_document_structures();
|
|
5900
|
+
|
|
5335
5901
|
// src/xml/upo-parser.ts
|
|
5336
5902
|
init_ksef_validation_error();
|
|
5337
5903
|
import { XMLParser } from "fast-xml-parser";
|
|
@@ -5450,21 +6016,54 @@ function parseUpoXml(xml) {
|
|
|
5450
6016
|
};
|
|
5451
6017
|
}
|
|
5452
6018
|
|
|
6019
|
+
// src/xml/invoice-field-extractor.ts
|
|
6020
|
+
import { XMLParser as XMLParser2 } from "fast-xml-parser";
|
|
6021
|
+
var invoiceParser = new XMLParser2({
|
|
6022
|
+
ignoreAttributes: false,
|
|
6023
|
+
attributeNamePrefix: "@_",
|
|
6024
|
+
parseTagValue: false,
|
|
6025
|
+
parseAttributeValue: false,
|
|
6026
|
+
removeNSPrefix: true,
|
|
6027
|
+
trimValues: false
|
|
6028
|
+
});
|
|
6029
|
+
function extractInvoiceFields(xml) {
|
|
6030
|
+
const parsed = invoiceParser.parse(xml);
|
|
6031
|
+
const root = parsed?.Faktura;
|
|
6032
|
+
if (!root || typeof root !== "object") {
|
|
6033
|
+
throw new Error(
|
|
6034
|
+
"Cannot extract invoice fields: missing <Faktura> root element. Ensure the XML is a valid KSeF invoice (FA_2, FA_3, or FA_RR)."
|
|
6035
|
+
);
|
|
6036
|
+
}
|
|
6037
|
+
if (root.Fa && typeof root.Fa === "object") {
|
|
6038
|
+
const invoiceDate = nonEmptyString(root.Fa.P_1);
|
|
6039
|
+
const invoiceNumber = nonEmptyString(root.Fa.P_2);
|
|
6040
|
+
if (invoiceDate && invoiceNumber) {
|
|
6041
|
+
return { invoiceNumber, invoiceDate };
|
|
6042
|
+
}
|
|
6043
|
+
}
|
|
6044
|
+
if (root.FakturaRR && typeof root.FakturaRR === "object") {
|
|
6045
|
+
const invoiceDate = nonEmptyString(root.FakturaRR.P_4B);
|
|
6046
|
+
const invoiceNumber = nonEmptyString(root.FakturaRR.P_4C);
|
|
6047
|
+
if (invoiceDate && invoiceNumber) {
|
|
6048
|
+
return { invoiceNumber, invoiceDate };
|
|
6049
|
+
}
|
|
6050
|
+
}
|
|
6051
|
+
throw new Error(
|
|
6052
|
+
"Cannot extract invoice number and date from XML. Expected <Fa><P_1>/<P_2> (FA format) or <FakturaRR><P_4B>/<P_4C> (RR format)."
|
|
6053
|
+
);
|
|
6054
|
+
}
|
|
6055
|
+
function nonEmptyString(value) {
|
|
6056
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
6057
|
+
}
|
|
6058
|
+
|
|
5453
6059
|
// src/workflows/online-session-workflow.ts
|
|
5454
6060
|
init_invoice_validator();
|
|
5455
6061
|
init_ksef_validation_error();
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
const encData = client.crypto.getEncryptionData();
|
|
5459
|
-
const formCode = options?.formCode ?? DEFAULT_FORM_CODE;
|
|
5460
|
-
const openResp = await client.onlineSession.openSession(
|
|
5461
|
-
{ formCode, encryption: encData.encryptionInfo },
|
|
5462
|
-
options?.upoVersion
|
|
5463
|
-
);
|
|
5464
|
-
const sessionRef = openResp.referenceNumber;
|
|
6062
|
+
function buildSessionHandle(params) {
|
|
6063
|
+
const { deps, sessionRef, validUntil, cipherKey, cipherIv, formCode, validate: validate2 } = params;
|
|
5465
6064
|
async function fetchUpo(pollOpts) {
|
|
5466
6065
|
const result = await pollUntil(
|
|
5467
|
-
() =>
|
|
6066
|
+
() => deps.sessionStatus.getSessionStatus(sessionRef),
|
|
5468
6067
|
(s) => s.status.code === 200 || s.status.code >= 400,
|
|
5469
6068
|
{ ...pollOpts, description: `UPO for session ${sessionRef}` }
|
|
5470
6069
|
);
|
|
@@ -5480,9 +6079,9 @@ async function openOnlineSession(client, options) {
|
|
|
5480
6079
|
}
|
|
5481
6080
|
return {
|
|
5482
6081
|
sessionRef,
|
|
5483
|
-
validUntil
|
|
6082
|
+
validUntil,
|
|
5484
6083
|
async sendInvoice(invoiceXml) {
|
|
5485
|
-
if (
|
|
6084
|
+
if (validate2) {
|
|
5486
6085
|
const xmlStr = typeof invoiceXml === "string" ? invoiceXml : new TextDecoder().decode(invoiceXml);
|
|
5487
6086
|
const vResult = await validate(xmlStr);
|
|
5488
6087
|
if (!vResult.valid) {
|
|
@@ -5493,10 +6092,10 @@ async function openOnlineSession(client, options) {
|
|
|
5493
6092
|
}
|
|
5494
6093
|
}
|
|
5495
6094
|
const data = typeof invoiceXml === "string" ? new TextEncoder().encode(invoiceXml) : invoiceXml;
|
|
5496
|
-
const plainMeta =
|
|
5497
|
-
const encrypted =
|
|
5498
|
-
const encMeta =
|
|
5499
|
-
const resp = await
|
|
6095
|
+
const plainMeta = deps.crypto.getFileMetadata(data);
|
|
6096
|
+
const encrypted = deps.crypto.encryptAES256(data, cipherKey, cipherIv);
|
|
6097
|
+
const encMeta = deps.crypto.getFileMetadata(encrypted);
|
|
6098
|
+
const resp = await deps.onlineSession.sendInvoice(sessionRef, {
|
|
5500
6099
|
invoiceHash: plainMeta.hashSHA,
|
|
5501
6100
|
invoiceSize: plainMeta.fileSize,
|
|
5502
6101
|
encryptedInvoiceHash: encMeta.hashSHA,
|
|
@@ -5506,7 +6105,7 @@ async function openOnlineSession(client, options) {
|
|
|
5506
6105
|
return resp.referenceNumber;
|
|
5507
6106
|
},
|
|
5508
6107
|
async close() {
|
|
5509
|
-
await
|
|
6108
|
+
await deps.onlineSession.closeSession(sessionRef);
|
|
5510
6109
|
},
|
|
5511
6110
|
async waitForUpo(pollOpts) {
|
|
5512
6111
|
return fetchUpo(pollOpts);
|
|
@@ -5515,13 +6114,75 @@ async function openOnlineSession(client, options) {
|
|
|
5515
6114
|
const upoInfo = await fetchUpo(pollOpts);
|
|
5516
6115
|
const parsed = [];
|
|
5517
6116
|
for (const page of upoInfo.pages) {
|
|
5518
|
-
const result = await
|
|
6117
|
+
const result = await deps.sessionStatus.getSessionUpo(sessionRef, page.referenceNumber);
|
|
5519
6118
|
parsed.push(parseUpoXml(result.upo));
|
|
5520
6119
|
}
|
|
5521
6120
|
return { ...upoInfo, parsed };
|
|
6121
|
+
},
|
|
6122
|
+
getState() {
|
|
6123
|
+
const token = deps.getAccessToken();
|
|
6124
|
+
if (!token) {
|
|
6125
|
+
throw new Error("Cannot serialize session state: no access token available");
|
|
6126
|
+
}
|
|
6127
|
+
return {
|
|
6128
|
+
referenceNumber: sessionRef,
|
|
6129
|
+
aesKey: Buffer.from(cipherKey).toString("base64"),
|
|
6130
|
+
iv: Buffer.from(cipherIv).toString("base64"),
|
|
6131
|
+
accessToken: token,
|
|
6132
|
+
formCode,
|
|
6133
|
+
validUntil,
|
|
6134
|
+
...validate2 ? { validate: validate2 } : {}
|
|
6135
|
+
};
|
|
5522
6136
|
}
|
|
5523
6137
|
};
|
|
5524
6138
|
}
|
|
6139
|
+
async function openOnlineSession(client, options) {
|
|
6140
|
+
await client.crypto.init();
|
|
6141
|
+
const encData = client.crypto.getEncryptionData();
|
|
6142
|
+
const formCode = options?.formCode ?? DEFAULT_FORM_CODE;
|
|
6143
|
+
const openResp = await client.onlineSession.openSession(
|
|
6144
|
+
{ formCode, encryption: encData.encryptionInfo },
|
|
6145
|
+
options?.upoVersion
|
|
6146
|
+
);
|
|
6147
|
+
return buildSessionHandle({
|
|
6148
|
+
deps: {
|
|
6149
|
+
crypto: client.crypto,
|
|
6150
|
+
onlineSession: client.onlineSession,
|
|
6151
|
+
sessionStatus: client.sessionStatus,
|
|
6152
|
+
getAccessToken: () => client.authManager.getAccessToken()
|
|
6153
|
+
},
|
|
6154
|
+
sessionRef: openResp.referenceNumber,
|
|
6155
|
+
validUntil: openResp.validUntil,
|
|
6156
|
+
cipherKey: encData.cipherKey,
|
|
6157
|
+
cipherIv: encData.cipherIv,
|
|
6158
|
+
formCode,
|
|
6159
|
+
validate: options?.validate
|
|
6160
|
+
});
|
|
6161
|
+
}
|
|
6162
|
+
function resumeOnlineSession(client, state, options) {
|
|
6163
|
+
const expiry = new Date(state.validUntil);
|
|
6164
|
+
if (expiry.getTime() <= Date.now()) {
|
|
6165
|
+
throw new KSeFSessionExpiredError(
|
|
6166
|
+
`Cannot resume session: expired at ${state.validUntil}`
|
|
6167
|
+
);
|
|
6168
|
+
}
|
|
6169
|
+
const scopedAuth = new DefaultAuthManager(() => Promise.resolve(null), state.accessToken);
|
|
6170
|
+
const scopedRestClient = client.createScopedRestClient(scopedAuth);
|
|
6171
|
+
return buildSessionHandle({
|
|
6172
|
+
deps: {
|
|
6173
|
+
crypto: client.crypto,
|
|
6174
|
+
onlineSession: new OnlineSessionService(scopedRestClient),
|
|
6175
|
+
sessionStatus: new SessionStatusService(scopedRestClient),
|
|
6176
|
+
getAccessToken: () => scopedAuth.getAccessToken()
|
|
6177
|
+
},
|
|
6178
|
+
sessionRef: state.referenceNumber,
|
|
6179
|
+
validUntil: state.validUntil,
|
|
6180
|
+
cipherKey: new Uint8Array(Buffer.from(state.aesKey, "base64")),
|
|
6181
|
+
cipherIv: new Uint8Array(Buffer.from(state.iv, "base64")),
|
|
6182
|
+
formCode: state.formCode,
|
|
6183
|
+
validate: options?.validate ?? state.validate
|
|
6184
|
+
});
|
|
6185
|
+
}
|
|
5525
6186
|
async function openSendAndClose(client, invoices, options) {
|
|
5526
6187
|
const handle = await openOnlineSession(client, options);
|
|
5527
6188
|
for (const inv of invoices) {
|
|
@@ -5532,7 +6193,11 @@ async function openSendAndClose(client, invoices, options) {
|
|
|
5532
6193
|
}
|
|
5533
6194
|
|
|
5534
6195
|
// src/workflows/batch-session-workflow.ts
|
|
6196
|
+
init_document_structures();
|
|
5535
6197
|
async function uploadBatch(client, zipData, options) {
|
|
6198
|
+
if (options?.parallelism !== void 0 && (!Number.isInteger(options.parallelism) || options.parallelism < 1)) {
|
|
6199
|
+
throw new Error("parallelism must be a positive integer");
|
|
6200
|
+
}
|
|
5536
6201
|
await client.crypto.init();
|
|
5537
6202
|
if (options?.validate) {
|
|
5538
6203
|
const { unzip: unzip2 } = await Promise.resolve().then(() => (init_zip(), zip_exports));
|
|
@@ -5575,7 +6240,7 @@ async function uploadBatch(client, zipData, options) {
|
|
|
5575
6240
|
},
|
|
5576
6241
|
ordinalNumber: i + 1
|
|
5577
6242
|
}));
|
|
5578
|
-
await client.batchSession.sendParts(openResp, sendingParts);
|
|
6243
|
+
await client.batchSession.sendParts(openResp, sendingParts, options?.parallelism);
|
|
5579
6244
|
await client.batchSession.closeSession(openResp.referenceNumber);
|
|
5580
6245
|
const result = await pollUntil(
|
|
5581
6246
|
() => client.sessionStatus.getSessionStatus(openResp.referenceNumber),
|
|
@@ -5596,6 +6261,9 @@ async function uploadBatch(client, zipData, options) {
|
|
|
5596
6261
|
};
|
|
5597
6262
|
}
|
|
5598
6263
|
async function uploadBatchStream(client, zipStreamFactory, zipSize, options) {
|
|
6264
|
+
if (options?.parallelism !== void 0 && (!Number.isInteger(options.parallelism) || options.parallelism < 1)) {
|
|
6265
|
+
throw new Error("parallelism must be a positive integer");
|
|
6266
|
+
}
|
|
5599
6267
|
await client.crypto.init();
|
|
5600
6268
|
const encData = client.crypto.getEncryptionData();
|
|
5601
6269
|
const formCode = options?.formCode ?? DEFAULT_FORM_CODE;
|
|
@@ -5617,7 +6285,7 @@ async function uploadBatchStream(client, zipStreamFactory, zipSize, options) {
|
|
|
5617
6285
|
},
|
|
5618
6286
|
options?.upoVersion
|
|
5619
6287
|
);
|
|
5620
|
-
await client.batchSession.sendPartsWithStream(openResp, streamParts);
|
|
6288
|
+
await client.batchSession.sendPartsWithStream(openResp, streamParts, options?.parallelism);
|
|
5621
6289
|
await client.batchSession.closeSession(openResp.referenceNumber);
|
|
5622
6290
|
const result = await pollUntil(
|
|
5623
6291
|
() => client.sessionStatus.getSessionStatus(openResp.referenceNumber),
|
|
@@ -5692,6 +6360,7 @@ async function doExport(client, filters, options) {
|
|
|
5692
6360
|
url: p.url,
|
|
5693
6361
|
method: p.method,
|
|
5694
6362
|
partSize: p.partSize,
|
|
6363
|
+
partHash: p.partHash,
|
|
5695
6364
|
encryptedPartSize: p.encryptedPartSize,
|
|
5696
6365
|
encryptedPartHash: p.encryptedPartHash,
|
|
5697
6366
|
expirationDate: p.expirationDate
|
|
@@ -5717,6 +6386,9 @@ async function exportAndDownload(client, filters, options) {
|
|
|
5717
6386
|
throw new Error(`Download failed for part ${part.ordinalNumber}: HTTP ${resp.status}`);
|
|
5718
6387
|
}
|
|
5719
6388
|
const encryptedData = new Uint8Array(await resp.arrayBuffer());
|
|
6389
|
+
if (options?.verifyHash !== false && !verifyHash(encryptedData, part.encryptedPartHash)) {
|
|
6390
|
+
throw new Error(`Hash mismatch for export part ${part.ordinalNumber}`);
|
|
6391
|
+
}
|
|
5720
6392
|
const decrypted = client.crypto.decryptAES256(encryptedData, encData.cipherKey, encData.cipherIv);
|
|
5721
6393
|
decryptedParts.push(decrypted);
|
|
5722
6394
|
}
|
|
@@ -5789,6 +6461,9 @@ async function incrementalExportAndDownload(client, options) {
|
|
|
5789
6461
|
throw new Error(`Download failed for part ${part.ordinalNumber}: HTTP ${resp.status}`);
|
|
5790
6462
|
}
|
|
5791
6463
|
const encryptedData = new Uint8Array(await resp.arrayBuffer());
|
|
6464
|
+
if (options.verifyHash !== false && !verifyHash(encryptedData, part.encryptedPartHash)) {
|
|
6465
|
+
throw new Error(`Hash mismatch for export part ${part.ordinalNumber}`);
|
|
6466
|
+
}
|
|
5792
6467
|
const decrypted = client.crypto.decryptAES256(encryptedData, encData.cipherKey, encData.cipherIv);
|
|
5793
6468
|
decryptedParts.push(decrypted);
|
|
5794
6469
|
}
|
|
@@ -5952,7 +6627,141 @@ async function authenticateWithPkcs12(client, options) {
|
|
|
5952
6627
|
});
|
|
5953
6628
|
}
|
|
5954
6629
|
|
|
6630
|
+
// src/offline/index.ts
|
|
6631
|
+
init_deadline();
|
|
6632
|
+
init_holidays();
|
|
6633
|
+
|
|
6634
|
+
// src/offline/storage.ts
|
|
6635
|
+
function matchesFilter(invoice, filter) {
|
|
6636
|
+
if (filter.status !== void 0) {
|
|
6637
|
+
const statuses = Array.isArray(filter.status) ? filter.status : [filter.status];
|
|
6638
|
+
if (!statuses.includes(invoice.status)) return false;
|
|
6639
|
+
}
|
|
6640
|
+
if (filter.mode !== void 0 && invoice.mode !== filter.mode) return false;
|
|
6641
|
+
if (filter.sellerNip !== void 0 && invoice.sellerNip !== filter.sellerNip) return false;
|
|
6642
|
+
if (filter.expiringBefore !== void 0) {
|
|
6643
|
+
const cutoff = typeof filter.expiringBefore === "string" ? new Date(filter.expiringBefore).getTime() : filter.expiringBefore.getTime();
|
|
6644
|
+
if (new Date(invoice.submitBy).getTime() >= cutoff) return false;
|
|
6645
|
+
}
|
|
6646
|
+
return true;
|
|
6647
|
+
}
|
|
6648
|
+
var InMemoryOfflineInvoiceStorage = class {
|
|
6649
|
+
store = /* @__PURE__ */ new Map();
|
|
6650
|
+
async save(invoice) {
|
|
6651
|
+
this.store.set(invoice.id, JSON.parse(JSON.stringify(invoice)));
|
|
6652
|
+
}
|
|
6653
|
+
async get(id) {
|
|
6654
|
+
const entry = this.store.get(id);
|
|
6655
|
+
return entry ? JSON.parse(JSON.stringify(entry)) : null;
|
|
6656
|
+
}
|
|
6657
|
+
async list(filter) {
|
|
6658
|
+
const all = [...this.store.values()];
|
|
6659
|
+
if (!filter) return all.map((i) => JSON.parse(JSON.stringify(i)));
|
|
6660
|
+
return all.filter((i) => matchesFilter(i, filter)).map((i) => JSON.parse(JSON.stringify(i)));
|
|
6661
|
+
}
|
|
6662
|
+
async update(id, updates) {
|
|
6663
|
+
const existing = this.store.get(id);
|
|
6664
|
+
if (!existing) throw new Error(`Offline invoice not found: ${id}`);
|
|
6665
|
+
this.store.set(id, JSON.parse(JSON.stringify({ ...existing, ...updates })));
|
|
6666
|
+
}
|
|
6667
|
+
async delete(id) {
|
|
6668
|
+
this.store.delete(id);
|
|
6669
|
+
}
|
|
6670
|
+
};
|
|
6671
|
+
|
|
6672
|
+
// src/offline/file-storage.ts
|
|
6673
|
+
import * as fs2 from "fs/promises";
|
|
6674
|
+
import * as path from "path";
|
|
6675
|
+
import * as os from "os";
|
|
6676
|
+
function resolveDir(dir) {
|
|
6677
|
+
if (dir === "~" || dir.startsWith("~/")) {
|
|
6678
|
+
return path.join(os.homedir(), dir.slice(1));
|
|
6679
|
+
}
|
|
6680
|
+
return dir;
|
|
6681
|
+
}
|
|
6682
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
6683
|
+
function validateId(id) {
|
|
6684
|
+
if (!UUID_RE.test(id)) {
|
|
6685
|
+
throw new Error(`Invalid invoice ID: ${id}`);
|
|
6686
|
+
}
|
|
6687
|
+
}
|
|
6688
|
+
var FileOfflineInvoiceStorage = class {
|
|
6689
|
+
dir;
|
|
6690
|
+
constructor(directory) {
|
|
6691
|
+
this.dir = resolveDir(directory ?? "~/.ksef/offline");
|
|
6692
|
+
}
|
|
6693
|
+
async ensureDir() {
|
|
6694
|
+
await fs2.mkdir(this.dir, { recursive: true });
|
|
6695
|
+
}
|
|
6696
|
+
filePath(id) {
|
|
6697
|
+
validateId(id);
|
|
6698
|
+
return path.join(this.dir, `${id}.json`);
|
|
6699
|
+
}
|
|
6700
|
+
async save(invoice) {
|
|
6701
|
+
await this.ensureDir();
|
|
6702
|
+
const file = this.filePath(invoice.id);
|
|
6703
|
+
const tmp = `${file}.tmp`;
|
|
6704
|
+
await fs2.writeFile(tmp, JSON.stringify(invoice, null, 2));
|
|
6705
|
+
await fs2.rename(tmp, file);
|
|
6706
|
+
}
|
|
6707
|
+
async get(id) {
|
|
6708
|
+
const file = this.filePath(id);
|
|
6709
|
+
try {
|
|
6710
|
+
return JSON.parse(await fs2.readFile(file, "utf-8"));
|
|
6711
|
+
} catch (err) {
|
|
6712
|
+
if (err instanceof Error && "code" in err && err.code === "ENOENT") {
|
|
6713
|
+
return null;
|
|
6714
|
+
}
|
|
6715
|
+
console.warn(`Warning: Failed to read offline invoice ${id}: ${err instanceof Error ? err.message : String(err)}`);
|
|
6716
|
+
return null;
|
|
6717
|
+
}
|
|
6718
|
+
}
|
|
6719
|
+
async list(filter) {
|
|
6720
|
+
let files;
|
|
6721
|
+
try {
|
|
6722
|
+
files = (await fs2.readdir(this.dir)).filter((f) => f.endsWith(".json"));
|
|
6723
|
+
} catch {
|
|
6724
|
+
return [];
|
|
6725
|
+
}
|
|
6726
|
+
const results = [];
|
|
6727
|
+
for (const file of files) {
|
|
6728
|
+
try {
|
|
6729
|
+
const data = JSON.parse(
|
|
6730
|
+
await fs2.readFile(path.join(this.dir, file), "utf-8")
|
|
6731
|
+
);
|
|
6732
|
+
if (!filter || matchesFilter(data, filter)) {
|
|
6733
|
+
results.push(data);
|
|
6734
|
+
}
|
|
6735
|
+
} catch (err) {
|
|
6736
|
+
console.warn(`Warning: Skipping corrupt offline invoice file ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
6737
|
+
}
|
|
6738
|
+
}
|
|
6739
|
+
return results;
|
|
6740
|
+
}
|
|
6741
|
+
/**
|
|
6742
|
+
* Update invoice metadata (read-modify-write).
|
|
6743
|
+
*
|
|
6744
|
+
* Note: No file locking — concurrent updates to the same ID may cause
|
|
6745
|
+
* lost writes. Acceptable for CLI (single process). Library consumers
|
|
6746
|
+
* running parallel operations should use external locking.
|
|
6747
|
+
*/
|
|
6748
|
+
async update(id, updates) {
|
|
6749
|
+
const existing = await this.get(id);
|
|
6750
|
+
if (!existing) throw new Error(`Offline invoice not found: ${id}`);
|
|
6751
|
+
await this.save({ ...existing, ...updates });
|
|
6752
|
+
}
|
|
6753
|
+
async delete(id) {
|
|
6754
|
+
const file = this.filePath(id);
|
|
6755
|
+
try {
|
|
6756
|
+
await fs2.unlink(file);
|
|
6757
|
+
} catch (e) {
|
|
6758
|
+
if (e instanceof Error && "code" in e && e.code !== "ENOENT") throw e;
|
|
6759
|
+
}
|
|
6760
|
+
}
|
|
6761
|
+
};
|
|
6762
|
+
|
|
5955
6763
|
// src/index.ts
|
|
6764
|
+
init_offline_invoice_workflow();
|
|
5956
6765
|
init_client();
|
|
5957
6766
|
export {
|
|
5958
6767
|
ActiveSessionsService,
|
|
@@ -5982,8 +6791,10 @@ export {
|
|
|
5982
6791
|
FORM_CODES,
|
|
5983
6792
|
FORM_CODE_KEYS,
|
|
5984
6793
|
FileHwmStore,
|
|
6794
|
+
FileOfflineInvoiceStorage,
|
|
5985
6795
|
INVOICE_TYPES_BY_SYSTEM_CODE,
|
|
5986
6796
|
InMemoryHwmStore,
|
|
6797
|
+
InMemoryOfflineInvoiceStorage,
|
|
5987
6798
|
InternalId,
|
|
5988
6799
|
InvoiceDownloadService,
|
|
5989
6800
|
InvoiceQueryFilterBuilder,
|
|
@@ -6007,6 +6818,7 @@ export {
|
|
|
6007
6818
|
LimitsService,
|
|
6008
6819
|
Nip,
|
|
6009
6820
|
NipVatUe,
|
|
6821
|
+
OfflineInvoiceWorkflow,
|
|
6010
6822
|
OnlineSessionService,
|
|
6011
6823
|
PERMISSION_DESCRIPTION_MAX_LENGTH,
|
|
6012
6824
|
PERMISSION_DESCRIPTION_MIN_LENGTH,
|
|
@@ -6036,6 +6848,7 @@ export {
|
|
|
6036
6848
|
UpoVersion,
|
|
6037
6849
|
VatUe,
|
|
6038
6850
|
VerificationLinkService,
|
|
6851
|
+
addBusinessDays,
|
|
6039
6852
|
authenticateWithCertificate,
|
|
6040
6853
|
authenticateWithExternalSignature,
|
|
6041
6854
|
authenticateWithPkcs12,
|
|
@@ -6043,6 +6856,7 @@ export {
|
|
|
6043
6856
|
batchValidationDetails,
|
|
6044
6857
|
buildUnsignedAuthTokenRequestXml,
|
|
6045
6858
|
calculateBackoff,
|
|
6859
|
+
calculateOfflineDeadline,
|
|
6046
6860
|
createZip,
|
|
6047
6861
|
decodeJwtPayload,
|
|
6048
6862
|
deduplicateByKsefNumber,
|
|
@@ -6052,9 +6866,16 @@ export {
|
|
|
6052
6866
|
defaultTransport,
|
|
6053
6867
|
exportAndDownload,
|
|
6054
6868
|
exportInvoices,
|
|
6869
|
+
extendDeadlineForMaintenance,
|
|
6870
|
+
extractInvoiceFields,
|
|
6871
|
+
getDefaultReason,
|
|
6055
6872
|
getEffectiveStartDate,
|
|
6056
6873
|
getFormCode,
|
|
6874
|
+
getPolishHolidays,
|
|
6875
|
+
getTimeUntilDeadline,
|
|
6057
6876
|
incrementalExportAndDownload,
|
|
6877
|
+
isExpired,
|
|
6878
|
+
isPolishHoliday,
|
|
6058
6879
|
isRetryableError,
|
|
6059
6880
|
isRetryableStatus,
|
|
6060
6881
|
isValidBase64,
|
|
@@ -6072,6 +6893,7 @@ export {
|
|
|
6072
6893
|
isValidReferenceNumber,
|
|
6073
6894
|
isValidSha256Base64,
|
|
6074
6895
|
isValidVatUe,
|
|
6896
|
+
nextBusinessDay,
|
|
6075
6897
|
openOnlineSession,
|
|
6076
6898
|
openSendAndClose,
|
|
6077
6899
|
parseFormCode,
|
|
@@ -6080,6 +6902,9 @@ export {
|
|
|
6080
6902
|
parseUpoXml,
|
|
6081
6903
|
pollUntil,
|
|
6082
6904
|
resolveOptions,
|
|
6905
|
+
resumeOnlineSession,
|
|
6906
|
+
runWithConcurrency,
|
|
6907
|
+
sha256Base642 as sha256Base64,
|
|
6083
6908
|
sleep,
|
|
6084
6909
|
unzip,
|
|
6085
6910
|
updateContinuationPoint,
|
|
@@ -6094,6 +6919,7 @@ export {
|
|
|
6094
6919
|
validatePresignedUrl,
|
|
6095
6920
|
validateSchema,
|
|
6096
6921
|
validateWellFormedness,
|
|
6922
|
+
verifyHash,
|
|
6097
6923
|
xmlToObject
|
|
6098
6924
|
};
|
|
6099
6925
|
//# sourceMappingURL=index.js.map
|