ksef-client-ts 0.7.1 → 0.8.0-alpha.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/dist/cli.js CHANGED
@@ -2371,7 +2371,7 @@ ${lines.join("\n")}
2371
2371
  });
2372
2372
 
2373
2373
  // src/crypto/cryptography-service.ts
2374
- import * as crypto from "crypto";
2374
+ import * as crypto from "node:crypto";
2375
2375
  import * as x509 from "@peculiar/x509";
2376
2376
  function setCryptoProvider() {
2377
2377
  x509.cryptoProvider.set(crypto.webcrypto);
@@ -2453,20 +2453,13 @@ var init_cryptography_service = __esm({
2453
2453
  * Generate a random AES-256 key and IV, then wrap the key with the
2454
2454
  * SymmetricKeyEncryption certificate's RSA public key (RSA-OAEP SHA-256).
2455
2455
  */
2456
- getEncryptionData() {
2456
+ async getEncryptionData() {
2457
2457
  const key = crypto.randomBytes(32);
2458
2458
  const iv = crypto.randomBytes(16);
2459
2459
  const certPem = this.fetcher.getSymmetricKeyEncryptionPem();
2460
- const encryptedKey = crypto.publicEncrypt(
2461
- {
2462
- key: certPem,
2463
- oaepHash: "sha256",
2464
- padding: crypto.constants.RSA_PKCS1_OAEP_PADDING
2465
- },
2466
- key
2467
- );
2460
+ const encryptedKey = await this.rsaOaepEncrypt(certPem, new Uint8Array(key));
2468
2461
  const encryptionInfo = {
2469
- encryptedSymmetricKey: encryptedKey.toString("base64"),
2462
+ encryptedSymmetricKey: Buffer.from(encryptedKey).toString("base64"),
2470
2463
  initializationVector: iv.toString("base64")
2471
2464
  };
2472
2465
  return {
@@ -2491,14 +2484,14 @@ var init_cryptography_service = __esm({
2491
2484
  * The EC variant outputs bytes in the Java-compatible format:
2492
2485
  * `[ephemeralSPKI | nonce(12) | ciphertext+tag]`.
2493
2486
  */
2494
- encryptKsefToken(token, challengeTimestamp) {
2487
+ async encryptKsefToken(token, challengeTimestamp) {
2495
2488
  const timestampMs = new Date(challengeTimestamp).getTime();
2496
2489
  const plaintext = Buffer.from(`${token}|${timestampMs}`, "utf-8");
2497
2490
  const certPem = this.fetcher.getKsefTokenEncryptionPem();
2498
2491
  const cert = new crypto.X509Certificate(certPem);
2499
2492
  const publicKey = cert.publicKey;
2500
2493
  if (publicKey.asymmetricKeyType === "rsa") {
2501
- return this.encryptRsaOaep(certPem, plaintext);
2494
+ return this.rsaOaepEncrypt(certPem, plaintext);
2502
2495
  }
2503
2496
  if (publicKey.asymmetricKeyType === "ec") {
2504
2497
  return this.encryptEcdhAesGcm(publicKey, plaintext);
@@ -2589,18 +2582,40 @@ var init_cryptography_service = __esm({
2589
2582
  // ---------------------------------------------------------------------------
2590
2583
  // Private helpers
2591
2584
  // ---------------------------------------------------------------------------
2592
- /** RSA-OAEP SHA-256 encryption. */
2593
- encryptRsaOaep(certPem, plaintext) {
2594
- return new Uint8Array(
2595
- crypto.publicEncrypt(
2596
- {
2597
- key: certPem,
2598
- oaepHash: "sha256",
2599
- padding: crypto.constants.RSA_PKCS1_OAEP_PADDING
2600
- },
2601
- plaintext
2602
- )
2585
+ /**
2586
+ * Extract the public-key SPKI DER bytes from a CERTIFICATE PEM. Web Crypto's
2587
+ * `importKey('spki', ...)` consumes raw DER; we parse the cert via native
2588
+ * `X509Certificate` (available on Node and Deno) and export the key directly.
2589
+ */
2590
+ spkiDerFromCert(certPem) {
2591
+ const publicKey = new crypto.X509Certificate(certPem).publicKey;
2592
+ return new Uint8Array(publicKey.export({ type: "spki", format: "der" }));
2593
+ }
2594
+ /**
2595
+ * RSA-OAEP SHA-256 encryption via Web Crypto.
2596
+ *
2597
+ * Uses `crypto.webcrypto.subtle` instead of `node:crypto.publicEncrypt`
2598
+ * because Deno's Node-compat layer silently ignores the `oaepHash` option
2599
+ * and defaults MGF1+OAEP to SHA-1, producing ciphertext KSeF rejects as
2600
+ * "Invalid token encryption". Web Crypto requires the hash up-front in
2601
+ * `importKey` as part of the key's algorithm identity, so there is no room
2602
+ * for a runtime to swap it — output is identical OAEP-SHA256 on every
2603
+ * Web Crypto implementation (Node 18+, Deno, Bun, browsers, edge runtimes).
2604
+ */
2605
+ async rsaOaepEncrypt(certPem, plaintext) {
2606
+ const key = await crypto.webcrypto.subtle.importKey(
2607
+ "spki",
2608
+ this.spkiDerFromCert(certPem),
2609
+ { name: "RSA-OAEP", hash: "SHA-256" },
2610
+ false,
2611
+ ["encrypt"]
2612
+ );
2613
+ const ciphertext = await crypto.webcrypto.subtle.encrypt(
2614
+ { name: "RSA-OAEP" },
2615
+ key,
2616
+ plaintext
2603
2617
  );
2618
+ return new Uint8Array(ciphertext);
2604
2619
  }
2605
2620
  /**
2606
2621
  * ECDH (P-256) + AES-256-GCM encryption.
@@ -2632,7 +2647,7 @@ var init_cryptography_service = __esm({
2632
2647
  });
2633
2648
 
2634
2649
  // src/qr/verification-link-service.ts
2635
- import crypto2 from "crypto";
2650
+ import crypto2 from "node:crypto";
2636
2651
  var VerificationLinkService;
2637
2652
  var init_verification_link_service = __esm({
2638
2653
  "src/qr/verification-link-service.ts"() {
@@ -2938,7 +2953,7 @@ var init_document_structures = __esm({
2938
2953
  });
2939
2954
 
2940
2955
  // src/workflows/offline-invoice-workflow.ts
2941
- import crypto3 from "crypto";
2956
+ import crypto3 from "node:crypto";
2942
2957
  var OfflineInvoiceWorkflow;
2943
2958
  var init_offline_invoice_workflow = __esm({
2944
2959
  "src/workflows/offline-invoice-workflow.ts"() {
@@ -3051,7 +3066,7 @@ var init_offline_invoice_workflow = __esm({
3051
3066
  }
3052
3067
  if (pending.length === 0) return result;
3053
3068
  await client.crypto.init();
3054
- const encData = client.crypto.getEncryptionData();
3069
+ const encData = await client.crypto.getEncryptionData();
3055
3070
  const formCode = options.formCode ?? DEFAULT_FORM_CODE;
3056
3071
  const openResp = await client.onlineSession.openSession(
3057
3072
  { formCode, encryption: encData.encryptionInfo }
@@ -3140,7 +3155,7 @@ var init_offline_invoice_workflow = __esm({
3140
3155
  }
3141
3156
  const originalHash = crypto3.createHash("sha256").update(original.invoiceXml).digest("base64");
3142
3157
  await client.crypto.init();
3143
- const encData = client.crypto.getEncryptionData();
3158
+ const encData = await client.crypto.getEncryptionData();
3144
3159
  const formCode = options.formCode ?? DEFAULT_FORM_CODE;
3145
3160
  const openResp = await client.onlineSession.openSession(
3146
3161
  { formCode, encryption: encData.encryptionInfo }
@@ -3230,7 +3245,7 @@ var signature_service_exports = {};
3230
3245
  __export(signature_service_exports, {
3231
3246
  SignatureService: () => SignatureService
3232
3247
  });
3233
- import * as crypto4 from "crypto";
3248
+ import * as crypto4 from "node:crypto";
3234
3249
  import { ExclusiveCanonicalization } from "xml-crypto";
3235
3250
  import { DOMParser, XMLSerializer } from "@xmldom/xmldom";
3236
3251
  function pickRsaAlgo() {
@@ -3690,7 +3705,7 @@ var init_client = __esm({
3690
3705
  async loginWithToken(token, nip) {
3691
3706
  const challenge2 = await this.auth.getChallenge();
3692
3707
  await this.crypto.init();
3693
- const encryptedToken = this.crypto.encryptKsefToken(token, challenge2.timestamp);
3708
+ const encryptedToken = await this.crypto.encryptKsefToken(token, challenge2.timestamp);
3694
3709
  const submitResult = await this.auth.submitKsefTokenAuthRequest({
3695
3710
  challenge: challenge2.challenge,
3696
3711
  contextIdentifier: { type: "Nip", value: nip },
@@ -6473,7 +6488,7 @@ var init_invoice_validator = __esm({
6473
6488
  });
6474
6489
 
6475
6490
  // src/builders/batch-file.ts
6476
- import * as crypto6 from "crypto";
6491
+ import * as crypto6 from "node:crypto";
6477
6492
  function splitBuffer(data, maxPartSize) {
6478
6493
  if (data.length <= maxPartSize) {
6479
6494
  return [data];
@@ -6679,7 +6694,7 @@ async function uploadBatch(client, zipData, options) {
6679
6694
  }
6680
6695
  }
6681
6696
  }
6682
- const encData = client.crypto.getEncryptionData();
6697
+ const encData = await client.crypto.getEncryptionData();
6683
6698
  const formCode = options?.formCode ?? DEFAULT_FORM_CODE;
6684
6699
  const encryptFn = (part) => client.crypto.encryptAES256(part, encData.cipherKey, encData.cipherIv);
6685
6700
  const { batchFile, encryptedParts } = BatchFileBuilder.build(zipData, encryptFn, {
@@ -6727,7 +6742,7 @@ async function uploadBatchStream(client, zipStreamFactory, zipSize, options) {
6727
6742
  throw new Error("parallelism must be a positive integer");
6728
6743
  }
6729
6744
  await client.crypto.init();
6730
- const encData = client.crypto.getEncryptionData();
6745
+ const encData = await client.crypto.getEncryptionData();
6731
6746
  const formCode = options?.formCode ?? DEFAULT_FORM_CODE;
6732
6747
  const encryptStreamFn = (stream) => client.crypto.encryptAES256Stream(stream, encData.cipherKey, encData.cipherIv);
6733
6748
  const hashStreamFn = (stream) => client.crypto.getFileMetadataFromStream(stream);
@@ -6802,18 +6817,18 @@ var init_batch_session_workflow = __esm({
6802
6817
  });
6803
6818
 
6804
6819
  // src/cli/index.ts
6805
- import { readFileSync as readFileSync11 } from "fs";
6806
- import { fileURLToPath as fileURLToPath2 } from "url";
6807
- import { dirname as dirname3, resolve } from "path";
6820
+ import { readFileSync as readFileSync11 } from "node:fs";
6821
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
6822
+ import { dirname as dirname3, resolve } from "node:path";
6808
6823
  import { defineCommand as defineCommand19, runMain } from "citty";
6809
6824
 
6810
6825
  // src/cli/commands/config.ts
6811
6826
  import { defineCommand } from "citty";
6812
6827
 
6813
6828
  // src/cli/config-store.ts
6814
- import * as fs from "fs";
6815
- import * as path from "path";
6816
- import * as os from "os";
6829
+ import * as fs from "node:fs";
6830
+ import * as path from "node:path";
6831
+ import * as os from "node:os";
6817
6832
 
6818
6833
  // src/cli/types.ts
6819
6834
  var ENV_MAP = {
@@ -6892,9 +6907,9 @@ function outputWarning(msg) {
6892
6907
  }
6893
6908
 
6894
6909
  // src/cli/session-store.ts
6895
- import * as fs2 from "fs";
6896
- import * as path2 from "path";
6897
- import * as os2 from "os";
6910
+ import * as fs2 from "node:fs";
6911
+ import * as path2 from "node:path";
6912
+ import * as os2 from "node:os";
6898
6913
  var SESSION_DIR = path2.join(os2.homedir(), ".ksef");
6899
6914
  var SESSION_FILE = path2.join(SESSION_DIR, "session.json");
6900
6915
  function loadSession() {
@@ -7149,7 +7164,7 @@ var configCommand = defineCommand({
7149
7164
  });
7150
7165
 
7151
7166
  // src/cli/commands/auth.ts
7152
- import * as fs5 from "fs";
7167
+ import * as fs5 from "node:fs";
7153
7168
  import { defineCommand as defineCommand2 } from "citty";
7154
7169
  import { consola as consola7 } from "consola";
7155
7170
 
@@ -7161,9 +7176,9 @@ import { consola as consola6 } from "consola";
7161
7176
  import { consola as consola5 } from "consola";
7162
7177
 
7163
7178
  // src/cli/credentials-store.ts
7164
- import * as fs3 from "fs";
7165
- import * as path3 from "path";
7166
- import * as os3 from "os";
7179
+ import * as fs3 from "node:fs";
7180
+ import * as path3 from "node:path";
7181
+ import * as os3 from "node:os";
7167
7182
  var CREDENTIALS_DIR = path3.join(os3.homedir(), ".ksef");
7168
7183
  var CREDENTIALS_FILE = path3.join(CREDENTIALS_DIR, "credentials.json");
7169
7184
  function loadCredentials() {
@@ -7196,7 +7211,7 @@ init_auth_xml_builder();
7196
7211
  async function authenticateWithToken(client, options) {
7197
7212
  const challenge2 = await client.auth.getChallenge();
7198
7213
  await client.crypto.init();
7199
- const encryptedToken = client.crypto.encryptKsefToken(options.token, challenge2.timestamp);
7214
+ const encryptedToken = await client.crypto.encryptKsefToken(options.token, challenge2.timestamp);
7200
7215
  const submitResult = await client.auth.submitKsefTokenAuthRequest({
7201
7216
  challenge: challenge2.challenge,
7202
7217
  contextIdentifier: { type: "Nip", value: options.nip },
@@ -7303,9 +7318,9 @@ async function requireSession(globalOpts) {
7303
7318
  }
7304
7319
 
7305
7320
  // src/cli/pending-challenge-store.ts
7306
- import * as fs4 from "fs";
7307
- import * as path4 from "path";
7308
- import * as os4 from "os";
7321
+ import * as fs4 from "node:fs";
7322
+ import * as path4 from "node:path";
7323
+ import * as os4 from "node:os";
7309
7324
  var PENDING_CHALLENGE_FILE = path4.join(os4.homedir(), ".ksef", "pending-challenge.json");
7310
7325
  function savePendingChallenge(data) {
7311
7326
  const dir = path4.dirname(PENDING_CHALLENGE_FILE);
@@ -7390,11 +7405,11 @@ var login = defineCommand2({
7390
7405
  if (token) {
7391
7406
  loginResult = await client.loginWithToken(token, nip);
7392
7407
  } else if (args.p12) {
7393
- const fs17 = await import("fs");
7408
+ const fs17 = await import("node:fs");
7394
7409
  const p12Buffer = fs17.readFileSync(args.p12);
7395
7410
  loginResult = await client.loginWithPkcs12(p12Buffer, args["p12-password"] ?? "", nip);
7396
7411
  } else if (args.cert && args.key) {
7397
- const fs17 = await import("fs");
7412
+ const fs17 = await import("node:fs");
7398
7413
  const certPem = fs17.readFileSync(args.cert, "utf-8");
7399
7414
  const keyPem = fs17.readFileSync(args.key, "utf-8");
7400
7415
  loginResult = await client.loginWithCertificate(certPem, keyPem, nip, args["key-password"]);
@@ -7745,7 +7760,7 @@ var authCommand = defineCommand2({
7745
7760
  });
7746
7761
 
7747
7762
  // src/cli/commands/session.ts
7748
- import * as fs6 from "fs";
7763
+ import * as fs6 from "node:fs";
7749
7764
  import { defineCommand as defineCommand3 } from "citty";
7750
7765
  import { consola as consola8 } from "consola";
7751
7766
  init_document_structures();
@@ -7780,7 +7795,7 @@ var open = defineCommand3({
7780
7795
  throw new Error("NIP is required. Provide --nip or set it via `ksef config set --nip <nip>`.");
7781
7796
  }
7782
7797
  await client.crypto.init();
7783
- const encryptionData = client.crypto.getEncryptionData();
7798
+ const encryptionData = await client.crypto.getEncryptionData();
7784
7799
  const formCodeKey = args.formCode;
7785
7800
  let formCode = DEFAULT_FORM_CODE;
7786
7801
  if (formCodeKey) {
@@ -8200,21 +8215,21 @@ var sessionCommand = defineCommand3({
8200
8215
  });
8201
8216
 
8202
8217
  // src/cli/commands/invoice.ts
8203
- import * as fs11 from "fs";
8204
- import * as path8 from "path";
8205
- import { Readable } from "stream";
8218
+ import * as fs11 from "node:fs";
8219
+ import * as path8 from "node:path";
8220
+ import { Readable } from "node:stream";
8206
8221
  import { defineCommand as defineCommand6 } from "citty";
8207
8222
  import { consola as consola11 } from "consola";
8208
8223
  init_document_structures();
8209
8224
 
8210
8225
  // src/cli/commands/export-incremental.ts
8211
- import * as fs8 from "fs";
8212
- import * as path5 from "path";
8226
+ import * as fs8 from "node:fs";
8227
+ import * as path5 from "node:path";
8213
8228
  import { defineCommand as defineCommand4 } from "citty";
8214
8229
  import { consola as consola9 } from "consola";
8215
8230
 
8216
8231
  // src/workflows/hwm-storage.ts
8217
- import * as fs7 from "fs/promises";
8232
+ import * as fs7 from "node:fs/promises";
8218
8233
  var FileHwmStore = class {
8219
8234
  constructor(filePath) {
8220
8235
  this.filePath = filePath;
@@ -8240,7 +8255,7 @@ init_zip();
8240
8255
  init_polling();
8241
8256
 
8242
8257
  // src/utils/hash.ts
8243
- import crypto5 from "crypto";
8258
+ import crypto5 from "node:crypto";
8244
8259
  function sha256Base64(data) {
8245
8260
  return crypto5.createHash("sha256").update(data).digest("base64");
8246
8261
  }
@@ -8251,7 +8266,7 @@ function verifyHash(data, expectedHash) {
8251
8266
  // src/workflows/invoice-export-workflow.ts
8252
8267
  async function doExport(client, filters, options) {
8253
8268
  await client.crypto.init();
8254
- const encData = client.crypto.getEncryptionData();
8269
+ const encData = await client.crypto.getEncryptionData();
8255
8270
  const opResp = await client.invoices.exportInvoices({
8256
8271
  encryption: encData.encryptionInfo,
8257
8272
  filters,
@@ -8492,10 +8507,10 @@ init_xml();
8492
8507
  init_invoice_validator();
8493
8508
 
8494
8509
  // src/validation/xsd-validator.ts
8495
- import { createRequire } from "module";
8496
- import * as fs9 from "fs";
8497
- import * as path6 from "path";
8498
- import { fileURLToPath, pathToFileURL } from "url";
8510
+ import { createRequire } from "node:module";
8511
+ import * as fs9 from "node:fs";
8512
+ import * as path6 from "node:path";
8513
+ import { fileURLToPath, pathToFileURL } from "node:url";
8499
8514
  var cachedPkgRoot = null;
8500
8515
  function locatePackageRoot() {
8501
8516
  if (cachedPkgRoot !== null) return cachedPkgRoot;
@@ -8600,8 +8615,8 @@ init_ksef_xsd_validation_error();
8600
8615
  // src/cli/commands/invoice-build-helpers.ts
8601
8616
  init_ksef_validation_error();
8602
8617
  init_ksef_xsd_validation_error();
8603
- import * as fs10 from "fs";
8604
- import * as path7 from "path";
8618
+ import * as fs10 from "node:fs";
8619
+ import * as path7 from "node:path";
8605
8620
  import { parse as parseYaml, YAMLParseError } from "yaml";
8606
8621
 
8607
8622
  // src/cli/commands/invoice-build-templates/fa2.json
@@ -9247,7 +9262,7 @@ var send = defineCommand6({
9247
9262
  }
9248
9263
  if (!args.json) consola11.start(`Sending ${xmlFiles.length} invoices via batch session...`);
9249
9264
  await client.crypto.init();
9250
- const encryptionData = client.crypto.getEncryptionData();
9265
+ const encryptionData = await client.crypto.getEncryptionData();
9251
9266
  const parts = fileBuffers.map(({ content }, i) => {
9252
9267
  const metadata = client.crypto.getFileMetadata(new Uint8Array(content));
9253
9268
  return {
@@ -9424,7 +9439,7 @@ var exportCmd = defineCommand6({
9424
9439
  const { client } = await requireSession(globalOpts);
9425
9440
  if (!args.json) consola11.start("Starting invoice export...");
9426
9441
  await client.crypto.init();
9427
- const encryptionData = client.crypto.getEncryptionData();
9442
+ const encryptionData = await client.crypto.getEncryptionData();
9428
9443
  const filters = buildQueryFilters(args);
9429
9444
  const result = await client.invoices.exportInvoices(
9430
9445
  { encryption: encryptionData.encryptionInfo, filters, onlyMetadata: args.onlyMetadata }
@@ -10279,11 +10294,11 @@ var tokenCommand = defineCommand8({
10279
10294
  // src/cli/commands/cert.ts
10280
10295
  import { defineCommand as defineCommand9 } from "citty";
10281
10296
  import { consola as consola13 } from "consola";
10282
- import fs12 from "fs";
10283
- import path9 from "path";
10297
+ import fs12 from "node:fs";
10298
+ import path9 from "node:path";
10284
10299
 
10285
10300
  // src/crypto/certificate-service.ts
10286
- import * as crypto7 from "crypto";
10301
+ import * as crypto7 from "node:crypto";
10287
10302
  import * as x5092 from "@peculiar/x509";
10288
10303
  var CertificateService = class {
10289
10304
  static getSha256Fingerprint(certPem) {
@@ -10722,7 +10737,7 @@ var certCommand = defineCommand9({
10722
10737
  });
10723
10738
 
10724
10739
  // src/cli/commands/qr.ts
10725
- import * as fs13 from "fs";
10740
+ import * as fs13 from "node:fs";
10726
10741
  import { defineCommand as defineCommand10 } from "citty";
10727
10742
 
10728
10743
  // src/qr/qrcode-service.ts
@@ -11874,9 +11889,9 @@ var completionCommand = defineCommand16({
11874
11889
  });
11875
11890
 
11876
11891
  // src/cli/commands/setup.ts
11877
- import * as fs14 from "fs";
11878
- import * as path10 from "path";
11879
- import * as os5 from "os";
11892
+ import * as fs14 from "node:fs";
11893
+ import * as path10 from "node:path";
11894
+ import * as os5 from "node:os";
11880
11895
  import { defineCommand as defineCommand17 } from "citty";
11881
11896
  import { consola as consola16 } from "consola";
11882
11897
  init_patterns();
@@ -11884,7 +11899,7 @@ init_auth_xml_builder();
11884
11899
  init_polling();
11885
11900
 
11886
11901
  // src/cli/utils/open-folder.ts
11887
- import { execFile } from "child_process";
11902
+ import { execFile } from "node:child_process";
11888
11903
  function openFolder(folderPath) {
11889
11904
  return new Promise((resolve2) => {
11890
11905
  const platform = process.platform;
@@ -12143,14 +12158,14 @@ var setupCommand = defineCommand17({
12143
12158
  });
12144
12159
 
12145
12160
  // src/cli/commands/offline.ts
12146
- import * as fs16 from "fs";
12161
+ import * as fs16 from "node:fs";
12147
12162
  import { defineCommand as defineCommand18 } from "citty";
12148
12163
  import { consola as consola17 } from "consola";
12149
12164
 
12150
12165
  // src/offline/file-storage.ts
12151
- import * as fs15 from "fs/promises";
12152
- import * as path11 from "path";
12153
- import * as os6 from "os";
12166
+ import * as fs15 from "node:fs/promises";
12167
+ import * as path11 from "node:path";
12168
+ import * as os6 from "node:os";
12154
12169
 
12155
12170
  // src/offline/storage.ts
12156
12171
  function matchesFilter(invoice3, filter) {