ksef-client-ts 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -955,7 +955,9 @@ function isValidVatUe(value) {
955
955
  return VatUe.test(value);
956
956
  }
957
957
  function isValidNipVatUe(value) {
958
- return NipVatUe.test(value);
958
+ if (!NipVatUe.test(value)) return false;
959
+ const nip = value.split("-")[0];
960
+ return isValidNip(nip);
959
961
  }
960
962
  function isValidInternalId(value) {
961
963
  return InternalId.test(value);
@@ -2967,6 +2969,32 @@ var init_online_session = __esm({
2967
2969
  }
2968
2970
  });
2969
2971
 
2972
+ // src/utils/concurrency.ts
2973
+ async function runWithConcurrency(tasks, parallelism) {
2974
+ if (tasks.length === 0) return;
2975
+ const limit = Math.max(1, Math.min(parallelism, tasks.length));
2976
+ const controller = new AbortController();
2977
+ let index = 0;
2978
+ const workers = Array.from({ length: limit }, async () => {
2979
+ while (index < tasks.length && !controller.signal.aborted) {
2980
+ const current = index;
2981
+ index += 1;
2982
+ try {
2983
+ await tasks[current](controller.signal);
2984
+ } catch (err) {
2985
+ controller.abort();
2986
+ throw err;
2987
+ }
2988
+ }
2989
+ });
2990
+ await Promise.all(workers);
2991
+ }
2992
+ var init_concurrency = __esm({
2993
+ "src/utils/concurrency.ts"() {
2994
+ "use strict";
2995
+ }
2996
+ });
2997
+
2970
2998
  // src/services/batch-session.ts
2971
2999
  var BatchSessionService;
2972
3000
  var init_batch_session = __esm({
@@ -2975,6 +3003,7 @@ var init_batch_session = __esm({
2975
3003
  init_ksef_feature();
2976
3004
  init_rest_request();
2977
3005
  init_routes();
3006
+ init_concurrency();
2978
3007
  BatchSessionService = class {
2979
3008
  restClient;
2980
3009
  constructor(restClient) {
@@ -2988,12 +3017,10 @@ var init_batch_session = __esm({
2988
3017
  const response = await this.restClient.execute(req);
2989
3018
  return response.body;
2990
3019
  }
2991
- async sendParts(openResponse, parts) {
2992
- const uploadRequests = openResponse.partUploadRequests;
2993
- const tasks = parts.map(async (part) => {
2994
- const uploadReq = uploadRequests.find(
2995
- (r) => r.ordinalNumber === part.ordinalNumber
2996
- );
3020
+ async sendParts(openResponse, parts, parallelism) {
3021
+ const uploadMap = new Map(openResponse.partUploadRequests.map((r) => [r.ordinalNumber, r]));
3022
+ const tasks = parts.map((part) => async (signal) => {
3023
+ const uploadReq = uploadMap.get(part.ordinalNumber);
2997
3024
  if (!uploadReq) {
2998
3025
  throw new Error(`No upload request found for part ${part.ordinalNumber}`);
2999
3026
  }
@@ -3001,25 +3028,38 @@ var init_batch_session = __esm({
3001
3028
  for (const [k, v] of Object.entries(uploadReq.headers)) {
3002
3029
  if (v != null) headers[k] = v;
3003
3030
  }
3004
- await fetch(uploadReq.url, {
3031
+ const resp = await fetch(uploadReq.url, {
3005
3032
  method: uploadReq.method,
3006
3033
  headers,
3007
- body: part.data
3034
+ body: part.data,
3035
+ signal
3008
3036
  });
3037
+ if (!resp.ok) {
3038
+ const body = await resp.text().catch(() => "");
3039
+ throw new Error(
3040
+ `Upload failed for part ${part.ordinalNumber}: HTTP ${resp.status}${body ? ` \u2014 ${body}` : ""}`
3041
+ );
3042
+ }
3009
3043
  });
3010
- await Promise.all(tasks);
3044
+ if (parallelism !== void 0) {
3045
+ await runWithConcurrency(tasks, parallelism);
3046
+ } else {
3047
+ const ac = new AbortController();
3048
+ await Promise.all(tasks.map((t) => t(ac.signal).catch((err) => {
3049
+ ac.abort();
3050
+ throw err;
3051
+ })));
3052
+ }
3011
3053
  }
3012
3054
  /**
3013
- * Upload parts sequentially (not in parallel) because each part uses a
3014
- * streaming body (`duplex: 'half'`). Parallel streaming uploads can cause
3015
- * backpressure issues and exceed memory limits for large payloads.
3055
+ * Upload parts using streaming bodies (`duplex: 'half'`).
3056
+ * By default uploads sequentially to avoid backpressure issues.
3057
+ * Pass `parallelism` to enable bounded concurrent uploads.
3016
3058
  */
3017
- async sendPartsWithStream(openResponse, parts) {
3018
- const uploadRequests = openResponse.partUploadRequests;
3019
- for (const part of parts) {
3020
- const uploadReq = uploadRequests.find(
3021
- (r) => r.ordinalNumber === part.ordinalNumber
3022
- );
3059
+ async sendPartsWithStream(openResponse, parts, parallelism) {
3060
+ const uploadMap = new Map(openResponse.partUploadRequests.map((r) => [r.ordinalNumber, r]));
3061
+ const uploadPart = async (part, signal) => {
3062
+ const uploadReq = uploadMap.get(part.ordinalNumber);
3023
3063
  if (!uploadReq) {
3024
3064
  throw new Error(`No upload request found for part ${part.ordinalNumber}`);
3025
3065
  }
@@ -3031,11 +3071,23 @@ var init_batch_session = __esm({
3031
3071
  method: uploadReq.method,
3032
3072
  headers,
3033
3073
  body: part.dataStream,
3074
+ signal,
3034
3075
  // @ts-expect-error -- Node 18+ undici supports duplex for streaming body
3035
3076
  duplex: "half"
3036
3077
  });
3037
3078
  if (!resp.ok) {
3038
- throw new Error(`Upload failed for part ${part.ordinalNumber}: HTTP ${resp.status}`);
3079
+ const body = await resp.text().catch(() => "");
3080
+ throw new Error(
3081
+ `Upload failed for part ${part.ordinalNumber}: HTTP ${resp.status}${body ? ` \u2014 ${body}` : ""}`
3082
+ );
3083
+ }
3084
+ };
3085
+ if (parallelism !== void 0) {
3086
+ await runWithConcurrency(parts.map((p) => (signal) => uploadPart(p, signal)), parallelism);
3087
+ } else {
3088
+ const { signal } = new AbortController();
3089
+ for (const part of parts) {
3090
+ await uploadPart(part, signal);
3039
3091
  }
3040
3092
  }
3041
3093
  }
@@ -3148,7 +3200,10 @@ var init_invoice_download = __esm({
3148
3200
  async getInvoice(ksefNumber) {
3149
3201
  const req = RestRequest.get(Routes.Invoices.byKsefNumber(ksefNumber));
3150
3202
  const response = await this.restClient.executeRaw(req);
3151
- return new TextDecoder().decode(response.body);
3203
+ return {
3204
+ xml: new TextDecoder().decode(response.body),
3205
+ hash: response.headers.get("x-ms-meta-hash") ?? void 0
3206
+ };
3152
3207
  }
3153
3208
  async queryInvoiceMetadata(filters, pageOffset, pageSize, sortOrder) {
3154
3209
  const req = RestRequest.post(Routes.Invoices.queryMetadata).body(filters);
@@ -4410,6 +4465,72 @@ var init_zip = __esm({
4410
4465
  }
4411
4466
  });
4412
4467
 
4468
+ // src/offline/holidays.ts
4469
+ function toDateKey(d) {
4470
+ return d.toISOString().slice(0, 10);
4471
+ }
4472
+ function dateKey(year, month, day) {
4473
+ return toDateKey(new Date(Date.UTC(year, month - 1, day)));
4474
+ }
4475
+ function addDays(d, n) {
4476
+ const result = new Date(d);
4477
+ result.setUTCDate(result.getUTCDate() + n);
4478
+ return result;
4479
+ }
4480
+ function computeEasterSunday(year) {
4481
+ const a = year % 19;
4482
+ const b = Math.floor(year / 100);
4483
+ const c = year % 100;
4484
+ const d = Math.floor(b / 4);
4485
+ const e = b % 4;
4486
+ const f = Math.floor((b + 8) / 25);
4487
+ const g = Math.floor((b - f + 1) / 3);
4488
+ const h = (19 * a + b - d - g + 15) % 30;
4489
+ const i = Math.floor(c / 4);
4490
+ const k = c % 4;
4491
+ const l = (32 + 2 * e + 2 * i - h - k) % 7;
4492
+ const m = Math.floor((a + 11 * h + 22 * l) / 451);
4493
+ const month = Math.floor((h + l - 7 * m + 114) / 31);
4494
+ const day = (h + l - 7 * m + 114) % 31 + 1;
4495
+ return new Date(Date.UTC(year, month - 1, day));
4496
+ }
4497
+ function getPolishHolidays(year) {
4498
+ const cached = holidayCache.get(year);
4499
+ if (cached) return cached;
4500
+ const easter = computeEasterSunday(year);
4501
+ const holidays = /* @__PURE__ */ new Set([
4502
+ // Fixed
4503
+ dateKey(year, 1, 1),
4504
+ dateKey(year, 1, 6),
4505
+ dateKey(year, 5, 1),
4506
+ dateKey(year, 5, 3),
4507
+ dateKey(year, 8, 15),
4508
+ dateKey(year, 11, 1),
4509
+ dateKey(year, 11, 11),
4510
+ dateKey(year, 12, 25),
4511
+ dateKey(year, 12, 26),
4512
+ // Wigilia — added by Dz.U. 2024 poz. 1965, effective from 2025
4513
+ ...year >= 2025 ? [dateKey(year, 12, 24)] : [],
4514
+ // Moveable
4515
+ toDateKey(easter),
4516
+ toDateKey(addDays(easter, 1)),
4517
+ toDateKey(addDays(easter, 49)),
4518
+ toDateKey(addDays(easter, 60))
4519
+ ]);
4520
+ holidayCache.set(year, holidays);
4521
+ return holidays;
4522
+ }
4523
+ function isPolishHoliday(date) {
4524
+ return getPolishHolidays(date.getUTCFullYear()).has(toDateKey(date));
4525
+ }
4526
+ var holidayCache;
4527
+ var init_holidays = __esm({
4528
+ "src/offline/holidays.ts"() {
4529
+ "use strict";
4530
+ holidayCache = /* @__PURE__ */ new Map();
4531
+ }
4532
+ });
4533
+
4413
4534
  // src/offline/deadline.ts
4414
4535
  function getDefaultReason(mode) {
4415
4536
  switch (mode) {
@@ -4426,7 +4547,7 @@ function getDefaultReason(mode) {
4426
4547
  function nextBusinessDay(from) {
4427
4548
  const d = new Date(from);
4428
4549
  d.setUTCDate(d.getUTCDate() + 1);
4429
- while (d.getUTCDay() === 0 || d.getUTCDay() === 6) {
4550
+ while (d.getUTCDay() === 0 || d.getUTCDay() === 6 || isPolishHoliday(d)) {
4430
4551
  d.setUTCDate(d.getUTCDate() + 1);
4431
4552
  }
4432
4553
  return d;
@@ -4439,7 +4560,7 @@ function addBusinessDays(from, days) {
4439
4560
  let remaining = days;
4440
4561
  while (remaining > 0) {
4441
4562
  d.setUTCDate(d.getUTCDate() + 1);
4442
- if (d.getUTCDay() !== 0 && d.getUTCDay() !== 6) {
4563
+ if (d.getUTCDay() !== 0 && d.getUTCDay() !== 6 && !isPolishHoliday(d)) {
4443
4564
  remaining--;
4444
4565
  }
4445
4566
  }
@@ -4505,12 +4626,13 @@ var FAR_FUTURE;
4505
4626
  var init_deadline = __esm({
4506
4627
  "src/offline/deadline.ts"() {
4507
4628
  "use strict";
4629
+ init_holidays();
4508
4630
  FAR_FUTURE = /* @__PURE__ */ new Date("9999-12-31T23:59:59Z");
4509
4631
  }
4510
4632
  });
4511
4633
 
4512
4634
  // src/workflows/offline-invoice-workflow.ts
4513
- import crypto6 from "crypto";
4635
+ import crypto7 from "crypto";
4514
4636
  var OfflineInvoiceWorkflow;
4515
4637
  var init_offline_invoice_workflow = __esm({
4516
4638
  "src/workflows/offline-invoice-workflow.ts"() {
@@ -4534,7 +4656,7 @@ var init_offline_invoice_workflow = __esm({
4534
4656
  }
4535
4657
  const mode = options?.mode ?? "offline24";
4536
4658
  const reason = getDefaultReason(mode);
4537
- const invoiceHashBase64 = crypto6.createHash("sha256").update(input.invoiceXml).digest("base64");
4659
+ const invoiceHashBase64 = crypto7.createHash("sha256").update(input.invoiceXml).digest("base64");
4538
4660
  const kod1Url = this.qrService.buildInvoiceVerificationUrl(
4539
4661
  input.sellerNip,
4540
4662
  input.invoiceDate,
@@ -4553,7 +4675,7 @@ var init_offline_invoice_workflow = __esm({
4553
4675
  }
4554
4676
  const submitBy = options?.customDeadline ? typeof options.customDeadline === "string" ? options.customDeadline : options.customDeadline.toISOString() : calculateOfflineDeadline(mode, input.invoiceDate, options?.maintenanceWindow).toISOString();
4555
4677
  const metadata = {
4556
- id: crypto6.randomUUID(),
4678
+ id: crypto7.randomUUID(),
4557
4679
  mode,
4558
4680
  reason,
4559
4681
  status: "GENERATED",
@@ -4709,7 +4831,7 @@ var init_offline_invoice_workflow = __esm({
4709
4831
  original.status === "CORRECTED" ? `Invoice ${rejectedInvoiceId} has already been corrected (by ${original.correctedBy})` : `Only rejected invoices can be corrected (current status: ${original.status})`
4710
4832
  );
4711
4833
  }
4712
- const originalHash = crypto6.createHash("sha256").update(original.invoiceXml).digest("base64");
4834
+ const originalHash = crypto7.createHash("sha256").update(original.invoiceXml).digest("base64");
4713
4835
  await client.crypto.init();
4714
4836
  const encData = client.crypto.getEncryptionData();
4715
4837
  const formCode = options.formCode ?? DEFAULT_FORM_CODE;
@@ -4722,9 +4844,9 @@ var init_offline_invoice_workflow = __esm({
4722
4844
  const plainMeta = client.crypto.getFileMetadata(data);
4723
4845
  const encrypted = client.crypto.encryptAES256(data, encData.cipherKey, encData.cipherIv);
4724
4846
  const encMeta = client.crypto.getFileMetadata(encrypted);
4725
- const correctionId = crypto6.randomUUID();
4847
+ const correctionId = crypto7.randomUUID();
4726
4848
  const submittedAt = (/* @__PURE__ */ new Date()).toISOString();
4727
- const correctedHashBase64 = crypto6.createHash("sha256").update(correctedInvoiceXml).digest("base64");
4849
+ const correctedHashBase64 = crypto7.createHash("sha256").update(correctedInvoiceXml).digest("base64");
4728
4850
  const kod1Url = this.qrService.buildInvoiceVerificationUrl(
4729
4851
  original.sellerNip,
4730
4852
  original.invoiceDate,
@@ -4879,6 +5001,7 @@ var init_client = __esm({
4879
5001
  qr;
4880
5002
  options;
4881
5003
  authManager;
5004
+ _baseRestClientConfig;
4882
5005
  _offline;
4883
5006
  constructor(options) {
4884
5007
  this.options = resolveOptions(options);
@@ -4890,6 +5013,8 @@ var init_client = __esm({
4890
5013
  });
4891
5014
  this.authManager = authManager;
4892
5015
  const restClientConfig = buildRestClientConfig(options, authManager);
5016
+ const { authManager: _am, ...baseConfig } = restClientConfig;
5017
+ this._baseRestClientConfig = baseConfig;
4893
5018
  const restClient = new RestClient(this.options, restClientConfig);
4894
5019
  const fetcher = new CertificateFetcher(restClient);
4895
5020
  this.crypto = new CryptographyService(fetcher);
@@ -4914,6 +5039,10 @@ var init_client = __esm({
4914
5039
  }
4915
5040
  return this._offline;
4916
5041
  }
5042
+ /** @internal Create a RestClient with a different AuthManager, preserving transport/retry/rateLimit config. */
5043
+ createScopedRestClient(authManager) {
5044
+ return new RestClient(this.options, { ...this._baseRestClientConfig, authManager });
5045
+ }
4917
5046
  async loginWithToken(token, nip) {
4918
5047
  const challenge = await this.auth.getChallenge();
4919
5048
  await this.crypto.init();
@@ -5733,6 +5862,18 @@ function parseKSeFTokenContext(token) {
5733
5862
  };
5734
5863
  }
5735
5864
 
5865
+ // src/utils/hash.ts
5866
+ import crypto6 from "crypto";
5867
+ function sha256Base642(data) {
5868
+ return crypto6.createHash("sha256").update(data).digest("base64");
5869
+ }
5870
+ function verifyHash(data, expectedHash) {
5871
+ return sha256Base642(data) === expectedHash;
5872
+ }
5873
+
5874
+ // src/utils/index.ts
5875
+ init_concurrency();
5876
+
5736
5877
  // src/workflows/polling.ts
5737
5878
  async function pollUntil(action, condition, options) {
5738
5879
  const intervalMs = options?.intervalMs ?? 2e3;
@@ -5751,6 +5892,10 @@ async function pollUntil(action, condition, options) {
5751
5892
  }
5752
5893
 
5753
5894
  // src/workflows/online-session-workflow.ts
5895
+ init_ksef_session_expired_error();
5896
+ init_auth_manager();
5897
+ init_online_session();
5898
+ init_session_status();
5754
5899
  init_document_structures();
5755
5900
 
5756
5901
  // src/xml/upo-parser.ts
@@ -5914,18 +6059,11 @@ function nonEmptyString(value) {
5914
6059
  // src/workflows/online-session-workflow.ts
5915
6060
  init_invoice_validator();
5916
6061
  init_ksef_validation_error();
5917
- async function openOnlineSession(client, options) {
5918
- await client.crypto.init();
5919
- const encData = client.crypto.getEncryptionData();
5920
- const formCode = options?.formCode ?? DEFAULT_FORM_CODE;
5921
- const openResp = await client.onlineSession.openSession(
5922
- { formCode, encryption: encData.encryptionInfo },
5923
- options?.upoVersion
5924
- );
5925
- const sessionRef = openResp.referenceNumber;
6062
+ function buildSessionHandle(params) {
6063
+ const { deps, sessionRef, validUntil, cipherKey, cipherIv, formCode, validate: validate2 } = params;
5926
6064
  async function fetchUpo(pollOpts) {
5927
6065
  const result = await pollUntil(
5928
- () => client.sessionStatus.getSessionStatus(sessionRef),
6066
+ () => deps.sessionStatus.getSessionStatus(sessionRef),
5929
6067
  (s) => s.status.code === 200 || s.status.code >= 400,
5930
6068
  { ...pollOpts, description: `UPO for session ${sessionRef}` }
5931
6069
  );
@@ -5941,9 +6079,9 @@ async function openOnlineSession(client, options) {
5941
6079
  }
5942
6080
  return {
5943
6081
  sessionRef,
5944
- validUntil: openResp.validUntil,
6082
+ validUntil,
5945
6083
  async sendInvoice(invoiceXml) {
5946
- if (options?.validate) {
6084
+ if (validate2) {
5947
6085
  const xmlStr = typeof invoiceXml === "string" ? invoiceXml : new TextDecoder().decode(invoiceXml);
5948
6086
  const vResult = await validate(xmlStr);
5949
6087
  if (!vResult.valid) {
@@ -5954,10 +6092,10 @@ async function openOnlineSession(client, options) {
5954
6092
  }
5955
6093
  }
5956
6094
  const data = typeof invoiceXml === "string" ? new TextEncoder().encode(invoiceXml) : invoiceXml;
5957
- const plainMeta = client.crypto.getFileMetadata(data);
5958
- const encrypted = client.crypto.encryptAES256(data, encData.cipherKey, encData.cipherIv);
5959
- const encMeta = client.crypto.getFileMetadata(encrypted);
5960
- const resp = await client.onlineSession.sendInvoice(sessionRef, {
6095
+ const plainMeta = deps.crypto.getFileMetadata(data);
6096
+ const encrypted = deps.crypto.encryptAES256(data, cipherKey, cipherIv);
6097
+ const encMeta = deps.crypto.getFileMetadata(encrypted);
6098
+ const resp = await deps.onlineSession.sendInvoice(sessionRef, {
5961
6099
  invoiceHash: plainMeta.hashSHA,
5962
6100
  invoiceSize: plainMeta.fileSize,
5963
6101
  encryptedInvoiceHash: encMeta.hashSHA,
@@ -5967,7 +6105,7 @@ async function openOnlineSession(client, options) {
5967
6105
  return resp.referenceNumber;
5968
6106
  },
5969
6107
  async close() {
5970
- await client.onlineSession.closeSession(sessionRef);
6108
+ await deps.onlineSession.closeSession(sessionRef);
5971
6109
  },
5972
6110
  async waitForUpo(pollOpts) {
5973
6111
  return fetchUpo(pollOpts);
@@ -5976,13 +6114,75 @@ async function openOnlineSession(client, options) {
5976
6114
  const upoInfo = await fetchUpo(pollOpts);
5977
6115
  const parsed = [];
5978
6116
  for (const page of upoInfo.pages) {
5979
- const result = await client.sessionStatus.getSessionUpo(sessionRef, page.referenceNumber);
6117
+ const result = await deps.sessionStatus.getSessionUpo(sessionRef, page.referenceNumber);
5980
6118
  parsed.push(parseUpoXml(result.upo));
5981
6119
  }
5982
6120
  return { ...upoInfo, parsed };
6121
+ },
6122
+ getState() {
6123
+ const token = deps.getAccessToken();
6124
+ if (!token) {
6125
+ throw new Error("Cannot serialize session state: no access token available");
6126
+ }
6127
+ return {
6128
+ referenceNumber: sessionRef,
6129
+ aesKey: Buffer.from(cipherKey).toString("base64"),
6130
+ iv: Buffer.from(cipherIv).toString("base64"),
6131
+ accessToken: token,
6132
+ formCode,
6133
+ validUntil,
6134
+ ...validate2 ? { validate: validate2 } : {}
6135
+ };
5983
6136
  }
5984
6137
  };
5985
6138
  }
6139
+ async function openOnlineSession(client, options) {
6140
+ await client.crypto.init();
6141
+ const encData = client.crypto.getEncryptionData();
6142
+ const formCode = options?.formCode ?? DEFAULT_FORM_CODE;
6143
+ const openResp = await client.onlineSession.openSession(
6144
+ { formCode, encryption: encData.encryptionInfo },
6145
+ options?.upoVersion
6146
+ );
6147
+ return buildSessionHandle({
6148
+ deps: {
6149
+ crypto: client.crypto,
6150
+ onlineSession: client.onlineSession,
6151
+ sessionStatus: client.sessionStatus,
6152
+ getAccessToken: () => client.authManager.getAccessToken()
6153
+ },
6154
+ sessionRef: openResp.referenceNumber,
6155
+ validUntil: openResp.validUntil,
6156
+ cipherKey: encData.cipherKey,
6157
+ cipherIv: encData.cipherIv,
6158
+ formCode,
6159
+ validate: options?.validate
6160
+ });
6161
+ }
6162
+ function resumeOnlineSession(client, state, options) {
6163
+ const expiry = new Date(state.validUntil);
6164
+ if (expiry.getTime() <= Date.now()) {
6165
+ throw new KSeFSessionExpiredError(
6166
+ `Cannot resume session: expired at ${state.validUntil}`
6167
+ );
6168
+ }
6169
+ const scopedAuth = new DefaultAuthManager(() => Promise.resolve(null), state.accessToken);
6170
+ const scopedRestClient = client.createScopedRestClient(scopedAuth);
6171
+ return buildSessionHandle({
6172
+ deps: {
6173
+ crypto: client.crypto,
6174
+ onlineSession: new OnlineSessionService(scopedRestClient),
6175
+ sessionStatus: new SessionStatusService(scopedRestClient),
6176
+ getAccessToken: () => scopedAuth.getAccessToken()
6177
+ },
6178
+ sessionRef: state.referenceNumber,
6179
+ validUntil: state.validUntil,
6180
+ cipherKey: new Uint8Array(Buffer.from(state.aesKey, "base64")),
6181
+ cipherIv: new Uint8Array(Buffer.from(state.iv, "base64")),
6182
+ formCode: state.formCode,
6183
+ validate: options?.validate ?? state.validate
6184
+ });
6185
+ }
5986
6186
  async function openSendAndClose(client, invoices, options) {
5987
6187
  const handle = await openOnlineSession(client, options);
5988
6188
  for (const inv of invoices) {
@@ -5995,6 +6195,9 @@ async function openSendAndClose(client, invoices, options) {
5995
6195
  // src/workflows/batch-session-workflow.ts
5996
6196
  init_document_structures();
5997
6197
  async function uploadBatch(client, zipData, options) {
6198
+ if (options?.parallelism !== void 0 && (!Number.isInteger(options.parallelism) || options.parallelism < 1)) {
6199
+ throw new Error("parallelism must be a positive integer");
6200
+ }
5998
6201
  await client.crypto.init();
5999
6202
  if (options?.validate) {
6000
6203
  const { unzip: unzip2 } = await Promise.resolve().then(() => (init_zip(), zip_exports));
@@ -6037,7 +6240,7 @@ async function uploadBatch(client, zipData, options) {
6037
6240
  },
6038
6241
  ordinalNumber: i + 1
6039
6242
  }));
6040
- await client.batchSession.sendParts(openResp, sendingParts);
6243
+ await client.batchSession.sendParts(openResp, sendingParts, options?.parallelism);
6041
6244
  await client.batchSession.closeSession(openResp.referenceNumber);
6042
6245
  const result = await pollUntil(
6043
6246
  () => client.sessionStatus.getSessionStatus(openResp.referenceNumber),
@@ -6058,6 +6261,9 @@ async function uploadBatch(client, zipData, options) {
6058
6261
  };
6059
6262
  }
6060
6263
  async function uploadBatchStream(client, zipStreamFactory, zipSize, options) {
6264
+ if (options?.parallelism !== void 0 && (!Number.isInteger(options.parallelism) || options.parallelism < 1)) {
6265
+ throw new Error("parallelism must be a positive integer");
6266
+ }
6061
6267
  await client.crypto.init();
6062
6268
  const encData = client.crypto.getEncryptionData();
6063
6269
  const formCode = options?.formCode ?? DEFAULT_FORM_CODE;
@@ -6079,7 +6285,7 @@ async function uploadBatchStream(client, zipStreamFactory, zipSize, options) {
6079
6285
  },
6080
6286
  options?.upoVersion
6081
6287
  );
6082
- await client.batchSession.sendPartsWithStream(openResp, streamParts);
6288
+ await client.batchSession.sendPartsWithStream(openResp, streamParts, options?.parallelism);
6083
6289
  await client.batchSession.closeSession(openResp.referenceNumber);
6084
6290
  const result = await pollUntil(
6085
6291
  () => client.sessionStatus.getSessionStatus(openResp.referenceNumber),
@@ -6154,6 +6360,7 @@ async function doExport(client, filters, options) {
6154
6360
  url: p.url,
6155
6361
  method: p.method,
6156
6362
  partSize: p.partSize,
6363
+ partHash: p.partHash,
6157
6364
  encryptedPartSize: p.encryptedPartSize,
6158
6365
  encryptedPartHash: p.encryptedPartHash,
6159
6366
  expirationDate: p.expirationDate
@@ -6179,6 +6386,9 @@ async function exportAndDownload(client, filters, options) {
6179
6386
  throw new Error(`Download failed for part ${part.ordinalNumber}: HTTP ${resp.status}`);
6180
6387
  }
6181
6388
  const encryptedData = new Uint8Array(await resp.arrayBuffer());
6389
+ if (options?.verifyHash !== false && !verifyHash(encryptedData, part.encryptedPartHash)) {
6390
+ throw new Error(`Hash mismatch for export part ${part.ordinalNumber}`);
6391
+ }
6182
6392
  const decrypted = client.crypto.decryptAES256(encryptedData, encData.cipherKey, encData.cipherIv);
6183
6393
  decryptedParts.push(decrypted);
6184
6394
  }
@@ -6251,6 +6461,9 @@ async function incrementalExportAndDownload(client, options) {
6251
6461
  throw new Error(`Download failed for part ${part.ordinalNumber}: HTTP ${resp.status}`);
6252
6462
  }
6253
6463
  const encryptedData = new Uint8Array(await resp.arrayBuffer());
6464
+ if (options.verifyHash !== false && !verifyHash(encryptedData, part.encryptedPartHash)) {
6465
+ throw new Error(`Hash mismatch for export part ${part.ordinalNumber}`);
6466
+ }
6254
6467
  const decrypted = client.crypto.decryptAES256(encryptedData, encData.cipherKey, encData.cipherIv);
6255
6468
  decryptedParts.push(decrypted);
6256
6469
  }
@@ -6416,6 +6629,7 @@ async function authenticateWithPkcs12(client, options) {
6416
6629
 
6417
6630
  // src/offline/index.ts
6418
6631
  init_deadline();
6632
+ init_holidays();
6419
6633
 
6420
6634
  // src/offline/storage.ts
6421
6635
  function matchesFilter(invoice, filter) {
@@ -6657,9 +6871,11 @@ export {
6657
6871
  getDefaultReason,
6658
6872
  getEffectiveStartDate,
6659
6873
  getFormCode,
6874
+ getPolishHolidays,
6660
6875
  getTimeUntilDeadline,
6661
6876
  incrementalExportAndDownload,
6662
6877
  isExpired,
6878
+ isPolishHoliday,
6663
6879
  isRetryableError,
6664
6880
  isRetryableStatus,
6665
6881
  isValidBase64,
@@ -6686,6 +6902,9 @@ export {
6686
6902
  parseUpoXml,
6687
6903
  pollUntil,
6688
6904
  resolveOptions,
6905
+ resumeOnlineSession,
6906
+ runWithConcurrency,
6907
+ sha256Base642 as sha256Base64,
6689
6908
  sleep,
6690
6909
  unzip,
6691
6910
  updateContinuationPoint,
@@ -6700,6 +6919,7 @@ export {
6700
6919
  validatePresignedUrl,
6701
6920
  validateSchema,
6702
6921
  validateWellFormedness,
6922
+ verifyHash,
6703
6923
  xmlToObject
6704
6924
  };
6705
6925
  //# sourceMappingURL=index.js.map