@nymbalhq/types 1.4.0 → 1.6.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/index.js CHANGED
@@ -19,7 +19,7 @@ var ConfigError = class extends NymbalError {
19
19
  };
20
20
  var ValidationError = class extends NymbalError {
21
21
  constructor(message, options = {}) {
22
- super("validation.failed", message, options);
22
+ super(options.code ?? "validation.failed", message, options);
23
23
  }
24
24
  };
25
25
  var NotFoundError = class extends NymbalError {
@@ -63,6 +63,26 @@ var CheckoutError = class extends NymbalError {
63
63
  super(`checkout.${code}`, message, options);
64
64
  }
65
65
  };
66
+ var CartLimitError = class extends NymbalError {
67
+ constructor(variantId, max, onCart) {
68
+ super("cart.qty_limit", `a cart line holds at most ${max}`, {
69
+ context: { variantId, max, onCart }
70
+ });
71
+ }
72
+ };
73
+ var REVIEW_REFUSAL_MESSAGES = {
74
+ submit_unsupported: "This store does not take reviews on its site.",
75
+ already_reviewed: "You have already reviewed this product.",
76
+ not_a_buyer: "Only a customer who bought this product can review it.",
77
+ rejected: "The review was not accepted."
78
+ };
79
+ var ReviewRefusedError = class extends NymbalError {
80
+ refusal;
81
+ constructor(refusal, message = REVIEW_REFUSAL_MESSAGES[refusal], options = {}) {
82
+ super(`reviews.${refusal}`, message, options);
83
+ this.refusal = refusal;
84
+ }
85
+ };
66
86
  var StatusTransitionError = class extends NymbalError {
67
87
  constructor(entity, fromStatus, toStatus, options = {}) {
68
88
  super(
@@ -150,6 +170,9 @@ var PRODUCT_SORT_LABELS = {
150
170
  "name-desc": "Name: Z to A"
151
171
  };
152
172
 
173
+ // src/domain/cart.ts
174
+ var CART_LINE_MAX_QTY = 99;
175
+
153
176
  // src/domain/order.ts
154
177
  var ORDER_STATUS_TRANSITIONS = {
155
178
  pending: ["confirmed", "cancelled"],
@@ -169,7 +192,12 @@ function assertOrderTransition(from, to) {
169
192
  throw new StatusTransitionError("order", from, to);
170
193
  }
171
194
  }
195
+ function agentSource(name) {
196
+ return `agent:${name}`;
197
+ }
172
198
  var ORDER_KEY_HEADER = "x-nymbal-order-key";
199
+ var ORDER_EMAIL_HEADER = "x-nymbal-order-email";
200
+ var SHOPPER_ATTESTATION_HEADER = "x-nymbal-shopper";
173
201
  function paymentState(order) {
174
202
  if (order.capturedMinor <= 0) return "unpaid";
175
203
  if (order.refundedMinor <= 0) return "paid";
@@ -213,6 +241,7 @@ var EVT_ORDER_REFUNDED = "order.refunded.v1";
213
241
  var EVT_ORDER_PARTIALLY_REFUNDED = "order.partially_refunded.v1";
214
242
  var EVT_ORDER_STATUS_CHANGED = "order.status_changed.v1";
215
243
  var EVT_ORDER_CONFIRMATION_RESEND_REQUESTED = "order.confirmation_resend_requested.v1";
244
+ var EVT_ORDER_DELETED = "order.deleted.v1";
216
245
  var EVT_INVENTORY_CHANGED = "inventory.changed.v1";
217
246
  var EVT_INVENTORY_RESERVED = "inventory.reserved.v1";
218
247
  var EVT_INVENTORY_RELEASED = "inventory.released.v1";
@@ -234,9 +263,17 @@ var EVT_PAYMENT_CAPTURED = "payment.captured.v1";
234
263
  var EVT_PAYMENT_FAILED = "payment.failed.v1";
235
264
  var EVT_PAYMENT_REFUNDED = "payment.refunded.v1";
236
265
  var EVT_ALERT_REQUESTED = "alert.requested.v1";
266
+ var EVT_ALERT_REPEATED = "alert.repeated.v1";
237
267
  var EVT_ALERT_CONFIRMED = "alert.confirmed.v1";
238
268
  var EVT_ALERT_UNSUBSCRIBED = "alert.unsubscribed.v1";
239
269
  var EVT_ALERT_SENT = "alert.sent.v1";
270
+ var EVT_ALERT_CONFIRMATION_SENT = "alert.confirmation_sent.v1";
271
+ var EVT_REVIEW_SUBMITTED = "review.submitted.v1";
272
+ var EVT_PROPOSAL_CREATED = "proposal.created.v1";
273
+ var EVT_PROPOSAL_APPROVED = "proposal.approved.v1";
274
+ var EVT_PROPOSAL_REJECTED = "proposal.rejected.v1";
275
+ var EVT_PROPOSAL_APPLIED = "proposal.applied.v1";
276
+ var EVT_PROPOSAL_FAILED = "proposal.failed.v1";
240
277
  var EVT_ADAPTER_ERROR = "adapter.error.v1";
241
278
  var EVT_SETTINGS_CHANGED = "settings.changed.v1";
242
279
  var STANDARD_EVENT_TYPES = [
@@ -261,6 +298,7 @@ var STANDARD_EVENT_TYPES = [
261
298
  EVT_ORDER_PARTIALLY_REFUNDED,
262
299
  EVT_ORDER_STATUS_CHANGED,
263
300
  EVT_ORDER_CONFIRMATION_RESEND_REQUESTED,
301
+ EVT_ORDER_DELETED,
264
302
  EVT_INVENTORY_CHANGED,
265
303
  EVT_INVENTORY_RESERVED,
266
304
  EVT_INVENTORY_RELEASED,
@@ -281,9 +319,17 @@ var STANDARD_EVENT_TYPES = [
281
319
  EVT_PAYMENT_FAILED,
282
320
  EVT_PAYMENT_REFUNDED,
283
321
  EVT_ALERT_REQUESTED,
322
+ EVT_ALERT_REPEATED,
284
323
  EVT_ALERT_CONFIRMED,
285
324
  EVT_ALERT_UNSUBSCRIBED,
286
325
  EVT_ALERT_SENT,
326
+ EVT_ALERT_CONFIRMATION_SENT,
327
+ EVT_REVIEW_SUBMITTED,
328
+ EVT_PROPOSAL_CREATED,
329
+ EVT_PROPOSAL_APPROVED,
330
+ EVT_PROPOSAL_REJECTED,
331
+ EVT_PROPOSAL_APPLIED,
332
+ EVT_PROPOSAL_FAILED,
287
333
  EVT_ADAPTER_ERROR,
288
334
  EVT_SETTINGS_CHANGED
289
335
  ];
@@ -354,7 +400,7 @@ async function boundedHealthCheck(check, options = {}) {
354
400
  }
355
401
 
356
402
  // src/adapters/document-order.ts
357
- var collator = new Intl.Collator(void 0, { sensitivity: "base" });
403
+ var collator;
358
404
  function rankOf(value) {
359
405
  if (typeof value === "number") return 0;
360
406
  if (typeof value === "string") return 1;
@@ -381,6 +427,7 @@ function compareDocumentValues(a, b) {
381
427
  case 0:
382
428
  return sign(a - b);
383
429
  case 1:
430
+ collator ??= new Intl.Collator(void 0, { sensitivity: "base" });
384
431
  return sign(collator.compare(a, b));
385
432
  case 2:
386
433
  return sign(compareOther(a, b));
@@ -512,6 +559,9 @@ function buildSecurityHeaders(security) {
512
559
  return headers;
513
560
  }
514
561
 
562
+ // src/adapters/commerce.ts
563
+ var SHIPPING_CAPABILITIES = ["rates", "labels", "tracking"];
564
+
515
565
  // src/adapters/webhook-delivery.ts
516
566
  var VERSIONED_EVENT = /^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*\.v[1-9][0-9]*$/;
517
567
  function isPlainObject(value) {
@@ -545,6 +595,82 @@ function webhookDeliveryProblem(providerName, producesEvents, delivery) {
545
595
  return null;
546
596
  }
547
597
 
598
+ // src/adapters/email-feedback.ts
599
+ function emailFeedbackType(providerName, what) {
600
+ return `${providerName}.${what}.v1`;
601
+ }
602
+ function payloadOf(event) {
603
+ const payload = event?.payload;
604
+ return typeof payload === "object" && payload !== null ? payload : {};
605
+ }
606
+ function emailFeedbackProblem(providerName, delivery) {
607
+ const bounced = emailFeedbackType(providerName, "bounced");
608
+ const complained = emailFeedbackType(providerName, "complained");
609
+ if (!Array.isArray(delivery.events)) return null;
610
+ for (const event of delivery.events) {
611
+ const type = event?.type;
612
+ if (type !== bounced && type !== complained) continue;
613
+ const payload = payloadOf(event);
614
+ const { recipient, messageId, occurredAt } = payload;
615
+ if (typeof recipient !== "string" || recipient.trim() === "") {
616
+ return { type, reason: "the recipient is missing or empty" };
617
+ }
618
+ if (type === bounced && payload["bounce"] !== "hard" && payload["bounce"] !== "soft") {
619
+ return { type, reason: "the bounce is neither 'hard' nor 'soft'" };
620
+ }
621
+ if (messageId !== null && typeof messageId !== "string") {
622
+ return { type, reason: "the messageId is neither a string nor null" };
623
+ }
624
+ if (typeof occurredAt !== "string" || Number.isNaN(Date.parse(occurredAt))) {
625
+ return { type, reason: "occurredAt is not a readable date" };
626
+ }
627
+ }
628
+ return null;
629
+ }
630
+
631
+ // src/adapters/shipping-tracking.ts
632
+ var SHIPPING_TRACKING_STATUSES = [
633
+ "pre_transit",
634
+ "in_transit",
635
+ "out_for_delivery",
636
+ "delivered",
637
+ "exception",
638
+ "returned",
639
+ "unknown"
640
+ ];
641
+ function shippingTrackingType(providerName) {
642
+ return `${providerName}.tracking.v1`;
643
+ }
644
+ function normaliseTrackingNumber(value) {
645
+ return value.replace(/\s+/g, "").toUpperCase();
646
+ }
647
+ function payloadOf2(event) {
648
+ const payload = event?.payload;
649
+ return typeof payload === "object" && payload !== null ? payload : {};
650
+ }
651
+ function shippingTrackingProblem(providerName, delivery) {
652
+ const tracking = shippingTrackingType(providerName);
653
+ if (!Array.isArray(delivery.events)) return null;
654
+ for (const event of delivery.events) {
655
+ const type = event?.type;
656
+ if (type !== tracking) continue;
657
+ const { trackingNumber, carrier, status, occurredAt } = payloadOf2(event);
658
+ if (typeof trackingNumber !== "string" || normaliseTrackingNumber(trackingNumber) === "") {
659
+ return { type, reason: "the trackingNumber is missing or empty" };
660
+ }
661
+ if (carrier !== null && typeof carrier !== "string") {
662
+ return { type, reason: "the carrier is neither a string nor null" };
663
+ }
664
+ if (!SHIPPING_TRACKING_STATUSES.includes(status)) {
665
+ return { type, reason: "the status is not one of the tracking statuses" };
666
+ }
667
+ if (typeof occurredAt !== "string" || Number.isNaN(Date.parse(occurredAt))) {
668
+ return { type, reason: "occurredAt is not a readable date" };
669
+ }
670
+ }
671
+ return null;
672
+ }
673
+
548
674
  // src/adapters/email-templates.ts
549
675
  function render(template, vars) {
550
676
  return template.replace(/\{\{\s*([\w.]+)\s*\}\}/g, (_, path) => {
@@ -572,10 +698,14 @@ We'll let you know when it ships.{{orderLink}}`
572
698
  },
573
699
  "order-shipped": {
574
700
  subject: "Order {{orderNumber}} is on its way",
701
+ // `trackingLink` and `orderLink` are whole paragraphs, absent for an unknown carrier or a store with no URL.
575
702
  body: `Hi {{customerName}},
576
703
 
577
704
  Your order {{orderNumber}} has shipped via {{carrier}}.
578
- Tracking: {{trackingNumber}}`
705
+ Tracking: {{trackingNumber}}{{trackingLink}}
706
+
707
+ In this parcel:
708
+ {{itemsList}}{{orderLink}}`
579
709
  },
580
710
  welcome: {
581
711
  subject: "Welcome to {{storeName}}",
@@ -670,16 +800,28 @@ var IDEMPOTENT_REPLAYED_HEADER = "idempotent-replayed";
670
800
  function isValidIdempotencyKey(value) {
671
801
  return /^[A-Za-z0-9._:-]{1,128}$/.test(value);
672
802
  }
803
+
804
+ // src/proposals.ts
805
+ var PREVIEWED_COMMANDS = [
806
+ "product.update",
807
+ "inventory.adjust",
808
+ "order.refund",
809
+ "order.cancel"
810
+ ];
673
811
  export {
674
812
  AdapterError,
675
813
  AuthError,
814
+ CART_LINE_MAX_QTY,
676
815
  CORRELATION_ID_HEADER,
816
+ CartLimitError,
677
817
  CheckoutError,
678
818
  ConfigError,
679
819
  DEFAULT_PRODUCT_SORT,
680
820
  DEFAULT_QUERY_LIMIT,
681
821
  EVT_ADAPTER_ERROR,
822
+ EVT_ALERT_CONFIRMATION_SENT,
682
823
  EVT_ALERT_CONFIRMED,
824
+ EVT_ALERT_REPEATED,
683
825
  EVT_ALERT_REQUESTED,
684
826
  EVT_ALERT_SENT,
685
827
  EVT_ALERT_UNSUBSCRIBED,
@@ -703,6 +845,7 @@ export {
703
845
  EVT_INVENTORY_RESERVED,
704
846
  EVT_ORDER_CANCELLED,
705
847
  EVT_ORDER_CONFIRMATION_RESEND_REQUESTED,
848
+ EVT_ORDER_DELETED,
706
849
  EVT_ORDER_DELIVERED,
707
850
  EVT_ORDER_FULFILLED,
708
851
  EVT_ORDER_PAID,
@@ -723,6 +866,12 @@ export {
723
866
  EVT_PRODUCT_UPDATED,
724
867
  EVT_PRODUCT_VARIANT_PRICE_CHANGED,
725
868
  EVT_PRODUCT_VIEWED,
869
+ EVT_PROPOSAL_APPLIED,
870
+ EVT_PROPOSAL_APPROVED,
871
+ EVT_PROPOSAL_CREATED,
872
+ EVT_PROPOSAL_FAILED,
873
+ EVT_PROPOSAL_REJECTED,
874
+ EVT_REVIEW_SUBMITTED,
726
875
  EVT_SETTINGS_CHANGED,
727
876
  EVT_VISITOR_MERGED,
728
877
  HEALTH_CHECK_TIMEOUT_MS,
@@ -731,19 +880,26 @@ export {
731
880
  IDEMPOTENT_REPLAYED_HEADER,
732
881
  NotFoundError,
733
882
  NymbalError,
883
+ ORDER_EMAIL_HEADER,
734
884
  ORDER_KEY_HEADER,
735
885
  ORDER_STATUS_TRANSITIONS,
736
886
  ORDER_TIMELINE_KINDS,
737
887
  OrderConflictError,
888
+ PREVIEWED_COMMANDS,
738
889
  PRODUCT_SORT_KEYS,
739
890
  PRODUCT_SORT_LABELS,
740
891
  PaymentError,
892
+ ReviewRefusedError,
741
893
  SECRET_MIN_LENGTH,
894
+ SHIPPING_CAPABILITIES,
895
+ SHIPPING_TRACKING_STATUSES,
896
+ SHOPPER_ATTESTATION_HEADER,
742
897
  STANDARD_EVENT_TYPES,
743
898
  STOCK_LABELS,
744
899
  StatusTransitionError,
745
900
  ValidationError,
746
901
  add,
902
+ agentSource,
747
903
  allocate,
748
904
  assertMinorUnits,
749
905
  assertOrderTransition,
@@ -754,6 +910,8 @@ export {
754
910
  connectionSecrets,
755
911
  displayUrl,
756
912
  documentValueAt,
913
+ emailFeedbackProblem,
914
+ emailFeedbackType,
757
915
  errorText,
758
916
  handlerFailedError,
759
917
  isValidCorrelationId,
@@ -761,10 +919,13 @@ export {
761
919
  matchesSortKey,
762
920
  money,
763
921
  mulBy,
922
+ normaliseTrackingNumber,
764
923
  paymentState,
765
924
  queryDocuments,
766
925
  refundableMinor,
767
926
  renderEmailTemplate,
927
+ shippingTrackingProblem,
928
+ shippingTrackingType,
768
929
  stockLevel,
769
930
  sub,
770
931
  sumMoney,