@cimplify/sdk 0.44.12 → 0.44.16

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 (99) hide show
  1. package/dist/advanced.d.mts +1 -1
  2. package/dist/advanced.d.ts +1 -1
  3. package/dist/advanced.js +21 -21
  4. package/dist/advanced.mjs +2 -2
  5. package/dist/{chunk-6TVQQYQ5.mjs → chunk-6AZZ6AOF.mjs} +21 -1
  6. package/dist/{chunk-EZK2FXTM.mjs → chunk-7NML226B.mjs} +1 -1
  7. package/dist/{chunk-BAWKQRIB.js → chunk-CFD4J32N.js} +21 -21
  8. package/dist/{chunk-OMAP327U.js → chunk-MBGQP6FZ.js} +62 -61
  9. package/dist/{chunk-V2O5AO7I.js → chunk-MNIKXG7W.js} +26 -0
  10. package/dist/{chunk-3WD6U5RD.mjs → chunk-UXLQ7WXI.mjs} +4 -3
  11. package/dist/chunk-XL4CX75J.js +9370 -0
  12. package/dist/chunk-ZLEFMQC6.mjs +9318 -0
  13. package/dist/cli/commands/deploy.mjs +9 -1
  14. package/dist/{client-lvLvB1_K.d.ts → client-D6ZisKco.d.ts} +7 -0
  15. package/dist/{client-CkGlXZRd.d.mts → client-Dh-bHyhU.d.mts} +7 -0
  16. package/dist/index.d.mts +1 -1
  17. package/dist/index.d.ts +1 -1
  18. package/dist/index.js +106 -106
  19. package/dist/index.mjs +3 -3
  20. package/dist/mock/cli.mjs +172 -23
  21. package/dist/mock/library.d.mts +2 -2
  22. package/dist/mock/library.d.ts +2 -2
  23. package/dist/mock/library.js +172 -23
  24. package/dist/mock/library.mjs +172 -23
  25. package/dist/mock/msw.d.mts +1 -1
  26. package/dist/mock/msw.d.ts +1 -1
  27. package/dist/mock/msw.js +172 -23
  28. package/dist/mock/msw.mjs +172 -23
  29. package/dist/react.d.mts +1 -1
  30. package/dist/react.d.ts +1 -1
  31. package/dist/react.js +80 -80
  32. package/dist/react.mjs +3 -3
  33. package/dist/server-CWpUlLOQ.d.mts +1003 -0
  34. package/dist/server-CWpUlLOQ.d.ts +1003 -0
  35. package/dist/{server-CUbuQxOV.d.ts → server-CnECAQDQ.d.mts} +28 -6
  36. package/dist/{server-CUbuQxOV.d.mts → server-CnECAQDQ.d.ts} +28 -6
  37. package/dist/server.d.mts +2 -2
  38. package/dist/server.d.ts +2 -2
  39. package/dist/server.js +5 -5
  40. package/dist/server.mjs +3 -3
  41. package/dist/testing/suite.d.mts +60 -0
  42. package/dist/testing/suite.d.ts +60 -0
  43. package/dist/testing/suite.js +169 -0
  44. package/dist/testing/suite.mjs +165 -0
  45. package/dist/testing.d.mts +1359 -0
  46. package/dist/testing.d.ts +1359 -0
  47. package/dist/testing.js +213 -0
  48. package/dist/testing.mjs +4 -0
  49. package/dist/utils.js +28 -28
  50. package/dist/utils.mjs +1 -1
  51. package/package.json +18 -3
  52. package/registry/account.json +15 -0
  53. package/registry/billing-plan-selector.json +16 -0
  54. package/registry/cart-drawer.json +17 -0
  55. package/registry/currency-selector.json +15 -0
  56. package/registry/customer-input-fields.json +15 -0
  57. package/registry/delivery-estimate.json +16 -0
  58. package/registry/index.json +105 -0
  59. package/registry/location-picker.json +15 -0
  60. package/registry/recently-viewed.json +16 -0
  61. package/registry/recommendation-carousel.json +16 -0
  62. package/registry/resource-picker.json +15 -0
  63. package/registry/session-message-banner.json +15 -0
  64. package/templates/storefront-bakery/AGENTS.md +12 -0
  65. package/templates/storefront-bakery/__tests__/brand.test.ts +4 -0
  66. package/templates/storefront-bakery/__tests__/cart-flow.test.ts +4 -0
  67. package/templates/storefront-bakery/__tests__/contract.test.ts +4 -0
  68. package/templates/storefront-bakery/package.json +11 -3
  69. package/templates/storefront-bakery/vitest.config.ts +9 -0
  70. package/templates/storefront-fashion/AGENTS.md +21 -1
  71. package/templates/storefront-fashion/__tests__/brand.test.ts +4 -0
  72. package/templates/storefront-fashion/__tests__/cart-flow.test.ts +4 -0
  73. package/templates/storefront-fashion/__tests__/contract.test.ts +4 -0
  74. package/templates/storefront-fashion/package.json +11 -3
  75. package/templates/storefront-fashion/vitest.config.ts +9 -0
  76. package/templates/storefront-grocery/AGENTS.md +12 -0
  77. package/templates/storefront-grocery/__tests__/brand.test.ts +4 -0
  78. package/templates/storefront-grocery/__tests__/cart-flow.test.ts +4 -0
  79. package/templates/storefront-grocery/__tests__/contract.test.ts +4 -0
  80. package/templates/storefront-grocery/package.json +11 -3
  81. package/templates/storefront-grocery/vitest.config.ts +9 -0
  82. package/templates/storefront-restaurant/AGENTS.md +12 -0
  83. package/templates/storefront-restaurant/__tests__/brand.test.ts +4 -0
  84. package/templates/storefront-restaurant/__tests__/cart-flow.test.ts +4 -0
  85. package/templates/storefront-restaurant/__tests__/contract.test.ts +4 -0
  86. package/templates/storefront-restaurant/package.json +11 -3
  87. package/templates/storefront-restaurant/vitest.config.ts +9 -0
  88. package/templates/storefront-retail/AGENTS.md +12 -0
  89. package/templates/storefront-retail/__tests__/brand.test.ts +4 -0
  90. package/templates/storefront-retail/__tests__/cart-flow.test.ts +4 -0
  91. package/templates/storefront-retail/__tests__/contract.test.ts +4 -0
  92. package/templates/storefront-retail/package.json +11 -3
  93. package/templates/storefront-retail/vitest.config.ts +9 -0
  94. package/templates/storefront-services/AGENTS.md +12 -0
  95. package/templates/storefront-services/__tests__/brand.test.ts +4 -0
  96. package/templates/storefront-services/__tests__/cart-flow.test.ts +4 -0
  97. package/templates/storefront-services/__tests__/contract.test.ts +4 -0
  98. package/templates/storefront-services/package.json +11 -3
  99. package/templates/storefront-services/vitest.config.ts +9 -0
@@ -0,0 +1,213 @@
1
+ 'use strict';
2
+
3
+ var chunkXL4CX75J_js = require('./chunk-XL4CX75J.js');
4
+ require('./chunk-MBGQP6FZ.js');
5
+ require('./chunk-CFD4J32N.js');
6
+ require('./chunk-MNIKXG7W.js');
7
+
8
+
9
+
10
+ Object.defineProperty(exports, "AddItemPayloadSchema", {
11
+ enumerable: true,
12
+ get: function () { return chunkXL4CX75J_js.AddItemPayloadSchema; }
13
+ });
14
+ Object.defineProperty(exports, "AddOnDetailsSchema", {
15
+ enumerable: true,
16
+ get: function () { return chunkXL4CX75J_js.AddOnDetailsSchema; }
17
+ });
18
+ Object.defineProperty(exports, "AuthorizationTypeSchema", {
19
+ enumerable: true,
20
+ get: function () { return chunkXL4CX75J_js.AuthorizationTypeSchema; }
21
+ });
22
+ Object.defineProperty(exports, "BrandContactSchema", {
23
+ enumerable: true,
24
+ get: function () { return chunkXL4CX75J_js.BrandContactSchema; }
25
+ });
26
+ Object.defineProperty(exports, "BrandFaqSchema", {
27
+ enumerable: true,
28
+ get: function () { return chunkXL4CX75J_js.BrandFaqSchema; }
29
+ });
30
+ Object.defineProperty(exports, "BrandHeroSchema", {
31
+ enumerable: true,
32
+ get: function () { return chunkXL4CX75J_js.BrandHeroSchema; }
33
+ });
34
+ Object.defineProperty(exports, "BrandNavLinkSchema", {
35
+ enumerable: true,
36
+ get: function () { return chunkXL4CX75J_js.BrandNavLinkSchema; }
37
+ });
38
+ Object.defineProperty(exports, "BrandPolicyPageSchema", {
39
+ enumerable: true,
40
+ get: function () { return chunkXL4CX75J_js.BrandPolicyPageSchema; }
41
+ });
42
+ Object.defineProperty(exports, "BrandPolicySectionSchema", {
43
+ enumerable: true,
44
+ get: function () { return chunkXL4CX75J_js.BrandPolicySectionSchema; }
45
+ });
46
+ Object.defineProperty(exports, "BrandSchema", {
47
+ enumerable: true,
48
+ get: function () { return chunkXL4CX75J_js.BrandSchema; }
49
+ });
50
+ Object.defineProperty(exports, "BrandSocialSchema", {
51
+ enumerable: true,
52
+ get: function () { return chunkXL4CX75J_js.BrandSocialSchema; }
53
+ });
54
+ Object.defineProperty(exports, "BundleResolvedSchema", {
55
+ enumerable: true,
56
+ get: function () { return chunkXL4CX75J_js.BundleResolvedSchema; }
57
+ });
58
+ Object.defineProperty(exports, "BundleSelectionInputSchema", {
59
+ enumerable: true,
60
+ get: function () { return chunkXL4CX75J_js.BundleSelectionInputSchema; }
61
+ });
62
+ Object.defineProperty(exports, "CartItemSchema", {
63
+ enumerable: true,
64
+ get: function () { return chunkXL4CX75J_js.CartItemSchema; }
65
+ });
66
+ Object.defineProperty(exports, "CartPricingSchema", {
67
+ enumerable: true,
68
+ get: function () { return chunkXL4CX75J_js.CartPricingSchema; }
69
+ });
70
+ Object.defineProperty(exports, "CartSchema", {
71
+ enumerable: true,
72
+ get: function () { return chunkXL4CX75J_js.CartSchema; }
73
+ });
74
+ Object.defineProperty(exports, "CheckoutBodySchema", {
75
+ enumerable: true,
76
+ get: function () { return chunkXL4CX75J_js.CheckoutBodySchema; }
77
+ });
78
+ Object.defineProperty(exports, "CheckoutCustomerSchema", {
79
+ enumerable: true,
80
+ get: function () { return chunkXL4CX75J_js.CheckoutCustomerSchema; }
81
+ });
82
+ Object.defineProperty(exports, "CheckoutResponseSchema", {
83
+ enumerable: true,
84
+ get: function () { return chunkXL4CX75J_js.CheckoutResponseSchema; }
85
+ });
86
+ Object.defineProperty(exports, "ChosenPriceSchema", {
87
+ enumerable: true,
88
+ get: function () { return chunkXL4CX75J_js.ChosenPriceSchema; }
89
+ });
90
+ Object.defineProperty(exports, "CompositeResolvedSchema", {
91
+ enumerable: true,
92
+ get: function () { return chunkXL4CX75J_js.CompositeResolvedSchema; }
93
+ });
94
+ Object.defineProperty(exports, "CompositeSelectionInputSchema", {
95
+ enumerable: true,
96
+ get: function () { return chunkXL4CX75J_js.CompositeSelectionInputSchema; }
97
+ });
98
+ Object.defineProperty(exports, "CountryCodeSchema", {
99
+ enumerable: true,
100
+ get: function () { return chunkXL4CX75J_js.CountryCodeSchema; }
101
+ });
102
+ Object.defineProperty(exports, "CurrencyCodeSchema", {
103
+ enumerable: true,
104
+ get: function () { return chunkXL4CX75J_js.CurrencyCodeSchema; }
105
+ });
106
+ Object.defineProperty(exports, "CustomerInputValueSchema", {
107
+ enumerable: true,
108
+ get: function () { return chunkXL4CX75J_js.CustomerInputValueSchema; }
109
+ });
110
+ Object.defineProperty(exports, "DiscountDetailsSchema", {
111
+ enumerable: true,
112
+ get: function () { return chunkXL4CX75J_js.DiscountDetailsSchema; }
113
+ });
114
+ Object.defineProperty(exports, "IsoDateTimeSchema", {
115
+ enumerable: true,
116
+ get: function () { return chunkXL4CX75J_js.IsoDateTimeSchema; }
117
+ });
118
+ Object.defineProperty(exports, "LineTypeSchema", {
119
+ enumerable: true,
120
+ get: function () { return chunkXL4CX75J_js.LineTypeSchema; }
121
+ });
122
+ Object.defineProperty(exports, "LocaleSchema", {
123
+ enumerable: true,
124
+ get: function () { return chunkXL4CX75J_js.LocaleSchema; }
125
+ });
126
+ Object.defineProperty(exports, "MoneyLooseSchema", {
127
+ enumerable: true,
128
+ get: function () { return chunkXL4CX75J_js.MoneyLooseSchema; }
129
+ });
130
+ Object.defineProperty(exports, "MoneySchema", {
131
+ enumerable: true,
132
+ get: function () { return chunkXL4CX75J_js.MoneySchema; }
133
+ });
134
+ Object.defineProperty(exports, "NextActionSchema", {
135
+ enumerable: true,
136
+ get: function () { return chunkXL4CX75J_js.NextActionSchema; }
137
+ });
138
+ Object.defineProperty(exports, "PhoneE164Schema", {
139
+ enumerable: true,
140
+ get: function () { return chunkXL4CX75J_js.PhoneE164Schema; }
141
+ });
142
+ Object.defineProperty(exports, "ProductTypeSchema", {
143
+ enumerable: true,
144
+ get: function () { return chunkXL4CX75J_js.ProductTypeSchema; }
145
+ });
146
+ Object.defineProperty(exports, "SCHEMA_VERSION", {
147
+ enumerable: true,
148
+ get: function () { return chunkXL4CX75J_js.SCHEMA_VERSION; }
149
+ });
150
+ Object.defineProperty(exports, "SchemaViolationError", {
151
+ enumerable: true,
152
+ get: function () { return chunkXL4CX75J_js.SchemaViolationError; }
153
+ });
154
+ Object.defineProperty(exports, "SeedNameSchema", {
155
+ enumerable: true,
156
+ get: function () { return chunkXL4CX75J_js.SeedNameSchema; }
157
+ });
158
+ Object.defineProperty(exports, "ServiceStatusSchema", {
159
+ enumerable: true,
160
+ get: function () { return chunkXL4CX75J_js.ServiceStatusSchema; }
161
+ });
162
+ Object.defineProperty(exports, "TimestampSchema", {
163
+ enumerable: true,
164
+ get: function () { return chunkXL4CX75J_js.TimestampSchema; }
165
+ });
166
+ Object.defineProperty(exports, "VariantDetailsSchema", {
167
+ enumerable: true,
168
+ get: function () { return chunkXL4CX75J_js.VariantDetailsSchema; }
169
+ });
170
+ Object.defineProperty(exports, "VariantDisplayAttributeSchema", {
171
+ enumerable: true,
172
+ get: function () { return chunkXL4CX75J_js.VariantDisplayAttributeSchema; }
173
+ });
174
+ Object.defineProperty(exports, "VariantInfoSchema", {
175
+ enumerable: true,
176
+ get: function () { return chunkXL4CX75J_js.VariantInfoSchema; }
177
+ });
178
+ Object.defineProperty(exports, "assertAddItemPayload", {
179
+ enumerable: true,
180
+ get: function () { return chunkXL4CX75J_js.assertAddItemPayload; }
181
+ });
182
+ Object.defineProperty(exports, "assertBrand", {
183
+ enumerable: true,
184
+ get: function () { return chunkXL4CX75J_js.assertBrand; }
185
+ });
186
+ Object.defineProperty(exports, "assertCart", {
187
+ enumerable: true,
188
+ get: function () { return chunkXL4CX75J_js.assertCart; }
189
+ });
190
+ Object.defineProperty(exports, "assertCartItem", {
191
+ enumerable: true,
192
+ get: function () { return chunkXL4CX75J_js.assertCartItem; }
193
+ });
194
+ Object.defineProperty(exports, "assertCheckoutBody", {
195
+ enumerable: true,
196
+ get: function () { return chunkXL4CX75J_js.assertCheckoutBody; }
197
+ });
198
+ Object.defineProperty(exports, "assertCheckoutResponse", {
199
+ enumerable: true,
200
+ get: function () { return chunkXL4CX75J_js.assertCheckoutResponse; }
201
+ });
202
+ Object.defineProperty(exports, "createTestClient", {
203
+ enumerable: true,
204
+ get: function () { return chunkXL4CX75J_js.createTestClient; }
205
+ });
206
+ Object.defineProperty(exports, "fixtures", {
207
+ enumerable: true,
208
+ get: function () { return chunkXL4CX75J_js.fixtures; }
209
+ });
210
+ Object.defineProperty(exports, "makeAssertion", {
211
+ enumerable: true,
212
+ get: function () { return chunkXL4CX75J_js.makeAssertion; }
213
+ });
@@ -0,0 +1,4 @@
1
+ export { AddItemPayloadSchema, AddOnDetailsSchema, AuthorizationTypeSchema, BrandContactSchema, BrandFaqSchema, BrandHeroSchema, BrandNavLinkSchema, BrandPolicyPageSchema, BrandPolicySectionSchema, BrandSchema, BrandSocialSchema, BundleResolvedSchema, BundleSelectionInputSchema, CartItemSchema, CartPricingSchema, CartSchema, CheckoutBodySchema, CheckoutCustomerSchema, CheckoutResponseSchema, ChosenPriceSchema, CompositeResolvedSchema, CompositeSelectionInputSchema, CountryCodeSchema, CurrencyCodeSchema, CustomerInputValueSchema, DiscountDetailsSchema, IsoDateTimeSchema, LineTypeSchema, LocaleSchema, MoneyLooseSchema, MoneySchema, NextActionSchema, PhoneE164Schema, ProductTypeSchema, SCHEMA_VERSION, SchemaViolationError, SeedNameSchema, ServiceStatusSchema, TimestampSchema, VariantDetailsSchema, VariantDisplayAttributeSchema, VariantInfoSchema, assertAddItemPayload, assertBrand, assertCart, assertCartItem, assertCheckoutBody, assertCheckoutResponse, createTestClient, fixtures, makeAssertion } from './chunk-ZLEFMQC6.mjs';
2
+ import './chunk-UXLQ7WXI.mjs';
3
+ import './chunk-7NML226B.mjs';
4
+ import './chunk-6AZZ6AOF.mjs';
package/dist/utils.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var chunkL5GKVQVJ_js = require('./chunk-L5GKVQVJ.js');
4
- var chunkV2O5AO7I_js = require('./chunk-V2O5AO7I.js');
4
+ var chunkMNIKXG7W_js = require('./chunk-MNIKXG7W.js');
5
5
 
6
6
 
7
7
 
@@ -19,109 +19,109 @@ Object.defineProperty(exports, "resolvePhoneCountryCode", {
19
19
  });
20
20
  Object.defineProperty(exports, "CURRENCY_SYMBOLS", {
21
21
  enumerable: true,
22
- get: function () { return chunkV2O5AO7I_js.CURRENCY_SYMBOLS; }
22
+ get: function () { return chunkMNIKXG7W_js.CURRENCY_SYMBOLS; }
23
23
  });
24
24
  Object.defineProperty(exports, "MOBILE_MONEY_PROVIDERS", {
25
25
  enumerable: true,
26
- get: function () { return chunkV2O5AO7I_js.MOBILE_MONEY_PROVIDERS; }
26
+ get: function () { return chunkMNIKXG7W_js.MOBILE_MONEY_PROVIDERS; }
27
27
  });
28
28
  Object.defineProperty(exports, "categorizePaymentError", {
29
29
  enumerable: true,
30
- get: function () { return chunkV2O5AO7I_js.categorizePaymentError; }
30
+ get: function () { return chunkMNIKXG7W_js.categorizePaymentError; }
31
31
  });
32
32
  Object.defineProperty(exports, "detectMobileMoneyProvider", {
33
33
  enumerable: true,
34
- get: function () { return chunkV2O5AO7I_js.detectMobileMoneyProvider; }
34
+ get: function () { return chunkMNIKXG7W_js.detectMobileMoneyProvider; }
35
35
  });
36
36
  Object.defineProperty(exports, "formatMoney", {
37
37
  enumerable: true,
38
- get: function () { return chunkV2O5AO7I_js.formatMoney; }
38
+ get: function () { return chunkMNIKXG7W_js.formatMoney; }
39
39
  });
40
40
  Object.defineProperty(exports, "formatNumberCompact", {
41
41
  enumerable: true,
42
- get: function () { return chunkV2O5AO7I_js.formatNumberCompact; }
42
+ get: function () { return chunkMNIKXG7W_js.formatNumberCompact; }
43
43
  });
44
44
  Object.defineProperty(exports, "formatPrice", {
45
45
  enumerable: true,
46
- get: function () { return chunkV2O5AO7I_js.formatPrice; }
46
+ get: function () { return chunkMNIKXG7W_js.formatPrice; }
47
47
  });
48
48
  Object.defineProperty(exports, "formatPriceAdjustment", {
49
49
  enumerable: true,
50
- get: function () { return chunkV2O5AO7I_js.formatPriceAdjustment; }
50
+ get: function () { return chunkMNIKXG7W_js.formatPriceAdjustment; }
51
51
  });
52
52
  Object.defineProperty(exports, "formatPriceCompact", {
53
53
  enumerable: true,
54
- get: function () { return chunkV2O5AO7I_js.formatPriceCompact; }
54
+ get: function () { return chunkMNIKXG7W_js.formatPriceCompact; }
55
55
  });
56
56
  Object.defineProperty(exports, "formatPriceWithTax", {
57
57
  enumerable: true,
58
- get: function () { return chunkV2O5AO7I_js.formatPriceWithTax; }
58
+ get: function () { return chunkMNIKXG7W_js.formatPriceWithTax; }
59
59
  });
60
60
  Object.defineProperty(exports, "formatProductPrice", {
61
61
  enumerable: true,
62
- get: function () { return chunkV2O5AO7I_js.formatProductPrice; }
62
+ get: function () { return chunkMNIKXG7W_js.formatProductPrice; }
63
63
  });
64
64
  Object.defineProperty(exports, "getBasePrice", {
65
65
  enumerable: true,
66
- get: function () { return chunkV2O5AO7I_js.getBasePrice; }
66
+ get: function () { return chunkMNIKXG7W_js.getBasePrice; }
67
67
  });
68
68
  Object.defineProperty(exports, "getCurrencySymbol", {
69
69
  enumerable: true,
70
- get: function () { return chunkV2O5AO7I_js.getCurrencySymbol; }
70
+ get: function () { return chunkMNIKXG7W_js.getCurrencySymbol; }
71
71
  });
72
72
  Object.defineProperty(exports, "getDiscountPercentage", {
73
73
  enumerable: true,
74
- get: function () { return chunkV2O5AO7I_js.getDiscountPercentage; }
74
+ get: function () { return chunkMNIKXG7W_js.getDiscountPercentage; }
75
75
  });
76
76
  Object.defineProperty(exports, "getDisplayPrice", {
77
77
  enumerable: true,
78
- get: function () { return chunkV2O5AO7I_js.getDisplayPrice; }
78
+ get: function () { return chunkMNIKXG7W_js.getDisplayPrice; }
79
79
  });
80
80
  Object.defineProperty(exports, "getMarkupPercentage", {
81
81
  enumerable: true,
82
- get: function () { return chunkV2O5AO7I_js.getMarkupPercentage; }
82
+ get: function () { return chunkMNIKXG7W_js.getMarkupPercentage; }
83
83
  });
84
84
  Object.defineProperty(exports, "getProductCurrency", {
85
85
  enumerable: true,
86
- get: function () { return chunkV2O5AO7I_js.getProductCurrency; }
86
+ get: function () { return chunkMNIKXG7W_js.getProductCurrency; }
87
87
  });
88
88
  Object.defineProperty(exports, "getTaxAmount", {
89
89
  enumerable: true,
90
- get: function () { return chunkV2O5AO7I_js.getTaxAmount; }
90
+ get: function () { return chunkMNIKXG7W_js.getTaxAmount; }
91
91
  });
92
92
  Object.defineProperty(exports, "hasTaxInfo", {
93
93
  enumerable: true,
94
- get: function () { return chunkV2O5AO7I_js.hasTaxInfo; }
94
+ get: function () { return chunkMNIKXG7W_js.hasTaxInfo; }
95
95
  });
96
96
  Object.defineProperty(exports, "isOnSale", {
97
97
  enumerable: true,
98
- get: function () { return chunkV2O5AO7I_js.isOnSale; }
98
+ get: function () { return chunkMNIKXG7W_js.isOnSale; }
99
99
  });
100
100
  Object.defineProperty(exports, "isPaymentStatusFailure", {
101
101
  enumerable: true,
102
- get: function () { return chunkV2O5AO7I_js.isPaymentStatusFailure; }
102
+ get: function () { return chunkMNIKXG7W_js.isPaymentStatusFailure; }
103
103
  });
104
104
  Object.defineProperty(exports, "isPaymentStatusRequiresAction", {
105
105
  enumerable: true,
106
- get: function () { return chunkV2O5AO7I_js.isPaymentStatusRequiresAction; }
106
+ get: function () { return chunkMNIKXG7W_js.isPaymentStatusRequiresAction; }
107
107
  });
108
108
  Object.defineProperty(exports, "isPaymentStatusSuccess", {
109
109
  enumerable: true,
110
- get: function () { return chunkV2O5AO7I_js.isPaymentStatusSuccess; }
110
+ get: function () { return chunkMNIKXG7W_js.isPaymentStatusSuccess; }
111
111
  });
112
112
  Object.defineProperty(exports, "isTaxInclusive", {
113
113
  enumerable: true,
114
- get: function () { return chunkV2O5AO7I_js.isTaxInclusive; }
114
+ get: function () { return chunkMNIKXG7W_js.isTaxInclusive; }
115
115
  });
116
116
  Object.defineProperty(exports, "normalizePaymentResponse", {
117
117
  enumerable: true,
118
- get: function () { return chunkV2O5AO7I_js.normalizePaymentResponse; }
118
+ get: function () { return chunkMNIKXG7W_js.normalizePaymentResponse; }
119
119
  });
120
120
  Object.defineProperty(exports, "normalizeStatusResponse", {
121
121
  enumerable: true,
122
- get: function () { return chunkV2O5AO7I_js.normalizeStatusResponse; }
122
+ get: function () { return chunkMNIKXG7W_js.normalizeStatusResponse; }
123
123
  });
124
124
  Object.defineProperty(exports, "parsePrice", {
125
125
  enumerable: true,
126
- get: function () { return chunkV2O5AO7I_js.parsePrice; }
126
+ get: function () { return chunkMNIKXG7W_js.parsePrice; }
127
127
  });
package/dist/utils.mjs CHANGED
@@ -1,2 +1,2 @@
1
1
  export { isValidPhoneForContext, normalizePhoneToE164, resolvePhoneCountryCode } from './chunk-YOIVZ6OA.mjs';
2
- export { CURRENCY_SYMBOLS, MOBILE_MONEY_PROVIDERS, categorizePaymentError, detectMobileMoneyProvider, formatMoney, formatNumberCompact, formatPrice, formatPriceAdjustment, formatPriceCompact, formatPriceWithTax, formatProductPrice, getBasePrice, getCurrencySymbol, getDiscountPercentage, getDisplayPrice, getMarkupPercentage, getProductCurrency, getTaxAmount, hasTaxInfo, isOnSale, isPaymentStatusFailure, isPaymentStatusRequiresAction, isPaymentStatusSuccess, isTaxInclusive, normalizePaymentResponse, normalizeStatusResponse, parsePrice } from './chunk-6TVQQYQ5.mjs';
2
+ export { CURRENCY_SYMBOLS, MOBILE_MONEY_PROVIDERS, categorizePaymentError, detectMobileMoneyProvider, formatMoney, formatNumberCompact, formatPrice, formatPriceAdjustment, formatPriceCompact, formatPriceWithTax, formatProductPrice, getBasePrice, getCurrencySymbol, getDiscountPercentage, getDisplayPrice, getMarkupPercentage, getProductCurrency, getTaxAmount, hasTaxInfo, isOnSale, isPaymentStatusFailure, isPaymentStatusRequiresAction, isPaymentStatusSuccess, isTaxInclusive, normalizePaymentResponse, normalizeStatusResponse, parsePrice } from './chunk-6AZZ6AOF.mjs';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cimplify/sdk",
3
- "version": "0.44.12",
3
+ "version": "0.44.16",
4
4
  "description": "Cimplify Commerce SDK for storefronts",
5
5
  "keywords": [
6
6
  "cimplify",
@@ -61,6 +61,16 @@
61
61
  "import": "./dist/mock/msw.mjs",
62
62
  "require": "./dist/mock/msw.js"
63
63
  },
64
+ "./testing": {
65
+ "types": "./dist/testing.d.ts",
66
+ "import": "./dist/testing.mjs",
67
+ "require": "./dist/testing.js"
68
+ },
69
+ "./testing/suite": {
70
+ "types": "./dist/testing/suite.d.ts",
71
+ "import": "./dist/testing/suite.mjs",
72
+ "require": "./dist/testing/suite.js"
73
+ },
64
74
  "./styles.css": "./dist/styles.css"
65
75
  },
66
76
  "scripts": {
@@ -81,7 +91,8 @@
81
91
  "peerDependencies": {
82
92
  "@paystack/inline-js": "^2.22.8",
83
93
  "react": ">=17.0.0",
84
- "msw": ">=2.0.0"
94
+ "msw": ">=2.0.0",
95
+ "vitest": ">=2.0.0"
85
96
  },
86
97
  "peerDependenciesMeta": {
87
98
  "@paystack/inline-js": {
@@ -92,6 +103,9 @@
92
103
  },
93
104
  "msw": {
94
105
  "optional": true
106
+ },
107
+ "vitest": {
108
+ "optional": true
95
109
  }
96
110
  },
97
111
  "devDependencies": {
@@ -116,6 +130,7 @@
116
130
  "@base-ui/react": "^1.4.1",
117
131
  "clsx": "^2.1.1",
118
132
  "libphonenumber-js": "1.12.41",
119
- "tailwind-merge": "^3.5.0"
133
+ "tailwind-merge": "^3.5.0",
134
+ "zod": "^4.4.3"
120
135
  }
121
136
  }
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "account",
3
+ "title": "CimplifyAccount",
4
+ "description": "Iframe wrapper for the Cimplify account portal — sign-in, orders, addresses, settings.",
5
+ "type": "component",
6
+ "registryDependencies": [
7
+ "cn"
8
+ ],
9
+ "files": [
10
+ {
11
+ "path": "account.tsx",
12
+ "content": "\"use client\";\n\nimport React, { useEffect, useRef, useState, useMemo } from \"react\";\nimport type { CimplifyClient } from \"../client\";\nimport { useOptionalCimplify } from \"@cimplify/sdk/react\";\nimport { cn } from \"@cimplify/sdk/react\";\n\nexport interface CimplifyAccountProps {\n /** CimplifyClient instance. Falls back to provider context. */\n client?: CimplifyClient;\n /** Override the Link base URL. */\n linkUrl?: string;\n /** Initial section to show: \"dashboard\" | \"orders\" | \"addresses\" | \"payment-methods\" | \"sessions\" | \"settings\". */\n section?: string;\n /** Appearance variables — passed to Link for theming. */\n appearance?: {\n theme?: \"light\" | \"dark\";\n variables?: {\n primaryColor?: string;\n fontFamily?: string;\n borderRadius?: string;\n };\n };\n /** Called when the user logs out from the account portal. */\n onLogout?: () => void;\n /** Additional CSS class for the container. */\n className?: string;\n}\n\nconst DEFAULT_LINK_URL = \"https://link.cimplify.io\";\n\nexport function CimplifyAccount({\n client: clientProp,\n linkUrl,\n section,\n appearance,\n onLogout,\n className,\n}: CimplifyAccountProps): React.ReactElement {\n const context = useOptionalCimplify();\n const client = clientProp ?? context?.client;\n const resolvedLinkUrl = linkUrl || DEFAULT_LINK_URL;\n\n const iframeRef = useRef<HTMLIFrameElement | null>(null);\n const [height, setHeight] = useState(400);\n const [isReady, setIsReady] = useState(false);\n\n const iframeSrc = useMemo(() => {\n const path = section ? `/elements/account/${section}` : \"/elements/account\";\n const url = new URL(path, resolvedLinkUrl);\n if (client) {\n const businessId = client.getBusinessId?.() ?? \"\";\n if (businessId) url.searchParams.set(\"businessId\", businessId);\n }\n return url.toString();\n }, [resolvedLinkUrl, section, client]);\n\n // Listen for messages from the iframe\n useEffect(() => {\n function handleMessage(event: MessageEvent) {\n if (!event.data || typeof event.data !== \"object\") return;\n\n // Only accept messages from our iframe\n if (iframeRef.current?.contentWindow && event.source !== iframeRef.current.contentWindow) {\n return;\n }\n\n switch (event.data.type) {\n case \"ready\":\n setIsReady(true);\n if (typeof event.data.height === \"number\") {\n setHeight(event.data.height);\n }\n // Send init with token + appearance\n sendInit();\n break;\n\n case \"height_change\":\n if (typeof event.data.height === \"number\") {\n setHeight(event.data.height);\n }\n break;\n\n case \"logout_complete\":\n onLogout?.();\n break;\n }\n }\n\n window.addEventListener(\"message\", handleMessage);\n return () => window.removeEventListener(\"message\", handleMessage);\n }, [appearance, client, onLogout]);\n\n function sendInit() {\n const contentWindow = iframeRef.current?.contentWindow;\n if (!contentWindow) return;\n\n const token = client?.getAccessToken?.();\n contentWindow.postMessage(\n {\n type: \"init\",\n token: token ?? undefined,\n appearance: appearance ?? undefined,\n },\n resolvedLinkUrl,\n );\n }\n\n return (\n <div\n data-cimplify-account\n className={cn(\"relative overflow-hidden\", className)}\n >\n {!isReady && (\n <div className=\"flex items-center justify-center py-16\">\n <div className=\"w-6 h-6 border-2 border-border border-t-foreground rounded-full animate-spin\" />\n </div>\n )}\n <iframe\n ref={iframeRef}\n src={iframeSrc}\n style={{\n border: \"none\",\n width: \"100%\",\n height: `${height}px`,\n display: isReady ? \"block\" : \"none\",\n overflow: \"hidden\",\n background: \"transparent\",\n }}\n allow=\"geolocation\"\n sandbox=\"allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox\"\n />\n </div>\n );\n}\n"
13
+ }
14
+ ]
15
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "billing-plan-selector",
3
+ "title": "BillingPlanSelector",
4
+ "description": "Subscription / billing-plan picker — surfaces eligible plans with pricing and trial periods.",
5
+ "type": "component",
6
+ "registryDependencies": [
7
+ "price",
8
+ "cn"
9
+ ],
10
+ "files": [
11
+ {
12
+ "path": "billing-plan-selector.tsx",
13
+ "content": "\"use client\";\n\nimport React from \"react\";\nimport type { ProductBillingPlan, BillingFrequency } from \"@cimplify/sdk\";\nimport { useBillingPlans } from \"./hooks/use-billing-plans\";\nimport { Price } from \"@cimplify/sdk/react\";\nimport { cn } from \"@cimplify/sdk/react\";\n\nexport interface BillingPlanSelectorClassNames {\n root?: string;\n plan?: string;\n activePlan?: string;\n frequency?: string;\n price?: string;\n setupFee?: string;\n trialBadge?: string;\n empty?: string;\n loading?: string;\n}\n\nexport interface BillingPlanSelectorProps {\n /** Product ID to fetch billing plans for. */\n productId: string;\n /** Override plans (skips fetch). For SSR, pass pre-fetched plans. */\n plans?: ProductBillingPlan[];\n /** Called when a plan is selected. */\n onPlanSelect?: (plan: ProductBillingPlan | null) => void;\n /** Currently selected plan ID. */\n selectedPlanId?: string | null;\n /** Show a \"One-time purchase\" option at the top. */\n showOneTimePurchase?: boolean;\n /** Label for the one-time purchase option. Default: \"One-time purchase\". */\n oneTimePurchaseLabel?: string;\n /** Custom plan card renderer. */\n renderPlan?: (plan: ProductBillingPlan, isActive: boolean) => React.ReactNode;\n className?: string;\n classNames?: BillingPlanSelectorClassNames;\n}\n\nconst FREQUENCY_LABELS: Record<BillingFrequency, string> = {\n weekly: \"Weekly\",\n biweekly: \"Biweekly\",\n monthly: \"Monthly\",\n quarterly: \"Quarterly\",\n annually: \"Annually\",\n};\n\nfunction formatMarkup(plan: ProductBillingPlan): string | null {\n if (!plan.markup_type || plan.markup_amount == null) return null;\n if (plan.markup_type === \"percentage\") return `+${plan.markup_amount}%`;\n return null;\n}\n\n/**\n * BillingPlanSelector — subscription/installment plan comparison and selection.\n *\n * Renders plan cards with frequency, markup, trial, and setup fee details.\n * Returns `null` when no plans are available (and one-time purchase is hidden).\n */\nexport function BillingPlanSelector({\n productId,\n plans: plansProp,\n onPlanSelect,\n selectedPlanId,\n showOneTimePurchase = false,\n oneTimePurchaseLabel = \"One-time purchase\",\n renderPlan,\n className,\n classNames,\n}: BillingPlanSelectorProps): React.ReactElement | null {\n const { plans: fetched, isLoading } = useBillingPlans(productId, {\n enabled: plansProp === undefined,\n });\n\n const plans = plansProp ?? fetched;\n\n if (isLoading && plans.length === 0) {\n return (\n <div\n data-cimplify-billing-plan-selector\n aria-busy=\"true\"\n className={cn(className, classNames?.root, classNames?.loading)}\n />\n );\n }\n\n if (plans.length === 0 && !showOneTimePurchase) {\n return null;\n }\n\n const isOneTimePurchaseSelected = selectedPlanId === null;\n\n return (\n <div data-cimplify-billing-plan-selector className={cn(className, classNames?.root)}>\n {showOneTimePurchase && (\n <button\n type=\"button\"\n onClick={() => onPlanSelect?.(null)}\n data-cimplify-billing-plan\n data-plan-type=\"one-time\"\n data-active={isOneTimePurchaseSelected || undefined}\n aria-pressed={isOneTimePurchaseSelected}\n className={cn(\n classNames?.plan,\n isOneTimePurchaseSelected && classNames?.activePlan,\n )}\n >\n <span data-cimplify-billing-frequency className={classNames?.frequency}>\n {oneTimePurchaseLabel}\n </span>\n </button>\n )}\n\n {plans.map((plan) => {\n const isActive = selectedPlanId === plan.id;\n const markup = formatMarkup(plan);\n\n return (\n <button\n key={plan.id}\n type=\"button\"\n onClick={() => onPlanSelect?.(plan)}\n data-cimplify-billing-plan\n data-plan-type={plan.plan_type}\n data-active={isActive || undefined}\n aria-pressed={isActive}\n className={cn(classNames?.plan, isActive && classNames?.activePlan)}\n >\n {renderPlan ? (\n renderPlan(plan, isActive)\n ) : (\n <>\n <span data-cimplify-billing-frequency className={classNames?.frequency}>\n {FREQUENCY_LABELS[plan.frequency]}\n </span>\n\n {markup && (\n <span data-cimplify-billing-markup className={classNames?.price}>\n {markup}\n </span>\n )}\n\n {plan.markup_type === \"fixed\" && plan.markup_amount != null && (\n <span data-cimplify-billing-markup className={classNames?.price}>\n +<Price amount={plan.markup_amount} />\n </span>\n )}\n\n {plan.trial_days > 0 && (\n <span data-cimplify-billing-trial className={classNames?.trialBadge}>\n {plan.trial_days}-day trial\n </span>\n )}\n\n {plan.setup_fee > 0 && (\n <span data-cimplify-billing-setup-fee className={classNames?.setupFee}>\n Setup fee: <Price amount={plan.setup_fee} />\n </span>\n )}\n\n {plan.plan_type === \"installment\" && plan.installment_periods != null && (\n <span data-cimplify-billing-periods>\n {plan.installment_periods} payments\n </span>\n )}\n </>\n )}\n </button>\n );\n })}\n </div>\n );\n}\n"
14
+ }
15
+ ]
16
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "cart-drawer",
3
+ "title": "CartDrawer",
4
+ "description": "Slide-in side cart drawer with provider context, free-shipping progress, animated subtotal, and empty state. Auto-opens on add-to-cart.",
5
+ "type": "component",
6
+ "registryDependencies": [
7
+ "cart-summary",
8
+ "price",
9
+ "cn"
10
+ ],
11
+ "files": [
12
+ {
13
+ "path": "cart-drawer.tsx",
14
+ "content": "\"use client\";\n\nimport React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from \"react\";\nimport { CartSummary } from \"./cart-summary\";\nimport { Price } from \"@cimplify/sdk/react\";\nimport { useCart } from \"@cimplify/sdk/react\";\nimport { parsePrice } from \"@cimplify/sdk\";\nimport { cn } from \"@cimplify/sdk/react\";\n\ninterface CartDrawerCtx {\n isOpen: boolean;\n open: () => void;\n close: () => void;\n toggle: () => void;\n}\n\nconst CartDrawerContext = createContext<CartDrawerCtx | null>(null);\n\nexport function useCartDrawer(): CartDrawerCtx {\n const ctx = useContext(CartDrawerContext);\n if (!ctx) {\n throw new Error(\"useCartDrawer must be used within <CartDrawerProvider>\");\n }\n return ctx;\n}\n\ninterface ProviderProps {\n children: React.ReactNode;\n /** Auto-open the drawer whenever the cart's pendingOpCount goes from 0 → >0. Default: true. */\n openOnAdd?: boolean;\n}\n\nexport function CartDrawerProvider({ children, openOnAdd = true }: ProviderProps): React.ReactElement {\n const [isOpen, setIsOpen] = useState(false);\n const cart = useCart();\n const lastPendingRef = useRef(0);\n\n const open = useCallback(() => setIsOpen(true), []);\n const close = useCallback(() => setIsOpen(false), []);\n const toggle = useCallback(() => setIsOpen((v) => !v), []);\n\n useEffect(() => {\n if (!openOnAdd) return;\n if (cart.pendingOpCount > lastPendingRef.current && cart.pendingOpCount > 0) {\n setIsOpen(true);\n }\n lastPendingRef.current = cart.pendingOpCount;\n }, [cart.pendingOpCount, openOnAdd]);\n\n const value = useMemo<CartDrawerCtx>(() => ({ isOpen, open, close, toggle }), [isOpen, open, close, toggle]);\n return <CartDrawerContext.Provider value={value}>{children}</CartDrawerContext.Provider>;\n}\n\nexport interface CartDrawerProps {\n /** Called when \"Checkout\" is clicked. Drawer auto-closes first. */\n onCheckout?: () => void;\n /** Called when \"Continue Shopping\" is clicked. Defaults to closing. */\n onContinueShopping?: () => void;\n /** Called when the empty state's CTA is clicked. */\n onShop?: () => void;\n /** Heading. */\n title?: string;\n /** Free-shipping threshold (in business currency, numeric). 0 disables the progress bar. */\n freeShippingThreshold?: number;\n /** Custom class on the panel. */\n className?: string;\n}\n\n/**\n * Animate a number toward `target` over ~250ms. Used for subtotal so it\n * feels alive when items are added/removed.\n */\nfunction useAnimatedNumber(target: number, durationMs = 250) {\n const [value, setValue] = useState(target);\n const fromRef = useRef(target);\n const startRef = useRef<number | null>(null);\n useEffect(() => {\n fromRef.current = value;\n startRef.current = null;\n let raf = 0;\n const tick = (t: number) => {\n if (startRef.current == null) startRef.current = t;\n const p = Math.min(1, (t - startRef.current) / durationMs);\n const eased = 1 - Math.pow(1 - p, 3);\n setValue(fromRef.current + (target - fromRef.current) * eased);\n if (p < 1) raf = requestAnimationFrame(tick);\n };\n raf = requestAnimationFrame(tick);\n return () => cancelAnimationFrame(raf);\n // value intentionally omitted — only re-trigger on target change\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [target, durationMs]);\n return value;\n}\n\nexport function CartDrawer({\n onCheckout,\n onContinueShopping,\n onShop,\n title = \"Cart\",\n freeShippingThreshold = 0,\n className,\n}: CartDrawerProps): React.ReactElement {\n const { isOpen, close } = useCartDrawer();\n const cart = useCart();\n const subtotalNum = parsePrice(cart.subtotal);\n const animatedSubtotal = useAnimatedNumber(subtotalNum);\n\n // Lock body scroll + close on Escape while open.\n useEffect(() => {\n if (!isOpen) return;\n const original = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n const onKey = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") close();\n };\n window.addEventListener(\"keydown\", onKey);\n return () => {\n document.body.style.overflow = original;\n window.removeEventListener(\"keydown\", onKey);\n };\n }, [isOpen, close]);\n\n const handleCheckout = () => {\n close();\n onCheckout?.();\n };\n\n const handleContinue = () => {\n onContinueShopping?.();\n close();\n };\n\n const handleShop = () => {\n onShop?.();\n close();\n };\n\n // Free-shipping progress\n const showShippingBar = freeShippingThreshold > 0 && !cart.isEmpty;\n const remainingForShipping = Math.max(0, freeShippingThreshold - subtotalNum);\n const shippingProgress = freeShippingThreshold > 0 ? Math.min(100, (subtotalNum / freeShippingThreshold) * 100) : 0;\n const shippingUnlocked = subtotalNum >= freeShippingThreshold && freeShippingThreshold > 0;\n\n return (\n <div\n data-cimplify-cart-drawer\n data-open={isOpen ? \"true\" : \"false\"}\n aria-hidden={!isOpen}\n className={cn(\n \"fixed inset-0 z-[200]\",\n isOpen ? \"pointer-events-auto\" : \"pointer-events-none\",\n )}\n >\n {/* Backdrop */}\n <div\n onClick={close}\n className={cn(\n \"absolute inset-0 bg-foreground/40 backdrop-blur-sm transition-opacity duration-300\",\n isOpen ? \"opacity-100\" : \"opacity-0\",\n )}\n />\n\n {/* Panel */}\n <aside\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={title}\n className={cn(\n \"absolute top-0 right-0 h-full w-full sm:max-w-[480px] bg-background shadow-2xl flex flex-col\",\n \"transition-transform duration-300\",\n isOpen ? \"translate-x-0\" : \"translate-x-full\",\n // ease-out cubic-bezier for a \"pull\" feel\n \"[transition-timing-function:cubic-bezier(0.16,1,0.3,1)]\",\n className,\n )}\n >\n {/* Header */}\n <header className=\"relative flex items-center justify-between gap-4 px-6 py-5 shrink-0\">\n <div className=\"flex items-baseline gap-2\">\n <h2 className=\"text-xl font-bold tracking-tight m-0\">{title}</h2>\n {cart.itemCount > 0 && (\n <span className=\"text-sm text-muted-foreground tabular-nums\">\n {cart.itemCount} {cart.itemCount === 1 ? \"item\" : \"items\"}\n </span>\n )}\n </div>\n <button\n type=\"button\"\n onClick={close}\n aria-label=\"Close cart\"\n className=\"grid place-items-center w-9 h-9 rounded-full hover:bg-muted text-muted-foreground hover:text-foreground transition-colors\"\n >\n <svg className=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" viewBox=\"0 0 24 24\" aria-hidden>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </header>\n\n {/* Free-shipping bar */}\n {showShippingBar && (\n <div data-cimplify-cart-shipping-bar className=\"px-6 pb-4 shrink-0\">\n <div className=\"flex items-baseline justify-between gap-2 mb-1.5 text-xs\">\n {shippingUnlocked ? (\n <span className=\"font-medium text-primary\">✓ Free shipping unlocked</span>\n ) : (\n <span className=\"text-muted-foreground\">\n <Price amount={remainingForShipping} className=\"font-semibold text-foreground\" /> away from free shipping\n </span>\n )}\n </div>\n <div className=\"h-1 rounded-full bg-muted overflow-hidden\">\n <div\n className=\"h-full bg-primary transition-[width] duration-500 ease-out\"\n style={{ width: `${shippingProgress}%` }}\n />\n </div>\n </div>\n )}\n\n {/* Items */}\n <div className=\"flex-1 overflow-y-auto px-6\">\n {cart.isEmpty ? (\n <EmptyState onShop={handleShop} />\n ) : (\n <CartSummary showTotals={false} showCheckoutButton={false} />\n )}\n </div>\n\n {/* Footer */}\n {!cart.isEmpty && (\n <footer className=\"border-t border-border px-6 py-5 shrink-0 space-y-3 bg-background\">\n <div className=\"flex items-baseline justify-between\">\n <span className=\"text-sm text-muted-foreground\">Subtotal</span>\n <Price amount={animatedSubtotal} className=\"text-lg font-bold tabular-nums\" />\n </div>\n <p className=\"text-[11px] text-muted-foreground\">\n Tax and shipping calculated at checkout.\n </p>\n <button\n type=\"button\"\n onClick={handleCheckout}\n className=\"w-full h-12 rounded-full bg-foreground text-background font-semibold text-sm hover:bg-foreground/90 active:scale-[0.99] transition-all\"\n >\n Checkout — <Price amount={cart.subtotal} className=\"tabular-nums\" />\n </button>\n <button\n type=\"button\"\n onClick={handleContinue}\n className=\"w-full text-xs font-medium text-muted-foreground hover:text-foreground transition-colors\"\n >\n Continue shopping\n </button>\n <div className=\"flex items-center justify-center gap-1.5 text-[10px] text-muted-foreground/70 uppercase tracking-wider pt-1\">\n <svg className=\"w-3 h-3\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" viewBox=\"0 0 24 24\" aria-hidden>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z\" />\n </svg>\n <span>Secure checkout</span>\n </div>\n </footer>\n )}\n </aside>\n </div>\n );\n}\n\nfunction EmptyState({ onShop }: { onShop: () => void }) {\n return (\n <div data-cimplify-cart-empty className=\"h-full grid place-items-center text-center py-16\">\n <div className=\"space-y-5 max-w-[260px]\">\n <div className=\"relative w-20 h-20 mx-auto\">\n <div className=\"absolute inset-0 rounded-full bg-muted\" />\n <svg\n className=\"relative w-10 h-10 m-auto top-1/2 -translate-y-1/2 text-muted-foreground/60\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n viewBox=\"0 0 24 24\"\n aria-hidden\n >\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M16 11V7a4 4 0 00-8 0v4M5 9h14l-1 12H6L5 9z\" />\n </svg>\n </div>\n <div>\n <p className=\"text-base font-semibold m-0\">Your cart is empty</p>\n <p className=\"text-sm text-muted-foreground mt-1\">\n Discover something you'll love.\n </p>\n </div>\n <button\n type=\"button\"\n onClick={onShop}\n className=\"inline-flex items-center gap-1.5 h-11 px-6 rounded-full bg-foreground text-background text-sm font-semibold hover:bg-foreground/90 active:scale-[0.99] transition-all\"\n >\n Shop now\n <svg className=\"w-3.5 h-3.5\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" viewBox=\"0 0 24 24\" aria-hidden>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M14 5l7 7m0 0l-7 7m7-7H3\" />\n </svg>\n </button>\n </div>\n </div>\n );\n}\n"
15
+ }
16
+ ]
17
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "currency-selector",
3
+ "title": "CurrencySelector",
4
+ "description": "Multi-currency switcher backed by the FX provider — locks display currency and quote ID.",
5
+ "type": "component",
6
+ "registryDependencies": [
7
+ "cn"
8
+ ],
9
+ "files": [
10
+ {
11
+ "path": "currency-selector.tsx",
12
+ "content": "\"use client\";\n\nimport React, { useState, useEffect, useRef } from \"react\";\nimport type { CurrencyCode } from \"@cimplify/sdk\";\nimport type { CimplifyError } from \"@cimplify/sdk\";\nimport type { FxRateResponse } from \"../types/fx\";\nimport { useOptionalCimplifyClient } from \"@cimplify/sdk/react\";\nimport { cn } from \"@cimplify/sdk/react\";\n\nexport interface CurrencySelectorClassNames {\n root?: string;\n option?: string;\n activeOption?: string;\n rate?: string;\n label?: string;\n loading?: string;\n}\n\nexport interface CurrencySelectorProps {\n /** Available currencies to display. */\n currencies: CurrencyCode[];\n /** Currently selected currency. */\n currentCurrency?: CurrencyCode;\n /** Called when a currency is selected. */\n onCurrencyChange?: (currency: CurrencyCode) => void;\n /** Show the exchange rate next to each currency. Requires `baseCurrency`. */\n showRate?: boolean;\n /** Base currency used for rate display. */\n baseCurrency?: CurrencyCode;\n /** Custom currency option renderer. */\n renderCurrency?: (\n currency: CurrencyCode,\n isActive: boolean,\n rate: FxRateResponse | null,\n ) => React.ReactNode;\n className?: string;\n classNames?: CurrencySelectorClassNames;\n}\n\n/**\n * CurrencySelector — currency switcher for international customers.\n *\n * Renders selectable currency options with optional exchange rate display.\n * Returns `null` when there is only one currency (no selector needed).\n */\nexport function CurrencySelector({\n currencies,\n currentCurrency,\n onCurrencyChange,\n showRate = false,\n baseCurrency,\n renderCurrency,\n className,\n classNames,\n}: CurrencySelectorProps): React.ReactElement | null {\n const context = useOptionalCimplifyClient();\n const client = context?.client ?? null;\n\n const [rates, setRates] = useState<Record<string, FxRateResponse>>({});\n const [isLoadingRates, setIsLoadingRates] = useState(false);\n const requestIdRef = useRef(0);\n\n useEffect(() => {\n if (!showRate || !baseCurrency || !client) return;\n\n const targets = currencies.filter((c) => c !== baseCurrency);\n if (targets.length === 0) return;\n\n const nextRequestId = ++requestIdRef.current;\n setIsLoadingRates(true);\n\n void (async () => {\n const fetched: Record<string, FxRateResponse> = {};\n\n await Promise.all(\n targets.map(async (currency) => {\n const res = await client.fx.getRate(baseCurrency, currency);\n if (res.ok) {\n fetched[currency] = res.value;\n }\n }),\n );\n\n if (nextRequestId !== requestIdRef.current) return;\n\n setRates(fetched);\n setIsLoadingRates(false);\n })();\n }, [client, showRate, baseCurrency, currencies]);\n\n if (currencies.length <= 1) {\n return null;\n }\n\n return (\n <div data-cimplify-currency-selector className={cn(className, classNames?.root)}>\n {currencies.map((currency) => {\n const isActive = currentCurrency === currency;\n const rate = rates[currency] ?? null;\n\n return (\n <button\n key={currency}\n type=\"button\"\n onClick={() => onCurrencyChange?.(currency)}\n data-cimplify-currency-option\n data-active={isActive || undefined}\n aria-pressed={isActive}\n className={cn(\n classNames?.option,\n isActive && classNames?.activeOption,\n )}\n >\n {renderCurrency ? (\n renderCurrency(currency, isActive, rate)\n ) : (\n <>\n <span data-cimplify-currency-label className={classNames?.label}>\n {currency}\n </span>\n {showRate && baseCurrency && currency !== baseCurrency && (\n <span data-cimplify-currency-rate className={classNames?.rate}>\n {isLoadingRates\n ? \"...\"\n : rate\n ? `1 ${baseCurrency} = ${rate.rate.toFixed(4)} ${currency}`\n : null}\n </span>\n )}\n </>\n )}\n </button>\n );\n })}\n </div>\n );\n}\n"
13
+ }
14
+ ]
15
+ }