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.cjs
CHANGED
|
@@ -1108,6 +1108,36 @@ var init_batch_session = __esm({
|
|
|
1108
1108
|
});
|
|
1109
1109
|
await Promise.all(tasks);
|
|
1110
1110
|
}
|
|
1111
|
+
/**
|
|
1112
|
+
* Upload parts sequentially (not in parallel) because each part uses a
|
|
1113
|
+
* streaming body (`duplex: 'half'`). Parallel streaming uploads can cause
|
|
1114
|
+
* backpressure issues and exceed memory limits for large payloads.
|
|
1115
|
+
*/
|
|
1116
|
+
async sendPartsWithStream(openResponse, parts) {
|
|
1117
|
+
const uploadRequests = openResponse.partUploadRequests;
|
|
1118
|
+
for (const part of parts) {
|
|
1119
|
+
const uploadReq = uploadRequests.find(
|
|
1120
|
+
(r) => r.ordinalNumber === part.ordinalNumber
|
|
1121
|
+
);
|
|
1122
|
+
if (!uploadReq) {
|
|
1123
|
+
throw new Error(`No upload request found for part ${part.ordinalNumber}`);
|
|
1124
|
+
}
|
|
1125
|
+
const headers = {};
|
|
1126
|
+
for (const [k, v] of Object.entries(uploadReq.headers)) {
|
|
1127
|
+
if (v != null) headers[k] = v;
|
|
1128
|
+
}
|
|
1129
|
+
const resp = await fetch(uploadReq.url, {
|
|
1130
|
+
method: uploadReq.method,
|
|
1131
|
+
headers,
|
|
1132
|
+
body: part.dataStream,
|
|
1133
|
+
// @ts-expect-error -- Node 18+ undici supports duplex for streaming body
|
|
1134
|
+
duplex: "half"
|
|
1135
|
+
});
|
|
1136
|
+
if (!resp.ok) {
|
|
1137
|
+
throw new Error(`Upload failed for part ${part.ordinalNumber}: HTTP ${resp.status}`);
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1111
1141
|
async closeSession(batchRef) {
|
|
1112
1142
|
const req = RestRequest.post(Routes.Sessions.Batch.close(batchRef));
|
|
1113
1143
|
await this.restClient.executeVoid(req);
|
|
@@ -1805,6 +1835,28 @@ var init_cryptography_service = __esm({
|
|
|
1805
1835
|
const cipher = crypto2.createCipheriv("aes-256-cbc", key, iv);
|
|
1806
1836
|
return new Uint8Array(Buffer.concat([cipher.update(content), cipher.final()]));
|
|
1807
1837
|
}
|
|
1838
|
+
/**
|
|
1839
|
+
* Encrypt with AES-256-CBC as a streaming transform.
|
|
1840
|
+
* Returns a ReadableStream that yields encrypted chunks as the input is read.
|
|
1841
|
+
*/
|
|
1842
|
+
encryptAES256Stream(input, key, iv) {
|
|
1843
|
+
const cipher = crypto2.createCipheriv("aes-256-cbc", key, iv);
|
|
1844
|
+
const transform = new TransformStream({
|
|
1845
|
+
transform(chunk, controller) {
|
|
1846
|
+
const encrypted = cipher.update(chunk);
|
|
1847
|
+
if (encrypted.length > 0) {
|
|
1848
|
+
controller.enqueue(new Uint8Array(encrypted));
|
|
1849
|
+
}
|
|
1850
|
+
},
|
|
1851
|
+
flush(controller) {
|
|
1852
|
+
const final = cipher.final();
|
|
1853
|
+
if (final.length > 0) {
|
|
1854
|
+
controller.enqueue(new Uint8Array(final));
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
});
|
|
1858
|
+
return input.pipeThrough(transform);
|
|
1859
|
+
}
|
|
1808
1860
|
/** Decrypt with AES-256-CBC. */
|
|
1809
1861
|
decryptAES256(content, key, iv) {
|
|
1810
1862
|
const decipher = crypto2.createDecipheriv("aes-256-cbc", key, iv);
|
|
@@ -1877,6 +1929,23 @@ var init_cryptography_service = __esm({
|
|
|
1877
1929
|
const hash = crypto2.createHash("sha256").update(file).digest("base64");
|
|
1878
1930
|
return { hashSHA: hash, fileSize: file.length };
|
|
1879
1931
|
}
|
|
1932
|
+
/** Compute SHA-256 hash (base64) and byte length from a ReadableStream. */
|
|
1933
|
+
async getFileMetadataFromStream(stream) {
|
|
1934
|
+
const hash = crypto2.createHash("sha256");
|
|
1935
|
+
let fileSize = 0;
|
|
1936
|
+
const reader = stream.getReader();
|
|
1937
|
+
try {
|
|
1938
|
+
for (; ; ) {
|
|
1939
|
+
const { done, value } = await reader.read();
|
|
1940
|
+
if (done) break;
|
|
1941
|
+
hash.update(value);
|
|
1942
|
+
fileSize += value.byteLength;
|
|
1943
|
+
}
|
|
1944
|
+
} finally {
|
|
1945
|
+
reader.releaseLock();
|
|
1946
|
+
}
|
|
1947
|
+
return { hashSHA: hash.digest("base64"), fileSize };
|
|
1948
|
+
}
|
|
1880
1949
|
// ---------------------------------------------------------------------------
|
|
1881
1950
|
// CSR generation
|
|
1882
1951
|
// ---------------------------------------------------------------------------
|
|
@@ -6791,6 +6860,32 @@ var init_pkcs12_loader = __esm({
|
|
|
6791
6860
|
}
|
|
6792
6861
|
});
|
|
6793
6862
|
|
|
6863
|
+
// src/crypto/auth-xml-builder.ts
|
|
6864
|
+
function buildUnsignedAuthTokenRequestXml(options) {
|
|
6865
|
+
const { challenge, contextIdentifier, subjectIdentifierType = "certificateSubject" } = options;
|
|
6866
|
+
const identifierElement = `<${contextIdentifier.type}>${xmlEscape(contextIdentifier.value)}</${contextIdentifier.type}>`;
|
|
6867
|
+
return [
|
|
6868
|
+
'<?xml version="1.0" encoding="utf-8"?>',
|
|
6869
|
+
`<AuthTokenRequest xmlns="${AUTH_TOKEN_REQUEST_NS}">`,
|
|
6870
|
+
`<Challenge>${xmlEscape(challenge)}</Challenge>`,
|
|
6871
|
+
`<ContextIdentifier>`,
|
|
6872
|
+
identifierElement,
|
|
6873
|
+
`</ContextIdentifier>`,
|
|
6874
|
+
`<SubjectIdentifierType>${xmlEscape(subjectIdentifierType)}</SubjectIdentifierType>`,
|
|
6875
|
+
`</AuthTokenRequest>`
|
|
6876
|
+
].join("");
|
|
6877
|
+
}
|
|
6878
|
+
function xmlEscape(str) {
|
|
6879
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
6880
|
+
}
|
|
6881
|
+
var AUTH_TOKEN_REQUEST_NS;
|
|
6882
|
+
var init_auth_xml_builder = __esm({
|
|
6883
|
+
"src/crypto/auth-xml-builder.ts"() {
|
|
6884
|
+
"use strict";
|
|
6885
|
+
AUTH_TOKEN_REQUEST_NS = "http://ksef.mf.gov.pl/auth/token/2.0";
|
|
6886
|
+
}
|
|
6887
|
+
});
|
|
6888
|
+
|
|
6794
6889
|
// src/qr/verification-link-service.ts
|
|
6795
6890
|
var import_node_crypto, VerificationLinkService;
|
|
6796
6891
|
var init_verification_link_service = __esm({
|
|
@@ -6855,19 +6950,11 @@ __export(client_exports, {
|
|
|
6855
6950
|
buildAuthTokenRequestXml: () => buildAuthTokenRequestXml
|
|
6856
6951
|
});
|
|
6857
6952
|
function buildAuthTokenRequestXml(challenge, nip, subjectIdentifierType = "certificateSubject") {
|
|
6858
|
-
return
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
`<Nip>${xmlEscape(nip)}</Nip>`,
|
|
6864
|
-
`</ContextIdentifier>`,
|
|
6865
|
-
`<SubjectIdentifierType>${xmlEscape(subjectIdentifierType)}</SubjectIdentifierType>`,
|
|
6866
|
-
`</AuthTokenRequest>`
|
|
6867
|
-
].join("");
|
|
6868
|
-
}
|
|
6869
|
-
function xmlEscape(str) {
|
|
6870
|
-
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
6953
|
+
return buildUnsignedAuthTokenRequestXml({
|
|
6954
|
+
challenge,
|
|
6955
|
+
contextIdentifier: { type: "Nip", value: nip },
|
|
6956
|
+
subjectIdentifierType
|
|
6957
|
+
});
|
|
6871
6958
|
}
|
|
6872
6959
|
function buildRestClientConfig(options, authManager) {
|
|
6873
6960
|
const config = { authManager };
|
|
@@ -6894,7 +6981,7 @@ function buildRestClientConfig(options, authManager) {
|
|
|
6894
6981
|
}
|
|
6895
6982
|
return config;
|
|
6896
6983
|
}
|
|
6897
|
-
var KSeFClient
|
|
6984
|
+
var KSeFClient;
|
|
6898
6985
|
var init_client = __esm({
|
|
6899
6986
|
"src/client.ts"() {
|
|
6900
6987
|
"use strict";
|
|
@@ -6920,6 +7007,7 @@ var init_client = __esm({
|
|
|
6920
7007
|
init_certificate_fetcher();
|
|
6921
7008
|
init_cryptography_service();
|
|
6922
7009
|
init_verification_link_service();
|
|
7010
|
+
init_auth_xml_builder();
|
|
6923
7011
|
KSeFClient = class {
|
|
6924
7012
|
auth;
|
|
6925
7013
|
activeSessions;
|
|
@@ -7013,7 +7101,6 @@ var init_client = __esm({
|
|
|
7013
7101
|
this.authManager.setRefreshToken(void 0);
|
|
7014
7102
|
}
|
|
7015
7103
|
};
|
|
7016
|
-
AUTH_TOKEN_REQUEST_NS = "http://ksef.mf.gov.pl/auth/token/2.0";
|
|
7017
7104
|
}
|
|
7018
7105
|
});
|
|
7019
7106
|
|
|
@@ -7043,6 +7130,11 @@ __export(index_exports, {
|
|
|
7043
7130
|
ENFORCE_XADES_COMPLIANCE: () => ENFORCE_XADES_COMPLIANCE,
|
|
7044
7131
|
EntityPermissionGrantBuilder: () => EntityPermissionGrantBuilder,
|
|
7045
7132
|
Environment: () => Environment,
|
|
7133
|
+
FORM_CODES: () => FORM_CODES,
|
|
7134
|
+
FORM_CODE_KEYS: () => FORM_CODE_KEYS,
|
|
7135
|
+
FileHwmStore: () => FileHwmStore,
|
|
7136
|
+
INVOICE_TYPES_BY_SYSTEM_CODE: () => INVOICE_TYPES_BY_SYSTEM_CODE,
|
|
7137
|
+
InMemoryHwmStore: () => InMemoryHwmStore,
|
|
7046
7138
|
InternalId: () => InternalId,
|
|
7047
7139
|
InvoiceDownloadService: () => InvoiceDownloadService,
|
|
7048
7140
|
InvoiceQueryFilterBuilder: () => InvoiceQueryFilterBuilder,
|
|
@@ -7088,21 +7180,29 @@ __export(index_exports, {
|
|
|
7088
7180
|
SessionStatusService: () => SessionStatusService,
|
|
7089
7181
|
Sha256Base64: () => Sha256Base64,
|
|
7090
7182
|
SignatureService: () => SignatureService,
|
|
7183
|
+
SystemCode: () => SystemCode,
|
|
7091
7184
|
TestDataService: () => TestDataService,
|
|
7092
7185
|
TokenService: () => TokenService,
|
|
7093
7186
|
UpoVersion: () => UpoVersion,
|
|
7094
7187
|
VatUe: () => VatUe,
|
|
7095
7188
|
VerificationLinkService: () => VerificationLinkService,
|
|
7096
7189
|
authenticateWithCertificate: () => authenticateWithCertificate,
|
|
7190
|
+
authenticateWithExternalSignature: () => authenticateWithExternalSignature,
|
|
7097
7191
|
authenticateWithPkcs12: () => authenticateWithPkcs12,
|
|
7098
7192
|
authenticateWithToken: () => authenticateWithToken,
|
|
7193
|
+
buildUnsignedAuthTokenRequestXml: () => buildUnsignedAuthTokenRequestXml,
|
|
7099
7194
|
calculateBackoff: () => calculateBackoff,
|
|
7195
|
+
createZip: () => createZip,
|
|
7196
|
+
deduplicateByKsefNumber: () => deduplicateByKsefNumber,
|
|
7100
7197
|
defaultPresignedUrlPolicy: () => defaultPresignedUrlPolicy,
|
|
7101
7198
|
defaultRateLimitPolicy: () => defaultRateLimitPolicy,
|
|
7102
7199
|
defaultRetryPolicy: () => defaultRetryPolicy,
|
|
7103
7200
|
defaultTransport: () => defaultTransport,
|
|
7104
7201
|
exportAndDownload: () => exportAndDownload,
|
|
7105
7202
|
exportInvoices: () => exportInvoices,
|
|
7203
|
+
getEffectiveStartDate: () => getEffectiveStartDate,
|
|
7204
|
+
getFormCode: () => getFormCode,
|
|
7205
|
+
incrementalExportAndDownload: () => incrementalExportAndDownload,
|
|
7106
7206
|
isRetryableError: () => isRetryableError,
|
|
7107
7207
|
isRetryableStatus: () => isRetryableStatus,
|
|
7108
7208
|
isValidBase64: () => isValidBase64,
|
|
@@ -7122,11 +7222,19 @@ __export(index_exports, {
|
|
|
7122
7222
|
isValidVatUe: () => isValidVatUe,
|
|
7123
7223
|
openOnlineSession: () => openOnlineSession,
|
|
7124
7224
|
openSendAndClose: () => openSendAndClose,
|
|
7225
|
+
parseFormCode: () => parseFormCode,
|
|
7125
7226
|
parseRetryAfter: () => parseRetryAfter,
|
|
7227
|
+
parseUpoXml: () => parseUpoXml,
|
|
7126
7228
|
pollUntil: () => pollUntil,
|
|
7127
7229
|
resolveOptions: () => resolveOptions,
|
|
7128
7230
|
sleep: () => sleep,
|
|
7231
|
+
unzip: () => unzip,
|
|
7232
|
+
updateContinuationPoint: () => updateContinuationPoint,
|
|
7129
7233
|
uploadBatch: () => uploadBatch,
|
|
7234
|
+
uploadBatchParsed: () => uploadBatchParsed,
|
|
7235
|
+
uploadBatchStream: () => uploadBatchStream,
|
|
7236
|
+
uploadBatchStreamParsed: () => uploadBatchStreamParsed,
|
|
7237
|
+
validateFormCodeForSession: () => validateFormCodeForSession,
|
|
7130
7238
|
validatePresignedUrl: () => validatePresignedUrl
|
|
7131
7239
|
});
|
|
7132
7240
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -7259,6 +7367,65 @@ var SUBUNIT_NAME_MAX_LENGTH = 256;
|
|
|
7259
7367
|
var PERMISSION_DESCRIPTION_MIN_LENGTH = 5;
|
|
7260
7368
|
var PERMISSION_DESCRIPTION_MAX_LENGTH = 256;
|
|
7261
7369
|
|
|
7370
|
+
// src/models/document-structures/types.ts
|
|
7371
|
+
var SystemCode = {
|
|
7372
|
+
FA_2: "FA (2)",
|
|
7373
|
+
FA_3: "FA (3)",
|
|
7374
|
+
PEF_3: "PEF (3)",
|
|
7375
|
+
PEF_KOR_3: "PEF_KOR (3)",
|
|
7376
|
+
FA_RR_1: "FA_RR (1)"
|
|
7377
|
+
};
|
|
7378
|
+
var FORM_CODES = {
|
|
7379
|
+
FA_2: { systemCode: "FA (2)", schemaVersion: "1-0E", value: "FA" },
|
|
7380
|
+
FA_3: { systemCode: "FA (3)", schemaVersion: "1-0E", value: "FA" },
|
|
7381
|
+
PEF_3: { systemCode: "PEF (3)", schemaVersion: "2-1", value: "PEF" },
|
|
7382
|
+
PEF_KOR_3: { systemCode: "PEF_KOR (3)", schemaVersion: "2-1", value: "PEF" },
|
|
7383
|
+
FA_RR_1_LEGACY: { systemCode: "FA_RR (1)", schemaVersion: "1-0E", value: "RR" },
|
|
7384
|
+
FA_RR_1_TRANSITION: { systemCode: "FA_RR (1)", schemaVersion: "1-1E", value: "RR" },
|
|
7385
|
+
FA_RR_1: { systemCode: "FA_RR (1)", schemaVersion: "1-1E", value: "FA_RR" }
|
|
7386
|
+
};
|
|
7387
|
+
var INVOICE_TYPES_BY_SYSTEM_CODE = {
|
|
7388
|
+
[SystemCode.FA_2]: ["Vat", "Zal", "Kor", "Roz", "Upr", "KorZal", "KorRoz"],
|
|
7389
|
+
[SystemCode.FA_3]: ["Vat", "Zal", "Kor", "Roz", "Upr", "KorZal", "KorRoz"],
|
|
7390
|
+
[SystemCode.PEF_3]: ["VatPef", "VatPefSp", "KorPef"],
|
|
7391
|
+
[SystemCode.PEF_KOR_3]: ["KorPef"],
|
|
7392
|
+
[SystemCode.FA_RR_1]: ["VatRr", "KorVatRr"]
|
|
7393
|
+
};
|
|
7394
|
+
var FORM_CODE_KEYS = {
|
|
7395
|
+
FA2: FORM_CODES.FA_2,
|
|
7396
|
+
FA3: FORM_CODES.FA_3,
|
|
7397
|
+
PEF3: FORM_CODES.PEF_3,
|
|
7398
|
+
PEFKOR3: FORM_CODES.PEF_KOR_3,
|
|
7399
|
+
FARR1: FORM_CODES.FA_RR_1
|
|
7400
|
+
};
|
|
7401
|
+
|
|
7402
|
+
// src/models/document-structures/helpers.ts
|
|
7403
|
+
var SYSTEM_CODE_TO_FORM_CODE = {
|
|
7404
|
+
[SystemCode.FA_2]: FORM_CODES.FA_2,
|
|
7405
|
+
[SystemCode.FA_3]: FORM_CODES.FA_3,
|
|
7406
|
+
[SystemCode.PEF_3]: FORM_CODES.PEF_3,
|
|
7407
|
+
[SystemCode.PEF_KOR_3]: FORM_CODES.PEF_KOR_3,
|
|
7408
|
+
[SystemCode.FA_RR_1]: FORM_CODES.FA_RR_1
|
|
7409
|
+
};
|
|
7410
|
+
var ALL_FORM_CODES = Object.values(FORM_CODES);
|
|
7411
|
+
var BATCH_DISALLOWED_SYSTEM_CODES = /* @__PURE__ */ new Set([
|
|
7412
|
+
SystemCode.PEF_3,
|
|
7413
|
+
SystemCode.PEF_KOR_3
|
|
7414
|
+
]);
|
|
7415
|
+
function getFormCode(systemCode) {
|
|
7416
|
+
return SYSTEM_CODE_TO_FORM_CODE[systemCode];
|
|
7417
|
+
}
|
|
7418
|
+
function parseFormCode(raw) {
|
|
7419
|
+
const match = ALL_FORM_CODES.find(
|
|
7420
|
+
(fc) => fc.systemCode === raw.systemCode && fc.schemaVersion === raw.schemaVersion && fc.value === raw.value
|
|
7421
|
+
);
|
|
7422
|
+
return match ?? raw;
|
|
7423
|
+
}
|
|
7424
|
+
function validateFormCodeForSession(formCode, sessionType) {
|
|
7425
|
+
if (sessionType === "online") return true;
|
|
7426
|
+
return !BATCH_DISALLOWED_SYSTEM_CODES.has(formCode.systemCode);
|
|
7427
|
+
}
|
|
7428
|
+
|
|
7262
7429
|
// src/services/index.ts
|
|
7263
7430
|
init_auth();
|
|
7264
7431
|
init_active_sessions();
|
|
@@ -7675,6 +7842,108 @@ var BatchFileBuilder = class {
|
|
|
7675
7842
|
encryptedParts
|
|
7676
7843
|
};
|
|
7677
7844
|
}
|
|
7845
|
+
/**
|
|
7846
|
+
* Stream-based variant: two-pass build from a stream factory.
|
|
7847
|
+
*
|
|
7848
|
+
* Pass 1: consume stream to compute ZIP hash.
|
|
7849
|
+
* Pass 2: re-create stream, split into parts, encrypt each, compute per-part metadata.
|
|
7850
|
+
*
|
|
7851
|
+
* @param zipStreamFactory - Factory that creates a fresh ReadableStream of the ZIP data
|
|
7852
|
+
* @param zipSize - Total ZIP byte size (from fs.stat or known in advance)
|
|
7853
|
+
* @param encryptStreamFn - Stream-to-stream AES-256-CBC encryption function
|
|
7854
|
+
* @param hashStreamFn - Stream-to-FileMetadata hashing function
|
|
7855
|
+
* @param options - Optional configuration
|
|
7856
|
+
*/
|
|
7857
|
+
static async buildFromStream(zipStreamFactory, zipSize, encryptStreamFn, hashStreamFn, options) {
|
|
7858
|
+
const maxPartSize = options?.maxPartSize ?? BATCH_MAX_PART_SIZE;
|
|
7859
|
+
if (maxPartSize <= 0) {
|
|
7860
|
+
throw new KSeFValidationError("maxPartSize must be a positive number");
|
|
7861
|
+
}
|
|
7862
|
+
if (zipSize === 0) {
|
|
7863
|
+
throw new KSeFValidationError("ZIP data must not be empty");
|
|
7864
|
+
}
|
|
7865
|
+
if (zipSize > BATCH_MAX_TOTAL_SIZE) {
|
|
7866
|
+
throw new KSeFValidationError(
|
|
7867
|
+
`ZIP size ${zipSize} exceeds maximum of ${BATCH_MAX_TOTAL_SIZE} bytes (5 GB)`
|
|
7868
|
+
);
|
|
7869
|
+
}
|
|
7870
|
+
const partCount = Math.ceil(zipSize / maxPartSize);
|
|
7871
|
+
if (partCount > BATCH_MAX_PARTS) {
|
|
7872
|
+
throw new KSeFValidationError(
|
|
7873
|
+
`Data requires ${partCount} parts, exceeding maximum of ${BATCH_MAX_PARTS}`
|
|
7874
|
+
);
|
|
7875
|
+
}
|
|
7876
|
+
const zipMeta = await hashStreamFn(zipStreamFactory());
|
|
7877
|
+
const fileParts = [];
|
|
7878
|
+
const streamParts = [];
|
|
7879
|
+
const reader = zipStreamFactory().getReader();
|
|
7880
|
+
let pending = [];
|
|
7881
|
+
let pendingSize = 0;
|
|
7882
|
+
for (let partIndex = 0; partIndex < partCount; partIndex++) {
|
|
7883
|
+
const isLastPart = partIndex === partCount - 1;
|
|
7884
|
+
const targetSize = isLastPart ? zipSize - partIndex * maxPartSize : maxPartSize;
|
|
7885
|
+
while (pendingSize < targetSize) {
|
|
7886
|
+
const { done, value } = await reader.read();
|
|
7887
|
+
if (done) break;
|
|
7888
|
+
pending.push(value);
|
|
7889
|
+
pendingSize += value.byteLength;
|
|
7890
|
+
}
|
|
7891
|
+
const buffer = new Uint8Array(Buffer.concat(pending));
|
|
7892
|
+
const partData = buffer.subarray(0, targetSize);
|
|
7893
|
+
if (buffer.byteLength > targetSize) {
|
|
7894
|
+
const remainder = buffer.subarray(targetSize);
|
|
7895
|
+
pending = [remainder];
|
|
7896
|
+
pendingSize = remainder.byteLength;
|
|
7897
|
+
} else {
|
|
7898
|
+
pending = [];
|
|
7899
|
+
pendingSize = 0;
|
|
7900
|
+
}
|
|
7901
|
+
const partStream = new ReadableStream({
|
|
7902
|
+
start(controller) {
|
|
7903
|
+
controller.enqueue(partData);
|
|
7904
|
+
controller.close();
|
|
7905
|
+
}
|
|
7906
|
+
});
|
|
7907
|
+
const encryptedStream = encryptStreamFn(partStream);
|
|
7908
|
+
const encryptedChunks = [];
|
|
7909
|
+
const encReader = encryptedStream.getReader();
|
|
7910
|
+
for (; ; ) {
|
|
7911
|
+
const { done, value } = await encReader.read();
|
|
7912
|
+
if (done) break;
|
|
7913
|
+
encryptedChunks.push(value);
|
|
7914
|
+
}
|
|
7915
|
+
const encryptedData = new Uint8Array(Buffer.concat(encryptedChunks));
|
|
7916
|
+
const encryptedMeta = sha256Base64(encryptedData);
|
|
7917
|
+
fileParts.push({
|
|
7918
|
+
ordinalNumber: partIndex + 1,
|
|
7919
|
+
fileSize: encryptedData.byteLength,
|
|
7920
|
+
fileHash: encryptedMeta
|
|
7921
|
+
});
|
|
7922
|
+
const uploadStream = new ReadableStream({
|
|
7923
|
+
start(controller) {
|
|
7924
|
+
controller.enqueue(encryptedData);
|
|
7925
|
+
controller.close();
|
|
7926
|
+
}
|
|
7927
|
+
});
|
|
7928
|
+
streamParts.push({
|
|
7929
|
+
dataStream: uploadStream,
|
|
7930
|
+
metadata: {
|
|
7931
|
+
hashSHA: encryptedMeta,
|
|
7932
|
+
fileSize: encryptedData.byteLength
|
|
7933
|
+
},
|
|
7934
|
+
ordinalNumber: partIndex + 1
|
|
7935
|
+
});
|
|
7936
|
+
}
|
|
7937
|
+
reader.releaseLock();
|
|
7938
|
+
return {
|
|
7939
|
+
batchFile: {
|
|
7940
|
+
fileSize: zipSize,
|
|
7941
|
+
fileHash: zipMeta.hashSHA,
|
|
7942
|
+
fileParts
|
|
7943
|
+
},
|
|
7944
|
+
streamParts
|
|
7945
|
+
};
|
|
7946
|
+
}
|
|
7678
7947
|
};
|
|
7679
7948
|
function splitBuffer(data, maxPartSize) {
|
|
7680
7949
|
if (data.length <= maxPartSize) {
|
|
@@ -7778,6 +8047,7 @@ ${lines.join("\n")}
|
|
|
7778
8047
|
|
|
7779
8048
|
// src/crypto/index.ts
|
|
7780
8049
|
init_pkcs12_loader();
|
|
8050
|
+
init_auth_xml_builder();
|
|
7781
8051
|
|
|
7782
8052
|
// src/qr/index.ts
|
|
7783
8053
|
init_verification_link_service();
|
|
@@ -7841,6 +8111,94 @@ function escapeXml2(str) {
|
|
|
7841
8111
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
7842
8112
|
}
|
|
7843
8113
|
|
|
8114
|
+
// src/utils/zip.ts
|
|
8115
|
+
var import_yazl = require("yazl");
|
|
8116
|
+
var import_yauzl = require("yauzl");
|
|
8117
|
+
var DEFAULT_UNZIP_OPTIONS = {
|
|
8118
|
+
maxFiles: 1e4,
|
|
8119
|
+
maxTotalUncompressedSize: 2e9,
|
|
8120
|
+
maxFileUncompressedSize: 5e8,
|
|
8121
|
+
maxCompressionRatio: 200
|
|
8122
|
+
};
|
|
8123
|
+
async function createZip(entries) {
|
|
8124
|
+
return new Promise((resolve, reject) => {
|
|
8125
|
+
const zipfile = new import_yazl.ZipFile();
|
|
8126
|
+
for (const entry of entries) {
|
|
8127
|
+
zipfile.addBuffer(Buffer.from(entry.content), entry.fileName);
|
|
8128
|
+
}
|
|
8129
|
+
const chunks = [];
|
|
8130
|
+
zipfile.outputStream.on("data", (chunk) => chunks.push(chunk));
|
|
8131
|
+
zipfile.outputStream.on("error", (err) => reject(err));
|
|
8132
|
+
zipfile.outputStream.on("end", () => resolve(Buffer.concat(chunks)));
|
|
8133
|
+
zipfile.end();
|
|
8134
|
+
});
|
|
8135
|
+
}
|
|
8136
|
+
async function unzip(buffer, options = {}) {
|
|
8137
|
+
const limits = { ...DEFAULT_UNZIP_OPTIONS, ...options };
|
|
8138
|
+
return new Promise((resolve, reject) => {
|
|
8139
|
+
(0, import_yauzl.fromBuffer)(buffer, { lazyEntries: true }, (err, zipfile) => {
|
|
8140
|
+
if (err || !zipfile) {
|
|
8141
|
+
reject(err ?? new Error("Failed to open zip buffer"));
|
|
8142
|
+
return;
|
|
8143
|
+
}
|
|
8144
|
+
const files = /* @__PURE__ */ new Map();
|
|
8145
|
+
let totalUncompressed = 0;
|
|
8146
|
+
zipfile.readEntry();
|
|
8147
|
+
zipfile.on("entry", (entry) => {
|
|
8148
|
+
if (entry.fileName.endsWith("/")) {
|
|
8149
|
+
zipfile.readEntry();
|
|
8150
|
+
return;
|
|
8151
|
+
}
|
|
8152
|
+
if (limits.maxFiles > 0 && files.size >= limits.maxFiles) {
|
|
8153
|
+
reject(new Error("zip contains too many files"));
|
|
8154
|
+
return;
|
|
8155
|
+
}
|
|
8156
|
+
const uncompressedSize = entry.uncompressedSize ?? 0;
|
|
8157
|
+
const compressedSize = entry.compressedSize ?? 0;
|
|
8158
|
+
if (limits.maxFileUncompressedSize > 0 && uncompressedSize > limits.maxFileUncompressedSize) {
|
|
8159
|
+
reject(new Error("zip entry exceeds max_file_uncompressed_size"));
|
|
8160
|
+
return;
|
|
8161
|
+
}
|
|
8162
|
+
if (limits.maxTotalUncompressedSize > 0) {
|
|
8163
|
+
totalUncompressed += uncompressedSize;
|
|
8164
|
+
if (totalUncompressed > limits.maxTotalUncompressedSize) {
|
|
8165
|
+
reject(new Error("zip exceeds max_total_uncompressed_size"));
|
|
8166
|
+
return;
|
|
8167
|
+
}
|
|
8168
|
+
}
|
|
8169
|
+
if (limits.maxCompressionRatio !== null) {
|
|
8170
|
+
if (compressedSize === 0 && uncompressedSize > 0) {
|
|
8171
|
+
reject(new Error("zip entry has suspicious compression metadata"));
|
|
8172
|
+
return;
|
|
8173
|
+
}
|
|
8174
|
+
if (compressedSize > 0 && uncompressedSize > 0) {
|
|
8175
|
+
const ratio = uncompressedSize / compressedSize;
|
|
8176
|
+
if (ratio > limits.maxCompressionRatio) {
|
|
8177
|
+
reject(new Error("zip entry exceeds max_compression_ratio"));
|
|
8178
|
+
return;
|
|
8179
|
+
}
|
|
8180
|
+
}
|
|
8181
|
+
}
|
|
8182
|
+
zipfile.openReadStream(entry, (streamErr, stream) => {
|
|
8183
|
+
if (streamErr || !stream) {
|
|
8184
|
+
reject(streamErr ?? new Error("Failed to read zip entry"));
|
|
8185
|
+
return;
|
|
8186
|
+
}
|
|
8187
|
+
const chunks = [];
|
|
8188
|
+
stream.on("data", (chunk) => chunks.push(chunk));
|
|
8189
|
+
stream.on("error", (streamError) => reject(streamError));
|
|
8190
|
+
stream.on("end", () => {
|
|
8191
|
+
files.set(entry.fileName, Buffer.concat(chunks));
|
|
8192
|
+
zipfile.readEntry();
|
|
8193
|
+
});
|
|
8194
|
+
});
|
|
8195
|
+
});
|
|
8196
|
+
zipfile.on("end", () => resolve(files));
|
|
8197
|
+
zipfile.on("error", (zipErr) => reject(zipErr));
|
|
8198
|
+
});
|
|
8199
|
+
});
|
|
8200
|
+
}
|
|
8201
|
+
|
|
7844
8202
|
// src/workflows/polling.ts
|
|
7845
8203
|
async function pollUntil(action, condition, options) {
|
|
7846
8204
|
const intervalMs = options?.intervalMs ?? 2e3;
|
|
@@ -7858,17 +8216,150 @@ async function pollUntil(action, condition, options) {
|
|
|
7858
8216
|
);
|
|
7859
8217
|
}
|
|
7860
8218
|
|
|
8219
|
+
// src/xml/upo-parser.ts
|
|
8220
|
+
var import_fast_xml_parser = require("fast-xml-parser");
|
|
8221
|
+
init_ksef_validation_error();
|
|
8222
|
+
function isRecord(value) {
|
|
8223
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8224
|
+
}
|
|
8225
|
+
function requireRecord(value, at) {
|
|
8226
|
+
if (!isRecord(value)) {
|
|
8227
|
+
throw KSeFValidationError.fromField(at, `Expected object at ${at}`);
|
|
8228
|
+
}
|
|
8229
|
+
return value;
|
|
8230
|
+
}
|
|
8231
|
+
function requireString(value, at) {
|
|
8232
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
8233
|
+
throw KSeFValidationError.fromField(at, `Expected non-empty string at ${at}`);
|
|
8234
|
+
}
|
|
8235
|
+
return value;
|
|
8236
|
+
}
|
|
8237
|
+
function optionalRecord(value) {
|
|
8238
|
+
return isRecord(value) ? value : void 0;
|
|
8239
|
+
}
|
|
8240
|
+
function optionalString(value) {
|
|
8241
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
8242
|
+
}
|
|
8243
|
+
function requireNumberFromString(value, at) {
|
|
8244
|
+
const raw = requireString(value, at);
|
|
8245
|
+
const parsed = Number.parseInt(raw, 10);
|
|
8246
|
+
if (!Number.isFinite(parsed)) {
|
|
8247
|
+
throw KSeFValidationError.fromField(at, `Expected integer at ${at}, got "${raw}"`);
|
|
8248
|
+
}
|
|
8249
|
+
return parsed;
|
|
8250
|
+
}
|
|
8251
|
+
function ensureArray(value) {
|
|
8252
|
+
if (value == null) return [];
|
|
8253
|
+
return Array.isArray(value) ? value : [value];
|
|
8254
|
+
}
|
|
8255
|
+
function parseIdKontekstu(obj) {
|
|
8256
|
+
const nip = optionalString(obj.Nip);
|
|
8257
|
+
if (nip) return { kind: "Nip", nip };
|
|
8258
|
+
const idWewnetrzny = optionalString(obj.IdWewnetrzny);
|
|
8259
|
+
if (idWewnetrzny) return { kind: "IdWewnetrzny", idWewnetrzny };
|
|
8260
|
+
const idZlozonyVatUE = optionalString(obj.IdZlozonyVatUE);
|
|
8261
|
+
if (idZlozonyVatUE) return { kind: "IdZlozonyVatUE", idZlozonyVatUE };
|
|
8262
|
+
const idDostawcyUslugPeppol = optionalString(obj.IdDostawcyUslugPeppol);
|
|
8263
|
+
if (idDostawcyUslugPeppol) return { kind: "IdDostawcyUslugPeppol", idDostawcyUslugPeppol };
|
|
8264
|
+
throw KSeFValidationError.fromField(
|
|
8265
|
+
"Uwierzytelnienie.IdKontekstu",
|
|
8266
|
+
"Unsupported context identifier. Expected Nip | IdWewnetrzny | IdZlozonyVatUE | IdDostawcyUslugPeppol"
|
|
8267
|
+
);
|
|
8268
|
+
}
|
|
8269
|
+
function parseProof(obj) {
|
|
8270
|
+
const tokenRef = optionalString(obj.NumerReferencyjnyTokenaKSeF);
|
|
8271
|
+
if (tokenRef) return { kind: "NumerReferencyjnyTokenaKSeF", numerReferencyjnyTokenaKSeF: tokenRef };
|
|
8272
|
+
const docHash = optionalString(obj.SkrotDokumentuUwierzytelniajacego);
|
|
8273
|
+
if (docHash) return { kind: "SkrotDokumentuUwierzytelniajacego", skrotDokumentuUwierzytelniajacego: docHash };
|
|
8274
|
+
throw KSeFValidationError.fromField(
|
|
8275
|
+
"Uwierzytelnienie",
|
|
8276
|
+
"Unsupported auth proof. Expected NumerReferencyjnyTokenaKSeF | SkrotDokumentuUwierzytelniajacego"
|
|
8277
|
+
);
|
|
8278
|
+
}
|
|
8279
|
+
function parseUwierzytelnienie(obj) {
|
|
8280
|
+
const idKontekstu = parseIdKontekstu(
|
|
8281
|
+
requireRecord(obj.IdKontekstu, "Uwierzytelnienie.IdKontekstu")
|
|
8282
|
+
);
|
|
8283
|
+
const proof = parseProof(obj);
|
|
8284
|
+
return { idKontekstu, proof };
|
|
8285
|
+
}
|
|
8286
|
+
function parseOpisPotwierdzenia(obj) {
|
|
8287
|
+
return {
|
|
8288
|
+
strona: requireNumberFromString(obj.Strona, "OpisPotwierdzenia.Strona"),
|
|
8289
|
+
liczbaStron: requireNumberFromString(obj.LiczbaStron, "OpisPotwierdzenia.LiczbaStron"),
|
|
8290
|
+
zakresDokumentowOd: requireNumberFromString(obj.ZakresDokumentowOd, "OpisPotwierdzenia.ZakresDokumentowOd"),
|
|
8291
|
+
zakresDokumentowDo: requireNumberFromString(obj.ZakresDokumentowDo, "OpisPotwierdzenia.ZakresDokumentowDo"),
|
|
8292
|
+
calkowitaLiczbaDokumentow: requireNumberFromString(obj.CalkowitaLiczbaDokumentow, "OpisPotwierdzenia.CalkowitaLiczbaDokumentow")
|
|
8293
|
+
};
|
|
8294
|
+
}
|
|
8295
|
+
function parseDokument(obj) {
|
|
8296
|
+
return {
|
|
8297
|
+
nipSprzedawcy: requireString(obj.NipSprzedawcy, "Dokument.NipSprzedawcy"),
|
|
8298
|
+
numerKSeFDokumentu: requireString(obj.NumerKSeFDokumentu, "Dokument.NumerKSeFDokumentu"),
|
|
8299
|
+
numerFaktury: requireString(obj.NumerFaktury, "Dokument.NumerFaktury"),
|
|
8300
|
+
dataWystawieniaFaktury: requireString(obj.DataWystawieniaFaktury, "Dokument.DataWystawieniaFaktury"),
|
|
8301
|
+
dataPrzeslaniaDokumentu: requireString(obj.DataPrzeslaniaDokumentu, "Dokument.DataPrzeslaniaDokumentu"),
|
|
8302
|
+
dataNadaniaNumeruKSeF: requireString(obj.DataNadaniaNumeruKSeF, "Dokument.DataNadaniaNumeruKSeF"),
|
|
8303
|
+
skrotDokumentu: requireString(obj.SkrotDokumentu, "Dokument.SkrotDokumentu"),
|
|
8304
|
+
trybWysylki: requireString(obj.TrybWysylki, "Dokument.TrybWysylki")
|
|
8305
|
+
};
|
|
8306
|
+
}
|
|
8307
|
+
var upoParser = new import_fast_xml_parser.XMLParser({
|
|
8308
|
+
ignoreAttributes: false,
|
|
8309
|
+
attributeNamePrefix: "@_",
|
|
8310
|
+
parseTagValue: false,
|
|
8311
|
+
parseAttributeValue: false,
|
|
8312
|
+
removeNSPrefix: true,
|
|
8313
|
+
trimValues: false
|
|
8314
|
+
});
|
|
8315
|
+
function parseUpoXml(xml) {
|
|
8316
|
+
const text = Buffer.isBuffer(xml) ? xml.toString("utf8") : xml;
|
|
8317
|
+
const parsed = upoParser.parse(text);
|
|
8318
|
+
const potwierdzenie = requireRecord(parsed?.Potwierdzenie, "Potwierdzenie");
|
|
8319
|
+
const opisPotwierdzenia = optionalRecord(potwierdzenie.OpisPotwierdzenia);
|
|
8320
|
+
const dokumenty = ensureArray(potwierdzenie.Dokument).map(
|
|
8321
|
+
(doc, i) => parseDokument(requireRecord(doc, `Dokument[${i}]`))
|
|
8322
|
+
);
|
|
8323
|
+
if (dokumenty.length === 0) {
|
|
8324
|
+
throw KSeFValidationError.fromField("Dokument", "Expected at least one Dokument in UPO");
|
|
8325
|
+
}
|
|
8326
|
+
return {
|
|
8327
|
+
nazwaPodmiotuPrzyjmujacego: requireString(potwierdzenie.NazwaPodmiotuPrzyjmujacego, "NazwaPodmiotuPrzyjmujacego"),
|
|
8328
|
+
numerReferencyjnySesji: requireString(potwierdzenie.NumerReferencyjnySesji, "NumerReferencyjnySesji"),
|
|
8329
|
+
uwierzytelnienie: parseUwierzytelnienie(requireRecord(potwierdzenie.Uwierzytelnienie, "Uwierzytelnienie")),
|
|
8330
|
+
...opisPotwierdzenia && { opisPotwierdzenia: parseOpisPotwierdzenia(opisPotwierdzenia) },
|
|
8331
|
+
nazwaStrukturyLogicznej: requireString(potwierdzenie.NazwaStrukturyLogicznej, "NazwaStrukturyLogicznej"),
|
|
8332
|
+
kodFormularza: requireString(potwierdzenie.KodFormularza, "KodFormularza"),
|
|
8333
|
+
dokumenty
|
|
8334
|
+
};
|
|
8335
|
+
}
|
|
8336
|
+
|
|
7861
8337
|
// src/workflows/online-session-workflow.ts
|
|
7862
|
-
var DEFAULT_FORM_CODE = { systemCode: "FA", schemaVersion: "3", value: "FA (3)" };
|
|
7863
8338
|
async function openOnlineSession(client, options) {
|
|
7864
8339
|
await client.crypto.init();
|
|
7865
8340
|
const encData = client.crypto.getEncryptionData();
|
|
7866
|
-
const formCode = options?.formCode ??
|
|
8341
|
+
const formCode = options?.formCode ?? FORM_CODES.FA_2;
|
|
7867
8342
|
const openResp = await client.onlineSession.openSession(
|
|
7868
8343
|
{ formCode, encryption: encData.encryptionInfo },
|
|
7869
8344
|
options?.upoVersion
|
|
7870
8345
|
);
|
|
7871
8346
|
const sessionRef = openResp.referenceNumber;
|
|
8347
|
+
async function fetchUpo(pollOpts) {
|
|
8348
|
+
const result = await pollUntil(
|
|
8349
|
+
() => client.sessionStatus.getSessionStatus(sessionRef),
|
|
8350
|
+
(s) => s.status.code === 200 || s.status.code >= 400,
|
|
8351
|
+
{ ...pollOpts, description: `UPO for session ${sessionRef}` }
|
|
8352
|
+
);
|
|
8353
|
+
if (result.status.code !== 200) {
|
|
8354
|
+
throw new Error(`Session failed: ${result.status.code} \u2014 ${result.status.description}`);
|
|
8355
|
+
}
|
|
8356
|
+
return {
|
|
8357
|
+
pages: result.upo?.pages ?? [],
|
|
8358
|
+
invoiceCount: result.invoiceCount,
|
|
8359
|
+
successfulInvoiceCount: result.successfulInvoiceCount,
|
|
8360
|
+
failedInvoiceCount: result.failedInvoiceCount
|
|
8361
|
+
};
|
|
8362
|
+
}
|
|
7872
8363
|
return {
|
|
7873
8364
|
sessionRef,
|
|
7874
8365
|
validUntil: openResp.validUntil,
|
|
@@ -7890,20 +8381,16 @@ async function openOnlineSession(client, options) {
|
|
|
7890
8381
|
await client.onlineSession.closeSession(sessionRef);
|
|
7891
8382
|
},
|
|
7892
8383
|
async waitForUpo(pollOpts) {
|
|
7893
|
-
|
|
7894
|
-
|
|
7895
|
-
|
|
7896
|
-
|
|
7897
|
-
|
|
7898
|
-
|
|
7899
|
-
|
|
7900
|
-
|
|
7901
|
-
|
|
7902
|
-
|
|
7903
|
-
invoiceCount: result.invoiceCount,
|
|
7904
|
-
successfulInvoiceCount: result.successfulInvoiceCount,
|
|
7905
|
-
failedInvoiceCount: result.failedInvoiceCount
|
|
7906
|
-
};
|
|
8384
|
+
return fetchUpo(pollOpts);
|
|
8385
|
+
},
|
|
8386
|
+
async waitForUpoParsed(pollOpts) {
|
|
8387
|
+
const upoInfo = await fetchUpo(pollOpts);
|
|
8388
|
+
const parsed = [];
|
|
8389
|
+
for (const page of upoInfo.pages) {
|
|
8390
|
+
const result = await client.sessionStatus.getSessionUpo(sessionRef, page.referenceNumber);
|
|
8391
|
+
parsed.push(parseUpoXml(result.upo));
|
|
8392
|
+
}
|
|
8393
|
+
return { ...upoInfo, parsed };
|
|
7907
8394
|
}
|
|
7908
8395
|
};
|
|
7909
8396
|
}
|
|
@@ -7917,11 +8404,10 @@ async function openSendAndClose(client, invoices, options) {
|
|
|
7917
8404
|
}
|
|
7918
8405
|
|
|
7919
8406
|
// src/workflows/batch-session-workflow.ts
|
|
7920
|
-
var DEFAULT_FORM_CODE2 = { systemCode: "FA", schemaVersion: "3", value: "FA (3)" };
|
|
7921
8407
|
async function uploadBatch(client, zipData, options) {
|
|
7922
8408
|
await client.crypto.init();
|
|
7923
8409
|
const encData = client.crypto.getEncryptionData();
|
|
7924
|
-
const formCode = options?.formCode ??
|
|
8410
|
+
const formCode = options?.formCode ?? FORM_CODES.FA_2;
|
|
7925
8411
|
const encryptFn = (part) => client.crypto.encryptAES256(part, encData.cipherKey, encData.cipherIv);
|
|
7926
8412
|
const { batchFile, encryptedParts } = BatchFileBuilder.build(zipData, encryptFn, {
|
|
7927
8413
|
maxPartSize: options?.maxPartSize
|
|
@@ -7963,6 +8449,72 @@ async function uploadBatch(client, zipData, options) {
|
|
|
7963
8449
|
}
|
|
7964
8450
|
};
|
|
7965
8451
|
}
|
|
8452
|
+
async function uploadBatchStream(client, zipStreamFactory, zipSize, options) {
|
|
8453
|
+
await client.crypto.init();
|
|
8454
|
+
const encData = client.crypto.getEncryptionData();
|
|
8455
|
+
const formCode = options?.formCode ?? FORM_CODES.FA_2;
|
|
8456
|
+
const encryptStreamFn = (stream) => client.crypto.encryptAES256Stream(stream, encData.cipherKey, encData.cipherIv);
|
|
8457
|
+
const hashStreamFn = (stream) => client.crypto.getFileMetadataFromStream(stream);
|
|
8458
|
+
const { batchFile, streamParts } = await BatchFileBuilder.buildFromStream(
|
|
8459
|
+
zipStreamFactory,
|
|
8460
|
+
zipSize,
|
|
8461
|
+
encryptStreamFn,
|
|
8462
|
+
hashStreamFn,
|
|
8463
|
+
{ maxPartSize: options?.maxPartSize }
|
|
8464
|
+
);
|
|
8465
|
+
const openResp = await client.batchSession.openSession(
|
|
8466
|
+
{
|
|
8467
|
+
formCode,
|
|
8468
|
+
encryption: encData.encryptionInfo,
|
|
8469
|
+
batchFile,
|
|
8470
|
+
offlineMode: options?.offlineMode
|
|
8471
|
+
},
|
|
8472
|
+
options?.upoVersion
|
|
8473
|
+
);
|
|
8474
|
+
await client.batchSession.sendPartsWithStream(openResp, streamParts);
|
|
8475
|
+
await client.batchSession.closeSession(openResp.referenceNumber);
|
|
8476
|
+
const result = await pollUntil(
|
|
8477
|
+
() => client.sessionStatus.getSessionStatus(openResp.referenceNumber),
|
|
8478
|
+
(s) => s.status.code === 200 || s.status.code >= 400,
|
|
8479
|
+
{ ...options?.pollOptions, description: `UPO for batch ${openResp.referenceNumber}` }
|
|
8480
|
+
);
|
|
8481
|
+
if (result.status.code !== 200) {
|
|
8482
|
+
throw new Error(`Batch session failed: ${result.status.code} \u2014 ${result.status.description}`);
|
|
8483
|
+
}
|
|
8484
|
+
return {
|
|
8485
|
+
sessionRef: openResp.referenceNumber,
|
|
8486
|
+
upo: {
|
|
8487
|
+
pages: result.upo?.pages ?? [],
|
|
8488
|
+
invoiceCount: result.invoiceCount,
|
|
8489
|
+
successfulInvoiceCount: result.successfulInvoiceCount,
|
|
8490
|
+
failedInvoiceCount: result.failedInvoiceCount
|
|
8491
|
+
}
|
|
8492
|
+
};
|
|
8493
|
+
}
|
|
8494
|
+
async function uploadBatchStreamParsed(client, zipStreamFactory, zipSize, options) {
|
|
8495
|
+
const result = await uploadBatchStream(client, zipStreamFactory, zipSize, options);
|
|
8496
|
+
const parsed = [];
|
|
8497
|
+
for (const page of result.upo.pages) {
|
|
8498
|
+
const upoResult = await client.sessionStatus.getSessionUpo(result.sessionRef, page.referenceNumber);
|
|
8499
|
+
parsed.push(parseUpoXml(upoResult.upo));
|
|
8500
|
+
}
|
|
8501
|
+
return {
|
|
8502
|
+
sessionRef: result.sessionRef,
|
|
8503
|
+
upo: { ...result.upo, parsed }
|
|
8504
|
+
};
|
|
8505
|
+
}
|
|
8506
|
+
async function uploadBatchParsed(client, zipData, options) {
|
|
8507
|
+
const result = await uploadBatch(client, zipData, options);
|
|
8508
|
+
const parsed = [];
|
|
8509
|
+
for (const page of result.upo.pages) {
|
|
8510
|
+
const upoResult = await client.sessionStatus.getSessionUpo(result.sessionRef, page.referenceNumber);
|
|
8511
|
+
parsed.push(parseUpoXml(upoResult.upo));
|
|
8512
|
+
}
|
|
8513
|
+
return {
|
|
8514
|
+
sessionRef: result.sessionRef,
|
|
8515
|
+
upo: { ...result.upo, parsed }
|
|
8516
|
+
};
|
|
8517
|
+
}
|
|
7966
8518
|
|
|
7967
8519
|
// src/workflows/invoice-export-workflow.ts
|
|
7968
8520
|
async function doExport(client, filters, options) {
|
|
@@ -7986,6 +8538,7 @@ async function doExport(client, filters, options) {
|
|
|
7986
8538
|
}
|
|
7987
8539
|
return {
|
|
7988
8540
|
encData,
|
|
8541
|
+
referenceNumber: opResp.referenceNumber,
|
|
7989
8542
|
result: {
|
|
7990
8543
|
parts: result.package.parts.map((p) => ({
|
|
7991
8544
|
ordinalNumber: p.ordinalNumber,
|
|
@@ -7998,7 +8551,8 @@ async function doExport(client, filters, options) {
|
|
|
7998
8551
|
})),
|
|
7999
8552
|
invoiceCount: result.package.invoiceCount,
|
|
8000
8553
|
isTruncated: result.package.isTruncated,
|
|
8001
|
-
permanentStorageHwmDate: result.package.permanentStorageHwmDate
|
|
8554
|
+
permanentStorageHwmDate: result.package.permanentStorageHwmDate,
|
|
8555
|
+
lastPermanentStorageDate: result.package.lastPermanentStorageDate
|
|
8002
8556
|
}
|
|
8003
8557
|
};
|
|
8004
8558
|
}
|
|
@@ -8019,13 +8573,144 @@ async function exportAndDownload(client, filters, options) {
|
|
|
8019
8573
|
const decrypted = client.crypto.decryptAES256(encryptedData, encData.cipherKey, encData.cipherIv);
|
|
8020
8574
|
decryptedParts.push(decrypted);
|
|
8021
8575
|
}
|
|
8576
|
+
if (options?.extract) {
|
|
8577
|
+
const zipBuffer = Buffer.concat(decryptedParts);
|
|
8578
|
+
const files = await unzip(zipBuffer, options.unzipOptions);
|
|
8579
|
+
return { ...exportResult, files };
|
|
8580
|
+
}
|
|
8022
8581
|
return {
|
|
8023
8582
|
...exportResult,
|
|
8024
8583
|
decryptedParts
|
|
8025
8584
|
};
|
|
8026
8585
|
}
|
|
8027
8586
|
|
|
8587
|
+
// src/workflows/hwm-coordinator.ts
|
|
8588
|
+
function updateContinuationPoint(points, subjectType, pkg) {
|
|
8589
|
+
if (pkg.isTruncated && pkg.lastPermanentStorageDate) {
|
|
8590
|
+
points[subjectType] = pkg.lastPermanentStorageDate;
|
|
8591
|
+
} else if (pkg.permanentStorageHwmDate) {
|
|
8592
|
+
points[subjectType] = pkg.permanentStorageHwmDate;
|
|
8593
|
+
} else {
|
|
8594
|
+
delete points[subjectType];
|
|
8595
|
+
}
|
|
8596
|
+
}
|
|
8597
|
+
function getEffectiveStartDate(points, subjectType, windowFrom) {
|
|
8598
|
+
return points[subjectType] ?? windowFrom;
|
|
8599
|
+
}
|
|
8600
|
+
function deduplicateByKsefNumber(entries) {
|
|
8601
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8602
|
+
return entries.filter((entry) => {
|
|
8603
|
+
const key = entry.ksefNumber.toLowerCase();
|
|
8604
|
+
if (seen.has(key)) return false;
|
|
8605
|
+
seen.add(key);
|
|
8606
|
+
return true;
|
|
8607
|
+
});
|
|
8608
|
+
}
|
|
8609
|
+
|
|
8610
|
+
// src/workflows/incremental-export-workflow.ts
|
|
8611
|
+
async function incrementalExportAndDownload(client, options) {
|
|
8612
|
+
const maxIterations = options.maxIterations ?? 20;
|
|
8613
|
+
const points = options.continuationPoints;
|
|
8614
|
+
if (options.store) {
|
|
8615
|
+
const loaded = await options.store.load();
|
|
8616
|
+
for (const [key, value] of Object.entries(loaded)) {
|
|
8617
|
+
if (value !== void 0 && points[key] === void 0) {
|
|
8618
|
+
points[key] = value;
|
|
8619
|
+
}
|
|
8620
|
+
}
|
|
8621
|
+
}
|
|
8622
|
+
const referenceNumbers = [];
|
|
8623
|
+
const decryptedParts = [];
|
|
8624
|
+
let previousFrom;
|
|
8625
|
+
let iteration = 0;
|
|
8626
|
+
for (; iteration < maxIterations; iteration++) {
|
|
8627
|
+
const effectiveFrom = getEffectiveStartDate(points, options.subjectType, options.windowFrom);
|
|
8628
|
+
if (previousFrom !== void 0 && effectiveFrom === previousFrom) {
|
|
8629
|
+
break;
|
|
8630
|
+
}
|
|
8631
|
+
previousFrom = effectiveFrom;
|
|
8632
|
+
const filters = options.filtersFactory ? options.filtersFactory(effectiveFrom, options.windowTo) : buildDefaultFilters(options.subjectType, effectiveFrom, options.windowTo);
|
|
8633
|
+
const { result, encData, referenceNumber } = await doExport(client, filters, {
|
|
8634
|
+
onlyMetadata: options.onlyMetadata,
|
|
8635
|
+
pollOptions: options.pollOptions
|
|
8636
|
+
});
|
|
8637
|
+
referenceNumbers.push(referenceNumber);
|
|
8638
|
+
const download = options.transport ?? fetch;
|
|
8639
|
+
for (const part of result.parts) {
|
|
8640
|
+
const resp = await download(part.url, { method: part.method });
|
|
8641
|
+
if (!resp.ok) {
|
|
8642
|
+
throw new Error(`Download failed for part ${part.ordinalNumber}: HTTP ${resp.status}`);
|
|
8643
|
+
}
|
|
8644
|
+
const encryptedData = new Uint8Array(await resp.arrayBuffer());
|
|
8645
|
+
const decrypted = client.crypto.decryptAES256(encryptedData, encData.cipherKey, encData.cipherIv);
|
|
8646
|
+
decryptedParts.push(decrypted);
|
|
8647
|
+
}
|
|
8648
|
+
updateContinuationPoint(points, options.subjectType, {
|
|
8649
|
+
isTruncated: result.isTruncated,
|
|
8650
|
+
lastPermanentStorageDate: result.lastPermanentStorageDate,
|
|
8651
|
+
permanentStorageHwmDate: result.permanentStorageHwmDate
|
|
8652
|
+
});
|
|
8653
|
+
if (options.store) {
|
|
8654
|
+
await options.store.save(points);
|
|
8655
|
+
}
|
|
8656
|
+
options.onIterationComplete?.(iteration, result);
|
|
8657
|
+
if (!result.isTruncated) {
|
|
8658
|
+
iteration++;
|
|
8659
|
+
break;
|
|
8660
|
+
}
|
|
8661
|
+
}
|
|
8662
|
+
return {
|
|
8663
|
+
referenceNumbers,
|
|
8664
|
+
invoices: [],
|
|
8665
|
+
decryptedParts,
|
|
8666
|
+
continuationPoints: points,
|
|
8667
|
+
iterationCount: iteration
|
|
8668
|
+
};
|
|
8669
|
+
}
|
|
8670
|
+
function buildDefaultFilters(subjectType, from, to) {
|
|
8671
|
+
return {
|
|
8672
|
+
subjectType,
|
|
8673
|
+
dateRange: {
|
|
8674
|
+
dateType: "PermanentStorage",
|
|
8675
|
+
from,
|
|
8676
|
+
to
|
|
8677
|
+
}
|
|
8678
|
+
};
|
|
8679
|
+
}
|
|
8680
|
+
|
|
8681
|
+
// src/workflows/hwm-storage.ts
|
|
8682
|
+
var fs = __toESM(require("fs/promises"), 1);
|
|
8683
|
+
var InMemoryHwmStore = class {
|
|
8684
|
+
points = {};
|
|
8685
|
+
async load() {
|
|
8686
|
+
return { ...this.points };
|
|
8687
|
+
}
|
|
8688
|
+
async save(points) {
|
|
8689
|
+
this.points = { ...points };
|
|
8690
|
+
}
|
|
8691
|
+
};
|
|
8692
|
+
var FileHwmStore = class {
|
|
8693
|
+
constructor(filePath) {
|
|
8694
|
+
this.filePath = filePath;
|
|
8695
|
+
}
|
|
8696
|
+
async load() {
|
|
8697
|
+
try {
|
|
8698
|
+
const data = await fs.readFile(this.filePath, "utf-8");
|
|
8699
|
+
return JSON.parse(data);
|
|
8700
|
+
} catch (err) {
|
|
8701
|
+
if (err.code === "ENOENT") {
|
|
8702
|
+
return {};
|
|
8703
|
+
}
|
|
8704
|
+
throw err;
|
|
8705
|
+
}
|
|
8706
|
+
}
|
|
8707
|
+
async save(points) {
|
|
8708
|
+
await fs.writeFile(this.filePath, JSON.stringify(points, null, 2), "utf-8");
|
|
8709
|
+
}
|
|
8710
|
+
};
|
|
8711
|
+
|
|
8028
8712
|
// src/workflows/auth-workflow.ts
|
|
8713
|
+
init_auth_xml_builder();
|
|
8029
8714
|
async function authenticateWithToken(client, options) {
|
|
8030
8715
|
const challenge = await client.auth.getChallenge();
|
|
8031
8716
|
await client.crypto.init();
|
|
@@ -8079,6 +8764,34 @@ async function authenticateWithCertificate(client, options) {
|
|
|
8079
8764
|
refreshTokenValidUntil: tokens.refreshToken.validUntil
|
|
8080
8765
|
};
|
|
8081
8766
|
}
|
|
8767
|
+
async function authenticateWithExternalSignature(client, options) {
|
|
8768
|
+
const challenge = await client.auth.getChallenge();
|
|
8769
|
+
const unsignedXml = buildUnsignedAuthTokenRequestXml({
|
|
8770
|
+
challenge: challenge.challenge,
|
|
8771
|
+
contextIdentifier: options.contextIdentifier
|
|
8772
|
+
});
|
|
8773
|
+
const signedXml = await options.signXml(unsignedXml);
|
|
8774
|
+
const submitResult = await client.auth.submitXadesAuthRequest(
|
|
8775
|
+
signedXml,
|
|
8776
|
+
options.verifyCertificateChain ?? false,
|
|
8777
|
+
options.enforceXadesCompliance ?? false
|
|
8778
|
+
);
|
|
8779
|
+
const authToken = submitResult.authenticationToken.token;
|
|
8780
|
+
await pollUntil(
|
|
8781
|
+
() => client.auth.getAuthStatus(submitResult.referenceNumber, authToken),
|
|
8782
|
+
(s) => s.status.code !== 100,
|
|
8783
|
+
{ ...options.pollOptions, description: `auth ${submitResult.referenceNumber}` }
|
|
8784
|
+
);
|
|
8785
|
+
const tokens = await client.auth.getAccessToken(authToken);
|
|
8786
|
+
client.authManager.setAccessToken(tokens.accessToken.token);
|
|
8787
|
+
client.authManager.setRefreshToken(tokens.refreshToken.token);
|
|
8788
|
+
return {
|
|
8789
|
+
accessToken: tokens.accessToken.token,
|
|
8790
|
+
accessTokenValidUntil: tokens.accessToken.validUntil,
|
|
8791
|
+
refreshToken: tokens.refreshToken.token,
|
|
8792
|
+
refreshTokenValidUntil: tokens.refreshToken.validUntil
|
|
8793
|
+
};
|
|
8794
|
+
}
|
|
8082
8795
|
async function authenticateWithPkcs12(client, options) {
|
|
8083
8796
|
const { Pkcs12Loader: Pkcs12Loader2 } = await Promise.resolve().then(() => (init_pkcs12_loader(), pkcs12_loader_exports));
|
|
8084
8797
|
const { certificatePem, privateKeyPem } = Pkcs12Loader2.load(options.p12, options.password);
|
|
@@ -8119,6 +8832,11 @@ init_client();
|
|
|
8119
8832
|
ENFORCE_XADES_COMPLIANCE,
|
|
8120
8833
|
EntityPermissionGrantBuilder,
|
|
8121
8834
|
Environment,
|
|
8835
|
+
FORM_CODES,
|
|
8836
|
+
FORM_CODE_KEYS,
|
|
8837
|
+
FileHwmStore,
|
|
8838
|
+
INVOICE_TYPES_BY_SYSTEM_CODE,
|
|
8839
|
+
InMemoryHwmStore,
|
|
8122
8840
|
InternalId,
|
|
8123
8841
|
InvoiceDownloadService,
|
|
8124
8842
|
InvoiceQueryFilterBuilder,
|
|
@@ -8164,21 +8882,29 @@ init_client();
|
|
|
8164
8882
|
SessionStatusService,
|
|
8165
8883
|
Sha256Base64,
|
|
8166
8884
|
SignatureService,
|
|
8885
|
+
SystemCode,
|
|
8167
8886
|
TestDataService,
|
|
8168
8887
|
TokenService,
|
|
8169
8888
|
UpoVersion,
|
|
8170
8889
|
VatUe,
|
|
8171
8890
|
VerificationLinkService,
|
|
8172
8891
|
authenticateWithCertificate,
|
|
8892
|
+
authenticateWithExternalSignature,
|
|
8173
8893
|
authenticateWithPkcs12,
|
|
8174
8894
|
authenticateWithToken,
|
|
8895
|
+
buildUnsignedAuthTokenRequestXml,
|
|
8175
8896
|
calculateBackoff,
|
|
8897
|
+
createZip,
|
|
8898
|
+
deduplicateByKsefNumber,
|
|
8176
8899
|
defaultPresignedUrlPolicy,
|
|
8177
8900
|
defaultRateLimitPolicy,
|
|
8178
8901
|
defaultRetryPolicy,
|
|
8179
8902
|
defaultTransport,
|
|
8180
8903
|
exportAndDownload,
|
|
8181
8904
|
exportInvoices,
|
|
8905
|
+
getEffectiveStartDate,
|
|
8906
|
+
getFormCode,
|
|
8907
|
+
incrementalExportAndDownload,
|
|
8182
8908
|
isRetryableError,
|
|
8183
8909
|
isRetryableStatus,
|
|
8184
8910
|
isValidBase64,
|
|
@@ -8198,11 +8924,19 @@ init_client();
|
|
|
8198
8924
|
isValidVatUe,
|
|
8199
8925
|
openOnlineSession,
|
|
8200
8926
|
openSendAndClose,
|
|
8927
|
+
parseFormCode,
|
|
8201
8928
|
parseRetryAfter,
|
|
8929
|
+
parseUpoXml,
|
|
8202
8930
|
pollUntil,
|
|
8203
8931
|
resolveOptions,
|
|
8204
8932
|
sleep,
|
|
8933
|
+
unzip,
|
|
8934
|
+
updateContinuationPoint,
|
|
8205
8935
|
uploadBatch,
|
|
8936
|
+
uploadBatchParsed,
|
|
8937
|
+
uploadBatchStream,
|
|
8938
|
+
uploadBatchStreamParsed,
|
|
8939
|
+
validateFormCodeForSession,
|
|
8206
8940
|
validatePresignedUrl
|
|
8207
8941
|
});
|
|
8208
8942
|
//# sourceMappingURL=index.cjs.map
|