@openlup/platform 0.18.0 → 0.20.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.
Files changed (105) hide show
  1. package/CHANGELOG.md +146 -0
  2. package/README.md +34 -11
  3. package/dist/catalog/adminBundleReadHandler.d.ts +2 -1
  4. package/dist/catalog/adminBundleReadHandler.js +4 -4
  5. package/dist/catalog/bundleCatalogReadPort.d.ts +8 -1
  6. package/dist/catalog/bundleCatalogStore.d.ts +3 -1
  7. package/dist/catalog/bundleCatalogStore.js +5 -1
  8. package/dist/catalog/bundlePriceSelection.d.ts +13 -0
  9. package/dist/catalog/bundlePriceSelection.js +26 -0
  10. package/dist/catalog/contracts/bundleRead.d.ts +18 -0
  11. package/dist/catalog/contracts/draft.js +1 -1
  12. package/dist/catalog/effectiveBundleCatalogQueries.d.ts +8 -0
  13. package/dist/catalog/effectiveBundleCatalogQueries.js +79 -0
  14. package/dist/catalog/effectiveBundleCatalogStore.d.ts +4 -0
  15. package/dist/catalog/effectiveBundleCatalogStore.js +156 -0
  16. package/dist/catalog/index.d.ts +1 -0
  17. package/dist/catalog/index.js +1 -0
  18. package/dist/composition/index.d.ts +2 -0
  19. package/dist/composition/index.js +2 -0
  20. package/dist/composition/inventoryContribution.d.ts +16 -0
  21. package/dist/composition/inventoryContribution.js +17 -0
  22. package/dist/composition/shippingContribution.d.ts +16 -0
  23. package/dist/composition/shippingContribution.js +17 -0
  24. package/dist/inventory/bindings.d.ts +35 -0
  25. package/dist/inventory/bindings.js +45 -0
  26. package/dist/inventory/index.d.ts +1 -0
  27. package/dist/inventory/index.js +1 -0
  28. package/dist/pricing/commercePriceAuthority.d.ts +2 -0
  29. package/dist/pricing/commercePriceAuthority.js +61 -0
  30. package/dist/pricing/commerceQuoteHandler.d.ts +7 -3
  31. package/dist/pricing/commerceQuoteHandler.js +16 -4
  32. package/dist/pricing/dbBackedCommerceQuotePort.d.ts +8 -0
  33. package/dist/pricing/dbBackedCommerceQuotePort.js +46 -29
  34. package/dist/pricing/index.d.ts +2 -1
  35. package/dist/pricing/index.js +1 -1
  36. package/dist/pricing/offerPolicySchemas.d.ts +2 -2
  37. package/dist/pricing/offerPricingSchemas.d.ts +2 -2
  38. package/dist/pricing/pricingPolicyToken.d.ts +2 -2
  39. package/dist/pricing/quoteSchemas.d.ts +18 -18
  40. package/dist/pricing/quoteShippingPromotions.d.ts +20 -0
  41. package/dist/pricing/quoteShippingPromotions.js +1 -0
  42. package/dist/promotions/adminPromotionCodesContracts.d.ts +7 -7
  43. package/dist/promotions/commerceQuoteReadPreparation.d.ts +3 -0
  44. package/dist/promotions/commerceQuoteReadPreparation.js +27 -0
  45. package/dist/promotions/index.d.ts +3 -0
  46. package/dist/promotions/index.js +1 -0
  47. package/dist/promotions/promotionQuoteAcceptance.d.ts +5 -0
  48. package/dist/promotions/promotionQuoteAcceptance.js +12 -12
  49. package/dist/promotions/promotionQuoteBinding.d.ts +4 -2
  50. package/dist/promotions/promotionQuoteBinding.js +5 -3
  51. package/dist/promotions/quoteAcquisitionBinding.d.ts +6 -0
  52. package/dist/promotions/quoteAcquisitionBinding.js +1 -0
  53. package/dist/promotions/quoteAutomaticPromotions.d.ts +3 -1
  54. package/dist/promotions/quoteAutomaticPromotions.js +28 -4
  55. package/dist/promotions/quotePromoDiscounts.d.ts +5 -1
  56. package/dist/promotions/quotePromoDiscounts.js +8 -2
  57. package/dist/promotions/quotePromotionV2.d.ts +7 -6
  58. package/dist/promotions/quotePromotionV2.js +15 -3
  59. package/dist/promotions/quoteShippingPolicy.d.ts +23 -0
  60. package/dist/promotions/quoteShippingPolicy.js +47 -0
  61. package/dist/shipping/bindings.d.ts +62 -0
  62. package/dist/shipping/bindings.js +56 -0
  63. package/dist/shipping/contracts/index.d.ts +1 -0
  64. package/dist/shipping/contracts/threshold.d.ts +22 -0
  65. package/dist/shipping/contracts/threshold.js +1 -0
  66. package/dist/shipping/index.d.ts +17 -0
  67. package/dist/shipping/index.js +7 -0
  68. package/dist/shipping/thresholdPolicy.d.ts +42 -0
  69. package/dist/shipping/thresholdPolicy.js +60 -0
  70. package/package.json +2 -2
  71. package/release-gates.json +136 -134
  72. package/sql/20261021200000_shipping_flat_rate_seed.sql +1 -0
  73. package/src/catalog/adminBundleReadHandler.ts +6 -4
  74. package/src/catalog/bundleCatalogReadPort.ts +6 -2
  75. package/src/catalog/bundleCatalogStore.ts +5 -0
  76. package/src/catalog/bundlePriceSelection.ts +25 -0
  77. package/src/catalog/contracts/bundleRead.ts +2 -2
  78. package/src/catalog/contracts/draft.ts +1 -1
  79. package/src/catalog/effectiveBundleCatalogQueries.ts +80 -0
  80. package/src/catalog/effectiveBundleCatalogStore.ts +208 -0
  81. package/src/catalog/index.ts +1 -0
  82. package/src/composition/index.ts +2 -0
  83. package/src/composition/inventoryContribution.ts +24 -0
  84. package/src/composition/shippingContribution.ts +24 -0
  85. package/src/inventory/bindings.ts +61 -0
  86. package/src/inventory/index.ts +1 -0
  87. package/src/pricing/commercePriceAuthority.ts +65 -0
  88. package/src/pricing/commerceQuoteHandler.ts +20 -5
  89. package/src/pricing/dbBackedCommerceQuotePort.ts +50 -30
  90. package/src/pricing/index.ts +2 -1
  91. package/src/pricing/quoteShippingPromotions.ts +21 -0
  92. package/src/promotions/commerceQuoteReadPreparation.ts +29 -0
  93. package/src/promotions/index.ts +3 -0
  94. package/src/promotions/promotionQuoteAcceptance.ts +17 -10
  95. package/src/promotions/promotionQuoteBinding.ts +7 -2
  96. package/src/promotions/quoteAcquisitionBinding.ts +7 -0
  97. package/src/promotions/quoteAutomaticPromotions.ts +30 -4
  98. package/src/promotions/quotePromoDiscounts.ts +11 -2
  99. package/src/promotions/quotePromotionV2.ts +20 -4
  100. package/src/promotions/quoteShippingPolicy.ts +61 -0
  101. package/src/shipping/bindings.ts +90 -0
  102. package/src/shipping/contracts/index.ts +1 -0
  103. package/src/shipping/contracts/threshold.ts +22 -0
  104. package/src/shipping/index.ts +9 -0
  105. package/src/shipping/thresholdPolicy.ts +65 -0
@@ -1,7 +1,7 @@
1
1
  export * from "./contracts/index.js";
2
2
  export { CommercePriceAuthorityError } from "./ports.js";
3
3
  export type { CommercePriceAuthorityPort } from "./ports.js";
4
- export { createCommercePriceAuthorityPort, selectUniqueEffectivePriceList } from "./commercePriceAuthority.js";
4
+ export { createCommercePriceAuthorityPort, createConcurrentCommercePriceAuthorityPort, selectUniqueEffectivePriceList } from "./commercePriceAuthority.js";
5
5
  export type { CommercePriceAuthorityReadPort, TimedAuthorityValue } from "./commercePriceAuthority.js";
6
6
  export { subscriptionPricePolicySemanticsSchema, subscriptionPricePolicyCanonicalInputSchema, subscriptionPricePolicyRevisionSchema, SubscriptionPricePolicyDigestError, canonicalSubscriptionPricePolicyTuple, digestSubscriptionPricePolicy, assertSubscriptionPricePolicyDigest, deriveSubscriptionUnitPrice } from "./subscriptionPricePolicy.js";
7
7
  export { offerPolicyVersionSchema, promotionEngineVersionSchema, pricingPolicyRequestSchema, pricingPolicySnapshotSchema } from "./offerPolicySchemas.js";
@@ -49,3 +49,4 @@ export * from "./acquisitionOfferPricingSchemas.js";
49
49
  export * from "./acquisitionOfferPricing.js";
50
50
  export * from "./acquisitionOfferPricingHandler.js";
51
51
  export * from "./acquisitionReadiness.js";
52
+ export type { QuoteShippingPromotionInput, QuoteShippingPromotionResult } from "./quoteShippingPromotions.js";
@@ -1,6 +1,6 @@
1
1
  export * from "./contracts/index.js";
2
2
  export { CommercePriceAuthorityError } from "./ports.js";
3
- export { createCommercePriceAuthorityPort, selectUniqueEffectivePriceList } from "./commercePriceAuthority.js";
3
+ export { createCommercePriceAuthorityPort, createConcurrentCommercePriceAuthorityPort, selectUniqueEffectivePriceList } from "./commercePriceAuthority.js";
4
4
  export { subscriptionPricePolicySemanticsSchema, subscriptionPricePolicyCanonicalInputSchema, subscriptionPricePolicyRevisionSchema, SubscriptionPricePolicyDigestError, canonicalSubscriptionPricePolicyTuple, digestSubscriptionPricePolicy, assertSubscriptionPricePolicyDigest, deriveSubscriptionUnitPrice } from "./subscriptionPricePolicy.js";
5
5
  export { offerPolicyVersionSchema, promotionEngineVersionSchema, pricingPolicyRequestSchema, pricingPolicySnapshotSchema } from "./offerPolicySchemas.js";
6
6
  export { createPricingPolicyToken, verifyPricingPolicyToken, resolvePricingPolicy } from "./pricingPolicyToken.js";
@@ -4,8 +4,8 @@ export declare const offerPolicyVersionSchema: z.ZodEnum<{
4
4
  "commerce.offer-policy.v2": "commerce.offer-policy.v2";
5
5
  }>;
6
6
  export declare const promotionEngineVersionSchema: z.ZodEnum<{
7
- "promotion-engine.v2": "promotion-engine.v2";
8
7
  "promotion-engine.v1": "promotion-engine.v1";
8
+ "promotion-engine.v2": "promotion-engine.v2";
9
9
  }>;
10
10
  /** Client capability plus an opaque server-signed assignment. Never a version selector. */
11
11
  export declare const pricingPolicyRequestSchema: z.ZodObject<{
@@ -18,8 +18,8 @@ export declare const pricingPolicySnapshotSchema: z.ZodObject<{
18
18
  "commerce.offer-policy.v2": "commerce.offer-policy.v2";
19
19
  }>;
20
20
  promotionEngineVersion: z.ZodEnum<{
21
- "promotion-engine.v2": "promotion-engine.v2";
22
21
  "promotion-engine.v1": "promotion-engine.v1";
22
+ "promotion-engine.v2": "promotion-engine.v2";
23
23
  }>;
24
24
  pricingPolicyToken: z.ZodOptional<z.ZodString>;
25
25
  }, z.core.$strict>;
@@ -99,8 +99,8 @@ export declare function createCommerceOfferPricingSchemas<ScopeV1 extends string
99
99
  "commerce.offer-policy.v2": "commerce.offer-policy.v2";
100
100
  }>;
101
101
  promotionEngineVersion: z.ZodEnum<{
102
- "promotion-engine.v2": "promotion-engine.v2";
103
102
  "promotion-engine.v1": "promotion-engine.v1";
103
+ "promotion-engine.v2": "promotion-engine.v2";
104
104
  }>;
105
105
  pricingPolicyToken: z.ZodOptional<z.ZodString>;
106
106
  }, z.core.$strict>;
@@ -241,8 +241,8 @@ export declare function createCommerceOfferPricingSchemas<ScopeV1 extends string
241
241
  "commerce.offer-policy.v2": "commerce.offer-policy.v2";
242
242
  }>;
243
243
  promotionEngineVersion: z.ZodEnum<{
244
- "promotion-engine.v2": "promotion-engine.v2";
245
244
  "promotion-engine.v1": "promotion-engine.v1";
245
+ "promotion-engine.v2": "promotion-engine.v2";
246
246
  }>;
247
247
  pricingPolicyToken: z.ZodOptional<z.ZodString>;
248
248
  }, z.core.$strict>;
@@ -29,11 +29,11 @@ export declare function resolvePricingPolicy(input: {
29
29
  promotionEngineVersion: PromotionEngineVersion;
30
30
  } | {
31
31
  offerPolicyVersion: "commerce.offer-policy.v1" | "commerce.offer-policy.v2";
32
- promotionEngineVersion: "promotion-engine.v2" | "promotion-engine.v1";
32
+ promotionEngineVersion: "promotion-engine.v1" | "promotion-engine.v2";
33
33
  pricingPolicyToken?: undefined;
34
34
  } | {
35
35
  offerPolicyVersion: "commerce.offer-policy.v1" | "commerce.offer-policy.v2";
36
- promotionEngineVersion: "promotion-engine.v2" | "promotion-engine.v1";
36
+ promotionEngineVersion: "promotion-engine.v1" | "promotion-engine.v2";
37
37
  pricingPolicyToken: string;
38
38
  };
39
39
  export {};
@@ -33,8 +33,8 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
33
33
  }, z.core.$strip>;
34
34
  quotePricingComponentSchema: z.ZodObject<{
35
35
  scope: z.ZodEnum<{
36
- order: "order";
37
36
  line: "line";
37
+ order: "order";
38
38
  }>;
39
39
  componentType: z.ZodEnum<{
40
40
  bundle: "bundle";
@@ -111,8 +111,8 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
111
111
  }, z.core.$strip>;
112
112
  pricingComponents: z.ZodOptional<z.ZodArray<z.ZodObject<{
113
113
  scope: z.ZodEnum<{
114
- order: "order";
115
114
  line: "line";
115
+ order: "order";
116
116
  }>;
117
117
  componentType: z.ZodEnum<{
118
118
  bundle: "bundle";
@@ -162,11 +162,11 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
162
162
  quoteCodeRejectionSchema: z.ZodObject<{
163
163
  code: z.ZodString;
164
164
  reason: z.ZodEnum<{
165
- expired: "expired";
166
165
  scope_not_applicable: "scope_not_applicable";
167
166
  not_recognized: "not_recognized";
168
167
  already_used: "already_used";
169
168
  not_eligible: "not_eligible";
169
+ expired: "expired";
170
170
  better_price_exists: "better_price_exists";
171
171
  }>;
172
172
  }, z.core.$strict>;
@@ -206,8 +206,8 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
206
206
  }, z.core.$strip>;
207
207
  pricingComponents: z.ZodOptional<z.ZodArray<z.ZodObject<{
208
208
  scope: z.ZodEnum<{
209
- order: "order";
210
209
  line: "line";
210
+ order: "order";
211
211
  }>;
212
212
  componentType: z.ZodEnum<{
213
213
  bundle: "bundle";
@@ -293,11 +293,11 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
293
293
  codeRejections: z.ZodOptional<z.ZodArray<z.ZodObject<{
294
294
  code: z.ZodString;
295
295
  reason: z.ZodEnum<{
296
- expired: "expired";
297
296
  scope_not_applicable: "scope_not_applicable";
298
297
  not_recognized: "not_recognized";
299
298
  already_used: "already_used";
300
299
  not_eligible: "not_eligible";
300
+ expired: "expired";
301
301
  better_price_exists: "better_price_exists";
302
302
  }>;
303
303
  }, z.core.$strict>>>;
@@ -322,8 +322,8 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
322
322
  }, z.core.$strict>], "reason">>>;
323
323
  pricingComponents: z.ZodOptional<z.ZodArray<z.ZodObject<{
324
324
  scope: z.ZodEnum<{
325
- order: "order";
326
325
  line: "line";
326
+ order: "order";
327
327
  }>;
328
328
  componentType: z.ZodEnum<{
329
329
  bundle: "bundle";
@@ -406,8 +406,8 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
406
406
  }, z.core.$strip>;
407
407
  pricingComponents: z.ZodOptional<z.ZodArray<z.ZodObject<{
408
408
  scope: z.ZodEnum<{
409
- order: "order";
410
409
  line: "line";
410
+ order: "order";
411
411
  }>;
412
412
  componentType: z.ZodEnum<{
413
413
  bundle: "bundle";
@@ -493,11 +493,11 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
493
493
  codeRejections: z.ZodOptional<z.ZodArray<z.ZodObject<{
494
494
  code: z.ZodString;
495
495
  reason: z.ZodEnum<{
496
- expired: "expired";
497
496
  scope_not_applicable: "scope_not_applicable";
498
497
  not_recognized: "not_recognized";
499
498
  already_used: "already_used";
500
499
  not_eligible: "not_eligible";
500
+ expired: "expired";
501
501
  better_price_exists: "better_price_exists";
502
502
  }>;
503
503
  }, z.core.$strict>>>;
@@ -522,8 +522,8 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
522
522
  }, z.core.$strict>], "reason">>>;
523
523
  pricingComponents: z.ZodOptional<z.ZodArray<z.ZodObject<{
524
524
  scope: z.ZodEnum<{
525
- order: "order";
526
525
  line: "line";
526
+ order: "order";
527
527
  }>;
528
528
  componentType: z.ZodEnum<{
529
529
  bundle: "bundle";
@@ -607,8 +607,8 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
607
607
  }, z.core.$strip>;
608
608
  pricingComponents: z.ZodOptional<z.ZodArray<z.ZodObject<{
609
609
  scope: z.ZodEnum<{
610
- order: "order";
611
610
  line: "line";
611
+ order: "order";
612
612
  }>;
613
613
  componentType: z.ZodEnum<{
614
614
  bundle: "bundle";
@@ -694,11 +694,11 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
694
694
  codeRejections: z.ZodOptional<z.ZodArray<z.ZodObject<{
695
695
  code: z.ZodString;
696
696
  reason: z.ZodEnum<{
697
- expired: "expired";
698
697
  scope_not_applicable: "scope_not_applicable";
699
698
  not_recognized: "not_recognized";
700
699
  already_used: "already_used";
701
700
  not_eligible: "not_eligible";
701
+ expired: "expired";
702
702
  better_price_exists: "better_price_exists";
703
703
  }>;
704
704
  }, z.core.$strict>>>;
@@ -723,8 +723,8 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
723
723
  }, z.core.$strict>], "reason">>>;
724
724
  pricingComponents: z.ZodOptional<z.ZodArray<z.ZodObject<{
725
725
  scope: z.ZodEnum<{
726
- order: "order";
727
726
  line: "line";
727
+ order: "order";
728
728
  }>;
729
729
  componentType: z.ZodEnum<{
730
730
  bundle: "bundle";
@@ -809,8 +809,8 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
809
809
  }, z.core.$strip>;
810
810
  pricingComponents: z.ZodOptional<z.ZodArray<z.ZodObject<{
811
811
  scope: z.ZodEnum<{
812
- order: "order";
813
812
  line: "line";
813
+ order: "order";
814
814
  }>;
815
815
  componentType: z.ZodEnum<{
816
816
  bundle: "bundle";
@@ -896,11 +896,11 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
896
896
  codeRejections: z.ZodOptional<z.ZodArray<z.ZodObject<{
897
897
  code: z.ZodString;
898
898
  reason: z.ZodEnum<{
899
- expired: "expired";
900
899
  scope_not_applicable: "scope_not_applicable";
901
900
  not_recognized: "not_recognized";
902
901
  already_used: "already_used";
903
902
  not_eligible: "not_eligible";
903
+ expired: "expired";
904
904
  better_price_exists: "better_price_exists";
905
905
  }>;
906
906
  }, z.core.$strict>>>;
@@ -925,8 +925,8 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
925
925
  }, z.core.$strict>], "reason">>>;
926
926
  pricingComponents: z.ZodOptional<z.ZodArray<z.ZodObject<{
927
927
  scope: z.ZodEnum<{
928
- order: "order";
929
928
  line: "line";
929
+ order: "order";
930
930
  }>;
931
931
  componentType: z.ZodEnum<{
932
932
  bundle: "bundle";
@@ -1010,8 +1010,8 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
1010
1010
  }, z.core.$strip>;
1011
1011
  pricingComponents: z.ZodOptional<z.ZodArray<z.ZodObject<{
1012
1012
  scope: z.ZodEnum<{
1013
- order: "order";
1014
1013
  line: "line";
1014
+ order: "order";
1015
1015
  }>;
1016
1016
  componentType: z.ZodEnum<{
1017
1017
  bundle: "bundle";
@@ -1097,11 +1097,11 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
1097
1097
  codeRejections: z.ZodOptional<z.ZodArray<z.ZodObject<{
1098
1098
  code: z.ZodString;
1099
1099
  reason: z.ZodEnum<{
1100
- expired: "expired";
1101
1100
  scope_not_applicable: "scope_not_applicable";
1102
1101
  not_recognized: "not_recognized";
1103
1102
  already_used: "already_used";
1104
1103
  not_eligible: "not_eligible";
1104
+ expired: "expired";
1105
1105
  better_price_exists: "better_price_exists";
1106
1106
  }>;
1107
1107
  }, z.core.$strict>>>;
@@ -1126,8 +1126,8 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
1126
1126
  }, z.core.$strict>], "reason">>>;
1127
1127
  pricingComponents: z.ZodOptional<z.ZodArray<z.ZodObject<{
1128
1128
  scope: z.ZodEnum<{
1129
- order: "order";
1130
1129
  line: "line";
1130
+ order: "order";
1131
1131
  }>;
1132
1132
  componentType: z.ZodEnum<{
1133
1133
  bundle: "bundle";
@@ -0,0 +1,20 @@
1
+ import type { ShippingTerms } from "../shipping/index.js";
2
+ import type { ResolvedAcquisitionTerms } from "./contracts/acquisitionTerms.js";
3
+ import type { CreateQuoteRequest } from "./contracts/quote.js";
4
+ import type { CreateQuoteOptions } from "./quotePorts.js";
5
+ import type { QuotePromotionResult } from "./dbBackedCommerceQuotePort.js";
6
+ export interface QuoteShippingPromotionInput<Request extends CreateQuoteRequest> {
7
+ request: Request;
8
+ options?: CreateQuoteOptions;
9
+ regionCode: string;
10
+ currency: string;
11
+ referenceProductMinor: number;
12
+ subtotalGrossMinor: number;
13
+ atTime: string;
14
+ }
15
+ export interface QuoteShippingPromotionResult extends QuotePromotionResult {
16
+ shippingGrossMinor: number;
17
+ hasShipping: boolean;
18
+ shippingTerms?: ShippingTerms;
19
+ acquisitionTerms?: ResolvedAcquisitionTerms;
20
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -107,8 +107,8 @@ export declare const promotionCodePreviewRequestSchema: z.ZodObject<{
107
107
  subscription_initial: "subscription_initial";
108
108
  }>>;
109
109
  promotionEngineVersion: z.ZodDefault<z.ZodEnum<{
110
- "promotion-engine.v2": "promotion-engine.v2";
111
110
  "promotion-engine.v1": "promotion-engine.v1";
111
+ "promotion-engine.v2": "promotion-engine.v2";
112
112
  }>>;
113
113
  minimumReferenceMinor: z.ZodDefault<z.ZodNumber>;
114
114
  context: z.ZodObject<{
@@ -150,8 +150,8 @@ export declare const promotionCodePreviewResultSchema: z.ZodObject<{
150
150
  export declare const promotionCodePreviewResponseSchema: z.ZodObject<{
151
151
  contractVersion: z.ZodLiteral<"commerce.v0">;
152
152
  promotionEngineVersion: z.ZodEnum<{
153
- "promotion-engine.v2": "promotion-engine.v2";
154
153
  "promotion-engine.v1": "promotion-engine.v1";
154
+ "promotion-engine.v2": "promotion-engine.v2";
155
155
  }>;
156
156
  previewProof: z.ZodString;
157
157
  results: z.ZodArray<z.ZodObject<{
@@ -292,8 +292,8 @@ export declare const promotionCodesListRequestSchema: z.ZodObject<{
292
292
  archived: "archived";
293
293
  all: "all";
294
294
  expired: "expired";
295
- paused: "paused";
296
295
  scheduled: "scheduled";
296
+ paused: "paused";
297
297
  expired_or_archived: "expired_or_archived";
298
298
  }>>;
299
299
  scope: z.ZodDefault<z.ZodEnum<{
@@ -358,8 +358,8 @@ export declare const promotionCodeSummarySchema: z.ZodObject<{
358
358
  validationState: z.ZodLiteral<"unsupported_legacy_value">;
359
359
  }, z.core.$strict>]>>;
360
360
  promotionEngineVersion: z.ZodEnum<{
361
- "promotion-engine.v2": "promotion-engine.v2";
362
361
  "promotion-engine.v1": "promotion-engine.v1";
362
+ "promotion-engine.v2": "promotion-engine.v2";
363
363
  }>;
364
364
  scopes: z.ZodArray<z.ZodEnum<{
365
365
  one_time: "one_time";
@@ -376,8 +376,8 @@ export declare const promotionCodeSummarySchema: z.ZodObject<{
376
376
  active: "active";
377
377
  archived: "archived";
378
378
  expired: "expired";
379
- paused: "paused";
380
379
  scheduled: "scheduled";
380
+ paused: "paused";
381
381
  }>;
382
382
  validFrom: z.ZodString;
383
383
  validTo: z.ZodNullable<z.ZodString>;
@@ -444,8 +444,8 @@ export declare const promotionCodesListResponseSchema: z.ZodObject<{
444
444
  validationState: z.ZodLiteral<"unsupported_legacy_value">;
445
445
  }, z.core.$strict>]>>;
446
446
  promotionEngineVersion: z.ZodEnum<{
447
- "promotion-engine.v2": "promotion-engine.v2";
448
447
  "promotion-engine.v1": "promotion-engine.v1";
448
+ "promotion-engine.v2": "promotion-engine.v2";
449
449
  }>;
450
450
  scopes: z.ZodArray<z.ZodEnum<{
451
451
  one_time: "one_time";
@@ -462,8 +462,8 @@ export declare const promotionCodesListResponseSchema: z.ZodObject<{
462
462
  active: "active";
463
463
  archived: "archived";
464
464
  expired: "expired";
465
- paused: "paused";
466
465
  scheduled: "scheduled";
466
+ paused: "paused";
467
467
  }>;
468
468
  validFrom: z.ZodString;
469
469
  validTo: z.ZodNullable<z.ZodString>;
@@ -0,0 +1,3 @@
1
+ import type { CommercePromoDataPort } from "./promoDataPort.js";
2
+ /** Allocate inside one quote resolution; clone each evaluator's mutable raw view. */
3
+ export declare function memoizeCommercePromoReads(inner: CommercePromoDataPort): CommercePromoDataPort;
@@ -0,0 +1,27 @@
1
+ /** Allocate inside one quote resolution; clone each evaluator's mutable raw view. */
2
+ export function memoizeCommercePromoReads(inner) {
3
+ const definitions = memoize(() => inner.listActivePromotions());
4
+ const paid = memoize((id) => inner.countPaidOrders(id));
5
+ const modes = memoize((id) => inner.countPaidOrdersByMode(id));
6
+ const redemptions = memoize((id) => inner.redemptionCounts(id));
7
+ const device = memoize((id) => inner.deviceFirstOrderRedeemed(id));
8
+ return {
9
+ listActivePromotions: async () => structuredClone(await definitions()),
10
+ countPaidOrders: paid,
11
+ countPaidOrdersByMode: async (id) => ({ ...await modes(id) }),
12
+ redemptionCounts: async (id) => structuredClone(await redemptions(id)),
13
+ deviceFirstOrderRedeemed: device,
14
+ };
15
+ }
16
+ function memoize(read) {
17
+ const values = new Map();
18
+ return (...args) => {
19
+ const key = JSON.stringify(args);
20
+ let value = values.get(key);
21
+ if (!value) {
22
+ value = Promise.resolve().then(() => read(...args));
23
+ values.set(key, value);
24
+ }
25
+ return value;
26
+ };
27
+ }
@@ -20,3 +20,6 @@ export * from "./promotionClaimSweepPort.js";
20
20
  export * from "./promotionClaims.js";
21
21
  export * from "./sql.js";
22
22
  export type { PromotionStatus, AdminPromotion, AdminPromotionV2Mirror, AdminPromotionSemanticBenefit, AdminPromotionsListResponse, AdminPromotionUpdatePayload, AdminPromotionUpdateRequest, AdminPromotionUpdateResponse } from "./contracts/index.js";
23
+ export type { QuoteAcquisitionBinding } from "./quoteAcquisitionBinding.js";
24
+ export { createQuoteShippingPromotions } from "./quoteShippingPolicy.js";
25
+ export type { QuoteShippingPromotionsDeps } from "./quoteShippingPolicy.js";
@@ -19,3 +19,4 @@ export * from "./adminPromotionCodesDataPort.js";
19
19
  export * from "./promotionClaimSweepPort.js";
20
20
  export * from "./promotionClaims.js";
21
21
  export * from "./sql.js";
22
+ export { createQuoteShippingPromotions } from "./quoteShippingPolicy.js";
@@ -1,3 +1,4 @@
1
+ import type { ShippingTerms } from "../shipping/index.js";
1
2
  import type { ResolvedAcquisitionTerms } from "../pricing/index.js";
2
3
  import type { CommerceQuote } from "../pricing/index.js";
3
4
  import type { PromotionAcceptanceTelemetry } from "./promotionAcceptanceTelemetry.js";
@@ -25,6 +26,7 @@ export declare function createPromotionQuoteAcceptance<Request extends {
25
26
  request: Request;
26
27
  quote: CommerceQuote;
27
28
  acquisitionTerms?: ResolvedAcquisitionTerms;
29
+ shippingTerms?: ShippingTerms;
28
30
  keyring: PromotionAcceptanceKeyring;
29
31
  now?: Date;
30
32
  }) => string | null;
@@ -32,6 +34,7 @@ export declare function createPromotionQuoteAcceptance<Request extends {
32
34
  request: Request;
33
35
  quote: CommerceQuote;
34
36
  acquisitionTerms?: ResolvedAcquisitionTerms;
37
+ shippingTerms?: ShippingTerms;
35
38
  keyring: PromotionAcceptanceKeyring;
36
39
  now?: Date;
37
40
  }) => {
@@ -43,6 +46,7 @@ export declare function createPromotionQuoteAcceptance<Request extends {
43
46
  request: Request;
44
47
  quote: CommerceQuote;
45
48
  acquisitionTerms?: ResolvedAcquisitionTerms;
49
+ shippingTerms?: ShippingTerms;
46
50
  expectedTotal: {
47
51
  amountMinor: number;
48
52
  currency: string;
@@ -55,6 +59,7 @@ export declare function createPromotionQuoteAcceptance<Request extends {
55
59
  request: Request;
56
60
  quote: CommerceQuote;
57
61
  acquisitionTerms?: ResolvedAcquisitionTerms;
62
+ shippingTerms?: ShippingTerms;
58
63
  expectedTotal: {
59
64
  amountMinor: number;
60
65
  currency: string;
@@ -25,7 +25,7 @@ export function createPromotionQuoteAcceptance({ projectRequest }) {
25
25
  }
26
26
  function issuePromotionQuoteAcceptanceWithOutcome(input) {
27
27
  const evidence = v2Discounts(input.quote);
28
- if (evidence.length === 0 && !input.acquisitionTerms)
28
+ if (evidence.length === 0 && !input.acquisitionTerms && !input.shippingTerms)
29
29
  return {
30
30
  token: null,
31
31
  diagnostic: rejected("no_v2_adjustment"),
@@ -47,8 +47,8 @@ export function createPromotionQuoteAcceptance({ projectRequest }) {
47
47
  engine: "promotion-engine.v2",
48
48
  requestBinding: requestBinding(input.request, input.keyring.current),
49
49
  codeBinding: codeBinding(input.request.promoCodes, input.keyring.current),
50
- quoteBinding: quoteBinding(input.quote, input.keyring.current, input.acquisitionTerms),
51
- sectionBindings: sectionBindings(input.quote, input.keyring.current, input.acquisitionTerms),
50
+ quoteBinding: quoteBinding(input.quote, input.keyring.current, input.acquisitionTerms, input.shippingTerms),
51
+ sectionBindings: sectionBindings(input.quote, input.keyring.current, input.acquisitionTerms, input.shippingTerms),
52
52
  totalMinor: input.quote.totalGross.amountMinor,
53
53
  currency: input.quote.totalGross.currency,
54
54
  });
@@ -78,8 +78,8 @@ export function createPromotionQuoteAcceptance({ projectRequest }) {
78
78
  if (payload.codeBinding !== codeBinding(input.request.promoCodes, secret)) {
79
79
  return { accepted: false, reason: "code_mismatch", ...base };
80
80
  }
81
- if (payload.quoteBinding !== quoteBinding(input.quote, secret, input.acquisitionTerms)) {
82
- const mismatchField = namedMismatchField(payload.sectionBindings, input.quote, secret, input.acquisitionTerms);
81
+ if (payload.quoteBinding !== quoteBinding(input.quote, secret, input.acquisitionTerms, input.shippingTerms)) {
82
+ const mismatchField = namedMismatchField(payload.sectionBindings, input.quote, secret, input.acquisitionTerms, input.shippingTerms);
83
83
  return { accepted: false, reason: "quote_mismatch", mismatchField, ...base };
84
84
  }
85
85
  if (payload.totalMinor !== input.expectedTotal.amountMinor) {
@@ -88,7 +88,7 @@ export function createPromotionQuoteAcceptance({ projectRequest }) {
88
88
  if (payload.currency !== input.expectedTotal.currency) {
89
89
  return { accepted: false, reason: "currency_mismatch", ...base };
90
90
  }
91
- if (v2Discounts(input.quote).length === 0 && !input.acquisitionTerms) {
91
+ if (v2Discounts(input.quote).length === 0 && !input.acquisitionTerms && !input.shippingTerms) {
92
92
  return { accepted: false, reason: "no_v2_adjustment_in_quote", ...base };
93
93
  }
94
94
  return { accepted: true, reason: "accepted", ...base };
@@ -161,11 +161,11 @@ export function createPromotionQuoteAcceptance({ projectRequest }) {
161
161
  function codeBinding(codes, secret) {
162
162
  return hmac(secret, stableJson([...new Set(codes.map((code) => code.trim().toUpperCase()))].sort()));
163
163
  }
164
- function quoteBinding(quote, secret, acquisitionTerms) {
165
- return hmac(secret, stableJson(projectPromotionQuoteMoney(quote, acquisitionTerms)));
164
+ function quoteBinding(quote, secret, acquisitionTerms, shippingTerms) {
165
+ return hmac(secret, stableJson(projectPromotionQuoteMoney(quote, acquisitionTerms, shippingTerms)));
166
166
  }
167
- function sectionBindings(quote, secret, acquisitionTerms) {
168
- const sections = promotionQuoteMoneySections(quote, acquisitionTerms);
167
+ function sectionBindings(quote, secret, acquisitionTerms, shippingTerms) {
168
+ const sections = promotionQuoteMoneySections(quote, acquisitionTerms, shippingTerms);
169
169
  return Object.fromEntries(PROMOTION_QUOTE_MONEY_SECTIONS
170
170
  .map((section) => [section, hmac(secret, stableJson(sections[section]))]));
171
171
  }
@@ -173,10 +173,10 @@ export function createPromotionQuoteAcceptance({ projectRequest }) {
173
173
  * The verifier holds only the issuer's HMACs, never the issued quote, so the
174
174
  * differing section is found by comparing per-section hashes in a fixed order.
175
175
  */
176
- function namedMismatchField(issued, quote, secret, acquisitionTerms) {
176
+ function namedMismatchField(issued, quote, secret, acquisitionTerms, shippingTerms) {
177
177
  if (!issued)
178
178
  return "other";
179
- const verified = sectionBindings(quote, secret, acquisitionTerms);
179
+ const verified = sectionBindings(quote, secret, acquisitionTerms, shippingTerms);
180
180
  return PROMOTION_QUOTE_MONEY_SECTIONS
181
181
  .find((section) => issued[section] !== verified[section]) ?? "other";
182
182
  }
@@ -1,3 +1,4 @@
1
+ import type { ShippingTerms } from "../shipping/index.js";
1
2
  import { type ResolvedAcquisitionTerms } from "../pricing/index.js";
2
3
  import type { CommerceMoney, CommerceQuote, CommerceQuoteDiscount } from "../pricing/index.js";
3
4
  /**
@@ -43,6 +44,7 @@ export interface PromotionQuoteMoneyDiscount {
43
44
  }
44
45
  export interface PromotionQuoteMoneyProjection {
45
46
  acquisitionTerms?: ResolvedAcquisitionTerms;
47
+ shippingTerms?: ShippingTerms;
46
48
  currency: string;
47
49
  totalGross: CommerceMoney;
48
50
  netTotal: CommerceMoney;
@@ -64,8 +66,8 @@ export type PromotionQuoteMismatchField = (typeof PROMOTION_QUOTE_MISMATCH_FIELD
64
66
  * Discount order is preserved: the promotion engine emits its lanes
65
67
  * deterministically, so a reordering is a real evaluation difference.
66
68
  */
67
- export declare function projectPromotionQuoteMoney(quote: CommerceQuote, acquisitionTerms?: ResolvedAcquisitionTerms): PromotionQuoteMoneyProjection;
69
+ export declare function projectPromotionQuoteMoney(quote: CommerceQuote, acquisitionTerms?: ResolvedAcquisitionTerms, shippingTerms?: ShippingTerms): PromotionQuoteMoneyProjection;
68
70
  /** Same projection, partitioned. The union of these five sections is the whole projection. */
69
- export declare function promotionQuoteMoneySections(quote: CommerceQuote, acquisitionTerms?: ResolvedAcquisitionTerms): Record<PromotionQuoteMoneySection, unknown>;
71
+ export declare function promotionQuoteMoneySections(quote: CommerceQuote, acquisitionTerms?: ResolvedAcquisitionTerms, shippingTerms?: ShippingTerms): Record<PromotionQuoteMoneySection, unknown>;
70
72
  /** Key-sorted, `undefined`-dropping JSON. Every acceptance binding hashes this shape. */
71
73
  export declare function stableJson(value: unknown): string;
@@ -16,7 +16,7 @@ export const PROMOTION_QUOTE_MISMATCH_FIELDS = [
16
16
  * Discount order is preserved: the promotion engine emits its lanes
17
17
  * deterministically, so a reordering is a real evaluation difference.
18
18
  */
19
- export function projectPromotionQuoteMoney(quote, acquisitionTerms) {
19
+ export function projectPromotionQuoteMoney(quote, acquisitionTerms, shippingTerms) {
20
20
  return {
21
21
  ...acquisitionQuoteBinding(acquisitionTerms),
22
22
  currency: quote.currency,
@@ -27,6 +27,7 @@ export function projectPromotionQuoteMoney(quote, acquisitionTerms) {
27
27
  discountTotalGross: quote.discountTotalGross,
28
28
  shippingGross: quote.shippingGross ?? null,
29
29
  shippingDiscountGross: quote.shippingDiscountGross ?? null,
30
+ ...(shippingTerms ? { shippingTerms } : {}),
30
31
  lines: quote.lines.map((line) => ({
31
32
  sku: line.sku,
32
33
  quantity: line.quantity,
@@ -52,14 +53,15 @@ export function projectPromotionQuoteMoney(quote, acquisitionTerms) {
52
53
  };
53
54
  }
54
55
  /** Same projection, partitioned. The union of these five sections is the whole projection. */
55
- export function promotionQuoteMoneySections(quote, acquisitionTerms) {
56
- const money = projectPromotionQuoteMoney(quote, acquisitionTerms);
56
+ export function promotionQuoteMoneySections(quote, acquisitionTerms, shippingTerms) {
57
+ const money = projectPromotionQuoteMoney(quote, acquisitionTerms, shippingTerms);
57
58
  return {
58
59
  lines: money.lines,
59
60
  discounts: money.discounts,
60
61
  shipping: {
61
62
  shippingGross: money.shippingGross,
62
63
  shippingDiscountGross: money.shippingDiscountGross,
64
+ ...(money.shippingTerms ? { shippingTerms: money.shippingTerms } : {}),
63
65
  },
64
66
  totals: {
65
67
  ...acquisitionQuoteBinding(money.acquisitionTerms),
@@ -0,0 +1,6 @@
1
+ import type { PromotionRow } from "@openlup/core/promo";
2
+ import type { AcquisitionFinancialPolicy } from "../pricing/index.js";
3
+ export interface QuoteAcquisitionBinding<Request = unknown> {
4
+ financialPolicy: AcquisitionFinancialPolicy<PromotionRow>;
5
+ capability(request: Request): unknown;
6
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,3 +1,4 @@
1
+ import type { QuoteAcquisitionBinding } from "./quoteAcquisitionBinding.js";
1
2
  import type { CreateQuoteRequest } from "../pricing/index.js";
2
3
  import type { CreateQuoteOptions } from "../pricing/index.js";
3
4
  import type { CommercePromoDataPort } from "./promoDataPort.js";
@@ -5,7 +6,8 @@ import type { createQuotePromoDiscounts, QuoteDiscountResult } from "./quoteProm
5
6
  import type { createQuotePromotionV2 } from "./quotePromotionV2.js";
6
7
  export declare function createQuoteAutomaticPromotions<Request extends CreateQuoteRequest & {
7
8
  visitorId?: string;
8
- }>({ resolveEmailEligibility, evaluateOrderTotalDiscounts, evaluateAutomaticPromotionV2, legacyPromotionDataPort }: {
9
+ }>({ resolveEmailEligibility, evaluateOrderTotalDiscounts, evaluateAutomaticPromotionV2, legacyPromotionDataPort, acquisition }: {
10
+ acquisition?: QuoteAcquisitionBinding<Request>;
9
11
  resolveEmailEligibility(request: Request): boolean;
10
12
  evaluateOrderTotalDiscounts: ReturnType<typeof createQuotePromoDiscounts>["evaluateOrderTotalDiscounts"];
11
13
  evaluateAutomaticPromotionV2: ReturnType<typeof createQuotePromotionV2>["evaluateAutomaticPromotionV2"];