ksef-client-ts 0.2.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 +21 -14
- package/dist/cli.js +1384 -262
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +948 -99
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +311 -6
- package/dist/index.d.ts +311 -6
- package/dist/index.js +926 -102
- package/dist/index.js.map +1 -1
- package/package.json +6 -2
package/dist/index.js
CHANGED
|
@@ -65,7 +65,8 @@ function resolveOptions(options = {}) {
|
|
|
65
65
|
lighthouseUrl: options.lighthouseUrl ?? env.lighthouseUrl,
|
|
66
66
|
apiVersion: options.apiVersion ?? DEFAULT_API_VERSION,
|
|
67
67
|
timeout: options.timeout ?? DEFAULT_TIMEOUT,
|
|
68
|
-
customHeaders: options.customHeaders ?? {}
|
|
68
|
+
customHeaders: options.customHeaders ?? {},
|
|
69
|
+
environmentName: options.environment ?? (options.baseUrl ? void 0 : "TEST")
|
|
69
70
|
};
|
|
70
71
|
}
|
|
71
72
|
var DEFAULT_API_VERSION, DEFAULT_TIMEOUT;
|
|
@@ -1105,6 +1106,36 @@ var init_batch_session = __esm({
|
|
|
1105
1106
|
});
|
|
1106
1107
|
await Promise.all(tasks);
|
|
1107
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
|
+
}
|
|
1108
1139
|
async closeSession(batchRef) {
|
|
1109
1140
|
const req = RestRequest.post(Routes.Sessions.Batch.close(batchRef));
|
|
1110
1141
|
await this.restClient.executeVoid(req);
|
|
@@ -1573,84 +1604,111 @@ var init_test_data = __esm({
|
|
|
1573
1604
|
"use strict";
|
|
1574
1605
|
init_rest_request();
|
|
1575
1606
|
init_routes();
|
|
1607
|
+
init_ksef_error();
|
|
1576
1608
|
TestDataService = class {
|
|
1577
1609
|
restClient;
|
|
1578
|
-
|
|
1610
|
+
environmentName;
|
|
1611
|
+
constructor(restClient, environmentName) {
|
|
1579
1612
|
this.restClient = restClient;
|
|
1613
|
+
this.environmentName = environmentName;
|
|
1614
|
+
}
|
|
1615
|
+
ensureTestEnvironment() {
|
|
1616
|
+
if (this.environmentName && this.environmentName !== "TEST") {
|
|
1617
|
+
throw new KSeFError(
|
|
1618
|
+
`Test data APIs are only available on the TEST environment (current: ${this.environmentName})`
|
|
1619
|
+
);
|
|
1620
|
+
}
|
|
1580
1621
|
}
|
|
1581
1622
|
// Subject management
|
|
1582
1623
|
async createSubject(request) {
|
|
1624
|
+
this.ensureTestEnvironment();
|
|
1583
1625
|
const req = RestRequest.post(Routes.TestData.createSubject).body(request);
|
|
1584
1626
|
await this.restClient.executeVoid(req);
|
|
1585
1627
|
}
|
|
1586
1628
|
async removeSubject(request) {
|
|
1629
|
+
this.ensureTestEnvironment();
|
|
1587
1630
|
const req = RestRequest.post(Routes.TestData.removeSubject).body(request);
|
|
1588
1631
|
await this.restClient.executeVoid(req);
|
|
1589
1632
|
}
|
|
1590
1633
|
// Person management
|
|
1591
1634
|
async createPerson(request) {
|
|
1635
|
+
this.ensureTestEnvironment();
|
|
1592
1636
|
const req = RestRequest.post(Routes.TestData.createPerson).body(request);
|
|
1593
1637
|
await this.restClient.executeVoid(req);
|
|
1594
1638
|
}
|
|
1595
1639
|
async removePerson(request) {
|
|
1640
|
+
this.ensureTestEnvironment();
|
|
1596
1641
|
const req = RestRequest.post(Routes.TestData.removePerson).body(request);
|
|
1597
1642
|
await this.restClient.executeVoid(req);
|
|
1598
1643
|
}
|
|
1599
1644
|
// Permissions
|
|
1600
1645
|
async grantPermissions(request) {
|
|
1646
|
+
this.ensureTestEnvironment();
|
|
1601
1647
|
const req = RestRequest.post(Routes.TestData.grantPerms).body(request);
|
|
1602
1648
|
await this.restClient.executeVoid(req);
|
|
1603
1649
|
}
|
|
1604
1650
|
async revokePermissions(request) {
|
|
1651
|
+
this.ensureTestEnvironment();
|
|
1605
1652
|
const req = RestRequest.post(Routes.TestData.revokePerms).body(request);
|
|
1606
1653
|
await this.restClient.executeVoid(req);
|
|
1607
1654
|
}
|
|
1608
1655
|
// Attachment permissions
|
|
1609
1656
|
async enableAttachment(request) {
|
|
1657
|
+
this.ensureTestEnvironment();
|
|
1610
1658
|
const req = RestRequest.post(Routes.TestData.enableAttach).body(request);
|
|
1611
1659
|
await this.restClient.executeVoid(req);
|
|
1612
1660
|
}
|
|
1613
1661
|
async disableAttachment(request) {
|
|
1662
|
+
this.ensureTestEnvironment();
|
|
1614
1663
|
const req = RestRequest.post(Routes.TestData.disableAttach).body(request);
|
|
1615
1664
|
await this.restClient.executeVoid(req);
|
|
1616
1665
|
}
|
|
1617
1666
|
// Session limits
|
|
1618
1667
|
async changeSessionLimits(request) {
|
|
1668
|
+
this.ensureTestEnvironment();
|
|
1619
1669
|
const req = RestRequest.post(Routes.TestData.changeSessionLimitsInCurrentContext).body(request);
|
|
1620
1670
|
await this.restClient.executeVoid(req);
|
|
1621
1671
|
}
|
|
1622
1672
|
async restoreDefaultSessionLimits() {
|
|
1673
|
+
this.ensureTestEnvironment();
|
|
1623
1674
|
const req = RestRequest.delete(Routes.TestData.restoreDefaultSessionLimitsInCurrentContext);
|
|
1624
1675
|
await this.restClient.executeVoid(req);
|
|
1625
1676
|
}
|
|
1626
1677
|
// Certificate limits
|
|
1627
1678
|
async changeCertificatesLimit(request) {
|
|
1679
|
+
this.ensureTestEnvironment();
|
|
1628
1680
|
const req = RestRequest.post(Routes.TestData.changeCertificatesLimitInCurrentSubject).body(request);
|
|
1629
1681
|
await this.restClient.executeVoid(req);
|
|
1630
1682
|
}
|
|
1631
1683
|
async restoreDefaultCertificatesLimit() {
|
|
1684
|
+
this.ensureTestEnvironment();
|
|
1632
1685
|
const req = RestRequest.delete(Routes.TestData.restoreDefaultCertificatesLimitInCurrentSubject);
|
|
1633
1686
|
await this.restClient.executeVoid(req);
|
|
1634
1687
|
}
|
|
1635
1688
|
// Rate limits
|
|
1636
1689
|
async setRateLimits(request) {
|
|
1690
|
+
this.ensureTestEnvironment();
|
|
1637
1691
|
const req = RestRequest.post(Routes.TestData.rateLimits).body(request);
|
|
1638
1692
|
await this.restClient.executeVoid(req);
|
|
1639
1693
|
}
|
|
1640
1694
|
async restoreDefaultRateLimits() {
|
|
1695
|
+
this.ensureTestEnvironment();
|
|
1641
1696
|
const req = RestRequest.delete(Routes.TestData.rateLimits);
|
|
1642
1697
|
await this.restClient.executeVoid(req);
|
|
1643
1698
|
}
|
|
1644
1699
|
async setProductionRateLimits() {
|
|
1700
|
+
this.ensureTestEnvironment();
|
|
1645
1701
|
const req = RestRequest.post(Routes.TestData.productionRateLimits);
|
|
1646
1702
|
await this.restClient.executeVoid(req);
|
|
1647
1703
|
}
|
|
1648
1704
|
// Context blocking
|
|
1649
1705
|
async blockContext(request) {
|
|
1706
|
+
this.ensureTestEnvironment();
|
|
1650
1707
|
const req = RestRequest.post(Routes.TestData.blockContext).body(request);
|
|
1651
1708
|
await this.restClient.executeVoid(req);
|
|
1652
1709
|
}
|
|
1653
1710
|
async unblockContext(request) {
|
|
1711
|
+
this.ensureTestEnvironment();
|
|
1654
1712
|
const req = RestRequest.post(Routes.TestData.unblockContext).body(request);
|
|
1655
1713
|
await this.restClient.executeVoid(req);
|
|
1656
1714
|
}
|
|
@@ -1727,10 +1785,10 @@ ${lines.join("\n")}
|
|
|
1727
1785
|
});
|
|
1728
1786
|
|
|
1729
1787
|
// src/crypto/cryptography-service.ts
|
|
1730
|
-
import * as
|
|
1788
|
+
import * as crypto2 from "crypto";
|
|
1731
1789
|
import * as x509 from "@peculiar/x509";
|
|
1732
1790
|
function setCryptoProvider() {
|
|
1733
|
-
x509.cryptoProvider.set(
|
|
1791
|
+
x509.cryptoProvider.set(crypto2.webcrypto);
|
|
1734
1792
|
}
|
|
1735
1793
|
function buildX500Name(fields) {
|
|
1736
1794
|
const parts = [];
|
|
@@ -1772,12 +1830,34 @@ var init_cryptography_service = __esm({
|
|
|
1772
1830
|
// ---------------------------------------------------------------------------
|
|
1773
1831
|
/** Encrypt with AES-256-CBC (PKCS7 padding is automatic in Node). */
|
|
1774
1832
|
encryptAES256(content, key, iv) {
|
|
1775
|
-
const cipher =
|
|
1833
|
+
const cipher = crypto2.createCipheriv("aes-256-cbc", key, iv);
|
|
1776
1834
|
return new Uint8Array(Buffer.concat([cipher.update(content), cipher.final()]));
|
|
1777
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
|
+
}
|
|
1778
1858
|
/** Decrypt with AES-256-CBC. */
|
|
1779
1859
|
decryptAES256(content, key, iv) {
|
|
1780
|
-
const decipher =
|
|
1860
|
+
const decipher = crypto2.createDecipheriv("aes-256-cbc", key, iv);
|
|
1781
1861
|
return new Uint8Array(Buffer.concat([decipher.update(content), decipher.final()]));
|
|
1782
1862
|
}
|
|
1783
1863
|
// ---------------------------------------------------------------------------
|
|
@@ -1788,14 +1868,14 @@ var init_cryptography_service = __esm({
|
|
|
1788
1868
|
* SymmetricKeyEncryption certificate's RSA public key (RSA-OAEP SHA-256).
|
|
1789
1869
|
*/
|
|
1790
1870
|
getEncryptionData() {
|
|
1791
|
-
const key =
|
|
1792
|
-
const iv =
|
|
1871
|
+
const key = crypto2.randomBytes(32);
|
|
1872
|
+
const iv = crypto2.randomBytes(16);
|
|
1793
1873
|
const certPem = this.fetcher.getSymmetricKeyEncryptionPem();
|
|
1794
|
-
const encryptedKey =
|
|
1874
|
+
const encryptedKey = crypto2.publicEncrypt(
|
|
1795
1875
|
{
|
|
1796
1876
|
key: certPem,
|
|
1797
1877
|
oaepHash: "sha256",
|
|
1798
|
-
padding:
|
|
1878
|
+
padding: crypto2.constants.RSA_PKCS1_OAEP_PADDING
|
|
1799
1879
|
},
|
|
1800
1880
|
key
|
|
1801
1881
|
);
|
|
@@ -1829,7 +1909,7 @@ var init_cryptography_service = __esm({
|
|
|
1829
1909
|
const timestampMs = new Date(challengeTimestamp).getTime();
|
|
1830
1910
|
const plaintext = Buffer.from(`${token}|${timestampMs}`, "utf-8");
|
|
1831
1911
|
const certPem = this.fetcher.getKsefTokenEncryptionPem();
|
|
1832
|
-
const cert = new
|
|
1912
|
+
const cert = new crypto2.X509Certificate(certPem);
|
|
1833
1913
|
const publicKey = cert.publicKey;
|
|
1834
1914
|
if (publicKey.asymmetricKeyType === "rsa") {
|
|
1835
1915
|
return this.encryptRsaOaep(certPem, plaintext);
|
|
@@ -1844,9 +1924,26 @@ var init_cryptography_service = __esm({
|
|
|
1844
1924
|
// ---------------------------------------------------------------------------
|
|
1845
1925
|
/** Compute SHA-256 hash (base64) and byte length. */
|
|
1846
1926
|
getFileMetadata(file) {
|
|
1847
|
-
const hash =
|
|
1927
|
+
const hash = crypto2.createHash("sha256").update(file).digest("base64");
|
|
1848
1928
|
return { hashSHA: hash, fileSize: file.length };
|
|
1849
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
|
+
}
|
|
1850
1947
|
// ---------------------------------------------------------------------------
|
|
1851
1948
|
// CSR generation
|
|
1852
1949
|
// ---------------------------------------------------------------------------
|
|
@@ -1859,7 +1956,7 @@ var init_cryptography_service = __esm({
|
|
|
1859
1956
|
publicExponent: new Uint8Array([1, 0, 1]),
|
|
1860
1957
|
modulusLength: 2048
|
|
1861
1958
|
};
|
|
1862
|
-
const keys = await
|
|
1959
|
+
const keys = await crypto2.webcrypto.subtle.generateKey(
|
|
1863
1960
|
algorithm,
|
|
1864
1961
|
true,
|
|
1865
1962
|
["sign", "verify"]
|
|
@@ -1870,7 +1967,7 @@ var init_cryptography_service = __esm({
|
|
|
1870
1967
|
signingAlgorithm: algorithm
|
|
1871
1968
|
});
|
|
1872
1969
|
const csrDer = new Uint8Array(csr.rawData);
|
|
1873
|
-
const pkcs8 = await
|
|
1970
|
+
const pkcs8 = await crypto2.webcrypto.subtle.exportKey("pkcs8", keys.privateKey);
|
|
1874
1971
|
const privateKeyPem = pemEncode(new Uint8Array(pkcs8), "PRIVATE KEY");
|
|
1875
1972
|
return { csrDer, privateKeyPem };
|
|
1876
1973
|
}
|
|
@@ -1881,7 +1978,7 @@ var init_cryptography_service = __esm({
|
|
|
1881
1978
|
name: "ECDSA",
|
|
1882
1979
|
namedCurve: "P-256"
|
|
1883
1980
|
};
|
|
1884
|
-
const keys = await
|
|
1981
|
+
const keys = await crypto2.webcrypto.subtle.generateKey(
|
|
1885
1982
|
algorithm,
|
|
1886
1983
|
true,
|
|
1887
1984
|
["sign", "verify"]
|
|
@@ -1892,7 +1989,7 @@ var init_cryptography_service = __esm({
|
|
|
1892
1989
|
signingAlgorithm: { name: "ECDSA", hash: "SHA-256" }
|
|
1893
1990
|
});
|
|
1894
1991
|
const csrDer = new Uint8Array(csr.rawData);
|
|
1895
|
-
const pkcs8 = await
|
|
1992
|
+
const pkcs8 = await crypto2.webcrypto.subtle.exportKey("pkcs8", keys.privateKey);
|
|
1896
1993
|
const privateKeyPem = pemEncode(new Uint8Array(pkcs8), "PRIVATE KEY");
|
|
1897
1994
|
return { csrDer, privateKeyPem };
|
|
1898
1995
|
}
|
|
@@ -1901,7 +1998,7 @@ var init_cryptography_service = __esm({
|
|
|
1901
1998
|
// ---------------------------------------------------------------------------
|
|
1902
1999
|
/** Parse a PEM-encoded private key into a Node.js KeyObject. */
|
|
1903
2000
|
parsePrivateKey(pem) {
|
|
1904
|
-
return
|
|
2001
|
+
return crypto2.createPrivateKey(pem);
|
|
1905
2002
|
}
|
|
1906
2003
|
// ---------------------------------------------------------------------------
|
|
1907
2004
|
// Private helpers
|
|
@@ -1909,11 +2006,11 @@ var init_cryptography_service = __esm({
|
|
|
1909
2006
|
/** RSA-OAEP SHA-256 encryption. */
|
|
1910
2007
|
encryptRsaOaep(certPem, plaintext) {
|
|
1911
2008
|
return new Uint8Array(
|
|
1912
|
-
|
|
2009
|
+
crypto2.publicEncrypt(
|
|
1913
2010
|
{
|
|
1914
2011
|
key: certPem,
|
|
1915
2012
|
oaepHash: "sha256",
|
|
1916
|
-
padding:
|
|
2013
|
+
padding: crypto2.constants.RSA_PKCS1_OAEP_PADDING
|
|
1917
2014
|
},
|
|
1918
2015
|
plaintext
|
|
1919
2016
|
)
|
|
@@ -1930,15 +2027,15 @@ var init_cryptography_service = __esm({
|
|
|
1930
2027
|
* (Java-compatible — GCM appends the 16-byte tag to the ciphertext).
|
|
1931
2028
|
*/
|
|
1932
2029
|
encryptEcdhAesGcm(receiverPublicKey, plaintext) {
|
|
1933
|
-
const { privateKey: ephPrivKey, publicKey: ephPubKey } =
|
|
2030
|
+
const { privateKey: ephPrivKey, publicKey: ephPubKey } = crypto2.generateKeyPairSync("ec", {
|
|
1934
2031
|
namedCurve: "prime256v1"
|
|
1935
2032
|
});
|
|
1936
|
-
const sharedSecret =
|
|
2033
|
+
const sharedSecret = crypto2.diffieHellman({
|
|
1937
2034
|
privateKey: ephPrivKey,
|
|
1938
2035
|
publicKey: receiverPublicKey
|
|
1939
2036
|
});
|
|
1940
|
-
const nonce =
|
|
1941
|
-
const cipher =
|
|
2037
|
+
const nonce = crypto2.randomBytes(12);
|
|
2038
|
+
const cipher = crypto2.createCipheriv("aes-256-gcm", sharedSecret, nonce);
|
|
1942
2039
|
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
1943
2040
|
const tag = cipher.getAuthTag();
|
|
1944
2041
|
const ephemeralSpki = ephPubKey.export({ type: "spki", format: "der" });
|
|
@@ -6489,7 +6586,7 @@ var signature_service_exports = {};
|
|
|
6489
6586
|
__export(signature_service_exports, {
|
|
6490
6587
|
SignatureService: () => SignatureService
|
|
6491
6588
|
});
|
|
6492
|
-
import * as
|
|
6589
|
+
import * as crypto3 from "crypto";
|
|
6493
6590
|
import { ExclusiveCanonicalization } from "xml-crypto";
|
|
6494
6591
|
function extractDerFromPem(pem) {
|
|
6495
6592
|
const base64 = pem.replace(/-----BEGIN [A-Z\s]+-----/g, "").replace(/-----END [A-Z\s]+-----/g, "").replace(/\s+/g, "");
|
|
@@ -6508,7 +6605,7 @@ function canonicalize(elem) {
|
|
|
6508
6605
|
function computeRootDigest(doc) {
|
|
6509
6606
|
const root = doc.documentElement;
|
|
6510
6607
|
const canonical = canonicalize(root);
|
|
6511
|
-
return
|
|
6608
|
+
return crypto3.createHash("sha256").update(canonical, "utf-8").digest("base64");
|
|
6512
6609
|
}
|
|
6513
6610
|
function computeSignedPropertiesDigest(qualifyingPropertiesXml) {
|
|
6514
6611
|
const parser = new import_xmldom.DOMParser();
|
|
@@ -6518,7 +6615,7 @@ function computeSignedPropertiesDigest(qualifyingPropertiesXml) {
|
|
|
6518
6615
|
throw new Error("SignedProperties element not found in QualifyingProperties");
|
|
6519
6616
|
}
|
|
6520
6617
|
const canonical = canonicalize(signedProps);
|
|
6521
|
-
return
|
|
6618
|
+
return crypto3.createHash("sha256").update(canonical, "utf-8").digest("base64");
|
|
6522
6619
|
}
|
|
6523
6620
|
function buildSignedInfo(signatureAlgorithm, rootDigest, signedPropertiesDigest) {
|
|
6524
6621
|
return [
|
|
@@ -6549,12 +6646,12 @@ function computeSignatureValue(canonicalSignedInfo, privateKeyPem, isEc) {
|
|
|
6549
6646
|
const data = Buffer.from(canonicalSignedInfo, "utf-8");
|
|
6550
6647
|
let signature;
|
|
6551
6648
|
if (isEc) {
|
|
6552
|
-
signature =
|
|
6649
|
+
signature = crypto3.sign("sha256", data, {
|
|
6553
6650
|
key: privateKeyPem,
|
|
6554
6651
|
dsaEncoding: "ieee-p1363"
|
|
6555
6652
|
});
|
|
6556
6653
|
} else {
|
|
6557
|
-
signature =
|
|
6654
|
+
signature = crypto3.sign("sha256", data, privateKeyPem);
|
|
6558
6655
|
}
|
|
6559
6656
|
return signature.toString("base64");
|
|
6560
6657
|
}
|
|
@@ -6654,11 +6751,11 @@ var init_signature_service = __esm({
|
|
|
6654
6751
|
}
|
|
6655
6752
|
const certDer = extractDerFromPem(certPem);
|
|
6656
6753
|
const certBase64 = certDer.toString("base64");
|
|
6657
|
-
const certDigest =
|
|
6658
|
-
const x5093 = new
|
|
6754
|
+
const certDigest = crypto3.createHash("sha256").update(certDer).digest("base64");
|
|
6755
|
+
const x5093 = new crypto3.X509Certificate(certPem);
|
|
6659
6756
|
const issuerName = normalizeIssuerDn(x5093.issuer);
|
|
6660
6757
|
const serialNumber = hexSerialToDecimal(x5093.serialNumber);
|
|
6661
|
-
const privateKey =
|
|
6758
|
+
const privateKey = crypto3.createPrivateKey(privateKeyPem);
|
|
6662
6759
|
const isEc = privateKey.asymmetricKeyType === "ec";
|
|
6663
6760
|
const signatureAlgorithm = isEc ? ECDSA_SHA256_SIGNATURE : RSA_SHA256_SIGNATURE;
|
|
6664
6761
|
const signingTime = new Date(Date.now() + CLOCK_SKEW_BUFFER_MS).toISOString();
|
|
@@ -6761,8 +6858,34 @@ var init_pkcs12_loader = __esm({
|
|
|
6761
6858
|
}
|
|
6762
6859
|
});
|
|
6763
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
|
+
|
|
6764
6887
|
// src/qr/verification-link-service.ts
|
|
6765
|
-
import
|
|
6888
|
+
import crypto5 from "crypto";
|
|
6766
6889
|
var VerificationLinkService;
|
|
6767
6890
|
var init_verification_link_service = __esm({
|
|
6768
6891
|
"src/qr/verification-link-service.ts"() {
|
|
@@ -6792,16 +6915,16 @@ var init_verification_link_service = __esm({
|
|
|
6792
6915
|
const hashBase64Url = this.base64ToBase64Url(invoiceHashBase64);
|
|
6793
6916
|
const pathWithoutSignature = `${this.baseQrUrl}/certificate/${contextType}/${contextId}/${sellerNip}/${certSerial}/${hashBase64Url}`;
|
|
6794
6917
|
const dataToSign = pathWithoutSignature.replace(/^https?:\/\//, "");
|
|
6795
|
-
const key =
|
|
6918
|
+
const key = crypto5.createPrivateKey(privateKeyPem);
|
|
6796
6919
|
let signature;
|
|
6797
6920
|
if (key.asymmetricKeyType === "rsa") {
|
|
6798
|
-
signature =
|
|
6921
|
+
signature = crypto5.sign("sha256", Buffer.from(dataToSign), {
|
|
6799
6922
|
key,
|
|
6800
|
-
padding:
|
|
6923
|
+
padding: crypto5.constants.RSA_PKCS1_PSS_PADDING,
|
|
6801
6924
|
saltLength: 32
|
|
6802
6925
|
});
|
|
6803
6926
|
} else if (key.asymmetricKeyType === "ec") {
|
|
6804
|
-
signature =
|
|
6927
|
+
signature = crypto5.sign("sha256", Buffer.from(dataToSign), {
|
|
6805
6928
|
key,
|
|
6806
6929
|
dsaEncoding: "ieee-p1363"
|
|
6807
6930
|
});
|
|
@@ -6825,19 +6948,11 @@ __export(client_exports, {
|
|
|
6825
6948
|
buildAuthTokenRequestXml: () => buildAuthTokenRequestXml
|
|
6826
6949
|
});
|
|
6827
6950
|
function buildAuthTokenRequestXml(challenge, nip, subjectIdentifierType = "certificateSubject") {
|
|
6828
|
-
return
|
|
6829
|
-
|
|
6830
|
-
|
|
6831
|
-
|
|
6832
|
-
|
|
6833
|
-
`<Nip>${xmlEscape(nip)}</Nip>`,
|
|
6834
|
-
`</ContextIdentifier>`,
|
|
6835
|
-
`<SubjectIdentifierType>${xmlEscape(subjectIdentifierType)}</SubjectIdentifierType>`,
|
|
6836
|
-
`</AuthTokenRequest>`
|
|
6837
|
-
].join("");
|
|
6838
|
-
}
|
|
6839
|
-
function xmlEscape(str) {
|
|
6840
|
-
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
|
+
});
|
|
6841
6956
|
}
|
|
6842
6957
|
function buildRestClientConfig(options, authManager) {
|
|
6843
6958
|
const config = { authManager };
|
|
@@ -6864,7 +6979,7 @@ function buildRestClientConfig(options, authManager) {
|
|
|
6864
6979
|
}
|
|
6865
6980
|
return config;
|
|
6866
6981
|
}
|
|
6867
|
-
var KSeFClient
|
|
6982
|
+
var KSeFClient;
|
|
6868
6983
|
var init_client = __esm({
|
|
6869
6984
|
"src/client.ts"() {
|
|
6870
6985
|
"use strict";
|
|
@@ -6890,6 +7005,7 @@ var init_client = __esm({
|
|
|
6890
7005
|
init_certificate_fetcher();
|
|
6891
7006
|
init_cryptography_service();
|
|
6892
7007
|
init_verification_link_service();
|
|
7008
|
+
init_auth_xml_builder();
|
|
6893
7009
|
KSeFClient = class {
|
|
6894
7010
|
auth;
|
|
6895
7011
|
activeSessions;
|
|
@@ -6933,7 +7049,7 @@ var init_client = __esm({
|
|
|
6933
7049
|
this.lighthouse = new LighthouseService(this.options);
|
|
6934
7050
|
this.limits = new LimitsService(restClient);
|
|
6935
7051
|
this.peppol = new PeppolService(restClient);
|
|
6936
|
-
this.testData = new TestDataService(restClient);
|
|
7052
|
+
this.testData = new TestDataService(restClient, this.options.environmentName);
|
|
6937
7053
|
this.qr = new VerificationLinkService(this.options.baseQrUrl);
|
|
6938
7054
|
}
|
|
6939
7055
|
async loginWithToken(token, nip) {
|
|
@@ -6983,7 +7099,6 @@ var init_client = __esm({
|
|
|
6983
7099
|
this.authManager.setRefreshToken(void 0);
|
|
6984
7100
|
}
|
|
6985
7101
|
};
|
|
6986
|
-
AUTH_TOKEN_REQUEST_NS = "http://ksef.mf.gov.pl/auth/token/2.0";
|
|
6987
7102
|
}
|
|
6988
7103
|
});
|
|
6989
7104
|
|
|
@@ -7117,6 +7232,65 @@ var SUBUNIT_NAME_MAX_LENGTH = 256;
|
|
|
7117
7232
|
var PERMISSION_DESCRIPTION_MIN_LENGTH = 5;
|
|
7118
7233
|
var PERMISSION_DESCRIPTION_MAX_LENGTH = 256;
|
|
7119
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
|
+
|
|
7120
7294
|
// src/services/index.ts
|
|
7121
7295
|
init_auth();
|
|
7122
7296
|
init_active_sessions();
|
|
@@ -7480,18 +7654,188 @@ var AuthorizationPermissionGrantBuilder = class {
|
|
|
7480
7654
|
}
|
|
7481
7655
|
};
|
|
7482
7656
|
|
|
7657
|
+
// src/builders/batch-file.ts
|
|
7658
|
+
init_ksef_validation_error();
|
|
7659
|
+
import * as crypto from "crypto";
|
|
7660
|
+
var BATCH_MAX_PART_SIZE = 1e8;
|
|
7661
|
+
var BATCH_MAX_TOTAL_SIZE = 5e9;
|
|
7662
|
+
var BATCH_MAX_PARTS = 50;
|
|
7663
|
+
var BatchFileBuilder = class {
|
|
7664
|
+
/**
|
|
7665
|
+
* Build batch file metadata and encrypted parts from a raw ZIP.
|
|
7666
|
+
*
|
|
7667
|
+
* @param zipBytes - Unencrypted ZIP data
|
|
7668
|
+
* @param encryptFn - AES-256-CBC encryption function (called per part)
|
|
7669
|
+
* @param options - Optional configuration
|
|
7670
|
+
*/
|
|
7671
|
+
static build(zipBytes, encryptFn, options) {
|
|
7672
|
+
const maxPartSize = options?.maxPartSize ?? BATCH_MAX_PART_SIZE;
|
|
7673
|
+
if (maxPartSize <= 0) {
|
|
7674
|
+
throw new KSeFValidationError("maxPartSize must be a positive number");
|
|
7675
|
+
}
|
|
7676
|
+
if (zipBytes.length === 0) {
|
|
7677
|
+
throw new KSeFValidationError("ZIP data must not be empty");
|
|
7678
|
+
}
|
|
7679
|
+
if (zipBytes.length > BATCH_MAX_TOTAL_SIZE) {
|
|
7680
|
+
throw new KSeFValidationError(
|
|
7681
|
+
`ZIP size ${zipBytes.length} exceeds maximum of ${BATCH_MAX_TOTAL_SIZE} bytes (5 GB)`
|
|
7682
|
+
);
|
|
7683
|
+
}
|
|
7684
|
+
const rawParts = splitBuffer(zipBytes, maxPartSize);
|
|
7685
|
+
if (rawParts.length > BATCH_MAX_PARTS) {
|
|
7686
|
+
throw new KSeFValidationError(
|
|
7687
|
+
`Data requires ${rawParts.length} parts, exceeding maximum of ${BATCH_MAX_PARTS}`
|
|
7688
|
+
);
|
|
7689
|
+
}
|
|
7690
|
+
const zipHash = sha256Base64(zipBytes);
|
|
7691
|
+
const encryptedParts = [];
|
|
7692
|
+
const fileParts = rawParts.map((raw, i) => {
|
|
7693
|
+
const encrypted = encryptFn(raw);
|
|
7694
|
+
encryptedParts.push(encrypted);
|
|
7695
|
+
return {
|
|
7696
|
+
ordinalNumber: i + 1,
|
|
7697
|
+
fileSize: encrypted.length,
|
|
7698
|
+
fileHash: sha256Base64(encrypted)
|
|
7699
|
+
};
|
|
7700
|
+
});
|
|
7701
|
+
return {
|
|
7702
|
+
batchFile: {
|
|
7703
|
+
fileSize: zipBytes.length,
|
|
7704
|
+
fileHash: zipHash,
|
|
7705
|
+
fileParts
|
|
7706
|
+
},
|
|
7707
|
+
encryptedParts
|
|
7708
|
+
};
|
|
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
|
+
}
|
|
7812
|
+
};
|
|
7813
|
+
function splitBuffer(data, maxPartSize) {
|
|
7814
|
+
if (data.length <= maxPartSize) {
|
|
7815
|
+
return [data];
|
|
7816
|
+
}
|
|
7817
|
+
const parts = [];
|
|
7818
|
+
for (let offset = 0; offset < data.length; offset += maxPartSize) {
|
|
7819
|
+
parts.push(data.subarray(offset, Math.min(offset + maxPartSize, data.length)));
|
|
7820
|
+
}
|
|
7821
|
+
return parts;
|
|
7822
|
+
}
|
|
7823
|
+
function sha256Base64(data) {
|
|
7824
|
+
return crypto.createHash("sha256").update(data).digest("base64");
|
|
7825
|
+
}
|
|
7826
|
+
|
|
7483
7827
|
// src/crypto/index.ts
|
|
7484
7828
|
init_certificate_fetcher();
|
|
7485
7829
|
init_cryptography_service();
|
|
7486
7830
|
init_signature_service();
|
|
7487
7831
|
|
|
7488
7832
|
// src/crypto/certificate-service.ts
|
|
7489
|
-
import * as
|
|
7833
|
+
import * as crypto4 from "crypto";
|
|
7490
7834
|
import * as x5092 from "@peculiar/x509";
|
|
7491
7835
|
var CertificateService = class {
|
|
7492
7836
|
static getSha256Fingerprint(certPem) {
|
|
7493
7837
|
const der = extractDerFromPem2(certPem);
|
|
7494
|
-
return
|
|
7838
|
+
return crypto4.createHash("sha256").update(der).digest("hex").toUpperCase();
|
|
7495
7839
|
}
|
|
7496
7840
|
static async generatePersonalCertificate(givenName, surname, serialNumber, commonName, method = "RSA") {
|
|
7497
7841
|
const nameParts = [];
|
|
@@ -7514,7 +7858,7 @@ var CertificateService = class {
|
|
|
7514
7858
|
}
|
|
7515
7859
|
};
|
|
7516
7860
|
async function generateSelfSigned(subject, method) {
|
|
7517
|
-
x5092.cryptoProvider.set(
|
|
7861
|
+
x5092.cryptoProvider.set(crypto4.webcrypto);
|
|
7518
7862
|
let algorithm;
|
|
7519
7863
|
let signingAlgorithm;
|
|
7520
7864
|
if (method === "ECDSA") {
|
|
@@ -7529,7 +7873,7 @@ async function generateSelfSigned(subject, method) {
|
|
|
7529
7873
|
};
|
|
7530
7874
|
signingAlgorithm = algorithm;
|
|
7531
7875
|
}
|
|
7532
|
-
const keys = await
|
|
7876
|
+
const keys = await crypto4.webcrypto.subtle.generateKey(
|
|
7533
7877
|
algorithm,
|
|
7534
7878
|
true,
|
|
7535
7879
|
["sign", "verify"]
|
|
@@ -7546,9 +7890,9 @@ async function generateSelfSigned(subject, method) {
|
|
|
7546
7890
|
serialNumber: Date.now().toString(16)
|
|
7547
7891
|
});
|
|
7548
7892
|
const certPem = cert.toString("pem");
|
|
7549
|
-
const pkcs8 = await
|
|
7893
|
+
const pkcs8 = await crypto4.webcrypto.subtle.exportKey("pkcs8", keys.privateKey);
|
|
7550
7894
|
const privateKeyPem = pemEncode2(new Uint8Array(pkcs8), "PRIVATE KEY");
|
|
7551
|
-
const fingerprint =
|
|
7895
|
+
const fingerprint = crypto4.createHash("sha256").update(Buffer.from(cert.rawData)).digest("hex").toUpperCase();
|
|
7552
7896
|
return { certificatePem: certPem, privateKeyPem, fingerprint };
|
|
7553
7897
|
}
|
|
7554
7898
|
function extractDerFromPem2(pem) {
|
|
@@ -7568,6 +7912,7 @@ ${lines.join("\n")}
|
|
|
7568
7912
|
|
|
7569
7913
|
// src/crypto/index.ts
|
|
7570
7914
|
init_pkcs12_loader();
|
|
7915
|
+
init_auth_xml_builder();
|
|
7571
7916
|
|
|
7572
7917
|
// src/qr/index.ts
|
|
7573
7918
|
init_verification_link_service();
|
|
@@ -7631,6 +7976,94 @@ function escapeXml2(str) {
|
|
|
7631
7976
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
7632
7977
|
}
|
|
7633
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
|
+
|
|
7634
8067
|
// src/workflows/polling.ts
|
|
7635
8068
|
async function pollUntil(action, condition, options) {
|
|
7636
8069
|
const intervalMs = options?.intervalMs ?? 2e3;
|
|
@@ -7648,17 +8081,150 @@ async function pollUntil(action, condition, options) {
|
|
|
7648
8081
|
);
|
|
7649
8082
|
}
|
|
7650
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
|
+
|
|
7651
8202
|
// src/workflows/online-session-workflow.ts
|
|
7652
|
-
var DEFAULT_FORM_CODE = { systemCode: "FA", schemaVersion: "3", value: "FA (3)" };
|
|
7653
8203
|
async function openOnlineSession(client, options) {
|
|
7654
8204
|
await client.crypto.init();
|
|
7655
8205
|
const encData = client.crypto.getEncryptionData();
|
|
7656
|
-
const formCode = options?.formCode ??
|
|
8206
|
+
const formCode = options?.formCode ?? FORM_CODES.FA_2;
|
|
7657
8207
|
const openResp = await client.onlineSession.openSession(
|
|
7658
8208
|
{ formCode, encryption: encData.encryptionInfo },
|
|
7659
8209
|
options?.upoVersion
|
|
7660
8210
|
);
|
|
7661
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
|
+
}
|
|
7662
8228
|
return {
|
|
7663
8229
|
sessionRef,
|
|
7664
8230
|
validUntil: openResp.validUntil,
|
|
@@ -7680,20 +8246,16 @@ async function openOnlineSession(client, options) {
|
|
|
7680
8246
|
await client.onlineSession.closeSession(sessionRef);
|
|
7681
8247
|
},
|
|
7682
8248
|
async waitForUpo(pollOpts) {
|
|
7683
|
-
|
|
7684
|
-
|
|
7685
|
-
|
|
7686
|
-
|
|
7687
|
-
|
|
7688
|
-
|
|
7689
|
-
|
|
7690
|
-
|
|
7691
|
-
|
|
7692
|
-
|
|
7693
|
-
invoiceCount: result.invoiceCount,
|
|
7694
|
-
successfulInvoiceCount: result.successfulInvoiceCount,
|
|
7695
|
-
failedInvoiceCount: result.failedInvoiceCount
|
|
7696
|
-
};
|
|
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 };
|
|
7697
8259
|
}
|
|
7698
8260
|
};
|
|
7699
8261
|
}
|
|
@@ -7707,32 +8269,36 @@ async function openSendAndClose(client, invoices, options) {
|
|
|
7707
8269
|
}
|
|
7708
8270
|
|
|
7709
8271
|
// src/workflows/batch-session-workflow.ts
|
|
7710
|
-
|
|
7711
|
-
async function uploadBatch(client, zipParts, totalFileSize, totalFileHash, options) {
|
|
8272
|
+
async function uploadBatch(client, zipData, options) {
|
|
7712
8273
|
await client.crypto.init();
|
|
7713
8274
|
const encData = client.crypto.getEncryptionData();
|
|
7714
|
-
const formCode = options?.formCode ??
|
|
7715
|
-
const
|
|
7716
|
-
|
|
7717
|
-
|
|
8275
|
+
const formCode = options?.formCode ?? FORM_CODES.FA_2;
|
|
8276
|
+
const encryptFn = (part) => client.crypto.encryptAES256(part, encData.cipherKey, encData.cipherIv);
|
|
8277
|
+
const { batchFile, encryptedParts } = BatchFileBuilder.build(zipData, encryptFn, {
|
|
8278
|
+
maxPartSize: options?.maxPartSize
|
|
7718
8279
|
});
|
|
7719
8280
|
const openResp = await client.batchSession.openSession(
|
|
7720
8281
|
{
|
|
7721
8282
|
formCode,
|
|
7722
8283
|
encryption: encData.encryptionInfo,
|
|
7723
|
-
batchFile
|
|
8284
|
+
batchFile,
|
|
8285
|
+
offlineMode: options?.offlineMode
|
|
7724
8286
|
},
|
|
7725
8287
|
options?.upoVersion
|
|
7726
8288
|
);
|
|
7727
|
-
const sendingParts =
|
|
7728
|
-
|
|
7729
|
-
|
|
7730
|
-
|
|
8289
|
+
const sendingParts = encryptedParts.map((part, i) => ({
|
|
8290
|
+
data: part.buffer.slice(part.byteOffset, part.byteOffset + part.byteLength),
|
|
8291
|
+
metadata: {
|
|
8292
|
+
hashSHA: batchFile.fileParts[i].fileHash,
|
|
8293
|
+
fileSize: batchFile.fileParts[i].fileSize
|
|
8294
|
+
},
|
|
8295
|
+
ordinalNumber: i + 1
|
|
8296
|
+
}));
|
|
7731
8297
|
await client.batchSession.sendParts(openResp, sendingParts);
|
|
7732
8298
|
await client.batchSession.closeSession(openResp.referenceNumber);
|
|
7733
8299
|
const result = await pollUntil(
|
|
7734
8300
|
() => client.sessionStatus.getSessionStatus(openResp.referenceNumber),
|
|
7735
|
-
(s) => s.status.code
|
|
8301
|
+
(s) => s.status.code === 200 || s.status.code >= 400,
|
|
7736
8302
|
{ ...options?.pollOptions, description: `UPO for batch ${openResp.referenceNumber}` }
|
|
7737
8303
|
);
|
|
7738
8304
|
if (result.status.code !== 200) {
|
|
@@ -7748,9 +8314,75 @@ async function uploadBatch(client, zipParts, totalFileSize, totalFileHash, optio
|
|
|
7748
8314
|
}
|
|
7749
8315
|
};
|
|
7750
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
|
+
}
|
|
7751
8383
|
|
|
7752
8384
|
// src/workflows/invoice-export-workflow.ts
|
|
7753
|
-
async function
|
|
8385
|
+
async function doExport(client, filters, options) {
|
|
7754
8386
|
await client.crypto.init();
|
|
7755
8387
|
const encData = client.crypto.getEncryptionData();
|
|
7756
8388
|
const opResp = await client.invoices.exportInvoices({
|
|
@@ -7760,7 +8392,7 @@ async function exportInvoices(client, filters, options) {
|
|
|
7760
8392
|
});
|
|
7761
8393
|
const result = await pollUntil(
|
|
7762
8394
|
() => client.invoices.getInvoiceExportStatus(opResp.referenceNumber),
|
|
7763
|
-
(s) => s.status.code
|
|
8395
|
+
(s) => s.status.code === 200 || s.status.code >= 400,
|
|
7764
8396
|
{ ...options?.pollOptions, description: `export ${opResp.referenceNumber}` }
|
|
7765
8397
|
);
|
|
7766
8398
|
if (result.status.code !== 200) {
|
|
@@ -7770,23 +8402,31 @@ async function exportInvoices(client, filters, options) {
|
|
|
7770
8402
|
throw new Error("Export completed but no package available");
|
|
7771
8403
|
}
|
|
7772
8404
|
return {
|
|
7773
|
-
|
|
7774
|
-
|
|
7775
|
-
|
|
7776
|
-
|
|
7777
|
-
|
|
7778
|
-
|
|
7779
|
-
|
|
7780
|
-
|
|
7781
|
-
|
|
7782
|
-
|
|
7783
|
-
|
|
7784
|
-
|
|
8405
|
+
encData,
|
|
8406
|
+
referenceNumber: opResp.referenceNumber,
|
|
8407
|
+
result: {
|
|
8408
|
+
parts: result.package.parts.map((p) => ({
|
|
8409
|
+
ordinalNumber: p.ordinalNumber,
|
|
8410
|
+
url: p.url,
|
|
8411
|
+
method: p.method,
|
|
8412
|
+
partSize: p.partSize,
|
|
8413
|
+
encryptedPartSize: p.encryptedPartSize,
|
|
8414
|
+
encryptedPartHash: p.encryptedPartHash,
|
|
8415
|
+
expirationDate: p.expirationDate
|
|
8416
|
+
})),
|
|
8417
|
+
invoiceCount: result.package.invoiceCount,
|
|
8418
|
+
isTruncated: result.package.isTruncated,
|
|
8419
|
+
permanentStorageHwmDate: result.package.permanentStorageHwmDate,
|
|
8420
|
+
lastPermanentStorageDate: result.package.lastPermanentStorageDate
|
|
8421
|
+
}
|
|
7785
8422
|
};
|
|
7786
8423
|
}
|
|
8424
|
+
async function exportInvoices(client, filters, options) {
|
|
8425
|
+
const { result } = await doExport(client, filters, options);
|
|
8426
|
+
return result;
|
|
8427
|
+
}
|
|
7787
8428
|
async function exportAndDownload(client, filters, options) {
|
|
7788
|
-
const encData = client
|
|
7789
|
-
const exportResult = await exportInvoices(client, filters, options);
|
|
8429
|
+
const { result: exportResult, encData } = await doExport(client, filters, options);
|
|
7790
8430
|
const download = options?.transport ?? fetch;
|
|
7791
8431
|
const decryptedParts = [];
|
|
7792
8432
|
for (const part of exportResult.parts) {
|
|
@@ -7798,13 +8438,144 @@ async function exportAndDownload(client, filters, options) {
|
|
|
7798
8438
|
const decrypted = client.crypto.decryptAES256(encryptedData, encData.cipherKey, encData.cipherIv);
|
|
7799
8439
|
decryptedParts.push(decrypted);
|
|
7800
8440
|
}
|
|
8441
|
+
if (options?.extract) {
|
|
8442
|
+
const zipBuffer = Buffer.concat(decryptedParts);
|
|
8443
|
+
const files = await unzip(zipBuffer, options.unzipOptions);
|
|
8444
|
+
return { ...exportResult, files };
|
|
8445
|
+
}
|
|
7801
8446
|
return {
|
|
7802
8447
|
...exportResult,
|
|
7803
8448
|
decryptedParts
|
|
7804
8449
|
};
|
|
7805
8450
|
}
|
|
7806
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
|
+
|
|
7807
8577
|
// src/workflows/auth-workflow.ts
|
|
8578
|
+
init_auth_xml_builder();
|
|
7808
8579
|
async function authenticateWithToken(client, options) {
|
|
7809
8580
|
const challenge = await client.auth.getChallenge();
|
|
7810
8581
|
await client.crypto.init();
|
|
@@ -7858,6 +8629,34 @@ async function authenticateWithCertificate(client, options) {
|
|
|
7858
8629
|
refreshTokenValidUntil: tokens.refreshToken.validUntil
|
|
7859
8630
|
};
|
|
7860
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
|
+
}
|
|
7861
8660
|
async function authenticateWithPkcs12(client, options) {
|
|
7862
8661
|
const { Pkcs12Loader: Pkcs12Loader2 } = await Promise.resolve().then(() => (init_pkcs12_loader(), pkcs12_loader_exports));
|
|
7863
8662
|
const { certificatePem, privateKeyPem } = Pkcs12Loader2.load(options.p12, options.password);
|
|
@@ -7879,7 +8678,11 @@ export {
|
|
|
7879
8678
|
AuthService,
|
|
7880
8679
|
AuthTokenRequestBuilder,
|
|
7881
8680
|
AuthorizationPermissionGrantBuilder,
|
|
8681
|
+
BATCH_MAX_PARTS,
|
|
8682
|
+
BATCH_MAX_PART_SIZE,
|
|
8683
|
+
BATCH_MAX_TOTAL_SIZE,
|
|
7882
8684
|
Base64String,
|
|
8685
|
+
BatchFileBuilder,
|
|
7883
8686
|
BatchSessionService,
|
|
7884
8687
|
CERTIFICATE_NAME_MAX_LENGTH,
|
|
7885
8688
|
CERTIFICATE_NAME_MIN_LENGTH,
|
|
@@ -7893,6 +8696,11 @@ export {
|
|
|
7893
8696
|
ENFORCE_XADES_COMPLIANCE,
|
|
7894
8697
|
EntityPermissionGrantBuilder,
|
|
7895
8698
|
Environment,
|
|
8699
|
+
FORM_CODES,
|
|
8700
|
+
FORM_CODE_KEYS,
|
|
8701
|
+
FileHwmStore,
|
|
8702
|
+
INVOICE_TYPES_BY_SYSTEM_CODE,
|
|
8703
|
+
InMemoryHwmStore,
|
|
7896
8704
|
InternalId,
|
|
7897
8705
|
InvoiceDownloadService,
|
|
7898
8706
|
InvoiceQueryFilterBuilder,
|
|
@@ -7938,21 +8746,29 @@ export {
|
|
|
7938
8746
|
SessionStatusService,
|
|
7939
8747
|
Sha256Base64,
|
|
7940
8748
|
SignatureService,
|
|
8749
|
+
SystemCode,
|
|
7941
8750
|
TestDataService,
|
|
7942
8751
|
TokenService,
|
|
7943
8752
|
UpoVersion,
|
|
7944
8753
|
VatUe,
|
|
7945
8754
|
VerificationLinkService,
|
|
7946
8755
|
authenticateWithCertificate,
|
|
8756
|
+
authenticateWithExternalSignature,
|
|
7947
8757
|
authenticateWithPkcs12,
|
|
7948
8758
|
authenticateWithToken,
|
|
8759
|
+
buildUnsignedAuthTokenRequestXml,
|
|
7949
8760
|
calculateBackoff,
|
|
8761
|
+
createZip,
|
|
8762
|
+
deduplicateByKsefNumber,
|
|
7950
8763
|
defaultPresignedUrlPolicy,
|
|
7951
8764
|
defaultRateLimitPolicy,
|
|
7952
8765
|
defaultRetryPolicy,
|
|
7953
8766
|
defaultTransport,
|
|
7954
8767
|
exportAndDownload,
|
|
7955
8768
|
exportInvoices,
|
|
8769
|
+
getEffectiveStartDate,
|
|
8770
|
+
getFormCode,
|
|
8771
|
+
incrementalExportAndDownload,
|
|
7956
8772
|
isRetryableError,
|
|
7957
8773
|
isRetryableStatus,
|
|
7958
8774
|
isValidBase64,
|
|
@@ -7972,11 +8788,19 @@ export {
|
|
|
7972
8788
|
isValidVatUe,
|
|
7973
8789
|
openOnlineSession,
|
|
7974
8790
|
openSendAndClose,
|
|
8791
|
+
parseFormCode,
|
|
7975
8792
|
parseRetryAfter,
|
|
8793
|
+
parseUpoXml,
|
|
7976
8794
|
pollUntil,
|
|
7977
8795
|
resolveOptions,
|
|
7978
8796
|
sleep,
|
|
8797
|
+
unzip,
|
|
8798
|
+
updateContinuationPoint,
|
|
7979
8799
|
uploadBatch,
|
|
8800
|
+
uploadBatchParsed,
|
|
8801
|
+
uploadBatchStream,
|
|
8802
|
+
uploadBatchStreamParsed,
|
|
8803
|
+
validateFormCodeForSession,
|
|
7980
8804
|
validatePresignedUrl
|
|
7981
8805
|
};
|
|
7982
8806
|
//# sourceMappingURL=index.js.map
|