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.cjs
CHANGED
|
@@ -320,21 +320,21 @@ var init_rest_request = __esm({
|
|
|
320
320
|
_query = [];
|
|
321
321
|
_presigned = false;
|
|
322
322
|
_skipAuthRetry = false;
|
|
323
|
-
constructor(method,
|
|
323
|
+
constructor(method, path2) {
|
|
324
324
|
this.method = method;
|
|
325
|
-
this.path =
|
|
325
|
+
this.path = path2;
|
|
326
326
|
}
|
|
327
|
-
static get(
|
|
328
|
-
return new _RestRequest("GET",
|
|
327
|
+
static get(path2) {
|
|
328
|
+
return new _RestRequest("GET", path2);
|
|
329
329
|
}
|
|
330
|
-
static post(
|
|
331
|
-
return new _RestRequest("POST",
|
|
330
|
+
static post(path2) {
|
|
331
|
+
return new _RestRequest("POST", path2);
|
|
332
332
|
}
|
|
333
|
-
static put(
|
|
334
|
-
return new _RestRequest("PUT",
|
|
333
|
+
static put(path2) {
|
|
334
|
+
return new _RestRequest("PUT", path2);
|
|
335
335
|
}
|
|
336
|
-
static delete(
|
|
337
|
-
return new _RestRequest("DELETE",
|
|
336
|
+
static delete(path2) {
|
|
337
|
+
return new _RestRequest("DELETE", path2);
|
|
338
338
|
}
|
|
339
339
|
body(data) {
|
|
340
340
|
this._body = data;
|
|
@@ -654,9 +654,9 @@ var init_rest_client = __esm({
|
|
|
654
654
|
return response;
|
|
655
655
|
}
|
|
656
656
|
buildUrl(request) {
|
|
657
|
-
const
|
|
657
|
+
const path2 = this.routeBuilder.build(request.path);
|
|
658
658
|
const base = this.options.baseUrl;
|
|
659
|
-
const url = new URL(`${base}${
|
|
659
|
+
const url = new URL(`${base}${path2}`);
|
|
660
660
|
const query = request.getQuery();
|
|
661
661
|
for (const [key, value] of query) {
|
|
662
662
|
url.searchParams.append(key, value);
|
|
@@ -977,7 +977,9 @@ function isValidVatUe(value) {
|
|
|
977
977
|
return VatUe.test(value);
|
|
978
978
|
}
|
|
979
979
|
function isValidNipVatUe(value) {
|
|
980
|
-
|
|
980
|
+
if (!NipVatUe.test(value)) return false;
|
|
981
|
+
const nip = value.split("-")[0];
|
|
982
|
+
return isValidNip(nip);
|
|
981
983
|
}
|
|
982
984
|
function isValidInternalId(value) {
|
|
983
985
|
return InternalId.test(value);
|
|
@@ -2668,11 +2670,11 @@ async function validateSchema(xml, options, _parsed) {
|
|
|
2668
2670
|
const prefix = rootElement ? `/${rootElement}/` : "/";
|
|
2669
2671
|
const validationErrors = result.error.issues.map((issue) => {
|
|
2670
2672
|
const zodPath = issue.path.join("/");
|
|
2671
|
-
const
|
|
2673
|
+
const path2 = zodPath ? `${prefix}${zodPath}` : rootElement ? `/${rootElement}` : void 0;
|
|
2672
2674
|
return {
|
|
2673
2675
|
code: mapZodErrorCode(issue),
|
|
2674
2676
|
message: issue.message,
|
|
2675
|
-
path
|
|
2677
|
+
path: path2
|
|
2676
2678
|
};
|
|
2677
2679
|
});
|
|
2678
2680
|
return { valid: false, schemaType, errors: validationErrors };
|
|
@@ -2712,9 +2714,9 @@ function validateBusinessRules(xml, _parsed) {
|
|
|
2712
2714
|
collectDateErrors(object, rootElement, errors);
|
|
2713
2715
|
return { valid: errors.length === 0, schemaType, errors };
|
|
2714
2716
|
}
|
|
2715
|
-
function collectNipPeselErrors(obj,
|
|
2717
|
+
function collectNipPeselErrors(obj, path2, errors) {
|
|
2716
2718
|
for (const [key, value] of Object.entries(obj)) {
|
|
2717
|
-
const currentPath =
|
|
2719
|
+
const currentPath = path2 ? `${path2}/${key}` : key;
|
|
2718
2720
|
if (key === "NIP" && typeof value === "string") {
|
|
2719
2721
|
if (!isValidNip(value)) {
|
|
2720
2722
|
errors.push({
|
|
@@ -2793,6 +2795,88 @@ var init_invoice_validator = __esm({
|
|
|
2793
2795
|
}
|
|
2794
2796
|
});
|
|
2795
2797
|
|
|
2798
|
+
// src/models/document-structures/types.ts
|
|
2799
|
+
var SystemCode, FORM_CODES, DEFAULT_FORM_CODE, INVOICE_TYPES_BY_SYSTEM_CODE, FORM_CODE_KEYS;
|
|
2800
|
+
var init_types = __esm({
|
|
2801
|
+
"src/models/document-structures/types.ts"() {
|
|
2802
|
+
"use strict";
|
|
2803
|
+
SystemCode = {
|
|
2804
|
+
FA_2: "FA (2)",
|
|
2805
|
+
FA_3: "FA (3)",
|
|
2806
|
+
PEF_3: "PEF (3)",
|
|
2807
|
+
PEF_KOR_3: "PEF_KOR (3)",
|
|
2808
|
+
FA_RR_1: "FA_RR (1)"
|
|
2809
|
+
};
|
|
2810
|
+
FORM_CODES = {
|
|
2811
|
+
FA_2: { systemCode: "FA (2)", schemaVersion: "1-0E", value: "FA" },
|
|
2812
|
+
FA_3: { systemCode: "FA (3)", schemaVersion: "1-0E", value: "FA" },
|
|
2813
|
+
PEF_3: { systemCode: "PEF (3)", schemaVersion: "2-1", value: "PEF" },
|
|
2814
|
+
PEF_KOR_3: { systemCode: "PEF_KOR (3)", schemaVersion: "2-1", value: "PEF" },
|
|
2815
|
+
FA_RR_1_LEGACY: { systemCode: "FA_RR (1)", schemaVersion: "1-0E", value: "RR" },
|
|
2816
|
+
FA_RR_1_TRANSITION: { systemCode: "FA_RR (1)", schemaVersion: "1-1E", value: "RR" },
|
|
2817
|
+
FA_RR_1: { systemCode: "FA_RR (1)", schemaVersion: "1-1E", value: "FA_RR" }
|
|
2818
|
+
};
|
|
2819
|
+
DEFAULT_FORM_CODE = FORM_CODES.FA_3;
|
|
2820
|
+
INVOICE_TYPES_BY_SYSTEM_CODE = {
|
|
2821
|
+
[SystemCode.FA_2]: ["Vat", "Zal", "Kor", "Roz", "Upr", "KorZal", "KorRoz"],
|
|
2822
|
+
[SystemCode.FA_3]: ["Vat", "Zal", "Kor", "Roz", "Upr", "KorZal", "KorRoz"],
|
|
2823
|
+
[SystemCode.PEF_3]: ["VatPef", "VatPefSp", "KorPef"],
|
|
2824
|
+
[SystemCode.PEF_KOR_3]: ["KorPef"],
|
|
2825
|
+
[SystemCode.FA_RR_1]: ["VatRr", "KorVatRr"]
|
|
2826
|
+
};
|
|
2827
|
+
FORM_CODE_KEYS = {
|
|
2828
|
+
FA2: FORM_CODES.FA_2,
|
|
2829
|
+
FA3: FORM_CODES.FA_3,
|
|
2830
|
+
PEF3: FORM_CODES.PEF_3,
|
|
2831
|
+
PEFKOR3: FORM_CODES.PEF_KOR_3,
|
|
2832
|
+
FARR1: FORM_CODES.FA_RR_1
|
|
2833
|
+
};
|
|
2834
|
+
}
|
|
2835
|
+
});
|
|
2836
|
+
|
|
2837
|
+
// src/models/document-structures/helpers.ts
|
|
2838
|
+
function getFormCode(systemCode) {
|
|
2839
|
+
return SYSTEM_CODE_TO_FORM_CODE[systemCode];
|
|
2840
|
+
}
|
|
2841
|
+
function parseFormCode(raw) {
|
|
2842
|
+
const match = ALL_FORM_CODES.find(
|
|
2843
|
+
(fc) => fc.systemCode === raw.systemCode && fc.schemaVersion === raw.schemaVersion && fc.value === raw.value
|
|
2844
|
+
);
|
|
2845
|
+
return match ?? raw;
|
|
2846
|
+
}
|
|
2847
|
+
function validateFormCodeForSession(formCode, sessionType) {
|
|
2848
|
+
if (sessionType === "online") return true;
|
|
2849
|
+
return !BATCH_DISALLOWED_SYSTEM_CODES.has(formCode.systemCode);
|
|
2850
|
+
}
|
|
2851
|
+
var SYSTEM_CODE_TO_FORM_CODE, ALL_FORM_CODES, BATCH_DISALLOWED_SYSTEM_CODES;
|
|
2852
|
+
var init_helpers = __esm({
|
|
2853
|
+
"src/models/document-structures/helpers.ts"() {
|
|
2854
|
+
"use strict";
|
|
2855
|
+
init_types();
|
|
2856
|
+
SYSTEM_CODE_TO_FORM_CODE = {
|
|
2857
|
+
[SystemCode.FA_2]: FORM_CODES.FA_2,
|
|
2858
|
+
[SystemCode.FA_3]: FORM_CODES.FA_3,
|
|
2859
|
+
[SystemCode.PEF_3]: FORM_CODES.PEF_3,
|
|
2860
|
+
[SystemCode.PEF_KOR_3]: FORM_CODES.PEF_KOR_3,
|
|
2861
|
+
[SystemCode.FA_RR_1]: FORM_CODES.FA_RR_1
|
|
2862
|
+
};
|
|
2863
|
+
ALL_FORM_CODES = Object.values(FORM_CODES);
|
|
2864
|
+
BATCH_DISALLOWED_SYSTEM_CODES = /* @__PURE__ */ new Set([
|
|
2865
|
+
SystemCode.PEF_3,
|
|
2866
|
+
SystemCode.PEF_KOR_3
|
|
2867
|
+
]);
|
|
2868
|
+
}
|
|
2869
|
+
});
|
|
2870
|
+
|
|
2871
|
+
// src/models/document-structures/index.ts
|
|
2872
|
+
var init_document_structures = __esm({
|
|
2873
|
+
"src/models/document-structures/index.ts"() {
|
|
2874
|
+
"use strict";
|
|
2875
|
+
init_types();
|
|
2876
|
+
init_helpers();
|
|
2877
|
+
}
|
|
2878
|
+
});
|
|
2879
|
+
|
|
2796
2880
|
// src/services/auth.ts
|
|
2797
2881
|
var AuthService;
|
|
2798
2882
|
var init_auth = __esm({
|
|
@@ -2908,6 +2992,32 @@ var init_online_session = __esm({
|
|
|
2908
2992
|
}
|
|
2909
2993
|
});
|
|
2910
2994
|
|
|
2995
|
+
// src/utils/concurrency.ts
|
|
2996
|
+
async function runWithConcurrency(tasks, parallelism) {
|
|
2997
|
+
if (tasks.length === 0) return;
|
|
2998
|
+
const limit = Math.max(1, Math.min(parallelism, tasks.length));
|
|
2999
|
+
const controller = new AbortController();
|
|
3000
|
+
let index = 0;
|
|
3001
|
+
const workers = Array.from({ length: limit }, async () => {
|
|
3002
|
+
while (index < tasks.length && !controller.signal.aborted) {
|
|
3003
|
+
const current = index;
|
|
3004
|
+
index += 1;
|
|
3005
|
+
try {
|
|
3006
|
+
await tasks[current](controller.signal);
|
|
3007
|
+
} catch (err) {
|
|
3008
|
+
controller.abort();
|
|
3009
|
+
throw err;
|
|
3010
|
+
}
|
|
3011
|
+
}
|
|
3012
|
+
});
|
|
3013
|
+
await Promise.all(workers);
|
|
3014
|
+
}
|
|
3015
|
+
var init_concurrency = __esm({
|
|
3016
|
+
"src/utils/concurrency.ts"() {
|
|
3017
|
+
"use strict";
|
|
3018
|
+
}
|
|
3019
|
+
});
|
|
3020
|
+
|
|
2911
3021
|
// src/services/batch-session.ts
|
|
2912
3022
|
var BatchSessionService;
|
|
2913
3023
|
var init_batch_session = __esm({
|
|
@@ -2916,6 +3026,7 @@ var init_batch_session = __esm({
|
|
|
2916
3026
|
init_ksef_feature();
|
|
2917
3027
|
init_rest_request();
|
|
2918
3028
|
init_routes();
|
|
3029
|
+
init_concurrency();
|
|
2919
3030
|
BatchSessionService = class {
|
|
2920
3031
|
restClient;
|
|
2921
3032
|
constructor(restClient) {
|
|
@@ -2929,12 +3040,10 @@ var init_batch_session = __esm({
|
|
|
2929
3040
|
const response = await this.restClient.execute(req);
|
|
2930
3041
|
return response.body;
|
|
2931
3042
|
}
|
|
2932
|
-
async sendParts(openResponse, parts) {
|
|
2933
|
-
const
|
|
2934
|
-
const tasks = parts.map(async (
|
|
2935
|
-
const uploadReq =
|
|
2936
|
-
(r) => r.ordinalNumber === part.ordinalNumber
|
|
2937
|
-
);
|
|
3043
|
+
async sendParts(openResponse, parts, parallelism) {
|
|
3044
|
+
const uploadMap = new Map(openResponse.partUploadRequests.map((r) => [r.ordinalNumber, r]));
|
|
3045
|
+
const tasks = parts.map((part) => async (signal) => {
|
|
3046
|
+
const uploadReq = uploadMap.get(part.ordinalNumber);
|
|
2938
3047
|
if (!uploadReq) {
|
|
2939
3048
|
throw new Error(`No upload request found for part ${part.ordinalNumber}`);
|
|
2940
3049
|
}
|
|
@@ -2942,25 +3051,38 @@ var init_batch_session = __esm({
|
|
|
2942
3051
|
for (const [k, v] of Object.entries(uploadReq.headers)) {
|
|
2943
3052
|
if (v != null) headers[k] = v;
|
|
2944
3053
|
}
|
|
2945
|
-
await fetch(uploadReq.url, {
|
|
3054
|
+
const resp = await fetch(uploadReq.url, {
|
|
2946
3055
|
method: uploadReq.method,
|
|
2947
3056
|
headers,
|
|
2948
|
-
body: part.data
|
|
3057
|
+
body: part.data,
|
|
3058
|
+
signal
|
|
2949
3059
|
});
|
|
3060
|
+
if (!resp.ok) {
|
|
3061
|
+
const body = await resp.text().catch(() => "");
|
|
3062
|
+
throw new Error(
|
|
3063
|
+
`Upload failed for part ${part.ordinalNumber}: HTTP ${resp.status}${body ? ` \u2014 ${body}` : ""}`
|
|
3064
|
+
);
|
|
3065
|
+
}
|
|
2950
3066
|
});
|
|
2951
|
-
|
|
3067
|
+
if (parallelism !== void 0) {
|
|
3068
|
+
await runWithConcurrency(tasks, parallelism);
|
|
3069
|
+
} else {
|
|
3070
|
+
const ac = new AbortController();
|
|
3071
|
+
await Promise.all(tasks.map((t) => t(ac.signal).catch((err) => {
|
|
3072
|
+
ac.abort();
|
|
3073
|
+
throw err;
|
|
3074
|
+
})));
|
|
3075
|
+
}
|
|
2952
3076
|
}
|
|
2953
3077
|
/**
|
|
2954
|
-
* Upload parts
|
|
2955
|
-
*
|
|
2956
|
-
*
|
|
3078
|
+
* Upload parts using streaming bodies (`duplex: 'half'`).
|
|
3079
|
+
* By default uploads sequentially to avoid backpressure issues.
|
|
3080
|
+
* Pass `parallelism` to enable bounded concurrent uploads.
|
|
2957
3081
|
*/
|
|
2958
|
-
async sendPartsWithStream(openResponse, parts) {
|
|
2959
|
-
const
|
|
2960
|
-
|
|
2961
|
-
const uploadReq =
|
|
2962
|
-
(r) => r.ordinalNumber === part.ordinalNumber
|
|
2963
|
-
);
|
|
3082
|
+
async sendPartsWithStream(openResponse, parts, parallelism) {
|
|
3083
|
+
const uploadMap = new Map(openResponse.partUploadRequests.map((r) => [r.ordinalNumber, r]));
|
|
3084
|
+
const uploadPart = async (part, signal) => {
|
|
3085
|
+
const uploadReq = uploadMap.get(part.ordinalNumber);
|
|
2964
3086
|
if (!uploadReq) {
|
|
2965
3087
|
throw new Error(`No upload request found for part ${part.ordinalNumber}`);
|
|
2966
3088
|
}
|
|
@@ -2972,11 +3094,23 @@ var init_batch_session = __esm({
|
|
|
2972
3094
|
method: uploadReq.method,
|
|
2973
3095
|
headers,
|
|
2974
3096
|
body: part.dataStream,
|
|
3097
|
+
signal,
|
|
2975
3098
|
// @ts-expect-error -- Node 18+ undici supports duplex for streaming body
|
|
2976
3099
|
duplex: "half"
|
|
2977
3100
|
});
|
|
2978
3101
|
if (!resp.ok) {
|
|
2979
|
-
|
|
3102
|
+
const body = await resp.text().catch(() => "");
|
|
3103
|
+
throw new Error(
|
|
3104
|
+
`Upload failed for part ${part.ordinalNumber}: HTTP ${resp.status}${body ? ` \u2014 ${body}` : ""}`
|
|
3105
|
+
);
|
|
3106
|
+
}
|
|
3107
|
+
};
|
|
3108
|
+
if (parallelism !== void 0) {
|
|
3109
|
+
await runWithConcurrency(parts.map((p) => (signal) => uploadPart(p, signal)), parallelism);
|
|
3110
|
+
} else {
|
|
3111
|
+
const { signal } = new AbortController();
|
|
3112
|
+
for (const part of parts) {
|
|
3113
|
+
await uploadPart(part, signal);
|
|
2980
3114
|
}
|
|
2981
3115
|
}
|
|
2982
3116
|
}
|
|
@@ -3089,7 +3223,10 @@ var init_invoice_download = __esm({
|
|
|
3089
3223
|
async getInvoice(ksefNumber) {
|
|
3090
3224
|
const req = RestRequest.get(Routes.Invoices.byKsefNumber(ksefNumber));
|
|
3091
3225
|
const response = await this.restClient.executeRaw(req);
|
|
3092
|
-
return
|
|
3226
|
+
return {
|
|
3227
|
+
xml: new TextDecoder().decode(response.body),
|
|
3228
|
+
hash: response.headers.get("x-ms-meta-hash") ?? void 0
|
|
3229
|
+
};
|
|
3093
3230
|
}
|
|
3094
3231
|
async queryInvoiceMetadata(filters, pageOffset, pageSize, sortOrder) {
|
|
3095
3232
|
const req = RestRequest.post(Routes.Invoices.queryMetadata).body(filters);
|
|
@@ -3358,20 +3495,20 @@ var init_lighthouse = __esm({
|
|
|
3358
3495
|
this.lighthouseUrl = options.lighthouseUrl;
|
|
3359
3496
|
this.timeout = options.timeout;
|
|
3360
3497
|
}
|
|
3361
|
-
async fetchJson(
|
|
3498
|
+
async fetchJson(path2) {
|
|
3362
3499
|
if (!this.lighthouseUrl) {
|
|
3363
3500
|
throw new KSeFError(
|
|
3364
3501
|
"Lighthouse API is not available for the DEMO environment. Use TEST or PROD instead."
|
|
3365
3502
|
);
|
|
3366
3503
|
}
|
|
3367
|
-
const response = await fetch(`${this.lighthouseUrl}${
|
|
3504
|
+
const response = await fetch(`${this.lighthouseUrl}${path2}`, {
|
|
3368
3505
|
headers: { Accept: "application/json" },
|
|
3369
3506
|
signal: AbortSignal.timeout(this.timeout)
|
|
3370
3507
|
});
|
|
3371
3508
|
if (!response.ok) {
|
|
3372
3509
|
const body = await response.text();
|
|
3373
3510
|
throw new KSeFError(
|
|
3374
|
-
`Lighthouse ${
|
|
3511
|
+
`Lighthouse ${path2} failed: HTTP ${response.status} \u2014 ${body}`
|
|
3375
3512
|
);
|
|
3376
3513
|
}
|
|
3377
3514
|
return await response.json();
|
|
@@ -4351,6 +4488,458 @@ var init_zip = __esm({
|
|
|
4351
4488
|
}
|
|
4352
4489
|
});
|
|
4353
4490
|
|
|
4491
|
+
// src/offline/holidays.ts
|
|
4492
|
+
function toDateKey(d) {
|
|
4493
|
+
return d.toISOString().slice(0, 10);
|
|
4494
|
+
}
|
|
4495
|
+
function dateKey(year, month, day) {
|
|
4496
|
+
return toDateKey(new Date(Date.UTC(year, month - 1, day)));
|
|
4497
|
+
}
|
|
4498
|
+
function addDays(d, n) {
|
|
4499
|
+
const result = new Date(d);
|
|
4500
|
+
result.setUTCDate(result.getUTCDate() + n);
|
|
4501
|
+
return result;
|
|
4502
|
+
}
|
|
4503
|
+
function computeEasterSunday(year) {
|
|
4504
|
+
const a = year % 19;
|
|
4505
|
+
const b = Math.floor(year / 100);
|
|
4506
|
+
const c = year % 100;
|
|
4507
|
+
const d = Math.floor(b / 4);
|
|
4508
|
+
const e = b % 4;
|
|
4509
|
+
const f = Math.floor((b + 8) / 25);
|
|
4510
|
+
const g = Math.floor((b - f + 1) / 3);
|
|
4511
|
+
const h = (19 * a + b - d - g + 15) % 30;
|
|
4512
|
+
const i = Math.floor(c / 4);
|
|
4513
|
+
const k = c % 4;
|
|
4514
|
+
const l = (32 + 2 * e + 2 * i - h - k) % 7;
|
|
4515
|
+
const m = Math.floor((a + 11 * h + 22 * l) / 451);
|
|
4516
|
+
const month = Math.floor((h + l - 7 * m + 114) / 31);
|
|
4517
|
+
const day = (h + l - 7 * m + 114) % 31 + 1;
|
|
4518
|
+
return new Date(Date.UTC(year, month - 1, day));
|
|
4519
|
+
}
|
|
4520
|
+
function getPolishHolidays(year) {
|
|
4521
|
+
const cached = holidayCache.get(year);
|
|
4522
|
+
if (cached) return cached;
|
|
4523
|
+
const easter = computeEasterSunday(year);
|
|
4524
|
+
const holidays = /* @__PURE__ */ new Set([
|
|
4525
|
+
// Fixed
|
|
4526
|
+
dateKey(year, 1, 1),
|
|
4527
|
+
dateKey(year, 1, 6),
|
|
4528
|
+
dateKey(year, 5, 1),
|
|
4529
|
+
dateKey(year, 5, 3),
|
|
4530
|
+
dateKey(year, 8, 15),
|
|
4531
|
+
dateKey(year, 11, 1),
|
|
4532
|
+
dateKey(year, 11, 11),
|
|
4533
|
+
dateKey(year, 12, 25),
|
|
4534
|
+
dateKey(year, 12, 26),
|
|
4535
|
+
// Wigilia — added by Dz.U. 2024 poz. 1965, effective from 2025
|
|
4536
|
+
...year >= 2025 ? [dateKey(year, 12, 24)] : [],
|
|
4537
|
+
// Moveable
|
|
4538
|
+
toDateKey(easter),
|
|
4539
|
+
toDateKey(addDays(easter, 1)),
|
|
4540
|
+
toDateKey(addDays(easter, 49)),
|
|
4541
|
+
toDateKey(addDays(easter, 60))
|
|
4542
|
+
]);
|
|
4543
|
+
holidayCache.set(year, holidays);
|
|
4544
|
+
return holidays;
|
|
4545
|
+
}
|
|
4546
|
+
function isPolishHoliday(date) {
|
|
4547
|
+
return getPolishHolidays(date.getUTCFullYear()).has(toDateKey(date));
|
|
4548
|
+
}
|
|
4549
|
+
var holidayCache;
|
|
4550
|
+
var init_holidays = __esm({
|
|
4551
|
+
"src/offline/holidays.ts"() {
|
|
4552
|
+
"use strict";
|
|
4553
|
+
holidayCache = /* @__PURE__ */ new Map();
|
|
4554
|
+
}
|
|
4555
|
+
});
|
|
4556
|
+
|
|
4557
|
+
// src/offline/deadline.ts
|
|
4558
|
+
function getDefaultReason(mode) {
|
|
4559
|
+
switch (mode) {
|
|
4560
|
+
case "offline24":
|
|
4561
|
+
return "PLANNED";
|
|
4562
|
+
case "offline":
|
|
4563
|
+
return "SYSTEM_UNAVAILABLE";
|
|
4564
|
+
case "awaryjny":
|
|
4565
|
+
return "EMERGENCY";
|
|
4566
|
+
case "awaria_calkowita":
|
|
4567
|
+
return "TOTAL_FAILURE";
|
|
4568
|
+
}
|
|
4569
|
+
}
|
|
4570
|
+
function nextBusinessDay(from) {
|
|
4571
|
+
const d = new Date(from);
|
|
4572
|
+
d.setUTCDate(d.getUTCDate() + 1);
|
|
4573
|
+
while (d.getUTCDay() === 0 || d.getUTCDay() === 6 || isPolishHoliday(d)) {
|
|
4574
|
+
d.setUTCDate(d.getUTCDate() + 1);
|
|
4575
|
+
}
|
|
4576
|
+
return d;
|
|
4577
|
+
}
|
|
4578
|
+
function addBusinessDays(from, days) {
|
|
4579
|
+
if (!Number.isInteger(days) || days < 0) {
|
|
4580
|
+
throw new Error(`days must be a non-negative integer, got ${days}`);
|
|
4581
|
+
}
|
|
4582
|
+
const d = new Date(from);
|
|
4583
|
+
let remaining = days;
|
|
4584
|
+
while (remaining > 0) {
|
|
4585
|
+
d.setUTCDate(d.getUTCDate() + 1);
|
|
4586
|
+
if (d.getUTCDay() !== 0 && d.getUTCDay() !== 6 && !isPolishHoliday(d)) {
|
|
4587
|
+
remaining--;
|
|
4588
|
+
}
|
|
4589
|
+
}
|
|
4590
|
+
return d;
|
|
4591
|
+
}
|
|
4592
|
+
function endOfDay(d) {
|
|
4593
|
+
const result = new Date(d);
|
|
4594
|
+
result.setUTCHours(23, 59, 59, 999);
|
|
4595
|
+
return result;
|
|
4596
|
+
}
|
|
4597
|
+
function getMaintenanceEndFallback(mw) {
|
|
4598
|
+
if (mw.endTime) {
|
|
4599
|
+
return new Date(mw.endTime);
|
|
4600
|
+
}
|
|
4601
|
+
const start = new Date(mw.startTime);
|
|
4602
|
+
start.setUTCDate(start.getUTCDate() + 7);
|
|
4603
|
+
return start;
|
|
4604
|
+
}
|
|
4605
|
+
function calculateOfflineDeadline(mode, invoiceDate, maintenanceWindow) {
|
|
4606
|
+
const date = typeof invoiceDate === "string" ? new Date(invoiceDate) : invoiceDate;
|
|
4607
|
+
switch (mode) {
|
|
4608
|
+
case "offline24": {
|
|
4609
|
+
return endOfDay(nextBusinessDay(date));
|
|
4610
|
+
}
|
|
4611
|
+
case "offline": {
|
|
4612
|
+
const base = maintenanceWindow ? getMaintenanceEndFallback(maintenanceWindow) : date;
|
|
4613
|
+
return endOfDay(nextBusinessDay(base));
|
|
4614
|
+
}
|
|
4615
|
+
case "awaryjny": {
|
|
4616
|
+
const base = maintenanceWindow ? getMaintenanceEndFallback(maintenanceWindow) : date;
|
|
4617
|
+
return endOfDay(addBusinessDays(base, 7));
|
|
4618
|
+
}
|
|
4619
|
+
case "awaria_calkowita": {
|
|
4620
|
+
return new Date(FAR_FUTURE);
|
|
4621
|
+
}
|
|
4622
|
+
}
|
|
4623
|
+
}
|
|
4624
|
+
function extendDeadlineForMaintenance(currentDeadline, maintenanceWindow, mode = "awaryjny") {
|
|
4625
|
+
const current = typeof currentDeadline === "string" ? new Date(currentDeadline) : new Date(currentDeadline);
|
|
4626
|
+
const mwEnd = getMaintenanceEndFallback(maintenanceWindow);
|
|
4627
|
+
if (mwEnd.getTime() > current.getTime()) {
|
|
4628
|
+
switch (mode) {
|
|
4629
|
+
case "offline24":
|
|
4630
|
+
case "offline":
|
|
4631
|
+
return endOfDay(nextBusinessDay(mwEnd));
|
|
4632
|
+
case "awaryjny":
|
|
4633
|
+
return endOfDay(addBusinessDays(mwEnd, 7));
|
|
4634
|
+
case "awaria_calkowita":
|
|
4635
|
+
return new Date(FAR_FUTURE);
|
|
4636
|
+
}
|
|
4637
|
+
}
|
|
4638
|
+
return current;
|
|
4639
|
+
}
|
|
4640
|
+
function isExpired(submitBy) {
|
|
4641
|
+
const deadline = typeof submitBy === "string" ? new Date(submitBy) : submitBy;
|
|
4642
|
+
return Date.now() > deadline.getTime();
|
|
4643
|
+
}
|
|
4644
|
+
function getTimeUntilDeadline(submitBy) {
|
|
4645
|
+
const deadline = typeof submitBy === "string" ? new Date(submitBy) : submitBy;
|
|
4646
|
+
return Math.max(0, deadline.getTime() - Date.now());
|
|
4647
|
+
}
|
|
4648
|
+
var FAR_FUTURE;
|
|
4649
|
+
var init_deadline = __esm({
|
|
4650
|
+
"src/offline/deadline.ts"() {
|
|
4651
|
+
"use strict";
|
|
4652
|
+
init_holidays();
|
|
4653
|
+
FAR_FUTURE = /* @__PURE__ */ new Date("9999-12-31T23:59:59Z");
|
|
4654
|
+
}
|
|
4655
|
+
});
|
|
4656
|
+
|
|
4657
|
+
// src/workflows/offline-invoice-workflow.ts
|
|
4658
|
+
var import_node_crypto3, OfflineInvoiceWorkflow;
|
|
4659
|
+
var init_offline_invoice_workflow = __esm({
|
|
4660
|
+
"src/workflows/offline-invoice-workflow.ts"() {
|
|
4661
|
+
"use strict";
|
|
4662
|
+
import_node_crypto3 = __toESM(require("crypto"), 1);
|
|
4663
|
+
init_ksef_api_error();
|
|
4664
|
+
init_deadline();
|
|
4665
|
+
init_document_structures();
|
|
4666
|
+
OfflineInvoiceWorkflow = class {
|
|
4667
|
+
constructor(qrService) {
|
|
4668
|
+
this.qrService = qrService;
|
|
4669
|
+
}
|
|
4670
|
+
async generate(input, options) {
|
|
4671
|
+
if (!input.invoiceXml || input.invoiceXml.trim().length === 0) {
|
|
4672
|
+
throw new Error("invoiceXml must not be empty");
|
|
4673
|
+
}
|
|
4674
|
+
if (!input.invoiceNumber) {
|
|
4675
|
+
throw new Error("invoiceNumber is required");
|
|
4676
|
+
}
|
|
4677
|
+
if (!input.sellerNip) {
|
|
4678
|
+
throw new Error("sellerNip is required");
|
|
4679
|
+
}
|
|
4680
|
+
const mode = options?.mode ?? "offline24";
|
|
4681
|
+
const reason = getDefaultReason(mode);
|
|
4682
|
+
const invoiceHashBase64 = import_node_crypto3.default.createHash("sha256").update(input.invoiceXml).digest("base64");
|
|
4683
|
+
const kod1Url = this.qrService.buildInvoiceVerificationUrl(
|
|
4684
|
+
input.sellerNip,
|
|
4685
|
+
input.invoiceDate,
|
|
4686
|
+
invoiceHashBase64
|
|
4687
|
+
);
|
|
4688
|
+
let kod2Url;
|
|
4689
|
+
if (options?.certificate) {
|
|
4690
|
+
kod2Url = this.qrService.buildCertificateVerificationUrl(
|
|
4691
|
+
input.sellerIdentifier.type,
|
|
4692
|
+
input.sellerIdentifier.value,
|
|
4693
|
+
input.sellerNip,
|
|
4694
|
+
options.certificate.certificateSerial,
|
|
4695
|
+
invoiceHashBase64,
|
|
4696
|
+
options.certificate.privateKeyPem
|
|
4697
|
+
);
|
|
4698
|
+
}
|
|
4699
|
+
const submitBy = options?.customDeadline ? typeof options.customDeadline === "string" ? options.customDeadline : options.customDeadline.toISOString() : calculateOfflineDeadline(mode, input.invoiceDate, options?.maintenanceWindow).toISOString();
|
|
4700
|
+
const metadata = {
|
|
4701
|
+
id: import_node_crypto3.default.randomUUID(),
|
|
4702
|
+
mode,
|
|
4703
|
+
reason,
|
|
4704
|
+
status: "GENERATED",
|
|
4705
|
+
invoiceNumber: input.invoiceNumber,
|
|
4706
|
+
invoiceDate: input.invoiceDate,
|
|
4707
|
+
invoiceXml: input.invoiceXml,
|
|
4708
|
+
sellerNip: input.sellerNip,
|
|
4709
|
+
sellerIdentifier: input.sellerIdentifier,
|
|
4710
|
+
buyerIdentifier: input.buyerIdentifier,
|
|
4711
|
+
totalAmount: input.totalAmount,
|
|
4712
|
+
currency: input.currency,
|
|
4713
|
+
kod1Url,
|
|
4714
|
+
kod2Url,
|
|
4715
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4716
|
+
submitBy,
|
|
4717
|
+
maintenanceWindowId: options?.maintenanceWindow?.id
|
|
4718
|
+
};
|
|
4719
|
+
if (options?.storage) {
|
|
4720
|
+
await options.storage.save(metadata);
|
|
4721
|
+
}
|
|
4722
|
+
return metadata;
|
|
4723
|
+
}
|
|
4724
|
+
async submit(client, options) {
|
|
4725
|
+
const { storage, checkExpiry = true } = options;
|
|
4726
|
+
let invoices;
|
|
4727
|
+
if (options.invoiceIds) {
|
|
4728
|
+
invoices = [];
|
|
4729
|
+
const notFound = [];
|
|
4730
|
+
for (const id of options.invoiceIds) {
|
|
4731
|
+
const inv = await storage.get(id);
|
|
4732
|
+
if (inv) {
|
|
4733
|
+
invoices.push(inv);
|
|
4734
|
+
} else {
|
|
4735
|
+
notFound.push(id);
|
|
4736
|
+
}
|
|
4737
|
+
}
|
|
4738
|
+
if (notFound.length > 0) {
|
|
4739
|
+
throw new Error(`Offline invoice(s) not found: ${notFound.join(", ")}`);
|
|
4740
|
+
}
|
|
4741
|
+
} else {
|
|
4742
|
+
invoices = await storage.list({ status: ["GENERATED", "QUEUED", "SUBMITTED"] });
|
|
4743
|
+
}
|
|
4744
|
+
const result = {
|
|
4745
|
+
total: invoices.length,
|
|
4746
|
+
submitted: 0,
|
|
4747
|
+
accepted: 0,
|
|
4748
|
+
rejected: 0,
|
|
4749
|
+
failed: 0,
|
|
4750
|
+
expired: 0,
|
|
4751
|
+
results: []
|
|
4752
|
+
};
|
|
4753
|
+
if (invoices.length === 0) return result;
|
|
4754
|
+
const pending = [];
|
|
4755
|
+
for (const inv of invoices) {
|
|
4756
|
+
if (checkExpiry && isExpired(inv.submitBy)) {
|
|
4757
|
+
await storage.update(inv.id, { status: "EXPIRED" });
|
|
4758
|
+
result.expired++;
|
|
4759
|
+
result.results.push({
|
|
4760
|
+
invoiceId: inv.id,
|
|
4761
|
+
invoiceNumber: inv.invoiceNumber,
|
|
4762
|
+
status: "EXPIRED"
|
|
4763
|
+
});
|
|
4764
|
+
} else {
|
|
4765
|
+
pending.push(inv);
|
|
4766
|
+
}
|
|
4767
|
+
}
|
|
4768
|
+
if (pending.length === 0) return result;
|
|
4769
|
+
await client.crypto.init();
|
|
4770
|
+
const encData = client.crypto.getEncryptionData();
|
|
4771
|
+
const formCode = options.formCode ?? DEFAULT_FORM_CODE;
|
|
4772
|
+
const openResp = await client.onlineSession.openSession(
|
|
4773
|
+
{ formCode, encryption: encData.encryptionInfo }
|
|
4774
|
+
);
|
|
4775
|
+
const sessionRef = openResp.referenceNumber;
|
|
4776
|
+
try {
|
|
4777
|
+
for (const inv of pending) {
|
|
4778
|
+
await storage.update(inv.id, { status: "QUEUED" });
|
|
4779
|
+
try {
|
|
4780
|
+
const data = new TextEncoder().encode(inv.invoiceXml);
|
|
4781
|
+
const plainMeta = client.crypto.getFileMetadata(data);
|
|
4782
|
+
const encrypted = client.crypto.encryptAES256(data, encData.cipherKey, encData.cipherIv);
|
|
4783
|
+
const encMeta = client.crypto.getFileMetadata(encrypted);
|
|
4784
|
+
await storage.update(inv.id, { status: "SUBMITTED", submittedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
4785
|
+
const resp = await client.onlineSession.sendInvoice(sessionRef, {
|
|
4786
|
+
invoiceHash: plainMeta.hashSHA,
|
|
4787
|
+
invoiceSize: plainMeta.fileSize,
|
|
4788
|
+
encryptedInvoiceHash: encMeta.hashSHA,
|
|
4789
|
+
encryptedInvoiceSize: encMeta.fileSize,
|
|
4790
|
+
encryptedInvoiceContent: Buffer.from(encrypted).toString("base64"),
|
|
4791
|
+
offlineMode: true
|
|
4792
|
+
});
|
|
4793
|
+
await storage.update(inv.id, {
|
|
4794
|
+
status: "ACCEPTED",
|
|
4795
|
+
acceptedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4796
|
+
ksefReferenceNumber: resp.referenceNumber
|
|
4797
|
+
});
|
|
4798
|
+
result.submitted++;
|
|
4799
|
+
result.accepted++;
|
|
4800
|
+
result.results.push({
|
|
4801
|
+
invoiceId: inv.id,
|
|
4802
|
+
invoiceNumber: inv.invoiceNumber,
|
|
4803
|
+
status: "ACCEPTED",
|
|
4804
|
+
ksefReferenceNumber: resp.referenceNumber
|
|
4805
|
+
});
|
|
4806
|
+
} catch (err) {
|
|
4807
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
4808
|
+
if (err instanceof KSeFApiError) {
|
|
4809
|
+
await storage.update(inv.id, {
|
|
4810
|
+
status: "REJECTED",
|
|
4811
|
+
error: { code: err.statusCode, message }
|
|
4812
|
+
});
|
|
4813
|
+
result.submitted++;
|
|
4814
|
+
result.rejected++;
|
|
4815
|
+
result.results.push({
|
|
4816
|
+
invoiceId: inv.id,
|
|
4817
|
+
invoiceNumber: inv.invoiceNumber,
|
|
4818
|
+
status: "REJECTED",
|
|
4819
|
+
error: { code: err.statusCode, message }
|
|
4820
|
+
});
|
|
4821
|
+
} else {
|
|
4822
|
+
await storage.update(inv.id, {
|
|
4823
|
+
status: "QUEUED",
|
|
4824
|
+
error: { code: 0, message }
|
|
4825
|
+
});
|
|
4826
|
+
result.failed++;
|
|
4827
|
+
result.results.push({
|
|
4828
|
+
invoiceId: inv.id,
|
|
4829
|
+
invoiceNumber: inv.invoiceNumber,
|
|
4830
|
+
status: "QUEUED",
|
|
4831
|
+
error: { code: 0, message }
|
|
4832
|
+
});
|
|
4833
|
+
}
|
|
4834
|
+
}
|
|
4835
|
+
}
|
|
4836
|
+
} finally {
|
|
4837
|
+
try {
|
|
4838
|
+
await client.onlineSession.closeSession(sessionRef);
|
|
4839
|
+
} catch {
|
|
4840
|
+
}
|
|
4841
|
+
}
|
|
4842
|
+
return result;
|
|
4843
|
+
}
|
|
4844
|
+
// TODO(perf): Each correction opens a separate KSeF session.
|
|
4845
|
+
// For batch corrections, consider a correctBatch() sharing one session (like submit()).
|
|
4846
|
+
async correct(client, options) {
|
|
4847
|
+
const { storage, rejectedInvoiceId, correctedInvoiceXml } = options;
|
|
4848
|
+
const original = await storage.get(rejectedInvoiceId);
|
|
4849
|
+
if (!original) {
|
|
4850
|
+
throw new Error(`Offline invoice not found: ${rejectedInvoiceId}`);
|
|
4851
|
+
}
|
|
4852
|
+
if (original.status !== "REJECTED") {
|
|
4853
|
+
throw new Error(
|
|
4854
|
+
original.status === "CORRECTED" ? `Invoice ${rejectedInvoiceId} has already been corrected (by ${original.correctedBy})` : `Only rejected invoices can be corrected (current status: ${original.status})`
|
|
4855
|
+
);
|
|
4856
|
+
}
|
|
4857
|
+
const originalHash = import_node_crypto3.default.createHash("sha256").update(original.invoiceXml).digest("base64");
|
|
4858
|
+
await client.crypto.init();
|
|
4859
|
+
const encData = client.crypto.getEncryptionData();
|
|
4860
|
+
const formCode = options.formCode ?? DEFAULT_FORM_CODE;
|
|
4861
|
+
const openResp = await client.onlineSession.openSession(
|
|
4862
|
+
{ formCode, encryption: encData.encryptionInfo }
|
|
4863
|
+
);
|
|
4864
|
+
const sessionRef = openResp.referenceNumber;
|
|
4865
|
+
try {
|
|
4866
|
+
const data = new TextEncoder().encode(correctedInvoiceXml);
|
|
4867
|
+
const plainMeta = client.crypto.getFileMetadata(data);
|
|
4868
|
+
const encrypted = client.crypto.encryptAES256(data, encData.cipherKey, encData.cipherIv);
|
|
4869
|
+
const encMeta = client.crypto.getFileMetadata(encrypted);
|
|
4870
|
+
const correctionId = import_node_crypto3.default.randomUUID();
|
|
4871
|
+
const submittedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4872
|
+
const correctedHashBase64 = import_node_crypto3.default.createHash("sha256").update(correctedInvoiceXml).digest("base64");
|
|
4873
|
+
const kod1Url = this.qrService.buildInvoiceVerificationUrl(
|
|
4874
|
+
original.sellerNip,
|
|
4875
|
+
original.invoiceDate,
|
|
4876
|
+
correctedHashBase64
|
|
4877
|
+
);
|
|
4878
|
+
let kod2Url;
|
|
4879
|
+
if (options.certificate) {
|
|
4880
|
+
kod2Url = this.qrService.buildCertificateVerificationUrl(
|
|
4881
|
+
original.sellerIdentifier.type,
|
|
4882
|
+
original.sellerIdentifier.value,
|
|
4883
|
+
original.sellerNip,
|
|
4884
|
+
options.certificate.certificateSerial,
|
|
4885
|
+
correctedHashBase64,
|
|
4886
|
+
options.certificate.privateKeyPem
|
|
4887
|
+
);
|
|
4888
|
+
}
|
|
4889
|
+
const correctionMetadata = {
|
|
4890
|
+
id: correctionId,
|
|
4891
|
+
mode: original.mode,
|
|
4892
|
+
reason: original.reason,
|
|
4893
|
+
status: "SUBMITTED",
|
|
4894
|
+
invoiceNumber: original.invoiceNumber,
|
|
4895
|
+
invoiceDate: original.invoiceDate,
|
|
4896
|
+
invoiceXml: correctedInvoiceXml,
|
|
4897
|
+
sellerNip: original.sellerNip,
|
|
4898
|
+
sellerIdentifier: original.sellerIdentifier,
|
|
4899
|
+
buyerIdentifier: original.buyerIdentifier,
|
|
4900
|
+
kod1Url,
|
|
4901
|
+
kod2Url,
|
|
4902
|
+
generatedAt: submittedAt,
|
|
4903
|
+
submitBy: original.submitBy,
|
|
4904
|
+
submittedAt,
|
|
4905
|
+
correctedInvoiceId: rejectedInvoiceId
|
|
4906
|
+
};
|
|
4907
|
+
await storage.save(correctionMetadata);
|
|
4908
|
+
const resp = await client.onlineSession.sendInvoice(sessionRef, {
|
|
4909
|
+
invoiceHash: plainMeta.hashSHA,
|
|
4910
|
+
invoiceSize: plainMeta.fileSize,
|
|
4911
|
+
encryptedInvoiceHash: encMeta.hashSHA,
|
|
4912
|
+
encryptedInvoiceSize: encMeta.fileSize,
|
|
4913
|
+
encryptedInvoiceContent: Buffer.from(encrypted).toString("base64"),
|
|
4914
|
+
offlineMode: true,
|
|
4915
|
+
hashOfCorrectedInvoice: originalHash
|
|
4916
|
+
});
|
|
4917
|
+
await storage.update(correctionId, {
|
|
4918
|
+
status: "ACCEPTED",
|
|
4919
|
+
acceptedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4920
|
+
ksefReferenceNumber: resp.referenceNumber
|
|
4921
|
+
});
|
|
4922
|
+
await storage.update(rejectedInvoiceId, {
|
|
4923
|
+
status: "CORRECTED",
|
|
4924
|
+
correctedBy: correctionId
|
|
4925
|
+
});
|
|
4926
|
+
return {
|
|
4927
|
+
invoiceId: correctionId,
|
|
4928
|
+
invoiceNumber: correctionMetadata.invoiceNumber,
|
|
4929
|
+
status: "ACCEPTED",
|
|
4930
|
+
ksefReferenceNumber: resp.referenceNumber
|
|
4931
|
+
};
|
|
4932
|
+
} finally {
|
|
4933
|
+
try {
|
|
4934
|
+
await client.onlineSession.closeSession(sessionRef);
|
|
4935
|
+
} catch {
|
|
4936
|
+
}
|
|
4937
|
+
}
|
|
4938
|
+
}
|
|
4939
|
+
};
|
|
4940
|
+
}
|
|
4941
|
+
});
|
|
4942
|
+
|
|
4354
4943
|
// src/client.ts
|
|
4355
4944
|
var client_exports = {};
|
|
4356
4945
|
__export(client_exports, {
|
|
@@ -4416,6 +5005,7 @@ var init_client = __esm({
|
|
|
4416
5005
|
init_cryptography_service();
|
|
4417
5006
|
init_verification_link_service();
|
|
4418
5007
|
init_auth_xml_builder();
|
|
5008
|
+
init_offline_invoice_workflow();
|
|
4419
5009
|
KSeFClient = class {
|
|
4420
5010
|
auth;
|
|
4421
5011
|
activeSessions;
|
|
@@ -4434,6 +5024,8 @@ var init_client = __esm({
|
|
|
4434
5024
|
qr;
|
|
4435
5025
|
options;
|
|
4436
5026
|
authManager;
|
|
5027
|
+
_baseRestClientConfig;
|
|
5028
|
+
_offline;
|
|
4437
5029
|
constructor(options) {
|
|
4438
5030
|
this.options = resolveOptions(options);
|
|
4439
5031
|
const authManager = options?.authManager ?? new DefaultAuthManager(async () => {
|
|
@@ -4444,6 +5036,8 @@ var init_client = __esm({
|
|
|
4444
5036
|
});
|
|
4445
5037
|
this.authManager = authManager;
|
|
4446
5038
|
const restClientConfig = buildRestClientConfig(options, authManager);
|
|
5039
|
+
const { authManager: _am, ...baseConfig } = restClientConfig;
|
|
5040
|
+
this._baseRestClientConfig = baseConfig;
|
|
4447
5041
|
const restClient = new RestClient(this.options, restClientConfig);
|
|
4448
5042
|
const fetcher = new CertificateFetcher(restClient);
|
|
4449
5043
|
this.crypto = new CryptographyService(fetcher);
|
|
@@ -4462,6 +5056,16 @@ var init_client = __esm({
|
|
|
4462
5056
|
this.testData = new TestDataService(restClient, this.options.environmentName);
|
|
4463
5057
|
this.qr = new VerificationLinkService(this.options.baseQrUrl);
|
|
4464
5058
|
}
|
|
5059
|
+
get offline() {
|
|
5060
|
+
if (!this._offline) {
|
|
5061
|
+
this._offline = new OfflineInvoiceWorkflow(this.qr);
|
|
5062
|
+
}
|
|
5063
|
+
return this._offline;
|
|
5064
|
+
}
|
|
5065
|
+
/** @internal Create a RestClient with a different AuthManager, preserving transport/retry/rateLimit config. */
|
|
5066
|
+
createScopedRestClient(authManager) {
|
|
5067
|
+
return new RestClient(this.options, { ...this._baseRestClientConfig, authManager });
|
|
5068
|
+
}
|
|
4465
5069
|
async loginWithToken(token, nip) {
|
|
4466
5070
|
const challenge = await this.auth.getChallenge();
|
|
4467
5071
|
await this.crypto.init();
|
|
@@ -4542,8 +5146,10 @@ __export(index_exports, {
|
|
|
4542
5146
|
FORM_CODES: () => FORM_CODES,
|
|
4543
5147
|
FORM_CODE_KEYS: () => FORM_CODE_KEYS,
|
|
4544
5148
|
FileHwmStore: () => FileHwmStore,
|
|
5149
|
+
FileOfflineInvoiceStorage: () => FileOfflineInvoiceStorage,
|
|
4545
5150
|
INVOICE_TYPES_BY_SYSTEM_CODE: () => INVOICE_TYPES_BY_SYSTEM_CODE,
|
|
4546
5151
|
InMemoryHwmStore: () => InMemoryHwmStore,
|
|
5152
|
+
InMemoryOfflineInvoiceStorage: () => InMemoryOfflineInvoiceStorage,
|
|
4547
5153
|
InternalId: () => InternalId,
|
|
4548
5154
|
InvoiceDownloadService: () => InvoiceDownloadService,
|
|
4549
5155
|
InvoiceQueryFilterBuilder: () => InvoiceQueryFilterBuilder,
|
|
@@ -4567,6 +5173,7 @@ __export(index_exports, {
|
|
|
4567
5173
|
LimitsService: () => LimitsService,
|
|
4568
5174
|
Nip: () => Nip,
|
|
4569
5175
|
NipVatUe: () => NipVatUe,
|
|
5176
|
+
OfflineInvoiceWorkflow: () => OfflineInvoiceWorkflow,
|
|
4570
5177
|
OnlineSessionService: () => OnlineSessionService,
|
|
4571
5178
|
PERMISSION_DESCRIPTION_MAX_LENGTH: () => PERMISSION_DESCRIPTION_MAX_LENGTH,
|
|
4572
5179
|
PERMISSION_DESCRIPTION_MIN_LENGTH: () => PERMISSION_DESCRIPTION_MIN_LENGTH,
|
|
@@ -4596,6 +5203,7 @@ __export(index_exports, {
|
|
|
4596
5203
|
UpoVersion: () => UpoVersion,
|
|
4597
5204
|
VatUe: () => VatUe,
|
|
4598
5205
|
VerificationLinkService: () => VerificationLinkService,
|
|
5206
|
+
addBusinessDays: () => addBusinessDays,
|
|
4599
5207
|
authenticateWithCertificate: () => authenticateWithCertificate,
|
|
4600
5208
|
authenticateWithExternalSignature: () => authenticateWithExternalSignature,
|
|
4601
5209
|
authenticateWithPkcs12: () => authenticateWithPkcs12,
|
|
@@ -4603,6 +5211,7 @@ __export(index_exports, {
|
|
|
4603
5211
|
batchValidationDetails: () => batchValidationDetails,
|
|
4604
5212
|
buildUnsignedAuthTokenRequestXml: () => buildUnsignedAuthTokenRequestXml,
|
|
4605
5213
|
calculateBackoff: () => calculateBackoff,
|
|
5214
|
+
calculateOfflineDeadline: () => calculateOfflineDeadline,
|
|
4606
5215
|
createZip: () => createZip,
|
|
4607
5216
|
decodeJwtPayload: () => decodeJwtPayload,
|
|
4608
5217
|
deduplicateByKsefNumber: () => deduplicateByKsefNumber,
|
|
@@ -4612,9 +5221,16 @@ __export(index_exports, {
|
|
|
4612
5221
|
defaultTransport: () => defaultTransport,
|
|
4613
5222
|
exportAndDownload: () => exportAndDownload,
|
|
4614
5223
|
exportInvoices: () => exportInvoices,
|
|
5224
|
+
extendDeadlineForMaintenance: () => extendDeadlineForMaintenance,
|
|
5225
|
+
extractInvoiceFields: () => extractInvoiceFields,
|
|
5226
|
+
getDefaultReason: () => getDefaultReason,
|
|
4615
5227
|
getEffectiveStartDate: () => getEffectiveStartDate,
|
|
4616
5228
|
getFormCode: () => getFormCode,
|
|
5229
|
+
getPolishHolidays: () => getPolishHolidays,
|
|
5230
|
+
getTimeUntilDeadline: () => getTimeUntilDeadline,
|
|
4617
5231
|
incrementalExportAndDownload: () => incrementalExportAndDownload,
|
|
5232
|
+
isExpired: () => isExpired,
|
|
5233
|
+
isPolishHoliday: () => isPolishHoliday,
|
|
4618
5234
|
isRetryableError: () => isRetryableError,
|
|
4619
5235
|
isRetryableStatus: () => isRetryableStatus,
|
|
4620
5236
|
isValidBase64: () => isValidBase64,
|
|
@@ -4632,6 +5248,7 @@ __export(index_exports, {
|
|
|
4632
5248
|
isValidReferenceNumber: () => isValidReferenceNumber,
|
|
4633
5249
|
isValidSha256Base64: () => isValidSha256Base64,
|
|
4634
5250
|
isValidVatUe: () => isValidVatUe,
|
|
5251
|
+
nextBusinessDay: () => nextBusinessDay,
|
|
4635
5252
|
openOnlineSession: () => openOnlineSession,
|
|
4636
5253
|
openSendAndClose: () => openSendAndClose,
|
|
4637
5254
|
parseFormCode: () => parseFormCode,
|
|
@@ -4640,6 +5257,9 @@ __export(index_exports, {
|
|
|
4640
5257
|
parseUpoXml: () => parseUpoXml,
|
|
4641
5258
|
pollUntil: () => pollUntil,
|
|
4642
5259
|
resolveOptions: () => resolveOptions,
|
|
5260
|
+
resumeOnlineSession: () => resumeOnlineSession,
|
|
5261
|
+
runWithConcurrency: () => runWithConcurrency,
|
|
5262
|
+
sha256Base64: () => sha256Base642,
|
|
4643
5263
|
sleep: () => sleep,
|
|
4644
5264
|
unzip: () => unzip,
|
|
4645
5265
|
updateContinuationPoint: () => updateContinuationPoint,
|
|
@@ -4654,6 +5274,7 @@ __export(index_exports, {
|
|
|
4654
5274
|
validatePresignedUrl: () => validatePresignedUrl,
|
|
4655
5275
|
validateSchema: () => validateSchema,
|
|
4656
5276
|
validateWellFormedness: () => validateWellFormedness,
|
|
5277
|
+
verifyHash: () => verifyHash,
|
|
4657
5278
|
xmlToObject: () => xmlToObject
|
|
4658
5279
|
});
|
|
4659
5280
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -4689,65 +5310,8 @@ init_xml_to_object();
|
|
|
4689
5310
|
init_schema_registry();
|
|
4690
5311
|
init_invoice_validator();
|
|
4691
5312
|
|
|
4692
|
-
// src/models/
|
|
4693
|
-
|
|
4694
|
-
FA_2: "FA (2)",
|
|
4695
|
-
FA_3: "FA (3)",
|
|
4696
|
-
PEF_3: "PEF (3)",
|
|
4697
|
-
PEF_KOR_3: "PEF_KOR (3)",
|
|
4698
|
-
FA_RR_1: "FA_RR (1)"
|
|
4699
|
-
};
|
|
4700
|
-
var FORM_CODES = {
|
|
4701
|
-
FA_2: { systemCode: "FA (2)", schemaVersion: "1-0E", value: "FA" },
|
|
4702
|
-
FA_3: { systemCode: "FA (3)", schemaVersion: "1-0E", value: "FA" },
|
|
4703
|
-
PEF_3: { systemCode: "PEF (3)", schemaVersion: "2-1", value: "PEF" },
|
|
4704
|
-
PEF_KOR_3: { systemCode: "PEF_KOR (3)", schemaVersion: "2-1", value: "PEF" },
|
|
4705
|
-
FA_RR_1_LEGACY: { systemCode: "FA_RR (1)", schemaVersion: "1-0E", value: "RR" },
|
|
4706
|
-
FA_RR_1_TRANSITION: { systemCode: "FA_RR (1)", schemaVersion: "1-1E", value: "RR" },
|
|
4707
|
-
FA_RR_1: { systemCode: "FA_RR (1)", schemaVersion: "1-1E", value: "FA_RR" }
|
|
4708
|
-
};
|
|
4709
|
-
var DEFAULT_FORM_CODE = FORM_CODES.FA_3;
|
|
4710
|
-
var INVOICE_TYPES_BY_SYSTEM_CODE = {
|
|
4711
|
-
[SystemCode.FA_2]: ["Vat", "Zal", "Kor", "Roz", "Upr", "KorZal", "KorRoz"],
|
|
4712
|
-
[SystemCode.FA_3]: ["Vat", "Zal", "Kor", "Roz", "Upr", "KorZal", "KorRoz"],
|
|
4713
|
-
[SystemCode.PEF_3]: ["VatPef", "VatPefSp", "KorPef"],
|
|
4714
|
-
[SystemCode.PEF_KOR_3]: ["KorPef"],
|
|
4715
|
-
[SystemCode.FA_RR_1]: ["VatRr", "KorVatRr"]
|
|
4716
|
-
};
|
|
4717
|
-
var FORM_CODE_KEYS = {
|
|
4718
|
-
FA2: FORM_CODES.FA_2,
|
|
4719
|
-
FA3: FORM_CODES.FA_3,
|
|
4720
|
-
PEF3: FORM_CODES.PEF_3,
|
|
4721
|
-
PEFKOR3: FORM_CODES.PEF_KOR_3,
|
|
4722
|
-
FARR1: FORM_CODES.FA_RR_1
|
|
4723
|
-
};
|
|
4724
|
-
|
|
4725
|
-
// src/models/document-structures/helpers.ts
|
|
4726
|
-
var SYSTEM_CODE_TO_FORM_CODE = {
|
|
4727
|
-
[SystemCode.FA_2]: FORM_CODES.FA_2,
|
|
4728
|
-
[SystemCode.FA_3]: FORM_CODES.FA_3,
|
|
4729
|
-
[SystemCode.PEF_3]: FORM_CODES.PEF_3,
|
|
4730
|
-
[SystemCode.PEF_KOR_3]: FORM_CODES.PEF_KOR_3,
|
|
4731
|
-
[SystemCode.FA_RR_1]: FORM_CODES.FA_RR_1
|
|
4732
|
-
};
|
|
4733
|
-
var ALL_FORM_CODES = Object.values(FORM_CODES);
|
|
4734
|
-
var BATCH_DISALLOWED_SYSTEM_CODES = /* @__PURE__ */ new Set([
|
|
4735
|
-
SystemCode.PEF_3,
|
|
4736
|
-
SystemCode.PEF_KOR_3
|
|
4737
|
-
]);
|
|
4738
|
-
function getFormCode(systemCode) {
|
|
4739
|
-
return SYSTEM_CODE_TO_FORM_CODE[systemCode];
|
|
4740
|
-
}
|
|
4741
|
-
function parseFormCode(raw) {
|
|
4742
|
-
const match = ALL_FORM_CODES.find(
|
|
4743
|
-
(fc) => fc.systemCode === raw.systemCode && fc.schemaVersion === raw.schemaVersion && fc.value === raw.value
|
|
4744
|
-
);
|
|
4745
|
-
return match ?? raw;
|
|
4746
|
-
}
|
|
4747
|
-
function validateFormCodeForSession(formCode, sessionType) {
|
|
4748
|
-
if (sessionType === "online") return true;
|
|
4749
|
-
return !BATCH_DISALLOWED_SYSTEM_CODES.has(formCode.systemCode);
|
|
4750
|
-
}
|
|
5313
|
+
// src/models/index.ts
|
|
5314
|
+
init_document_structures();
|
|
4751
5315
|
|
|
4752
5316
|
// src/services/index.ts
|
|
4753
5317
|
init_auth();
|
|
@@ -5482,6 +6046,18 @@ function parseKSeFTokenContext(token) {
|
|
|
5482
6046
|
};
|
|
5483
6047
|
}
|
|
5484
6048
|
|
|
6049
|
+
// src/utils/hash.ts
|
|
6050
|
+
var import_node_crypto2 = __toESM(require("crypto"), 1);
|
|
6051
|
+
function sha256Base642(data) {
|
|
6052
|
+
return import_node_crypto2.default.createHash("sha256").update(data).digest("base64");
|
|
6053
|
+
}
|
|
6054
|
+
function verifyHash(data, expectedHash) {
|
|
6055
|
+
return sha256Base642(data) === expectedHash;
|
|
6056
|
+
}
|
|
6057
|
+
|
|
6058
|
+
// src/utils/index.ts
|
|
6059
|
+
init_concurrency();
|
|
6060
|
+
|
|
5485
6061
|
// src/workflows/polling.ts
|
|
5486
6062
|
async function pollUntil(action, condition, options) {
|
|
5487
6063
|
const intervalMs = options?.intervalMs ?? 2e3;
|
|
@@ -5499,6 +6075,13 @@ async function pollUntil(action, condition, options) {
|
|
|
5499
6075
|
);
|
|
5500
6076
|
}
|
|
5501
6077
|
|
|
6078
|
+
// src/workflows/online-session-workflow.ts
|
|
6079
|
+
init_ksef_session_expired_error();
|
|
6080
|
+
init_auth_manager();
|
|
6081
|
+
init_online_session();
|
|
6082
|
+
init_session_status();
|
|
6083
|
+
init_document_structures();
|
|
6084
|
+
|
|
5502
6085
|
// src/xml/upo-parser.ts
|
|
5503
6086
|
var import_fast_xml_parser = require("fast-xml-parser");
|
|
5504
6087
|
init_ksef_validation_error();
|
|
@@ -5617,21 +6200,54 @@ function parseUpoXml(xml) {
|
|
|
5617
6200
|
};
|
|
5618
6201
|
}
|
|
5619
6202
|
|
|
6203
|
+
// src/xml/invoice-field-extractor.ts
|
|
6204
|
+
var import_fast_xml_parser2 = require("fast-xml-parser");
|
|
6205
|
+
var invoiceParser = new import_fast_xml_parser2.XMLParser({
|
|
6206
|
+
ignoreAttributes: false,
|
|
6207
|
+
attributeNamePrefix: "@_",
|
|
6208
|
+
parseTagValue: false,
|
|
6209
|
+
parseAttributeValue: false,
|
|
6210
|
+
removeNSPrefix: true,
|
|
6211
|
+
trimValues: false
|
|
6212
|
+
});
|
|
6213
|
+
function extractInvoiceFields(xml) {
|
|
6214
|
+
const parsed = invoiceParser.parse(xml);
|
|
6215
|
+
const root = parsed?.Faktura;
|
|
6216
|
+
if (!root || typeof root !== "object") {
|
|
6217
|
+
throw new Error(
|
|
6218
|
+
"Cannot extract invoice fields: missing <Faktura> root element. Ensure the XML is a valid KSeF invoice (FA_2, FA_3, or FA_RR)."
|
|
6219
|
+
);
|
|
6220
|
+
}
|
|
6221
|
+
if (root.Fa && typeof root.Fa === "object") {
|
|
6222
|
+
const invoiceDate = nonEmptyString(root.Fa.P_1);
|
|
6223
|
+
const invoiceNumber = nonEmptyString(root.Fa.P_2);
|
|
6224
|
+
if (invoiceDate && invoiceNumber) {
|
|
6225
|
+
return { invoiceNumber, invoiceDate };
|
|
6226
|
+
}
|
|
6227
|
+
}
|
|
6228
|
+
if (root.FakturaRR && typeof root.FakturaRR === "object") {
|
|
6229
|
+
const invoiceDate = nonEmptyString(root.FakturaRR.P_4B);
|
|
6230
|
+
const invoiceNumber = nonEmptyString(root.FakturaRR.P_4C);
|
|
6231
|
+
if (invoiceDate && invoiceNumber) {
|
|
6232
|
+
return { invoiceNumber, invoiceDate };
|
|
6233
|
+
}
|
|
6234
|
+
}
|
|
6235
|
+
throw new Error(
|
|
6236
|
+
"Cannot extract invoice number and date from XML. Expected <Fa><P_1>/<P_2> (FA format) or <FakturaRR><P_4B>/<P_4C> (RR format)."
|
|
6237
|
+
);
|
|
6238
|
+
}
|
|
6239
|
+
function nonEmptyString(value) {
|
|
6240
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
6241
|
+
}
|
|
6242
|
+
|
|
5620
6243
|
// src/workflows/online-session-workflow.ts
|
|
5621
6244
|
init_invoice_validator();
|
|
5622
6245
|
init_ksef_validation_error();
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
const encData = client.crypto.getEncryptionData();
|
|
5626
|
-
const formCode = options?.formCode ?? DEFAULT_FORM_CODE;
|
|
5627
|
-
const openResp = await client.onlineSession.openSession(
|
|
5628
|
-
{ formCode, encryption: encData.encryptionInfo },
|
|
5629
|
-
options?.upoVersion
|
|
5630
|
-
);
|
|
5631
|
-
const sessionRef = openResp.referenceNumber;
|
|
6246
|
+
function buildSessionHandle(params) {
|
|
6247
|
+
const { deps, sessionRef, validUntil, cipherKey, cipherIv, formCode, validate: validate2 } = params;
|
|
5632
6248
|
async function fetchUpo(pollOpts) {
|
|
5633
6249
|
const result = await pollUntil(
|
|
5634
|
-
() =>
|
|
6250
|
+
() => deps.sessionStatus.getSessionStatus(sessionRef),
|
|
5635
6251
|
(s) => s.status.code === 200 || s.status.code >= 400,
|
|
5636
6252
|
{ ...pollOpts, description: `UPO for session ${sessionRef}` }
|
|
5637
6253
|
);
|
|
@@ -5647,9 +6263,9 @@ async function openOnlineSession(client, options) {
|
|
|
5647
6263
|
}
|
|
5648
6264
|
return {
|
|
5649
6265
|
sessionRef,
|
|
5650
|
-
validUntil
|
|
6266
|
+
validUntil,
|
|
5651
6267
|
async sendInvoice(invoiceXml) {
|
|
5652
|
-
if (
|
|
6268
|
+
if (validate2) {
|
|
5653
6269
|
const xmlStr = typeof invoiceXml === "string" ? invoiceXml : new TextDecoder().decode(invoiceXml);
|
|
5654
6270
|
const vResult = await validate(xmlStr);
|
|
5655
6271
|
if (!vResult.valid) {
|
|
@@ -5660,10 +6276,10 @@ async function openOnlineSession(client, options) {
|
|
|
5660
6276
|
}
|
|
5661
6277
|
}
|
|
5662
6278
|
const data = typeof invoiceXml === "string" ? new TextEncoder().encode(invoiceXml) : invoiceXml;
|
|
5663
|
-
const plainMeta =
|
|
5664
|
-
const encrypted =
|
|
5665
|
-
const encMeta =
|
|
5666
|
-
const resp = await
|
|
6279
|
+
const plainMeta = deps.crypto.getFileMetadata(data);
|
|
6280
|
+
const encrypted = deps.crypto.encryptAES256(data, cipherKey, cipherIv);
|
|
6281
|
+
const encMeta = deps.crypto.getFileMetadata(encrypted);
|
|
6282
|
+
const resp = await deps.onlineSession.sendInvoice(sessionRef, {
|
|
5667
6283
|
invoiceHash: plainMeta.hashSHA,
|
|
5668
6284
|
invoiceSize: plainMeta.fileSize,
|
|
5669
6285
|
encryptedInvoiceHash: encMeta.hashSHA,
|
|
@@ -5673,7 +6289,7 @@ async function openOnlineSession(client, options) {
|
|
|
5673
6289
|
return resp.referenceNumber;
|
|
5674
6290
|
},
|
|
5675
6291
|
async close() {
|
|
5676
|
-
await
|
|
6292
|
+
await deps.onlineSession.closeSession(sessionRef);
|
|
5677
6293
|
},
|
|
5678
6294
|
async waitForUpo(pollOpts) {
|
|
5679
6295
|
return fetchUpo(pollOpts);
|
|
@@ -5682,13 +6298,75 @@ async function openOnlineSession(client, options) {
|
|
|
5682
6298
|
const upoInfo = await fetchUpo(pollOpts);
|
|
5683
6299
|
const parsed = [];
|
|
5684
6300
|
for (const page of upoInfo.pages) {
|
|
5685
|
-
const result = await
|
|
6301
|
+
const result = await deps.sessionStatus.getSessionUpo(sessionRef, page.referenceNumber);
|
|
5686
6302
|
parsed.push(parseUpoXml(result.upo));
|
|
5687
6303
|
}
|
|
5688
6304
|
return { ...upoInfo, parsed };
|
|
6305
|
+
},
|
|
6306
|
+
getState() {
|
|
6307
|
+
const token = deps.getAccessToken();
|
|
6308
|
+
if (!token) {
|
|
6309
|
+
throw new Error("Cannot serialize session state: no access token available");
|
|
6310
|
+
}
|
|
6311
|
+
return {
|
|
6312
|
+
referenceNumber: sessionRef,
|
|
6313
|
+
aesKey: Buffer.from(cipherKey).toString("base64"),
|
|
6314
|
+
iv: Buffer.from(cipherIv).toString("base64"),
|
|
6315
|
+
accessToken: token,
|
|
6316
|
+
formCode,
|
|
6317
|
+
validUntil,
|
|
6318
|
+
...validate2 ? { validate: validate2 } : {}
|
|
6319
|
+
};
|
|
5689
6320
|
}
|
|
5690
6321
|
};
|
|
5691
6322
|
}
|
|
6323
|
+
async function openOnlineSession(client, options) {
|
|
6324
|
+
await client.crypto.init();
|
|
6325
|
+
const encData = client.crypto.getEncryptionData();
|
|
6326
|
+
const formCode = options?.formCode ?? DEFAULT_FORM_CODE;
|
|
6327
|
+
const openResp = await client.onlineSession.openSession(
|
|
6328
|
+
{ formCode, encryption: encData.encryptionInfo },
|
|
6329
|
+
options?.upoVersion
|
|
6330
|
+
);
|
|
6331
|
+
return buildSessionHandle({
|
|
6332
|
+
deps: {
|
|
6333
|
+
crypto: client.crypto,
|
|
6334
|
+
onlineSession: client.onlineSession,
|
|
6335
|
+
sessionStatus: client.sessionStatus,
|
|
6336
|
+
getAccessToken: () => client.authManager.getAccessToken()
|
|
6337
|
+
},
|
|
6338
|
+
sessionRef: openResp.referenceNumber,
|
|
6339
|
+
validUntil: openResp.validUntil,
|
|
6340
|
+
cipherKey: encData.cipherKey,
|
|
6341
|
+
cipherIv: encData.cipherIv,
|
|
6342
|
+
formCode,
|
|
6343
|
+
validate: options?.validate
|
|
6344
|
+
});
|
|
6345
|
+
}
|
|
6346
|
+
function resumeOnlineSession(client, state, options) {
|
|
6347
|
+
const expiry = new Date(state.validUntil);
|
|
6348
|
+
if (expiry.getTime() <= Date.now()) {
|
|
6349
|
+
throw new KSeFSessionExpiredError(
|
|
6350
|
+
`Cannot resume session: expired at ${state.validUntil}`
|
|
6351
|
+
);
|
|
6352
|
+
}
|
|
6353
|
+
const scopedAuth = new DefaultAuthManager(() => Promise.resolve(null), state.accessToken);
|
|
6354
|
+
const scopedRestClient = client.createScopedRestClient(scopedAuth);
|
|
6355
|
+
return buildSessionHandle({
|
|
6356
|
+
deps: {
|
|
6357
|
+
crypto: client.crypto,
|
|
6358
|
+
onlineSession: new OnlineSessionService(scopedRestClient),
|
|
6359
|
+
sessionStatus: new SessionStatusService(scopedRestClient),
|
|
6360
|
+
getAccessToken: () => scopedAuth.getAccessToken()
|
|
6361
|
+
},
|
|
6362
|
+
sessionRef: state.referenceNumber,
|
|
6363
|
+
validUntil: state.validUntil,
|
|
6364
|
+
cipherKey: new Uint8Array(Buffer.from(state.aesKey, "base64")),
|
|
6365
|
+
cipherIv: new Uint8Array(Buffer.from(state.iv, "base64")),
|
|
6366
|
+
formCode: state.formCode,
|
|
6367
|
+
validate: options?.validate ?? state.validate
|
|
6368
|
+
});
|
|
6369
|
+
}
|
|
5692
6370
|
async function openSendAndClose(client, invoices, options) {
|
|
5693
6371
|
const handle = await openOnlineSession(client, options);
|
|
5694
6372
|
for (const inv of invoices) {
|
|
@@ -5699,7 +6377,11 @@ async function openSendAndClose(client, invoices, options) {
|
|
|
5699
6377
|
}
|
|
5700
6378
|
|
|
5701
6379
|
// src/workflows/batch-session-workflow.ts
|
|
6380
|
+
init_document_structures();
|
|
5702
6381
|
async function uploadBatch(client, zipData, options) {
|
|
6382
|
+
if (options?.parallelism !== void 0 && (!Number.isInteger(options.parallelism) || options.parallelism < 1)) {
|
|
6383
|
+
throw new Error("parallelism must be a positive integer");
|
|
6384
|
+
}
|
|
5703
6385
|
await client.crypto.init();
|
|
5704
6386
|
if (options?.validate) {
|
|
5705
6387
|
const { unzip: unzip2 } = await Promise.resolve().then(() => (init_zip(), zip_exports));
|
|
@@ -5742,7 +6424,7 @@ async function uploadBatch(client, zipData, options) {
|
|
|
5742
6424
|
},
|
|
5743
6425
|
ordinalNumber: i + 1
|
|
5744
6426
|
}));
|
|
5745
|
-
await client.batchSession.sendParts(openResp, sendingParts);
|
|
6427
|
+
await client.batchSession.sendParts(openResp, sendingParts, options?.parallelism);
|
|
5746
6428
|
await client.batchSession.closeSession(openResp.referenceNumber);
|
|
5747
6429
|
const result = await pollUntil(
|
|
5748
6430
|
() => client.sessionStatus.getSessionStatus(openResp.referenceNumber),
|
|
@@ -5763,6 +6445,9 @@ async function uploadBatch(client, zipData, options) {
|
|
|
5763
6445
|
};
|
|
5764
6446
|
}
|
|
5765
6447
|
async function uploadBatchStream(client, zipStreamFactory, zipSize, options) {
|
|
6448
|
+
if (options?.parallelism !== void 0 && (!Number.isInteger(options.parallelism) || options.parallelism < 1)) {
|
|
6449
|
+
throw new Error("parallelism must be a positive integer");
|
|
6450
|
+
}
|
|
5766
6451
|
await client.crypto.init();
|
|
5767
6452
|
const encData = client.crypto.getEncryptionData();
|
|
5768
6453
|
const formCode = options?.formCode ?? DEFAULT_FORM_CODE;
|
|
@@ -5784,7 +6469,7 @@ async function uploadBatchStream(client, zipStreamFactory, zipSize, options) {
|
|
|
5784
6469
|
},
|
|
5785
6470
|
options?.upoVersion
|
|
5786
6471
|
);
|
|
5787
|
-
await client.batchSession.sendPartsWithStream(openResp, streamParts);
|
|
6472
|
+
await client.batchSession.sendPartsWithStream(openResp, streamParts, options?.parallelism);
|
|
5788
6473
|
await client.batchSession.closeSession(openResp.referenceNumber);
|
|
5789
6474
|
const result = await pollUntil(
|
|
5790
6475
|
() => client.sessionStatus.getSessionStatus(openResp.referenceNumber),
|
|
@@ -5859,6 +6544,7 @@ async function doExport(client, filters, options) {
|
|
|
5859
6544
|
url: p.url,
|
|
5860
6545
|
method: p.method,
|
|
5861
6546
|
partSize: p.partSize,
|
|
6547
|
+
partHash: p.partHash,
|
|
5862
6548
|
encryptedPartSize: p.encryptedPartSize,
|
|
5863
6549
|
encryptedPartHash: p.encryptedPartHash,
|
|
5864
6550
|
expirationDate: p.expirationDate
|
|
@@ -5884,6 +6570,9 @@ async function exportAndDownload(client, filters, options) {
|
|
|
5884
6570
|
throw new Error(`Download failed for part ${part.ordinalNumber}: HTTP ${resp.status}`);
|
|
5885
6571
|
}
|
|
5886
6572
|
const encryptedData = new Uint8Array(await resp.arrayBuffer());
|
|
6573
|
+
if (options?.verifyHash !== false && !verifyHash(encryptedData, part.encryptedPartHash)) {
|
|
6574
|
+
throw new Error(`Hash mismatch for export part ${part.ordinalNumber}`);
|
|
6575
|
+
}
|
|
5887
6576
|
const decrypted = client.crypto.decryptAES256(encryptedData, encData.cipherKey, encData.cipherIv);
|
|
5888
6577
|
decryptedParts.push(decrypted);
|
|
5889
6578
|
}
|
|
@@ -5956,6 +6645,9 @@ async function incrementalExportAndDownload(client, options) {
|
|
|
5956
6645
|
throw new Error(`Download failed for part ${part.ordinalNumber}: HTTP ${resp.status}`);
|
|
5957
6646
|
}
|
|
5958
6647
|
const encryptedData = new Uint8Array(await resp.arrayBuffer());
|
|
6648
|
+
if (options.verifyHash !== false && !verifyHash(encryptedData, part.encryptedPartHash)) {
|
|
6649
|
+
throw new Error(`Hash mismatch for export part ${part.ordinalNumber}`);
|
|
6650
|
+
}
|
|
5959
6651
|
const decrypted = client.crypto.decryptAES256(encryptedData, encData.cipherKey, encData.cipherIv);
|
|
5960
6652
|
decryptedParts.push(decrypted);
|
|
5961
6653
|
}
|
|
@@ -6119,7 +6811,141 @@ async function authenticateWithPkcs12(client, options) {
|
|
|
6119
6811
|
});
|
|
6120
6812
|
}
|
|
6121
6813
|
|
|
6814
|
+
// src/offline/index.ts
|
|
6815
|
+
init_deadline();
|
|
6816
|
+
init_holidays();
|
|
6817
|
+
|
|
6818
|
+
// src/offline/storage.ts
|
|
6819
|
+
function matchesFilter(invoice, filter) {
|
|
6820
|
+
if (filter.status !== void 0) {
|
|
6821
|
+
const statuses = Array.isArray(filter.status) ? filter.status : [filter.status];
|
|
6822
|
+
if (!statuses.includes(invoice.status)) return false;
|
|
6823
|
+
}
|
|
6824
|
+
if (filter.mode !== void 0 && invoice.mode !== filter.mode) return false;
|
|
6825
|
+
if (filter.sellerNip !== void 0 && invoice.sellerNip !== filter.sellerNip) return false;
|
|
6826
|
+
if (filter.expiringBefore !== void 0) {
|
|
6827
|
+
const cutoff = typeof filter.expiringBefore === "string" ? new Date(filter.expiringBefore).getTime() : filter.expiringBefore.getTime();
|
|
6828
|
+
if (new Date(invoice.submitBy).getTime() >= cutoff) return false;
|
|
6829
|
+
}
|
|
6830
|
+
return true;
|
|
6831
|
+
}
|
|
6832
|
+
var InMemoryOfflineInvoiceStorage = class {
|
|
6833
|
+
store = /* @__PURE__ */ new Map();
|
|
6834
|
+
async save(invoice) {
|
|
6835
|
+
this.store.set(invoice.id, JSON.parse(JSON.stringify(invoice)));
|
|
6836
|
+
}
|
|
6837
|
+
async get(id) {
|
|
6838
|
+
const entry = this.store.get(id);
|
|
6839
|
+
return entry ? JSON.parse(JSON.stringify(entry)) : null;
|
|
6840
|
+
}
|
|
6841
|
+
async list(filter) {
|
|
6842
|
+
const all = [...this.store.values()];
|
|
6843
|
+
if (!filter) return all.map((i) => JSON.parse(JSON.stringify(i)));
|
|
6844
|
+
return all.filter((i) => matchesFilter(i, filter)).map((i) => JSON.parse(JSON.stringify(i)));
|
|
6845
|
+
}
|
|
6846
|
+
async update(id, updates) {
|
|
6847
|
+
const existing = this.store.get(id);
|
|
6848
|
+
if (!existing) throw new Error(`Offline invoice not found: ${id}`);
|
|
6849
|
+
this.store.set(id, JSON.parse(JSON.stringify({ ...existing, ...updates })));
|
|
6850
|
+
}
|
|
6851
|
+
async delete(id) {
|
|
6852
|
+
this.store.delete(id);
|
|
6853
|
+
}
|
|
6854
|
+
};
|
|
6855
|
+
|
|
6856
|
+
// src/offline/file-storage.ts
|
|
6857
|
+
var fs2 = __toESM(require("fs/promises"), 1);
|
|
6858
|
+
var path = __toESM(require("path"), 1);
|
|
6859
|
+
var os = __toESM(require("os"), 1);
|
|
6860
|
+
function resolveDir(dir) {
|
|
6861
|
+
if (dir === "~" || dir.startsWith("~/")) {
|
|
6862
|
+
return path.join(os.homedir(), dir.slice(1));
|
|
6863
|
+
}
|
|
6864
|
+
return dir;
|
|
6865
|
+
}
|
|
6866
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
6867
|
+
function validateId(id) {
|
|
6868
|
+
if (!UUID_RE.test(id)) {
|
|
6869
|
+
throw new Error(`Invalid invoice ID: ${id}`);
|
|
6870
|
+
}
|
|
6871
|
+
}
|
|
6872
|
+
var FileOfflineInvoiceStorage = class {
|
|
6873
|
+
dir;
|
|
6874
|
+
constructor(directory) {
|
|
6875
|
+
this.dir = resolveDir(directory ?? "~/.ksef/offline");
|
|
6876
|
+
}
|
|
6877
|
+
async ensureDir() {
|
|
6878
|
+
await fs2.mkdir(this.dir, { recursive: true });
|
|
6879
|
+
}
|
|
6880
|
+
filePath(id) {
|
|
6881
|
+
validateId(id);
|
|
6882
|
+
return path.join(this.dir, `${id}.json`);
|
|
6883
|
+
}
|
|
6884
|
+
async save(invoice) {
|
|
6885
|
+
await this.ensureDir();
|
|
6886
|
+
const file = this.filePath(invoice.id);
|
|
6887
|
+
const tmp = `${file}.tmp`;
|
|
6888
|
+
await fs2.writeFile(tmp, JSON.stringify(invoice, null, 2));
|
|
6889
|
+
await fs2.rename(tmp, file);
|
|
6890
|
+
}
|
|
6891
|
+
async get(id) {
|
|
6892
|
+
const file = this.filePath(id);
|
|
6893
|
+
try {
|
|
6894
|
+
return JSON.parse(await fs2.readFile(file, "utf-8"));
|
|
6895
|
+
} catch (err) {
|
|
6896
|
+
if (err instanceof Error && "code" in err && err.code === "ENOENT") {
|
|
6897
|
+
return null;
|
|
6898
|
+
}
|
|
6899
|
+
console.warn(`Warning: Failed to read offline invoice ${id}: ${err instanceof Error ? err.message : String(err)}`);
|
|
6900
|
+
return null;
|
|
6901
|
+
}
|
|
6902
|
+
}
|
|
6903
|
+
async list(filter) {
|
|
6904
|
+
let files;
|
|
6905
|
+
try {
|
|
6906
|
+
files = (await fs2.readdir(this.dir)).filter((f) => f.endsWith(".json"));
|
|
6907
|
+
} catch {
|
|
6908
|
+
return [];
|
|
6909
|
+
}
|
|
6910
|
+
const results = [];
|
|
6911
|
+
for (const file of files) {
|
|
6912
|
+
try {
|
|
6913
|
+
const data = JSON.parse(
|
|
6914
|
+
await fs2.readFile(path.join(this.dir, file), "utf-8")
|
|
6915
|
+
);
|
|
6916
|
+
if (!filter || matchesFilter(data, filter)) {
|
|
6917
|
+
results.push(data);
|
|
6918
|
+
}
|
|
6919
|
+
} catch (err) {
|
|
6920
|
+
console.warn(`Warning: Skipping corrupt offline invoice file ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
6921
|
+
}
|
|
6922
|
+
}
|
|
6923
|
+
return results;
|
|
6924
|
+
}
|
|
6925
|
+
/**
|
|
6926
|
+
* Update invoice metadata (read-modify-write).
|
|
6927
|
+
*
|
|
6928
|
+
* Note: No file locking — concurrent updates to the same ID may cause
|
|
6929
|
+
* lost writes. Acceptable for CLI (single process). Library consumers
|
|
6930
|
+
* running parallel operations should use external locking.
|
|
6931
|
+
*/
|
|
6932
|
+
async update(id, updates) {
|
|
6933
|
+
const existing = await this.get(id);
|
|
6934
|
+
if (!existing) throw new Error(`Offline invoice not found: ${id}`);
|
|
6935
|
+
await this.save({ ...existing, ...updates });
|
|
6936
|
+
}
|
|
6937
|
+
async delete(id) {
|
|
6938
|
+
const file = this.filePath(id);
|
|
6939
|
+
try {
|
|
6940
|
+
await fs2.unlink(file);
|
|
6941
|
+
} catch (e) {
|
|
6942
|
+
if (e instanceof Error && "code" in e && e.code !== "ENOENT") throw e;
|
|
6943
|
+
}
|
|
6944
|
+
}
|
|
6945
|
+
};
|
|
6946
|
+
|
|
6122
6947
|
// src/index.ts
|
|
6948
|
+
init_offline_invoice_workflow();
|
|
6123
6949
|
init_client();
|
|
6124
6950
|
// Annotate the CommonJS export names for ESM import in node:
|
|
6125
6951
|
0 && (module.exports = {
|
|
@@ -6150,8 +6976,10 @@ init_client();
|
|
|
6150
6976
|
FORM_CODES,
|
|
6151
6977
|
FORM_CODE_KEYS,
|
|
6152
6978
|
FileHwmStore,
|
|
6979
|
+
FileOfflineInvoiceStorage,
|
|
6153
6980
|
INVOICE_TYPES_BY_SYSTEM_CODE,
|
|
6154
6981
|
InMemoryHwmStore,
|
|
6982
|
+
InMemoryOfflineInvoiceStorage,
|
|
6155
6983
|
InternalId,
|
|
6156
6984
|
InvoiceDownloadService,
|
|
6157
6985
|
InvoiceQueryFilterBuilder,
|
|
@@ -6175,6 +7003,7 @@ init_client();
|
|
|
6175
7003
|
LimitsService,
|
|
6176
7004
|
Nip,
|
|
6177
7005
|
NipVatUe,
|
|
7006
|
+
OfflineInvoiceWorkflow,
|
|
6178
7007
|
OnlineSessionService,
|
|
6179
7008
|
PERMISSION_DESCRIPTION_MAX_LENGTH,
|
|
6180
7009
|
PERMISSION_DESCRIPTION_MIN_LENGTH,
|
|
@@ -6204,6 +7033,7 @@ init_client();
|
|
|
6204
7033
|
UpoVersion,
|
|
6205
7034
|
VatUe,
|
|
6206
7035
|
VerificationLinkService,
|
|
7036
|
+
addBusinessDays,
|
|
6207
7037
|
authenticateWithCertificate,
|
|
6208
7038
|
authenticateWithExternalSignature,
|
|
6209
7039
|
authenticateWithPkcs12,
|
|
@@ -6211,6 +7041,7 @@ init_client();
|
|
|
6211
7041
|
batchValidationDetails,
|
|
6212
7042
|
buildUnsignedAuthTokenRequestXml,
|
|
6213
7043
|
calculateBackoff,
|
|
7044
|
+
calculateOfflineDeadline,
|
|
6214
7045
|
createZip,
|
|
6215
7046
|
decodeJwtPayload,
|
|
6216
7047
|
deduplicateByKsefNumber,
|
|
@@ -6220,9 +7051,16 @@ init_client();
|
|
|
6220
7051
|
defaultTransport,
|
|
6221
7052
|
exportAndDownload,
|
|
6222
7053
|
exportInvoices,
|
|
7054
|
+
extendDeadlineForMaintenance,
|
|
7055
|
+
extractInvoiceFields,
|
|
7056
|
+
getDefaultReason,
|
|
6223
7057
|
getEffectiveStartDate,
|
|
6224
7058
|
getFormCode,
|
|
7059
|
+
getPolishHolidays,
|
|
7060
|
+
getTimeUntilDeadline,
|
|
6225
7061
|
incrementalExportAndDownload,
|
|
7062
|
+
isExpired,
|
|
7063
|
+
isPolishHoliday,
|
|
6226
7064
|
isRetryableError,
|
|
6227
7065
|
isRetryableStatus,
|
|
6228
7066
|
isValidBase64,
|
|
@@ -6240,6 +7078,7 @@ init_client();
|
|
|
6240
7078
|
isValidReferenceNumber,
|
|
6241
7079
|
isValidSha256Base64,
|
|
6242
7080
|
isValidVatUe,
|
|
7081
|
+
nextBusinessDay,
|
|
6243
7082
|
openOnlineSession,
|
|
6244
7083
|
openSendAndClose,
|
|
6245
7084
|
parseFormCode,
|
|
@@ -6248,6 +7087,9 @@ init_client();
|
|
|
6248
7087
|
parseUpoXml,
|
|
6249
7088
|
pollUntil,
|
|
6250
7089
|
resolveOptions,
|
|
7090
|
+
resumeOnlineSession,
|
|
7091
|
+
runWithConcurrency,
|
|
7092
|
+
sha256Base64,
|
|
6251
7093
|
sleep,
|
|
6252
7094
|
unzip,
|
|
6253
7095
|
updateContinuationPoint,
|
|
@@ -6262,6 +7104,7 @@ init_client();
|
|
|
6262
7104
|
validatePresignedUrl,
|
|
6263
7105
|
validateSchema,
|
|
6264
7106
|
validateWellFormedness,
|
|
7107
|
+
verifyHash,
|
|
6265
7108
|
xmlToObject
|
|
6266
7109
|
});
|
|
6267
7110
|
//# sourceMappingURL=index.cjs.map
|