ksef-client-ts 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -8
- package/dist/cli.js +1359 -264
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +768 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +260 -1
- package/dist/index.d.ts +260 -1
- package/dist/index.js +747 -34
- package/dist/index.js.map +1 -1
- package/package.json +6 -2
package/dist/index.js
CHANGED
|
@@ -1106,6 +1106,36 @@ var init_batch_session = __esm({
|
|
|
1106
1106
|
});
|
|
1107
1107
|
await Promise.all(tasks);
|
|
1108
1108
|
}
|
|
1109
|
+
/**
|
|
1110
|
+
* Upload parts sequentially (not in parallel) because each part uses a
|
|
1111
|
+
* streaming body (`duplex: 'half'`). Parallel streaming uploads can cause
|
|
1112
|
+
* backpressure issues and exceed memory limits for large payloads.
|
|
1113
|
+
*/
|
|
1114
|
+
async sendPartsWithStream(openResponse, parts) {
|
|
1115
|
+
const uploadRequests = openResponse.partUploadRequests;
|
|
1116
|
+
for (const part of parts) {
|
|
1117
|
+
const uploadReq = uploadRequests.find(
|
|
1118
|
+
(r) => r.ordinalNumber === part.ordinalNumber
|
|
1119
|
+
);
|
|
1120
|
+
if (!uploadReq) {
|
|
1121
|
+
throw new Error(`No upload request found for part ${part.ordinalNumber}`);
|
|
1122
|
+
}
|
|
1123
|
+
const headers = {};
|
|
1124
|
+
for (const [k, v] of Object.entries(uploadReq.headers)) {
|
|
1125
|
+
if (v != null) headers[k] = v;
|
|
1126
|
+
}
|
|
1127
|
+
const resp = await fetch(uploadReq.url, {
|
|
1128
|
+
method: uploadReq.method,
|
|
1129
|
+
headers,
|
|
1130
|
+
body: part.dataStream,
|
|
1131
|
+
// @ts-expect-error -- Node 18+ undici supports duplex for streaming body
|
|
1132
|
+
duplex: "half"
|
|
1133
|
+
});
|
|
1134
|
+
if (!resp.ok) {
|
|
1135
|
+
throw new Error(`Upload failed for part ${part.ordinalNumber}: HTTP ${resp.status}`);
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1109
1139
|
async closeSession(batchRef) {
|
|
1110
1140
|
const req = RestRequest.post(Routes.Sessions.Batch.close(batchRef));
|
|
1111
1141
|
await this.restClient.executeVoid(req);
|
|
@@ -1803,6 +1833,28 @@ var init_cryptography_service = __esm({
|
|
|
1803
1833
|
const cipher = crypto2.createCipheriv("aes-256-cbc", key, iv);
|
|
1804
1834
|
return new Uint8Array(Buffer.concat([cipher.update(content), cipher.final()]));
|
|
1805
1835
|
}
|
|
1836
|
+
/**
|
|
1837
|
+
* Encrypt with AES-256-CBC as a streaming transform.
|
|
1838
|
+
* Returns a ReadableStream that yields encrypted chunks as the input is read.
|
|
1839
|
+
*/
|
|
1840
|
+
encryptAES256Stream(input, key, iv) {
|
|
1841
|
+
const cipher = crypto2.createCipheriv("aes-256-cbc", key, iv);
|
|
1842
|
+
const transform = new TransformStream({
|
|
1843
|
+
transform(chunk, controller) {
|
|
1844
|
+
const encrypted = cipher.update(chunk);
|
|
1845
|
+
if (encrypted.length > 0) {
|
|
1846
|
+
controller.enqueue(new Uint8Array(encrypted));
|
|
1847
|
+
}
|
|
1848
|
+
},
|
|
1849
|
+
flush(controller) {
|
|
1850
|
+
const final = cipher.final();
|
|
1851
|
+
if (final.length > 0) {
|
|
1852
|
+
controller.enqueue(new Uint8Array(final));
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
});
|
|
1856
|
+
return input.pipeThrough(transform);
|
|
1857
|
+
}
|
|
1806
1858
|
/** Decrypt with AES-256-CBC. */
|
|
1807
1859
|
decryptAES256(content, key, iv) {
|
|
1808
1860
|
const decipher = crypto2.createDecipheriv("aes-256-cbc", key, iv);
|
|
@@ -1875,6 +1927,23 @@ var init_cryptography_service = __esm({
|
|
|
1875
1927
|
const hash = crypto2.createHash("sha256").update(file).digest("base64");
|
|
1876
1928
|
return { hashSHA: hash, fileSize: file.length };
|
|
1877
1929
|
}
|
|
1930
|
+
/** Compute SHA-256 hash (base64) and byte length from a ReadableStream. */
|
|
1931
|
+
async getFileMetadataFromStream(stream) {
|
|
1932
|
+
const hash = crypto2.createHash("sha256");
|
|
1933
|
+
let fileSize = 0;
|
|
1934
|
+
const reader = stream.getReader();
|
|
1935
|
+
try {
|
|
1936
|
+
for (; ; ) {
|
|
1937
|
+
const { done, value } = await reader.read();
|
|
1938
|
+
if (done) break;
|
|
1939
|
+
hash.update(value);
|
|
1940
|
+
fileSize += value.byteLength;
|
|
1941
|
+
}
|
|
1942
|
+
} finally {
|
|
1943
|
+
reader.releaseLock();
|
|
1944
|
+
}
|
|
1945
|
+
return { hashSHA: hash.digest("base64"), fileSize };
|
|
1946
|
+
}
|
|
1878
1947
|
// ---------------------------------------------------------------------------
|
|
1879
1948
|
// CSR generation
|
|
1880
1949
|
// ---------------------------------------------------------------------------
|
|
@@ -6789,6 +6858,32 @@ var init_pkcs12_loader = __esm({
|
|
|
6789
6858
|
}
|
|
6790
6859
|
});
|
|
6791
6860
|
|
|
6861
|
+
// src/crypto/auth-xml-builder.ts
|
|
6862
|
+
function buildUnsignedAuthTokenRequestXml(options) {
|
|
6863
|
+
const { challenge, contextIdentifier, subjectIdentifierType = "certificateSubject" } = options;
|
|
6864
|
+
const identifierElement = `<${contextIdentifier.type}>${xmlEscape(contextIdentifier.value)}</${contextIdentifier.type}>`;
|
|
6865
|
+
return [
|
|
6866
|
+
'<?xml version="1.0" encoding="utf-8"?>',
|
|
6867
|
+
`<AuthTokenRequest xmlns="${AUTH_TOKEN_REQUEST_NS}">`,
|
|
6868
|
+
`<Challenge>${xmlEscape(challenge)}</Challenge>`,
|
|
6869
|
+
`<ContextIdentifier>`,
|
|
6870
|
+
identifierElement,
|
|
6871
|
+
`</ContextIdentifier>`,
|
|
6872
|
+
`<SubjectIdentifierType>${xmlEscape(subjectIdentifierType)}</SubjectIdentifierType>`,
|
|
6873
|
+
`</AuthTokenRequest>`
|
|
6874
|
+
].join("");
|
|
6875
|
+
}
|
|
6876
|
+
function xmlEscape(str) {
|
|
6877
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
6878
|
+
}
|
|
6879
|
+
var AUTH_TOKEN_REQUEST_NS;
|
|
6880
|
+
var init_auth_xml_builder = __esm({
|
|
6881
|
+
"src/crypto/auth-xml-builder.ts"() {
|
|
6882
|
+
"use strict";
|
|
6883
|
+
AUTH_TOKEN_REQUEST_NS = "http://ksef.mf.gov.pl/auth/token/2.0";
|
|
6884
|
+
}
|
|
6885
|
+
});
|
|
6886
|
+
|
|
6792
6887
|
// src/qr/verification-link-service.ts
|
|
6793
6888
|
import crypto5 from "crypto";
|
|
6794
6889
|
var VerificationLinkService;
|
|
@@ -6853,19 +6948,11 @@ __export(client_exports, {
|
|
|
6853
6948
|
buildAuthTokenRequestXml: () => buildAuthTokenRequestXml
|
|
6854
6949
|
});
|
|
6855
6950
|
function buildAuthTokenRequestXml(challenge, nip, subjectIdentifierType = "certificateSubject") {
|
|
6856
|
-
return
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
`<Nip>${xmlEscape(nip)}</Nip>`,
|
|
6862
|
-
`</ContextIdentifier>`,
|
|
6863
|
-
`<SubjectIdentifierType>${xmlEscape(subjectIdentifierType)}</SubjectIdentifierType>`,
|
|
6864
|
-
`</AuthTokenRequest>`
|
|
6865
|
-
].join("");
|
|
6866
|
-
}
|
|
6867
|
-
function xmlEscape(str) {
|
|
6868
|
-
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
6951
|
+
return buildUnsignedAuthTokenRequestXml({
|
|
6952
|
+
challenge,
|
|
6953
|
+
contextIdentifier: { type: "Nip", value: nip },
|
|
6954
|
+
subjectIdentifierType
|
|
6955
|
+
});
|
|
6869
6956
|
}
|
|
6870
6957
|
function buildRestClientConfig(options, authManager) {
|
|
6871
6958
|
const config = { authManager };
|
|
@@ -6892,7 +6979,7 @@ function buildRestClientConfig(options, authManager) {
|
|
|
6892
6979
|
}
|
|
6893
6980
|
return config;
|
|
6894
6981
|
}
|
|
6895
|
-
var KSeFClient
|
|
6982
|
+
var KSeFClient;
|
|
6896
6983
|
var init_client = __esm({
|
|
6897
6984
|
"src/client.ts"() {
|
|
6898
6985
|
"use strict";
|
|
@@ -6918,6 +7005,7 @@ var init_client = __esm({
|
|
|
6918
7005
|
init_certificate_fetcher();
|
|
6919
7006
|
init_cryptography_service();
|
|
6920
7007
|
init_verification_link_service();
|
|
7008
|
+
init_auth_xml_builder();
|
|
6921
7009
|
KSeFClient = class {
|
|
6922
7010
|
auth;
|
|
6923
7011
|
activeSessions;
|
|
@@ -7011,7 +7099,6 @@ var init_client = __esm({
|
|
|
7011
7099
|
this.authManager.setRefreshToken(void 0);
|
|
7012
7100
|
}
|
|
7013
7101
|
};
|
|
7014
|
-
AUTH_TOKEN_REQUEST_NS = "http://ksef.mf.gov.pl/auth/token/2.0";
|
|
7015
7102
|
}
|
|
7016
7103
|
});
|
|
7017
7104
|
|
|
@@ -7145,6 +7232,65 @@ var SUBUNIT_NAME_MAX_LENGTH = 256;
|
|
|
7145
7232
|
var PERMISSION_DESCRIPTION_MIN_LENGTH = 5;
|
|
7146
7233
|
var PERMISSION_DESCRIPTION_MAX_LENGTH = 256;
|
|
7147
7234
|
|
|
7235
|
+
// src/models/document-structures/types.ts
|
|
7236
|
+
var SystemCode = {
|
|
7237
|
+
FA_2: "FA (2)",
|
|
7238
|
+
FA_3: "FA (3)",
|
|
7239
|
+
PEF_3: "PEF (3)",
|
|
7240
|
+
PEF_KOR_3: "PEF_KOR (3)",
|
|
7241
|
+
FA_RR_1: "FA_RR (1)"
|
|
7242
|
+
};
|
|
7243
|
+
var FORM_CODES = {
|
|
7244
|
+
FA_2: { systemCode: "FA (2)", schemaVersion: "1-0E", value: "FA" },
|
|
7245
|
+
FA_3: { systemCode: "FA (3)", schemaVersion: "1-0E", value: "FA" },
|
|
7246
|
+
PEF_3: { systemCode: "PEF (3)", schemaVersion: "2-1", value: "PEF" },
|
|
7247
|
+
PEF_KOR_3: { systemCode: "PEF_KOR (3)", schemaVersion: "2-1", value: "PEF" },
|
|
7248
|
+
FA_RR_1_LEGACY: { systemCode: "FA_RR (1)", schemaVersion: "1-0E", value: "RR" },
|
|
7249
|
+
FA_RR_1_TRANSITION: { systemCode: "FA_RR (1)", schemaVersion: "1-1E", value: "RR" },
|
|
7250
|
+
FA_RR_1: { systemCode: "FA_RR (1)", schemaVersion: "1-1E", value: "FA_RR" }
|
|
7251
|
+
};
|
|
7252
|
+
var INVOICE_TYPES_BY_SYSTEM_CODE = {
|
|
7253
|
+
[SystemCode.FA_2]: ["Vat", "Zal", "Kor", "Roz", "Upr", "KorZal", "KorRoz"],
|
|
7254
|
+
[SystemCode.FA_3]: ["Vat", "Zal", "Kor", "Roz", "Upr", "KorZal", "KorRoz"],
|
|
7255
|
+
[SystemCode.PEF_3]: ["VatPef", "VatPefSp", "KorPef"],
|
|
7256
|
+
[SystemCode.PEF_KOR_3]: ["KorPef"],
|
|
7257
|
+
[SystemCode.FA_RR_1]: ["VatRr", "KorVatRr"]
|
|
7258
|
+
};
|
|
7259
|
+
var FORM_CODE_KEYS = {
|
|
7260
|
+
FA2: FORM_CODES.FA_2,
|
|
7261
|
+
FA3: FORM_CODES.FA_3,
|
|
7262
|
+
PEF3: FORM_CODES.PEF_3,
|
|
7263
|
+
PEFKOR3: FORM_CODES.PEF_KOR_3,
|
|
7264
|
+
FARR1: FORM_CODES.FA_RR_1
|
|
7265
|
+
};
|
|
7266
|
+
|
|
7267
|
+
// src/models/document-structures/helpers.ts
|
|
7268
|
+
var SYSTEM_CODE_TO_FORM_CODE = {
|
|
7269
|
+
[SystemCode.FA_2]: FORM_CODES.FA_2,
|
|
7270
|
+
[SystemCode.FA_3]: FORM_CODES.FA_3,
|
|
7271
|
+
[SystemCode.PEF_3]: FORM_CODES.PEF_3,
|
|
7272
|
+
[SystemCode.PEF_KOR_3]: FORM_CODES.PEF_KOR_3,
|
|
7273
|
+
[SystemCode.FA_RR_1]: FORM_CODES.FA_RR_1
|
|
7274
|
+
};
|
|
7275
|
+
var ALL_FORM_CODES = Object.values(FORM_CODES);
|
|
7276
|
+
var BATCH_DISALLOWED_SYSTEM_CODES = /* @__PURE__ */ new Set([
|
|
7277
|
+
SystemCode.PEF_3,
|
|
7278
|
+
SystemCode.PEF_KOR_3
|
|
7279
|
+
]);
|
|
7280
|
+
function getFormCode(systemCode) {
|
|
7281
|
+
return SYSTEM_CODE_TO_FORM_CODE[systemCode];
|
|
7282
|
+
}
|
|
7283
|
+
function parseFormCode(raw) {
|
|
7284
|
+
const match = ALL_FORM_CODES.find(
|
|
7285
|
+
(fc) => fc.systemCode === raw.systemCode && fc.schemaVersion === raw.schemaVersion && fc.value === raw.value
|
|
7286
|
+
);
|
|
7287
|
+
return match ?? raw;
|
|
7288
|
+
}
|
|
7289
|
+
function validateFormCodeForSession(formCode, sessionType) {
|
|
7290
|
+
if (sessionType === "online") return true;
|
|
7291
|
+
return !BATCH_DISALLOWED_SYSTEM_CODES.has(formCode.systemCode);
|
|
7292
|
+
}
|
|
7293
|
+
|
|
7148
7294
|
// src/services/index.ts
|
|
7149
7295
|
init_auth();
|
|
7150
7296
|
init_active_sessions();
|
|
@@ -7561,6 +7707,108 @@ var BatchFileBuilder = class {
|
|
|
7561
7707
|
encryptedParts
|
|
7562
7708
|
};
|
|
7563
7709
|
}
|
|
7710
|
+
/**
|
|
7711
|
+
* Stream-based variant: two-pass build from a stream factory.
|
|
7712
|
+
*
|
|
7713
|
+
* Pass 1: consume stream to compute ZIP hash.
|
|
7714
|
+
* Pass 2: re-create stream, split into parts, encrypt each, compute per-part metadata.
|
|
7715
|
+
*
|
|
7716
|
+
* @param zipStreamFactory - Factory that creates a fresh ReadableStream of the ZIP data
|
|
7717
|
+
* @param zipSize - Total ZIP byte size (from fs.stat or known in advance)
|
|
7718
|
+
* @param encryptStreamFn - Stream-to-stream AES-256-CBC encryption function
|
|
7719
|
+
* @param hashStreamFn - Stream-to-FileMetadata hashing function
|
|
7720
|
+
* @param options - Optional configuration
|
|
7721
|
+
*/
|
|
7722
|
+
static async buildFromStream(zipStreamFactory, zipSize, encryptStreamFn, hashStreamFn, options) {
|
|
7723
|
+
const maxPartSize = options?.maxPartSize ?? BATCH_MAX_PART_SIZE;
|
|
7724
|
+
if (maxPartSize <= 0) {
|
|
7725
|
+
throw new KSeFValidationError("maxPartSize must be a positive number");
|
|
7726
|
+
}
|
|
7727
|
+
if (zipSize === 0) {
|
|
7728
|
+
throw new KSeFValidationError("ZIP data must not be empty");
|
|
7729
|
+
}
|
|
7730
|
+
if (zipSize > BATCH_MAX_TOTAL_SIZE) {
|
|
7731
|
+
throw new KSeFValidationError(
|
|
7732
|
+
`ZIP size ${zipSize} exceeds maximum of ${BATCH_MAX_TOTAL_SIZE} bytes (5 GB)`
|
|
7733
|
+
);
|
|
7734
|
+
}
|
|
7735
|
+
const partCount = Math.ceil(zipSize / maxPartSize);
|
|
7736
|
+
if (partCount > BATCH_MAX_PARTS) {
|
|
7737
|
+
throw new KSeFValidationError(
|
|
7738
|
+
`Data requires ${partCount} parts, exceeding maximum of ${BATCH_MAX_PARTS}`
|
|
7739
|
+
);
|
|
7740
|
+
}
|
|
7741
|
+
const zipMeta = await hashStreamFn(zipStreamFactory());
|
|
7742
|
+
const fileParts = [];
|
|
7743
|
+
const streamParts = [];
|
|
7744
|
+
const reader = zipStreamFactory().getReader();
|
|
7745
|
+
let pending = [];
|
|
7746
|
+
let pendingSize = 0;
|
|
7747
|
+
for (let partIndex = 0; partIndex < partCount; partIndex++) {
|
|
7748
|
+
const isLastPart = partIndex === partCount - 1;
|
|
7749
|
+
const targetSize = isLastPart ? zipSize - partIndex * maxPartSize : maxPartSize;
|
|
7750
|
+
while (pendingSize < targetSize) {
|
|
7751
|
+
const { done, value } = await reader.read();
|
|
7752
|
+
if (done) break;
|
|
7753
|
+
pending.push(value);
|
|
7754
|
+
pendingSize += value.byteLength;
|
|
7755
|
+
}
|
|
7756
|
+
const buffer = new Uint8Array(Buffer.concat(pending));
|
|
7757
|
+
const partData = buffer.subarray(0, targetSize);
|
|
7758
|
+
if (buffer.byteLength > targetSize) {
|
|
7759
|
+
const remainder = buffer.subarray(targetSize);
|
|
7760
|
+
pending = [remainder];
|
|
7761
|
+
pendingSize = remainder.byteLength;
|
|
7762
|
+
} else {
|
|
7763
|
+
pending = [];
|
|
7764
|
+
pendingSize = 0;
|
|
7765
|
+
}
|
|
7766
|
+
const partStream = new ReadableStream({
|
|
7767
|
+
start(controller) {
|
|
7768
|
+
controller.enqueue(partData);
|
|
7769
|
+
controller.close();
|
|
7770
|
+
}
|
|
7771
|
+
});
|
|
7772
|
+
const encryptedStream = encryptStreamFn(partStream);
|
|
7773
|
+
const encryptedChunks = [];
|
|
7774
|
+
const encReader = encryptedStream.getReader();
|
|
7775
|
+
for (; ; ) {
|
|
7776
|
+
const { done, value } = await encReader.read();
|
|
7777
|
+
if (done) break;
|
|
7778
|
+
encryptedChunks.push(value);
|
|
7779
|
+
}
|
|
7780
|
+
const encryptedData = new Uint8Array(Buffer.concat(encryptedChunks));
|
|
7781
|
+
const encryptedMeta = sha256Base64(encryptedData);
|
|
7782
|
+
fileParts.push({
|
|
7783
|
+
ordinalNumber: partIndex + 1,
|
|
7784
|
+
fileSize: encryptedData.byteLength,
|
|
7785
|
+
fileHash: encryptedMeta
|
|
7786
|
+
});
|
|
7787
|
+
const uploadStream = new ReadableStream({
|
|
7788
|
+
start(controller) {
|
|
7789
|
+
controller.enqueue(encryptedData);
|
|
7790
|
+
controller.close();
|
|
7791
|
+
}
|
|
7792
|
+
});
|
|
7793
|
+
streamParts.push({
|
|
7794
|
+
dataStream: uploadStream,
|
|
7795
|
+
metadata: {
|
|
7796
|
+
hashSHA: encryptedMeta,
|
|
7797
|
+
fileSize: encryptedData.byteLength
|
|
7798
|
+
},
|
|
7799
|
+
ordinalNumber: partIndex + 1
|
|
7800
|
+
});
|
|
7801
|
+
}
|
|
7802
|
+
reader.releaseLock();
|
|
7803
|
+
return {
|
|
7804
|
+
batchFile: {
|
|
7805
|
+
fileSize: zipSize,
|
|
7806
|
+
fileHash: zipMeta.hashSHA,
|
|
7807
|
+
fileParts
|
|
7808
|
+
},
|
|
7809
|
+
streamParts
|
|
7810
|
+
};
|
|
7811
|
+
}
|
|
7564
7812
|
};
|
|
7565
7813
|
function splitBuffer(data, maxPartSize) {
|
|
7566
7814
|
if (data.length <= maxPartSize) {
|
|
@@ -7664,6 +7912,7 @@ ${lines.join("\n")}
|
|
|
7664
7912
|
|
|
7665
7913
|
// src/crypto/index.ts
|
|
7666
7914
|
init_pkcs12_loader();
|
|
7915
|
+
init_auth_xml_builder();
|
|
7667
7916
|
|
|
7668
7917
|
// src/qr/index.ts
|
|
7669
7918
|
init_verification_link_service();
|
|
@@ -7727,6 +7976,94 @@ function escapeXml2(str) {
|
|
|
7727
7976
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
7728
7977
|
}
|
|
7729
7978
|
|
|
7979
|
+
// src/utils/zip.ts
|
|
7980
|
+
import { ZipFile } from "yazl";
|
|
7981
|
+
import { fromBuffer } from "yauzl";
|
|
7982
|
+
var DEFAULT_UNZIP_OPTIONS = {
|
|
7983
|
+
maxFiles: 1e4,
|
|
7984
|
+
maxTotalUncompressedSize: 2e9,
|
|
7985
|
+
maxFileUncompressedSize: 5e8,
|
|
7986
|
+
maxCompressionRatio: 200
|
|
7987
|
+
};
|
|
7988
|
+
async function createZip(entries) {
|
|
7989
|
+
return new Promise((resolve, reject) => {
|
|
7990
|
+
const zipfile = new ZipFile();
|
|
7991
|
+
for (const entry of entries) {
|
|
7992
|
+
zipfile.addBuffer(Buffer.from(entry.content), entry.fileName);
|
|
7993
|
+
}
|
|
7994
|
+
const chunks = [];
|
|
7995
|
+
zipfile.outputStream.on("data", (chunk) => chunks.push(chunk));
|
|
7996
|
+
zipfile.outputStream.on("error", (err) => reject(err));
|
|
7997
|
+
zipfile.outputStream.on("end", () => resolve(Buffer.concat(chunks)));
|
|
7998
|
+
zipfile.end();
|
|
7999
|
+
});
|
|
8000
|
+
}
|
|
8001
|
+
async function unzip(buffer, options = {}) {
|
|
8002
|
+
const limits = { ...DEFAULT_UNZIP_OPTIONS, ...options };
|
|
8003
|
+
return new Promise((resolve, reject) => {
|
|
8004
|
+
fromBuffer(buffer, { lazyEntries: true }, (err, zipfile) => {
|
|
8005
|
+
if (err || !zipfile) {
|
|
8006
|
+
reject(err ?? new Error("Failed to open zip buffer"));
|
|
8007
|
+
return;
|
|
8008
|
+
}
|
|
8009
|
+
const files = /* @__PURE__ */ new Map();
|
|
8010
|
+
let totalUncompressed = 0;
|
|
8011
|
+
zipfile.readEntry();
|
|
8012
|
+
zipfile.on("entry", (entry) => {
|
|
8013
|
+
if (entry.fileName.endsWith("/")) {
|
|
8014
|
+
zipfile.readEntry();
|
|
8015
|
+
return;
|
|
8016
|
+
}
|
|
8017
|
+
if (limits.maxFiles > 0 && files.size >= limits.maxFiles) {
|
|
8018
|
+
reject(new Error("zip contains too many files"));
|
|
8019
|
+
return;
|
|
8020
|
+
}
|
|
8021
|
+
const uncompressedSize = entry.uncompressedSize ?? 0;
|
|
8022
|
+
const compressedSize = entry.compressedSize ?? 0;
|
|
8023
|
+
if (limits.maxFileUncompressedSize > 0 && uncompressedSize > limits.maxFileUncompressedSize) {
|
|
8024
|
+
reject(new Error("zip entry exceeds max_file_uncompressed_size"));
|
|
8025
|
+
return;
|
|
8026
|
+
}
|
|
8027
|
+
if (limits.maxTotalUncompressedSize > 0) {
|
|
8028
|
+
totalUncompressed += uncompressedSize;
|
|
8029
|
+
if (totalUncompressed > limits.maxTotalUncompressedSize) {
|
|
8030
|
+
reject(new Error("zip exceeds max_total_uncompressed_size"));
|
|
8031
|
+
return;
|
|
8032
|
+
}
|
|
8033
|
+
}
|
|
8034
|
+
if (limits.maxCompressionRatio !== null) {
|
|
8035
|
+
if (compressedSize === 0 && uncompressedSize > 0) {
|
|
8036
|
+
reject(new Error("zip entry has suspicious compression metadata"));
|
|
8037
|
+
return;
|
|
8038
|
+
}
|
|
8039
|
+
if (compressedSize > 0 && uncompressedSize > 0) {
|
|
8040
|
+
const ratio = uncompressedSize / compressedSize;
|
|
8041
|
+
if (ratio > limits.maxCompressionRatio) {
|
|
8042
|
+
reject(new Error("zip entry exceeds max_compression_ratio"));
|
|
8043
|
+
return;
|
|
8044
|
+
}
|
|
8045
|
+
}
|
|
8046
|
+
}
|
|
8047
|
+
zipfile.openReadStream(entry, (streamErr, stream) => {
|
|
8048
|
+
if (streamErr || !stream) {
|
|
8049
|
+
reject(streamErr ?? new Error("Failed to read zip entry"));
|
|
8050
|
+
return;
|
|
8051
|
+
}
|
|
8052
|
+
const chunks = [];
|
|
8053
|
+
stream.on("data", (chunk) => chunks.push(chunk));
|
|
8054
|
+
stream.on("error", (streamError) => reject(streamError));
|
|
8055
|
+
stream.on("end", () => {
|
|
8056
|
+
files.set(entry.fileName, Buffer.concat(chunks));
|
|
8057
|
+
zipfile.readEntry();
|
|
8058
|
+
});
|
|
8059
|
+
});
|
|
8060
|
+
});
|
|
8061
|
+
zipfile.on("end", () => resolve(files));
|
|
8062
|
+
zipfile.on("error", (zipErr) => reject(zipErr));
|
|
8063
|
+
});
|
|
8064
|
+
});
|
|
8065
|
+
}
|
|
8066
|
+
|
|
7730
8067
|
// src/workflows/polling.ts
|
|
7731
8068
|
async function pollUntil(action, condition, options) {
|
|
7732
8069
|
const intervalMs = options?.intervalMs ?? 2e3;
|
|
@@ -7744,17 +8081,150 @@ async function pollUntil(action, condition, options) {
|
|
|
7744
8081
|
);
|
|
7745
8082
|
}
|
|
7746
8083
|
|
|
8084
|
+
// src/xml/upo-parser.ts
|
|
8085
|
+
init_ksef_validation_error();
|
|
8086
|
+
import { XMLParser } from "fast-xml-parser";
|
|
8087
|
+
function isRecord(value) {
|
|
8088
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8089
|
+
}
|
|
8090
|
+
function requireRecord(value, at) {
|
|
8091
|
+
if (!isRecord(value)) {
|
|
8092
|
+
throw KSeFValidationError.fromField(at, `Expected object at ${at}`);
|
|
8093
|
+
}
|
|
8094
|
+
return value;
|
|
8095
|
+
}
|
|
8096
|
+
function requireString(value, at) {
|
|
8097
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
8098
|
+
throw KSeFValidationError.fromField(at, `Expected non-empty string at ${at}`);
|
|
8099
|
+
}
|
|
8100
|
+
return value;
|
|
8101
|
+
}
|
|
8102
|
+
function optionalRecord(value) {
|
|
8103
|
+
return isRecord(value) ? value : void 0;
|
|
8104
|
+
}
|
|
8105
|
+
function optionalString(value) {
|
|
8106
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
8107
|
+
}
|
|
8108
|
+
function requireNumberFromString(value, at) {
|
|
8109
|
+
const raw = requireString(value, at);
|
|
8110
|
+
const parsed = Number.parseInt(raw, 10);
|
|
8111
|
+
if (!Number.isFinite(parsed)) {
|
|
8112
|
+
throw KSeFValidationError.fromField(at, `Expected integer at ${at}, got "${raw}"`);
|
|
8113
|
+
}
|
|
8114
|
+
return parsed;
|
|
8115
|
+
}
|
|
8116
|
+
function ensureArray(value) {
|
|
8117
|
+
if (value == null) return [];
|
|
8118
|
+
return Array.isArray(value) ? value : [value];
|
|
8119
|
+
}
|
|
8120
|
+
function parseIdKontekstu(obj) {
|
|
8121
|
+
const nip = optionalString(obj.Nip);
|
|
8122
|
+
if (nip) return { kind: "Nip", nip };
|
|
8123
|
+
const idWewnetrzny = optionalString(obj.IdWewnetrzny);
|
|
8124
|
+
if (idWewnetrzny) return { kind: "IdWewnetrzny", idWewnetrzny };
|
|
8125
|
+
const idZlozonyVatUE = optionalString(obj.IdZlozonyVatUE);
|
|
8126
|
+
if (idZlozonyVatUE) return { kind: "IdZlozonyVatUE", idZlozonyVatUE };
|
|
8127
|
+
const idDostawcyUslugPeppol = optionalString(obj.IdDostawcyUslugPeppol);
|
|
8128
|
+
if (idDostawcyUslugPeppol) return { kind: "IdDostawcyUslugPeppol", idDostawcyUslugPeppol };
|
|
8129
|
+
throw KSeFValidationError.fromField(
|
|
8130
|
+
"Uwierzytelnienie.IdKontekstu",
|
|
8131
|
+
"Unsupported context identifier. Expected Nip | IdWewnetrzny | IdZlozonyVatUE | IdDostawcyUslugPeppol"
|
|
8132
|
+
);
|
|
8133
|
+
}
|
|
8134
|
+
function parseProof(obj) {
|
|
8135
|
+
const tokenRef = optionalString(obj.NumerReferencyjnyTokenaKSeF);
|
|
8136
|
+
if (tokenRef) return { kind: "NumerReferencyjnyTokenaKSeF", numerReferencyjnyTokenaKSeF: tokenRef };
|
|
8137
|
+
const docHash = optionalString(obj.SkrotDokumentuUwierzytelniajacego);
|
|
8138
|
+
if (docHash) return { kind: "SkrotDokumentuUwierzytelniajacego", skrotDokumentuUwierzytelniajacego: docHash };
|
|
8139
|
+
throw KSeFValidationError.fromField(
|
|
8140
|
+
"Uwierzytelnienie",
|
|
8141
|
+
"Unsupported auth proof. Expected NumerReferencyjnyTokenaKSeF | SkrotDokumentuUwierzytelniajacego"
|
|
8142
|
+
);
|
|
8143
|
+
}
|
|
8144
|
+
function parseUwierzytelnienie(obj) {
|
|
8145
|
+
const idKontekstu = parseIdKontekstu(
|
|
8146
|
+
requireRecord(obj.IdKontekstu, "Uwierzytelnienie.IdKontekstu")
|
|
8147
|
+
);
|
|
8148
|
+
const proof = parseProof(obj);
|
|
8149
|
+
return { idKontekstu, proof };
|
|
8150
|
+
}
|
|
8151
|
+
function parseOpisPotwierdzenia(obj) {
|
|
8152
|
+
return {
|
|
8153
|
+
strona: requireNumberFromString(obj.Strona, "OpisPotwierdzenia.Strona"),
|
|
8154
|
+
liczbaStron: requireNumberFromString(obj.LiczbaStron, "OpisPotwierdzenia.LiczbaStron"),
|
|
8155
|
+
zakresDokumentowOd: requireNumberFromString(obj.ZakresDokumentowOd, "OpisPotwierdzenia.ZakresDokumentowOd"),
|
|
8156
|
+
zakresDokumentowDo: requireNumberFromString(obj.ZakresDokumentowDo, "OpisPotwierdzenia.ZakresDokumentowDo"),
|
|
8157
|
+
calkowitaLiczbaDokumentow: requireNumberFromString(obj.CalkowitaLiczbaDokumentow, "OpisPotwierdzenia.CalkowitaLiczbaDokumentow")
|
|
8158
|
+
};
|
|
8159
|
+
}
|
|
8160
|
+
function parseDokument(obj) {
|
|
8161
|
+
return {
|
|
8162
|
+
nipSprzedawcy: requireString(obj.NipSprzedawcy, "Dokument.NipSprzedawcy"),
|
|
8163
|
+
numerKSeFDokumentu: requireString(obj.NumerKSeFDokumentu, "Dokument.NumerKSeFDokumentu"),
|
|
8164
|
+
numerFaktury: requireString(obj.NumerFaktury, "Dokument.NumerFaktury"),
|
|
8165
|
+
dataWystawieniaFaktury: requireString(obj.DataWystawieniaFaktury, "Dokument.DataWystawieniaFaktury"),
|
|
8166
|
+
dataPrzeslaniaDokumentu: requireString(obj.DataPrzeslaniaDokumentu, "Dokument.DataPrzeslaniaDokumentu"),
|
|
8167
|
+
dataNadaniaNumeruKSeF: requireString(obj.DataNadaniaNumeruKSeF, "Dokument.DataNadaniaNumeruKSeF"),
|
|
8168
|
+
skrotDokumentu: requireString(obj.SkrotDokumentu, "Dokument.SkrotDokumentu"),
|
|
8169
|
+
trybWysylki: requireString(obj.TrybWysylki, "Dokument.TrybWysylki")
|
|
8170
|
+
};
|
|
8171
|
+
}
|
|
8172
|
+
var upoParser = new XMLParser({
|
|
8173
|
+
ignoreAttributes: false,
|
|
8174
|
+
attributeNamePrefix: "@_",
|
|
8175
|
+
parseTagValue: false,
|
|
8176
|
+
parseAttributeValue: false,
|
|
8177
|
+
removeNSPrefix: true,
|
|
8178
|
+
trimValues: false
|
|
8179
|
+
});
|
|
8180
|
+
function parseUpoXml(xml) {
|
|
8181
|
+
const text = Buffer.isBuffer(xml) ? xml.toString("utf8") : xml;
|
|
8182
|
+
const parsed = upoParser.parse(text);
|
|
8183
|
+
const potwierdzenie = requireRecord(parsed?.Potwierdzenie, "Potwierdzenie");
|
|
8184
|
+
const opisPotwierdzenia = optionalRecord(potwierdzenie.OpisPotwierdzenia);
|
|
8185
|
+
const dokumenty = ensureArray(potwierdzenie.Dokument).map(
|
|
8186
|
+
(doc, i) => parseDokument(requireRecord(doc, `Dokument[${i}]`))
|
|
8187
|
+
);
|
|
8188
|
+
if (dokumenty.length === 0) {
|
|
8189
|
+
throw KSeFValidationError.fromField("Dokument", "Expected at least one Dokument in UPO");
|
|
8190
|
+
}
|
|
8191
|
+
return {
|
|
8192
|
+
nazwaPodmiotuPrzyjmujacego: requireString(potwierdzenie.NazwaPodmiotuPrzyjmujacego, "NazwaPodmiotuPrzyjmujacego"),
|
|
8193
|
+
numerReferencyjnySesji: requireString(potwierdzenie.NumerReferencyjnySesji, "NumerReferencyjnySesji"),
|
|
8194
|
+
uwierzytelnienie: parseUwierzytelnienie(requireRecord(potwierdzenie.Uwierzytelnienie, "Uwierzytelnienie")),
|
|
8195
|
+
...opisPotwierdzenia && { opisPotwierdzenia: parseOpisPotwierdzenia(opisPotwierdzenia) },
|
|
8196
|
+
nazwaStrukturyLogicznej: requireString(potwierdzenie.NazwaStrukturyLogicznej, "NazwaStrukturyLogicznej"),
|
|
8197
|
+
kodFormularza: requireString(potwierdzenie.KodFormularza, "KodFormularza"),
|
|
8198
|
+
dokumenty
|
|
8199
|
+
};
|
|
8200
|
+
}
|
|
8201
|
+
|
|
7747
8202
|
// src/workflows/online-session-workflow.ts
|
|
7748
|
-
var DEFAULT_FORM_CODE = { systemCode: "FA", schemaVersion: "3", value: "FA (3)" };
|
|
7749
8203
|
async function openOnlineSession(client, options) {
|
|
7750
8204
|
await client.crypto.init();
|
|
7751
8205
|
const encData = client.crypto.getEncryptionData();
|
|
7752
|
-
const formCode = options?.formCode ??
|
|
8206
|
+
const formCode = options?.formCode ?? FORM_CODES.FA_2;
|
|
7753
8207
|
const openResp = await client.onlineSession.openSession(
|
|
7754
8208
|
{ formCode, encryption: encData.encryptionInfo },
|
|
7755
8209
|
options?.upoVersion
|
|
7756
8210
|
);
|
|
7757
8211
|
const sessionRef = openResp.referenceNumber;
|
|
8212
|
+
async function fetchUpo(pollOpts) {
|
|
8213
|
+
const result = await pollUntil(
|
|
8214
|
+
() => client.sessionStatus.getSessionStatus(sessionRef),
|
|
8215
|
+
(s) => s.status.code === 200 || s.status.code >= 400,
|
|
8216
|
+
{ ...pollOpts, description: `UPO for session ${sessionRef}` }
|
|
8217
|
+
);
|
|
8218
|
+
if (result.status.code !== 200) {
|
|
8219
|
+
throw new Error(`Session failed: ${result.status.code} \u2014 ${result.status.description}`);
|
|
8220
|
+
}
|
|
8221
|
+
return {
|
|
8222
|
+
pages: result.upo?.pages ?? [],
|
|
8223
|
+
invoiceCount: result.invoiceCount,
|
|
8224
|
+
successfulInvoiceCount: result.successfulInvoiceCount,
|
|
8225
|
+
failedInvoiceCount: result.failedInvoiceCount
|
|
8226
|
+
};
|
|
8227
|
+
}
|
|
7758
8228
|
return {
|
|
7759
8229
|
sessionRef,
|
|
7760
8230
|
validUntil: openResp.validUntil,
|
|
@@ -7776,20 +8246,16 @@ async function openOnlineSession(client, options) {
|
|
|
7776
8246
|
await client.onlineSession.closeSession(sessionRef);
|
|
7777
8247
|
},
|
|
7778
8248
|
async waitForUpo(pollOpts) {
|
|
7779
|
-
|
|
7780
|
-
|
|
7781
|
-
|
|
7782
|
-
|
|
7783
|
-
|
|
7784
|
-
|
|
7785
|
-
|
|
7786
|
-
|
|
7787
|
-
|
|
7788
|
-
|
|
7789
|
-
invoiceCount: result.invoiceCount,
|
|
7790
|
-
successfulInvoiceCount: result.successfulInvoiceCount,
|
|
7791
|
-
failedInvoiceCount: result.failedInvoiceCount
|
|
7792
|
-
};
|
|
8249
|
+
return fetchUpo(pollOpts);
|
|
8250
|
+
},
|
|
8251
|
+
async waitForUpoParsed(pollOpts) {
|
|
8252
|
+
const upoInfo = await fetchUpo(pollOpts);
|
|
8253
|
+
const parsed = [];
|
|
8254
|
+
for (const page of upoInfo.pages) {
|
|
8255
|
+
const result = await client.sessionStatus.getSessionUpo(sessionRef, page.referenceNumber);
|
|
8256
|
+
parsed.push(parseUpoXml(result.upo));
|
|
8257
|
+
}
|
|
8258
|
+
return { ...upoInfo, parsed };
|
|
7793
8259
|
}
|
|
7794
8260
|
};
|
|
7795
8261
|
}
|
|
@@ -7803,11 +8269,10 @@ async function openSendAndClose(client, invoices, options) {
|
|
|
7803
8269
|
}
|
|
7804
8270
|
|
|
7805
8271
|
// src/workflows/batch-session-workflow.ts
|
|
7806
|
-
var DEFAULT_FORM_CODE2 = { systemCode: "FA", schemaVersion: "3", value: "FA (3)" };
|
|
7807
8272
|
async function uploadBatch(client, zipData, options) {
|
|
7808
8273
|
await client.crypto.init();
|
|
7809
8274
|
const encData = client.crypto.getEncryptionData();
|
|
7810
|
-
const formCode = options?.formCode ??
|
|
8275
|
+
const formCode = options?.formCode ?? FORM_CODES.FA_2;
|
|
7811
8276
|
const encryptFn = (part) => client.crypto.encryptAES256(part, encData.cipherKey, encData.cipherIv);
|
|
7812
8277
|
const { batchFile, encryptedParts } = BatchFileBuilder.build(zipData, encryptFn, {
|
|
7813
8278
|
maxPartSize: options?.maxPartSize
|
|
@@ -7849,6 +8314,72 @@ async function uploadBatch(client, zipData, options) {
|
|
|
7849
8314
|
}
|
|
7850
8315
|
};
|
|
7851
8316
|
}
|
|
8317
|
+
async function uploadBatchStream(client, zipStreamFactory, zipSize, options) {
|
|
8318
|
+
await client.crypto.init();
|
|
8319
|
+
const encData = client.crypto.getEncryptionData();
|
|
8320
|
+
const formCode = options?.formCode ?? FORM_CODES.FA_2;
|
|
8321
|
+
const encryptStreamFn = (stream) => client.crypto.encryptAES256Stream(stream, encData.cipherKey, encData.cipherIv);
|
|
8322
|
+
const hashStreamFn = (stream) => client.crypto.getFileMetadataFromStream(stream);
|
|
8323
|
+
const { batchFile, streamParts } = await BatchFileBuilder.buildFromStream(
|
|
8324
|
+
zipStreamFactory,
|
|
8325
|
+
zipSize,
|
|
8326
|
+
encryptStreamFn,
|
|
8327
|
+
hashStreamFn,
|
|
8328
|
+
{ maxPartSize: options?.maxPartSize }
|
|
8329
|
+
);
|
|
8330
|
+
const openResp = await client.batchSession.openSession(
|
|
8331
|
+
{
|
|
8332
|
+
formCode,
|
|
8333
|
+
encryption: encData.encryptionInfo,
|
|
8334
|
+
batchFile,
|
|
8335
|
+
offlineMode: options?.offlineMode
|
|
8336
|
+
},
|
|
8337
|
+
options?.upoVersion
|
|
8338
|
+
);
|
|
8339
|
+
await client.batchSession.sendPartsWithStream(openResp, streamParts);
|
|
8340
|
+
await client.batchSession.closeSession(openResp.referenceNumber);
|
|
8341
|
+
const result = await pollUntil(
|
|
8342
|
+
() => client.sessionStatus.getSessionStatus(openResp.referenceNumber),
|
|
8343
|
+
(s) => s.status.code === 200 || s.status.code >= 400,
|
|
8344
|
+
{ ...options?.pollOptions, description: `UPO for batch ${openResp.referenceNumber}` }
|
|
8345
|
+
);
|
|
8346
|
+
if (result.status.code !== 200) {
|
|
8347
|
+
throw new Error(`Batch session failed: ${result.status.code} \u2014 ${result.status.description}`);
|
|
8348
|
+
}
|
|
8349
|
+
return {
|
|
8350
|
+
sessionRef: openResp.referenceNumber,
|
|
8351
|
+
upo: {
|
|
8352
|
+
pages: result.upo?.pages ?? [],
|
|
8353
|
+
invoiceCount: result.invoiceCount,
|
|
8354
|
+
successfulInvoiceCount: result.successfulInvoiceCount,
|
|
8355
|
+
failedInvoiceCount: result.failedInvoiceCount
|
|
8356
|
+
}
|
|
8357
|
+
};
|
|
8358
|
+
}
|
|
8359
|
+
async function uploadBatchStreamParsed(client, zipStreamFactory, zipSize, options) {
|
|
8360
|
+
const result = await uploadBatchStream(client, zipStreamFactory, zipSize, options);
|
|
8361
|
+
const parsed = [];
|
|
8362
|
+
for (const page of result.upo.pages) {
|
|
8363
|
+
const upoResult = await client.sessionStatus.getSessionUpo(result.sessionRef, page.referenceNumber);
|
|
8364
|
+
parsed.push(parseUpoXml(upoResult.upo));
|
|
8365
|
+
}
|
|
8366
|
+
return {
|
|
8367
|
+
sessionRef: result.sessionRef,
|
|
8368
|
+
upo: { ...result.upo, parsed }
|
|
8369
|
+
};
|
|
8370
|
+
}
|
|
8371
|
+
async function uploadBatchParsed(client, zipData, options) {
|
|
8372
|
+
const result = await uploadBatch(client, zipData, options);
|
|
8373
|
+
const parsed = [];
|
|
8374
|
+
for (const page of result.upo.pages) {
|
|
8375
|
+
const upoResult = await client.sessionStatus.getSessionUpo(result.sessionRef, page.referenceNumber);
|
|
8376
|
+
parsed.push(parseUpoXml(upoResult.upo));
|
|
8377
|
+
}
|
|
8378
|
+
return {
|
|
8379
|
+
sessionRef: result.sessionRef,
|
|
8380
|
+
upo: { ...result.upo, parsed }
|
|
8381
|
+
};
|
|
8382
|
+
}
|
|
7852
8383
|
|
|
7853
8384
|
// src/workflows/invoice-export-workflow.ts
|
|
7854
8385
|
async function doExport(client, filters, options) {
|
|
@@ -7872,6 +8403,7 @@ async function doExport(client, filters, options) {
|
|
|
7872
8403
|
}
|
|
7873
8404
|
return {
|
|
7874
8405
|
encData,
|
|
8406
|
+
referenceNumber: opResp.referenceNumber,
|
|
7875
8407
|
result: {
|
|
7876
8408
|
parts: result.package.parts.map((p) => ({
|
|
7877
8409
|
ordinalNumber: p.ordinalNumber,
|
|
@@ -7884,7 +8416,8 @@ async function doExport(client, filters, options) {
|
|
|
7884
8416
|
})),
|
|
7885
8417
|
invoiceCount: result.package.invoiceCount,
|
|
7886
8418
|
isTruncated: result.package.isTruncated,
|
|
7887
|
-
permanentStorageHwmDate: result.package.permanentStorageHwmDate
|
|
8419
|
+
permanentStorageHwmDate: result.package.permanentStorageHwmDate,
|
|
8420
|
+
lastPermanentStorageDate: result.package.lastPermanentStorageDate
|
|
7888
8421
|
}
|
|
7889
8422
|
};
|
|
7890
8423
|
}
|
|
@@ -7905,13 +8438,144 @@ async function exportAndDownload(client, filters, options) {
|
|
|
7905
8438
|
const decrypted = client.crypto.decryptAES256(encryptedData, encData.cipherKey, encData.cipherIv);
|
|
7906
8439
|
decryptedParts.push(decrypted);
|
|
7907
8440
|
}
|
|
8441
|
+
if (options?.extract) {
|
|
8442
|
+
const zipBuffer = Buffer.concat(decryptedParts);
|
|
8443
|
+
const files = await unzip(zipBuffer, options.unzipOptions);
|
|
8444
|
+
return { ...exportResult, files };
|
|
8445
|
+
}
|
|
7908
8446
|
return {
|
|
7909
8447
|
...exportResult,
|
|
7910
8448
|
decryptedParts
|
|
7911
8449
|
};
|
|
7912
8450
|
}
|
|
7913
8451
|
|
|
8452
|
+
// src/workflows/hwm-coordinator.ts
|
|
8453
|
+
function updateContinuationPoint(points, subjectType, pkg) {
|
|
8454
|
+
if (pkg.isTruncated && pkg.lastPermanentStorageDate) {
|
|
8455
|
+
points[subjectType] = pkg.lastPermanentStorageDate;
|
|
8456
|
+
} else if (pkg.permanentStorageHwmDate) {
|
|
8457
|
+
points[subjectType] = pkg.permanentStorageHwmDate;
|
|
8458
|
+
} else {
|
|
8459
|
+
delete points[subjectType];
|
|
8460
|
+
}
|
|
8461
|
+
}
|
|
8462
|
+
function getEffectiveStartDate(points, subjectType, windowFrom) {
|
|
8463
|
+
return points[subjectType] ?? windowFrom;
|
|
8464
|
+
}
|
|
8465
|
+
function deduplicateByKsefNumber(entries) {
|
|
8466
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8467
|
+
return entries.filter((entry) => {
|
|
8468
|
+
const key = entry.ksefNumber.toLowerCase();
|
|
8469
|
+
if (seen.has(key)) return false;
|
|
8470
|
+
seen.add(key);
|
|
8471
|
+
return true;
|
|
8472
|
+
});
|
|
8473
|
+
}
|
|
8474
|
+
|
|
8475
|
+
// src/workflows/incremental-export-workflow.ts
|
|
8476
|
+
async function incrementalExportAndDownload(client, options) {
|
|
8477
|
+
const maxIterations = options.maxIterations ?? 20;
|
|
8478
|
+
const points = options.continuationPoints;
|
|
8479
|
+
if (options.store) {
|
|
8480
|
+
const loaded = await options.store.load();
|
|
8481
|
+
for (const [key, value] of Object.entries(loaded)) {
|
|
8482
|
+
if (value !== void 0 && points[key] === void 0) {
|
|
8483
|
+
points[key] = value;
|
|
8484
|
+
}
|
|
8485
|
+
}
|
|
8486
|
+
}
|
|
8487
|
+
const referenceNumbers = [];
|
|
8488
|
+
const decryptedParts = [];
|
|
8489
|
+
let previousFrom;
|
|
8490
|
+
let iteration = 0;
|
|
8491
|
+
for (; iteration < maxIterations; iteration++) {
|
|
8492
|
+
const effectiveFrom = getEffectiveStartDate(points, options.subjectType, options.windowFrom);
|
|
8493
|
+
if (previousFrom !== void 0 && effectiveFrom === previousFrom) {
|
|
8494
|
+
break;
|
|
8495
|
+
}
|
|
8496
|
+
previousFrom = effectiveFrom;
|
|
8497
|
+
const filters = options.filtersFactory ? options.filtersFactory(effectiveFrom, options.windowTo) : buildDefaultFilters(options.subjectType, effectiveFrom, options.windowTo);
|
|
8498
|
+
const { result, encData, referenceNumber } = await doExport(client, filters, {
|
|
8499
|
+
onlyMetadata: options.onlyMetadata,
|
|
8500
|
+
pollOptions: options.pollOptions
|
|
8501
|
+
});
|
|
8502
|
+
referenceNumbers.push(referenceNumber);
|
|
8503
|
+
const download = options.transport ?? fetch;
|
|
8504
|
+
for (const part of result.parts) {
|
|
8505
|
+
const resp = await download(part.url, { method: part.method });
|
|
8506
|
+
if (!resp.ok) {
|
|
8507
|
+
throw new Error(`Download failed for part ${part.ordinalNumber}: HTTP ${resp.status}`);
|
|
8508
|
+
}
|
|
8509
|
+
const encryptedData = new Uint8Array(await resp.arrayBuffer());
|
|
8510
|
+
const decrypted = client.crypto.decryptAES256(encryptedData, encData.cipherKey, encData.cipherIv);
|
|
8511
|
+
decryptedParts.push(decrypted);
|
|
8512
|
+
}
|
|
8513
|
+
updateContinuationPoint(points, options.subjectType, {
|
|
8514
|
+
isTruncated: result.isTruncated,
|
|
8515
|
+
lastPermanentStorageDate: result.lastPermanentStorageDate,
|
|
8516
|
+
permanentStorageHwmDate: result.permanentStorageHwmDate
|
|
8517
|
+
});
|
|
8518
|
+
if (options.store) {
|
|
8519
|
+
await options.store.save(points);
|
|
8520
|
+
}
|
|
8521
|
+
options.onIterationComplete?.(iteration, result);
|
|
8522
|
+
if (!result.isTruncated) {
|
|
8523
|
+
iteration++;
|
|
8524
|
+
break;
|
|
8525
|
+
}
|
|
8526
|
+
}
|
|
8527
|
+
return {
|
|
8528
|
+
referenceNumbers,
|
|
8529
|
+
invoices: [],
|
|
8530
|
+
decryptedParts,
|
|
8531
|
+
continuationPoints: points,
|
|
8532
|
+
iterationCount: iteration
|
|
8533
|
+
};
|
|
8534
|
+
}
|
|
8535
|
+
function buildDefaultFilters(subjectType, from, to) {
|
|
8536
|
+
return {
|
|
8537
|
+
subjectType,
|
|
8538
|
+
dateRange: {
|
|
8539
|
+
dateType: "PermanentStorage",
|
|
8540
|
+
from,
|
|
8541
|
+
to
|
|
8542
|
+
}
|
|
8543
|
+
};
|
|
8544
|
+
}
|
|
8545
|
+
|
|
8546
|
+
// src/workflows/hwm-storage.ts
|
|
8547
|
+
import * as fs from "fs/promises";
|
|
8548
|
+
var InMemoryHwmStore = class {
|
|
8549
|
+
points = {};
|
|
8550
|
+
async load() {
|
|
8551
|
+
return { ...this.points };
|
|
8552
|
+
}
|
|
8553
|
+
async save(points) {
|
|
8554
|
+
this.points = { ...points };
|
|
8555
|
+
}
|
|
8556
|
+
};
|
|
8557
|
+
var FileHwmStore = class {
|
|
8558
|
+
constructor(filePath) {
|
|
8559
|
+
this.filePath = filePath;
|
|
8560
|
+
}
|
|
8561
|
+
async load() {
|
|
8562
|
+
try {
|
|
8563
|
+
const data = await fs.readFile(this.filePath, "utf-8");
|
|
8564
|
+
return JSON.parse(data);
|
|
8565
|
+
} catch (err) {
|
|
8566
|
+
if (err.code === "ENOENT") {
|
|
8567
|
+
return {};
|
|
8568
|
+
}
|
|
8569
|
+
throw err;
|
|
8570
|
+
}
|
|
8571
|
+
}
|
|
8572
|
+
async save(points) {
|
|
8573
|
+
await fs.writeFile(this.filePath, JSON.stringify(points, null, 2), "utf-8");
|
|
8574
|
+
}
|
|
8575
|
+
};
|
|
8576
|
+
|
|
7914
8577
|
// src/workflows/auth-workflow.ts
|
|
8578
|
+
init_auth_xml_builder();
|
|
7915
8579
|
async function authenticateWithToken(client, options) {
|
|
7916
8580
|
const challenge = await client.auth.getChallenge();
|
|
7917
8581
|
await client.crypto.init();
|
|
@@ -7965,6 +8629,34 @@ async function authenticateWithCertificate(client, options) {
|
|
|
7965
8629
|
refreshTokenValidUntil: tokens.refreshToken.validUntil
|
|
7966
8630
|
};
|
|
7967
8631
|
}
|
|
8632
|
+
async function authenticateWithExternalSignature(client, options) {
|
|
8633
|
+
const challenge = await client.auth.getChallenge();
|
|
8634
|
+
const unsignedXml = buildUnsignedAuthTokenRequestXml({
|
|
8635
|
+
challenge: challenge.challenge,
|
|
8636
|
+
contextIdentifier: options.contextIdentifier
|
|
8637
|
+
});
|
|
8638
|
+
const signedXml = await options.signXml(unsignedXml);
|
|
8639
|
+
const submitResult = await client.auth.submitXadesAuthRequest(
|
|
8640
|
+
signedXml,
|
|
8641
|
+
options.verifyCertificateChain ?? false,
|
|
8642
|
+
options.enforceXadesCompliance ?? false
|
|
8643
|
+
);
|
|
8644
|
+
const authToken = submitResult.authenticationToken.token;
|
|
8645
|
+
await pollUntil(
|
|
8646
|
+
() => client.auth.getAuthStatus(submitResult.referenceNumber, authToken),
|
|
8647
|
+
(s) => s.status.code !== 100,
|
|
8648
|
+
{ ...options.pollOptions, description: `auth ${submitResult.referenceNumber}` }
|
|
8649
|
+
);
|
|
8650
|
+
const tokens = await client.auth.getAccessToken(authToken);
|
|
8651
|
+
client.authManager.setAccessToken(tokens.accessToken.token);
|
|
8652
|
+
client.authManager.setRefreshToken(tokens.refreshToken.token);
|
|
8653
|
+
return {
|
|
8654
|
+
accessToken: tokens.accessToken.token,
|
|
8655
|
+
accessTokenValidUntil: tokens.accessToken.validUntil,
|
|
8656
|
+
refreshToken: tokens.refreshToken.token,
|
|
8657
|
+
refreshTokenValidUntil: tokens.refreshToken.validUntil
|
|
8658
|
+
};
|
|
8659
|
+
}
|
|
7968
8660
|
async function authenticateWithPkcs12(client, options) {
|
|
7969
8661
|
const { Pkcs12Loader: Pkcs12Loader2 } = await Promise.resolve().then(() => (init_pkcs12_loader(), pkcs12_loader_exports));
|
|
7970
8662
|
const { certificatePem, privateKeyPem } = Pkcs12Loader2.load(options.p12, options.password);
|
|
@@ -8004,6 +8696,11 @@ export {
|
|
|
8004
8696
|
ENFORCE_XADES_COMPLIANCE,
|
|
8005
8697
|
EntityPermissionGrantBuilder,
|
|
8006
8698
|
Environment,
|
|
8699
|
+
FORM_CODES,
|
|
8700
|
+
FORM_CODE_KEYS,
|
|
8701
|
+
FileHwmStore,
|
|
8702
|
+
INVOICE_TYPES_BY_SYSTEM_CODE,
|
|
8703
|
+
InMemoryHwmStore,
|
|
8007
8704
|
InternalId,
|
|
8008
8705
|
InvoiceDownloadService,
|
|
8009
8706
|
InvoiceQueryFilterBuilder,
|
|
@@ -8049,21 +8746,29 @@ export {
|
|
|
8049
8746
|
SessionStatusService,
|
|
8050
8747
|
Sha256Base64,
|
|
8051
8748
|
SignatureService,
|
|
8749
|
+
SystemCode,
|
|
8052
8750
|
TestDataService,
|
|
8053
8751
|
TokenService,
|
|
8054
8752
|
UpoVersion,
|
|
8055
8753
|
VatUe,
|
|
8056
8754
|
VerificationLinkService,
|
|
8057
8755
|
authenticateWithCertificate,
|
|
8756
|
+
authenticateWithExternalSignature,
|
|
8058
8757
|
authenticateWithPkcs12,
|
|
8059
8758
|
authenticateWithToken,
|
|
8759
|
+
buildUnsignedAuthTokenRequestXml,
|
|
8060
8760
|
calculateBackoff,
|
|
8761
|
+
createZip,
|
|
8762
|
+
deduplicateByKsefNumber,
|
|
8061
8763
|
defaultPresignedUrlPolicy,
|
|
8062
8764
|
defaultRateLimitPolicy,
|
|
8063
8765
|
defaultRetryPolicy,
|
|
8064
8766
|
defaultTransport,
|
|
8065
8767
|
exportAndDownload,
|
|
8066
8768
|
exportInvoices,
|
|
8769
|
+
getEffectiveStartDate,
|
|
8770
|
+
getFormCode,
|
|
8771
|
+
incrementalExportAndDownload,
|
|
8067
8772
|
isRetryableError,
|
|
8068
8773
|
isRetryableStatus,
|
|
8069
8774
|
isValidBase64,
|
|
@@ -8083,11 +8788,19 @@ export {
|
|
|
8083
8788
|
isValidVatUe,
|
|
8084
8789
|
openOnlineSession,
|
|
8085
8790
|
openSendAndClose,
|
|
8791
|
+
parseFormCode,
|
|
8086
8792
|
parseRetryAfter,
|
|
8793
|
+
parseUpoXml,
|
|
8087
8794
|
pollUntil,
|
|
8088
8795
|
resolveOptions,
|
|
8089
8796
|
sleep,
|
|
8797
|
+
unzip,
|
|
8798
|
+
updateContinuationPoint,
|
|
8090
8799
|
uploadBatch,
|
|
8800
|
+
uploadBatchParsed,
|
|
8801
|
+
uploadBatchStream,
|
|
8802
|
+
uploadBatchStreamParsed,
|
|
8803
|
+
validateFormCodeForSession,
|
|
8091
8804
|
validatePresignedUrl
|
|
8092
8805
|
};
|
|
8093
8806
|
//# sourceMappingURL=index.js.map
|