@nile-squad/nylonpay-ts 1.0.3 → 1.0.5

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.cjs CHANGED
@@ -55,21 +55,6 @@ function createEmitter() {
55
55
  const emitter = { on, once, off, emit, clear, listenerCount };
56
56
  return emitter;
57
57
  }
58
- function generateFingerprint() {
59
- const components = [
60
- `type:${os.type()}`,
61
- `platform:${os.platform()}`,
62
- `arch:${os.arch()}`,
63
- `release:${os.release()}`,
64
- `hostname:${os.hostname()}`,
65
- `node:${process.versions.node}`,
66
- `v8:${process.versions.v8}`
67
- ].join("|");
68
- return crypto.createHash("sha256").update(components).digest("hex");
69
- }
70
- function generateNonce(length = 16) {
71
- return crypto.randomBytes(length).toString("hex");
72
- }
73
58
 
74
59
  // src/sdk.config.ts
75
60
  var DEFAULT_BASE_URL = "https://api.nylonpay.nilesquad.com/api/services";
@@ -78,6 +63,9 @@ var DEFAULT_MAX_RETRIES = 3;
78
63
  var DEFAULT_MAX_POLL_INTERVAL_MS = 2e3;
79
64
  var DEFAULT_MAX_POLL_DURATION_MS = 3e5;
80
65
  var DEFAULT_MAX_POLL_ATTEMPTS = 150;
66
+ var DEFAULT_STREAMING = true;
67
+ var STREAM_PATH = "/sse/transaction";
68
+ var MAX_STREAM_RECONNECTS = 2;
81
69
  var SDK_SERVICE = "sdk";
82
70
  var SDK_ACTIONS = {
83
71
  collectPayment: "sdk-collect-payment",
@@ -90,6 +78,21 @@ var SDK_ACTIONS = {
90
78
  createInvoice: "sdk-create-invoice"
91
79
  };
92
80
  var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
81
+ function generateFingerprint() {
82
+ const components = [
83
+ `type:${os.type()}`,
84
+ `platform:${os.platform()}`,
85
+ `arch:${os.arch()}`,
86
+ `release:${os.release()}`,
87
+ `hostname:${os.hostname()}`,
88
+ `node:${process.versions.node}`,
89
+ `v8:${process.versions.v8}`
90
+ ].join("|");
91
+ return crypto.createHash("sha256").update(components).digest("hex");
92
+ }
93
+ function generateNonce(length = 16) {
94
+ return crypto.randomBytes(length).toString("hex");
95
+ }
93
96
  function sortValue(value) {
94
97
  if (Array.isArray(value)) {
95
98
  return value.map((entry) => sortValue(entry));
@@ -120,6 +123,42 @@ function createSignature(input) {
120
123
  function createTimestamp() {
121
124
  return Date.now().toString();
122
125
  }
126
+
127
+ // src/sse-parse.ts
128
+ function parseBlock(block) {
129
+ let event = "message";
130
+ const dataLines = [];
131
+ for (const rawLine of block.split("\n")) {
132
+ const line = rawLine.replace(/\r$/, "");
133
+ if (line.startsWith(":")) {
134
+ continue;
135
+ }
136
+ if (line.startsWith("event:")) {
137
+ event = line.slice("event:".length).trim();
138
+ } else if (line.startsWith("data:")) {
139
+ dataLines.push(line.slice("data:".length).trim());
140
+ }
141
+ }
142
+ if (dataLines.length === 0) {
143
+ return null;
144
+ }
145
+ return { event, data: dataLines.join("\n") };
146
+ }
147
+ function parseSseBuffer(buffer) {
148
+ const messages = [];
149
+ let rest = buffer;
150
+ let separator = rest.indexOf("\n\n");
151
+ while (separator !== -1) {
152
+ const block = rest.slice(0, separator);
153
+ rest = rest.slice(separator + 2);
154
+ const message = parseBlock(block);
155
+ if (message) {
156
+ messages.push(message);
157
+ }
158
+ separator = rest.indexOf("\n\n");
159
+ }
160
+ return { messages, rest };
161
+ }
123
162
  function verifyResponseSignature(data, signature, secret) {
124
163
  const expectedSignature = crypto.createHmac("sha256", secret).update(createCanonicalPayload(data)).digest("hex");
125
164
  const providedBuffer = Buffer.from(signature, "hex");
@@ -132,6 +171,55 @@ function verifyResponseSignature(data, signature, secret) {
132
171
 
133
172
  // src/transport.ts
134
173
  var CACHED_FINGERPRINT = generateFingerprint();
174
+ function streamUrl(baseUrl) {
175
+ try {
176
+ return new URL(baseUrl).origin + STREAM_PATH;
177
+ } catch {
178
+ return baseUrl.replace(/\/api\/services\/?$/u, "") + STREAM_PATH;
179
+ }
180
+ }
181
+ var KNOWN_CATEGORIES = /* @__PURE__ */ new Set([
182
+ "auth",
183
+ "validation",
184
+ "limit",
185
+ "rate_limit",
186
+ "account",
187
+ "provider",
188
+ "not_found",
189
+ "internal",
190
+ "network",
191
+ "timeout"
192
+ ]);
193
+ var STATUS_CATEGORY = {
194
+ 408: "timeout",
195
+ 429: "rate_limit"
196
+ };
197
+ var ERROR_TYPE_SUFFIX = /^(.*?)\s*--\s*error-type:\s*([a-z_]+)\s*$/is;
198
+ function parseCategoryFromMessage(message) {
199
+ const match = ERROR_TYPE_SUFFIX.exec(message);
200
+ if (match?.[2] && KNOWN_CATEGORIES.has(match[2])) {
201
+ return {
202
+ category: match[2],
203
+ message: match[1] ?? message
204
+ };
205
+ }
206
+ return { category: null, message };
207
+ }
208
+ function buildHttpError(params) {
209
+ const parsed = parseCategoryFromMessage(params.message);
210
+ const category = parsed.category ?? STATUS_CATEGORY[params.statusCode] ?? (params.statusCode >= 500 ? "internal" : "validation");
211
+ return {
212
+ category,
213
+ message: parsed.message,
214
+ retryable: RETRYABLE_STATUS_CODES.has(params.statusCode)
215
+ };
216
+ }
217
+ function createSdkError(error) {
218
+ return Object.assign(new Error(error.message), {
219
+ category: error.category,
220
+ retryable: error.retryable
221
+ });
222
+ }
135
223
  function calculateBackoff(attempt) {
136
224
  const base = 2 ** attempt * 1e3;
137
225
  const jitter = Math.random() * 500;
@@ -234,21 +322,19 @@ function createTransport({
234
322
  await delay(calculateBackoff(currentAttempt));
235
323
  return attempt(currentAttempt + 1);
236
324
  }
237
- const sdkError = {
238
- code: `HTTP_${statusCode}`,
239
- message: errorMessage,
240
- statusCode,
241
- retryable
242
- };
243
325
  cleanup();
244
- return slangTs.Err(JSON.stringify(sdkError));
326
+ return slangTs.Err(
327
+ JSON.stringify(
328
+ buildHttpError({ message: errorMessage, statusCode })
329
+ )
330
+ );
245
331
  }
246
332
  const responseBody = await response.json();
247
333
  if (!responseBody || typeof responseBody !== "object" || !("status" in responseBody)) {
248
334
  cleanup();
249
335
  return slangTs.Err(
250
336
  JSON.stringify({
251
- code: "INVALID_RESPONSE",
337
+ category: "internal",
252
338
  message: "Response missing status field",
253
339
  retryable: false
254
340
  })
@@ -267,7 +353,7 @@ function createTransport({
267
353
  cleanup();
268
354
  return slangTs.Err(
269
355
  JSON.stringify({
270
- code: "RESPONSE_TAMPERED",
356
+ category: "internal",
271
357
  message: "Response signature verification failed",
272
358
  retryable: false
273
359
  })
@@ -284,7 +370,7 @@ function createTransport({
284
370
  cleanup();
285
371
  const isAbort = error instanceof DOMException && error.name === "AbortError";
286
372
  const sdkError = {
287
- code: isAbort ? "TIMEOUT" : "NETWORK_ERROR",
373
+ category: isAbort ? "timeout" : "network",
288
374
  message: isAbort ? `Request timed out after ${timeoutMs}ms` : String(error),
289
375
  retryable: true
290
376
  };
@@ -297,17 +383,105 @@ function createTransport({
297
383
  }
298
384
  return attempt(0);
299
385
  }
300
- return { send, parseError };
386
+ function openStream(input) {
387
+ const controller = new AbortController();
388
+ let closed = false;
389
+ const close = () => {
390
+ if (closed) {
391
+ return;
392
+ }
393
+ closed = true;
394
+ controller.abort();
395
+ };
396
+ const payload = {
397
+ reference: input.reference,
398
+ _fingerprint: CACHED_FINGERPRINT
399
+ };
400
+ const headers = buildAuthHeaders({ apiKey, apiSecret, payload });
401
+ const url = streamUrl(baseUrl);
402
+ const run = async () => {
403
+ const fetchResult = await slangTs.safeTry(
404
+ () => fetchImpl(url, {
405
+ method: "POST",
406
+ headers,
407
+ body: JSON.stringify(payload),
408
+ signal: controller.signal
409
+ })
410
+ );
411
+ if (fetchResult.isErr) {
412
+ if (!closed) {
413
+ input.onError(fetchResult.error);
414
+ }
415
+ return;
416
+ }
417
+ const response = fetchResult.value;
418
+ if (!response.ok || !response.body) {
419
+ const textResult = await slangTs.safeTry(() => response.text());
420
+ const message = textResult.isOk ? textResult.value : `HTTP ${response.status}`;
421
+ if (!closed) {
422
+ input.onError(message || `HTTP ${response.status}`);
423
+ }
424
+ return;
425
+ }
426
+ const reader = response.body.getReader();
427
+ const decoder = new TextDecoder();
428
+ let buffer = "";
429
+ while (!closed) {
430
+ const chunkResult = await slangTs.safeTry(() => reader.read());
431
+ if (chunkResult.isErr) {
432
+ if (!closed) {
433
+ input.onError(chunkResult.error);
434
+ }
435
+ return;
436
+ }
437
+ const chunk = chunkResult.value;
438
+ if (chunk.done) {
439
+ break;
440
+ }
441
+ buffer += decoder.decode(chunk.value, { stream: true });
442
+ const { messages, rest } = parseSseBuffer(buffer);
443
+ buffer = rest;
444
+ for (const message of messages) {
445
+ if (message.event === "status") {
446
+ const statusResult = await slangTs.safeTry(
447
+ () => JSON.parse(message.data)
448
+ );
449
+ if (statusResult.isOk) {
450
+ input.onStatus(statusResult.value);
451
+ }
452
+ } else if (message.event === "error") {
453
+ const errDataResult = await slangTs.safeTry(
454
+ () => JSON.parse(message.data)
455
+ );
456
+ const text = errDataResult.isOk ? errDataResult.value.message ?? message.data : message.data;
457
+ close();
458
+ input.onError(text);
459
+ return;
460
+ }
461
+ }
462
+ }
463
+ if (!closed) {
464
+ input.onClose();
465
+ }
466
+ };
467
+ void run();
468
+ return { close };
469
+ }
470
+ return { send, openStream, parseError };
301
471
  }
302
472
  function parseError(error) {
303
473
  try {
304
474
  const parsed = JSON.parse(error);
305
- if (parsed && typeof parsed === "object" && "code" in parsed && "message" in parsed && typeof parsed.code === "string" && typeof parsed.message === "string") {
475
+ if (parsed && typeof parsed === "object" && "category" in parsed && "message" in parsed && typeof parsed.category === "string" && typeof parsed.message === "string") {
306
476
  return parsed;
307
477
  }
308
478
  } catch {
309
479
  }
310
- return { code: "UNKNOWN", message: error };
480
+ const fromSuffix = parseCategoryFromMessage(error);
481
+ return {
482
+ category: fromSuffix.category ?? "internal",
483
+ message: fromSuffix.message
484
+ };
311
485
  }
312
486
 
313
487
  // src/payment.ts
@@ -325,10 +499,14 @@ var TERMINAL_STATES = /* @__PURE__ */ new Set([
325
499
  "failed",
326
500
  "cancelled"
327
501
  ]);
502
+ function normalizeStatus(raw) {
503
+ if (raw === "completed") return "successful";
504
+ return raw;
505
+ }
328
506
  function createPaymentInstance(initialResponse, deps) {
329
507
  const state = {
330
508
  reference: initialResponse.reference,
331
- status: initialResponse.status,
509
+ status: normalizeStatus(initialResponse.status),
332
510
  transaction: null,
333
511
  pollingTimer: null,
334
512
  resolved: false,
@@ -339,11 +517,15 @@ function createPaymentInstance(initialResponse, deps) {
339
517
  fetchTransaction: deps.fetchTransaction,
340
518
  pollIntervalMs: deps.pollIntervalMs ?? 2e3,
341
519
  maxPollDuration: deps.maxPollDuration ?? 3e5,
342
- maxPollAttempts: deps.maxPollAttempts ?? 150
520
+ maxPollAttempts: deps.maxPollAttempts ?? 150,
521
+ streaming: deps.streaming ?? false,
522
+ openStream: deps.openStream,
523
+ streamHandle: null,
524
+ streamReconnects: 0
343
525
  };
344
526
  function resolveWithError(error) {
345
527
  state.resolved = true;
346
- stopPolling();
528
+ stopUpdates();
347
529
  emitEvent("error", parseError(error).message);
348
530
  }
349
531
  function emitEvent(event, error) {
@@ -363,13 +545,14 @@ function createPaymentInstance(initialResponse, deps) {
363
545
  state.transaction = txResult.value;
364
546
  const event = statusToEvent(status);
365
547
  if (event) {
366
- emitEvent(event);
548
+ const error = status === "failed" ? state.transaction.failureReason ?? void 0 : void 0;
549
+ emitEvent(event, error);
367
550
  }
368
551
  } else {
369
552
  emitEvent("error", `Failed to fetch transaction: ${txResult.error}`);
370
553
  }
371
554
  state.resolved = true;
372
- stopPolling();
555
+ stopUpdates();
373
556
  }
374
557
  async function handleStatusUpdate(response) {
375
558
  if (response.reference !== state.reference) {
@@ -378,7 +561,7 @@ function createPaymentInstance(initialResponse, deps) {
378
561
  );
379
562
  return;
380
563
  }
381
- const newStatus = response.status;
564
+ const newStatus = normalizeStatus(response.status);
382
565
  const oldStatus = state.status;
383
566
  state.status = newStatus;
384
567
  if (newStatus !== oldStatus) {
@@ -393,13 +576,13 @@ function createPaymentInstance(initialResponse, deps) {
393
576
  }
394
577
  }
395
578
  function handlePollError(error) {
396
- const isNotFound = error.includes("not found") || error.includes("NOT_FOUND");
397
- if (isNotFound) {
579
+ const parsed = parseError(error);
580
+ if (parsed.category === "not_found") {
398
581
  return;
399
582
  }
400
- emitEvent("error", parseError(error).message);
583
+ emitEvent("error", parsed.message);
401
584
  state.resolved = true;
402
- stopPolling();
585
+ stopUpdates();
403
586
  }
404
587
  function scheduleNextPoll() {
405
588
  if (state.resolved || state.pollingTimer) {
@@ -412,7 +595,7 @@ function createPaymentInstance(initialResponse, deps) {
412
595
  }
413
596
  async function pollStatus() {
414
597
  if (state.resolved) {
415
- stopPolling();
598
+ stopUpdates();
416
599
  return;
417
600
  }
418
601
  if (state.pollAttempts >= state.maxPollAttempts) {
@@ -431,19 +614,66 @@ function createPaymentInstance(initialResponse, deps) {
431
614
  handlePollError(result.error);
432
615
  }
433
616
  if (state.resolved) {
434
- stopPolling();
617
+ stopUpdates();
435
618
  return;
436
619
  }
437
620
  scheduleNextPoll();
438
621
  }
439
- function startPolling() {
622
+ function closeStream() {
623
+ if (state.streamHandle) {
624
+ state.streamHandle.close();
625
+ state.streamHandle = null;
626
+ }
627
+ }
628
+ function startStream() {
629
+ if (state.resolved || !state.openStream) {
630
+ return;
631
+ }
632
+ state.streamHandle = state.openStream({
633
+ reference: state.reference,
634
+ onStatus: (status) => {
635
+ void handleStatusUpdate(status);
636
+ },
637
+ onError: () => handleStreamFailure(),
638
+ onClose: () => handleStreamFailure()
639
+ });
640
+ }
641
+ function handleStreamFailure() {
642
+ closeStream();
643
+ if (state.resolved) {
644
+ return;
645
+ }
646
+ if (state.streamReconnects < MAX_STREAM_RECONNECTS) {
647
+ state.streamReconnects += 1;
648
+ const backoff = 500 * 2 ** (state.streamReconnects - 1) + Math.random() * 250;
649
+ setTimeout(() => {
650
+ if (!state.resolved) {
651
+ startStream();
652
+ }
653
+ }, backoff);
654
+ return;
655
+ }
440
656
  scheduleNextPoll();
441
657
  }
442
- function stopPolling() {
658
+ function startUpdates() {
659
+ if (TERMINAL_STATES.has(state.status)) {
660
+ setTimeout(() => {
661
+ void handleTerminalState(state.status);
662
+ }, 0);
663
+ return;
664
+ }
665
+ if (state.streaming && state.openStream) {
666
+ startStream();
667
+ return;
668
+ }
669
+ scheduleNextPoll();
670
+ }
671
+ function stopUpdates() {
443
672
  if (state.pollingTimer) {
444
673
  clearTimeout(state.pollingTimer);
445
674
  state.pollingTimer = null;
446
675
  }
676
+ closeStream();
447
677
  }
448
678
  function on(event, handler) {
449
679
  state.emitter.on(event, handler);
@@ -458,37 +688,28 @@ function createPaymentInstance(initialResponse, deps) {
458
688
  return paymentInstance;
459
689
  }
460
690
  function wait() {
461
- return new Promise((resolve, reject) => {
691
+ return new Promise((resolve) => {
462
692
  if (state.resolved) {
463
- if (state.status === "successful" && state.transaction) {
464
- resolve(state.transaction);
465
- } else {
466
- reject(new Error(`Payment ${state.status ?? "error"}`));
467
- }
693
+ resolve(
694
+ state.status === "successful" && state.transaction ? state.transaction : null
695
+ );
468
696
  return;
469
697
  }
470
698
  function onSuccess() {
471
699
  cleanup();
472
- if (state.transaction) {
473
- resolve(state.transaction);
474
- } else {
475
- reject(
476
- new Error("Payment successful but transaction data unavailable")
477
- );
478
- }
700
+ resolve(state.transaction ?? null);
479
701
  }
480
702
  function onFailed() {
481
703
  cleanup();
482
- reject(new Error("Payment failed"));
704
+ resolve(null);
483
705
  }
484
706
  function onCancelled() {
485
707
  cleanup();
486
- reject(new Error("Payment cancelled"));
708
+ resolve(null);
487
709
  }
488
- function onError(data) {
710
+ function onError() {
489
711
  cleanup();
490
- const eventData = data;
491
- reject(new Error(eventData.error ?? "Payment error"));
712
+ resolve(null);
492
713
  }
493
714
  function cleanup() {
494
715
  state.emitter.off("success", onSuccess);
@@ -514,7 +735,7 @@ function createPaymentInstance(initialResponse, deps) {
514
735
  off,
515
736
  wait
516
737
  };
517
- startPolling();
738
+ startUpdates();
518
739
  return paymentInstance;
519
740
  }
520
741
  function verifyWebhookSignature(input) {
@@ -532,14 +753,17 @@ function verifyWebhookSignature(input) {
532
753
  function generateReference() {
533
754
  return crypto.randomBytes(16).toString("hex").slice(0, 15);
534
755
  }
756
+ function throwValidation(message) {
757
+ throw createSdkError({ category: "validation", message });
758
+ }
535
759
  function validateAmount(amount) {
536
760
  if (!Number.isInteger(amount) || amount <= 0) {
537
- throw new Error("amount must be a positive integer");
761
+ throwValidation("amount must be a positive integer");
538
762
  }
539
763
  }
540
764
  function validateNonEmpty(value, fieldName) {
541
765
  if (!value || value.trim() === "") {
542
- throw new Error(`${fieldName} is required`);
766
+ throwValidation(`${fieldName} is required`);
543
767
  }
544
768
  }
545
769
  function createSdkInstance(config) {
@@ -562,7 +786,9 @@ function createSdkInstance(config) {
562
786
  }),
563
787
  pollIntervalMs: config.maxPollIntervalMs,
564
788
  maxPollDuration: config.maxPollDurationMs,
565
- maxPollAttempts: config.maxPollAttempts
789
+ maxPollAttempts: config.maxPollAttempts,
790
+ streaming: config.streaming,
791
+ openStream: config.streaming ? transport.openStream : void 0
566
792
  };
567
793
  async function collectPayment(input) {
568
794
  const reference = input.reference ?? generateReference();
@@ -571,7 +797,7 @@ function createSdkInstance(config) {
571
797
  validateNonEmpty(input.customer.phoneNumber, "customer.phoneNumber");
572
798
  validateNonEmpty(input.description, "description");
573
799
  if (input.method === "bank" && !input.bank) {
574
- throw new Error('bank details are required when method is "bank"');
800
+ throwValidation('bank details are required when method is "bank"');
575
801
  }
576
802
  let payload = { ...input, reference };
577
803
  if (config.hooks?.beforeCollect) {
@@ -592,19 +818,10 @@ function createSdkInstance(config) {
592
818
  payload
593
819
  );
594
820
  }
595
- if (result.isOk) {
596
- return createPaymentInstance(result.value, commonDeps);
597
- }
598
- return createPaymentInstance(
599
- { reference, status: "pending" },
600
- {
601
- fetchStatus: async () => slangTs.Err(result.error),
602
- fetchTransaction: async () => slangTs.Err(result.error),
603
- pollIntervalMs: 0,
604
- maxPollAttempts: 1,
605
- maxPollDuration: Number.MAX_SAFE_INTEGER
606
- }
607
- );
821
+ if (result.isErr) {
822
+ throw createSdkError(parseError(result.error));
823
+ }
824
+ return createPaymentInstance(result.value, commonDeps);
608
825
  }
609
826
  async function collectPaymentAndResolve(input) {
610
827
  const reference = input.reference ?? generateReference();
@@ -613,7 +830,7 @@ function createSdkInstance(config) {
613
830
  validateNonEmpty(input.customer.phoneNumber, "customer.phoneNumber");
614
831
  validateNonEmpty(input.description, "description");
615
832
  if (input.method === "bank" && !input.bank) {
616
- throw new Error('bank details are required when method is "bank"');
833
+ throwValidation('bank details are required when method is "bank"');
617
834
  }
618
835
  let payload = { ...input, reference };
619
836
  if (config.hooks?.beforeCollect) {
@@ -672,19 +889,10 @@ function createSdkInstance(config) {
672
889
  payload
673
890
  );
674
891
  }
675
- if (result.isOk) {
676
- return createPaymentInstance(result.value, commonDeps);
677
- }
678
- return createPaymentInstance(
679
- { reference, status: "pending" },
680
- {
681
- fetchStatus: async () => slangTs.Err(result.error),
682
- fetchTransaction: async () => slangTs.Err(result.error),
683
- pollIntervalMs: 0,
684
- maxPollAttempts: 1,
685
- maxPollDuration: Number.MAX_SAFE_INTEGER
686
- }
687
- );
892
+ if (result.isErr) {
893
+ throw createSdkError(parseError(result.error));
894
+ }
895
+ return createPaymentInstance(result.value, commonDeps);
688
896
  }
689
897
  async function makePayoutAndResolve(input) {
690
898
  const reference = input.reference ?? generateReference();
@@ -737,7 +945,7 @@ function createSdkInstance(config) {
737
945
  }
738
946
  async function getTransaction(input) {
739
947
  if (!input.id && !input.reference) {
740
- throw new Error("id or reference is required");
948
+ throwValidation("id or reference is required");
741
949
  }
742
950
  const result = await transport.send({
743
951
  action: SDK_ACTIONS.getTransaction,
@@ -765,14 +973,14 @@ function createSdkInstance(config) {
765
973
  validateNonEmpty(input.description, "description");
766
974
  if (input.items) {
767
975
  if (input.items.length > 50) {
768
- throw new Error("items must not exceed 50");
976
+ throwValidation("items must not exceed 50");
769
977
  }
770
978
  for (const item of input.items) {
771
979
  if (!Number.isInteger(item.quantity) || item.quantity <= 0) {
772
- throw new Error("item quantity must be a positive integer");
980
+ throwValidation("item quantity must be a positive integer");
773
981
  }
774
982
  if (!Number.isInteger(item.unitPrice) || item.unitPrice <= 0) {
775
- throw new Error("item unitPrice must be a positive integer");
983
+ throwValidation("item unitPrice must be a positive integer");
776
984
  }
777
985
  }
778
986
  }
@@ -832,6 +1040,7 @@ function createNylonPay(config) {
832
1040
  maxPollIntervalMs: config.maxPollIntervalMs ?? DEFAULT_MAX_POLL_INTERVAL_MS,
833
1041
  maxPollDurationMs: config.maxPollDurationMs ?? DEFAULT_MAX_POLL_DURATION_MS,
834
1042
  maxPollAttempts: config.maxPollAttempts ?? DEFAULT_MAX_POLL_ATTEMPTS,
1043
+ streaming: config.streaming ?? DEFAULT_STREAMING,
835
1044
  fetch: config.fetch ?? globalThis.fetch.bind(globalThis),
836
1045
  hooks: config.hooks
837
1046
  };
@@ -841,6 +1050,7 @@ function createNylonPay(config) {
841
1050
  }
842
1051
 
843
1052
  exports.createNylonPay = createNylonPay;
1053
+ exports.createSdkError = createSdkError;
844
1054
  exports.parseError = parseError;
845
1055
  exports.verifyWebhookSignature = verifyWebhookSignature;
846
1056
  //# sourceMappingURL=index.cjs.map