@mmailaender/convex-creem 0.1.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 (383) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +1176 -0
  3. package/dist/client/helpers.d.ts +17 -0
  4. package/dist/client/helpers.d.ts.map +1 -0
  5. package/dist/client/helpers.js +43 -0
  6. package/dist/client/helpers.js.map +1 -0
  7. package/dist/client/index.d.ts +1041 -0
  8. package/dist/client/index.d.ts.map +1 -0
  9. package/dist/client/index.js +1068 -0
  10. package/dist/client/index.js.map +1 -0
  11. package/dist/client/parsers.d.ts +45 -0
  12. package/dist/client/parsers.d.ts.map +1 -0
  13. package/dist/client/parsers.js +138 -0
  14. package/dist/client/parsers.js.map +1 -0
  15. package/dist/client/polyfill.d.ts +2 -0
  16. package/dist/client/polyfill.d.ts.map +1 -0
  17. package/dist/client/polyfill.js +3 -0
  18. package/dist/client/polyfill.js.map +1 -0
  19. package/dist/component/_generated/api.d.ts +36 -0
  20. package/dist/component/_generated/api.d.ts.map +1 -0
  21. package/dist/component/_generated/api.js +31 -0
  22. package/dist/component/_generated/api.js.map +1 -0
  23. package/dist/component/_generated/component.d.ts +542 -0
  24. package/dist/component/_generated/component.d.ts.map +1 -0
  25. package/dist/component/_generated/component.js +11 -0
  26. package/dist/component/_generated/component.js.map +1 -0
  27. package/dist/component/_generated/dataModel.d.ts +46 -0
  28. package/dist/component/_generated/dataModel.d.ts.map +1 -0
  29. package/dist/component/_generated/dataModel.js +11 -0
  30. package/dist/component/_generated/dataModel.js.map +1 -0
  31. package/dist/component/_generated/server.d.ts +121 -0
  32. package/dist/component/_generated/server.d.ts.map +1 -0
  33. package/dist/component/_generated/server.js +78 -0
  34. package/dist/component/_generated/server.js.map +1 -0
  35. package/dist/component/convex.config.d.ts +3 -0
  36. package/dist/component/convex.config.d.ts.map +1 -0
  37. package/dist/component/convex.config.js +3 -0
  38. package/dist/component/convex.config.js.map +1 -0
  39. package/dist/component/lib.d.ts +1005 -0
  40. package/dist/component/lib.d.ts.map +1 -0
  41. package/dist/component/lib.js +647 -0
  42. package/dist/component/lib.js.map +1 -0
  43. package/dist/component/schema.d.ts +191 -0
  44. package/dist/component/schema.d.ts.map +1 -0
  45. package/dist/component/schema.js +104 -0
  46. package/dist/component/schema.js.map +1 -0
  47. package/dist/component/util.d.ts +61 -0
  48. package/dist/component/util.d.ts.map +1 -0
  49. package/dist/component/util.js +142 -0
  50. package/dist/component/util.js.map +1 -0
  51. package/dist/core/catalog.d.ts +18 -0
  52. package/dist/core/catalog.d.ts.map +1 -0
  53. package/dist/core/catalog.js +82 -0
  54. package/dist/core/catalog.js.map +1 -0
  55. package/dist/core/index.d.ts +9 -0
  56. package/dist/core/index.d.ts.map +1 -0
  57. package/dist/core/index.js +9 -0
  58. package/dist/core/index.js.map +1 -0
  59. package/dist/core/markdown.d.ts +12 -0
  60. package/dist/core/markdown.d.ts.map +1 -0
  61. package/dist/core/markdown.js +26 -0
  62. package/dist/core/markdown.js.map +1 -0
  63. package/dist/core/payments.d.ts +11 -0
  64. package/dist/core/payments.d.ts.map +1 -0
  65. package/dist/core/payments.js +27 -0
  66. package/dist/core/payments.js.map +1 -0
  67. package/dist/core/pendingCheckout.d.ts +15 -0
  68. package/dist/core/pendingCheckout.d.ts.map +1 -0
  69. package/dist/core/pendingCheckout.js +40 -0
  70. package/dist/core/pendingCheckout.js.map +1 -0
  71. package/dist/core/resolver.d.ts +11 -0
  72. package/dist/core/resolver.d.ts.map +1 -0
  73. package/dist/core/resolver.js +106 -0
  74. package/dist/core/resolver.js.map +1 -0
  75. package/dist/core/selectors.d.ts +12 -0
  76. package/dist/core/selectors.d.ts.map +1 -0
  77. package/dist/core/selectors.js +18 -0
  78. package/dist/core/selectors.js.map +1 -0
  79. package/dist/core/subscriptionUpdate.d.ts +20 -0
  80. package/dist/core/subscriptionUpdate.d.ts.map +1 -0
  81. package/dist/core/subscriptionUpdate.js +64 -0
  82. package/dist/core/subscriptionUpdate.js.map +1 -0
  83. package/dist/core/types.d.ts +170 -0
  84. package/dist/core/types.d.ts.map +1 -0
  85. package/dist/core/types.js +15 -0
  86. package/dist/core/types.js.map +1 -0
  87. package/dist/design-system/colors/color-utils.d.ts +10 -0
  88. package/dist/design-system/colors/color-utils.d.ts.map +1 -0
  89. package/dist/design-system/colors/color-utils.js +91 -0
  90. package/dist/design-system/colors/color-utils.js.map +1 -0
  91. package/dist/design-system/colors/config.d.ts +33 -0
  92. package/dist/design-system/colors/config.d.ts.map +1 -0
  93. package/dist/design-system/colors/config.js +224 -0
  94. package/dist/design-system/colors/config.js.map +1 -0
  95. package/dist/design-system/colors/index.d.ts +3 -0
  96. package/dist/design-system/colors/index.d.ts.map +1 -0
  97. package/dist/design-system/colors/index.js +3 -0
  98. package/dist/design-system/colors/index.js.map +1 -0
  99. package/dist/design-system/rounded/config.d.ts +31 -0
  100. package/dist/design-system/rounded/config.d.ts.map +1 -0
  101. package/dist/design-system/rounded/config.js +76 -0
  102. package/dist/design-system/rounded/config.js.map +1 -0
  103. package/dist/design-system/rounded/index.d.ts +2 -0
  104. package/dist/design-system/rounded/index.d.ts.map +1 -0
  105. package/dist/design-system/rounded/index.js +2 -0
  106. package/dist/design-system/rounded/index.js.map +1 -0
  107. package/dist/design-system/typography/config.d.ts +55 -0
  108. package/dist/design-system/typography/config.d.ts.map +1 -0
  109. package/dist/design-system/typography/config.js +308 -0
  110. package/dist/design-system/typography/config.js.map +1 -0
  111. package/dist/design-system/typography/index.d.ts +3 -0
  112. package/dist/design-system/typography/index.d.ts.map +1 -0
  113. package/dist/design-system/typography/index.js +3 -0
  114. package/dist/design-system/typography/index.js.map +1 -0
  115. package/dist/design-system/typography/tokens.d.ts +23 -0
  116. package/dist/design-system/typography/tokens.d.ts.map +1 -0
  117. package/dist/design-system/typography/tokens.js +99 -0
  118. package/dist/design-system/typography/tokens.js.map +1 -0
  119. package/dist/react/hooks/useCheckoutSuccessParams.d.ts +2 -0
  120. package/dist/react/hooks/useCheckoutSuccessParams.d.ts.map +1 -0
  121. package/dist/react/hooks/useCheckoutSuccessParams.js +5 -0
  122. package/dist/react/hooks/useCheckoutSuccessParams.js.map +1 -0
  123. package/dist/react/index.d.ts +25 -0
  124. package/dist/react/index.d.ts.map +1 -0
  125. package/dist/react/index.js +22 -0
  126. package/dist/react/index.js.map +1 -0
  127. package/dist/react/primitives/BillingGate.d.ts +8 -0
  128. package/dist/react/primitives/BillingGate.d.ts.map +1 -0
  129. package/dist/react/primitives/BillingGate.js +13 -0
  130. package/dist/react/primitives/BillingGate.js.map +1 -0
  131. package/dist/react/primitives/BillingToggle.d.ts +8 -0
  132. package/dist/react/primitives/BillingToggle.d.ts.map +1 -0
  133. package/dist/react/primitives/BillingToggle.js +12 -0
  134. package/dist/react/primitives/BillingToggle.js.map +1 -0
  135. package/dist/react/primitives/CheckoutButton.d.ts +11 -0
  136. package/dist/react/primitives/CheckoutButton.d.ts.map +1 -0
  137. package/dist/react/primitives/CheckoutButton.js +21 -0
  138. package/dist/react/primitives/CheckoutButton.js.map +1 -0
  139. package/dist/react/primitives/CheckoutSuccessSummary.d.ts +7 -0
  140. package/dist/react/primitives/CheckoutSuccessSummary.d.ts.map +1 -0
  141. package/dist/react/primitives/CheckoutSuccessSummary.js +11 -0
  142. package/dist/react/primitives/CheckoutSuccessSummary.js.map +1 -0
  143. package/dist/react/primitives/CustomerPortalButton.d.ts +8 -0
  144. package/dist/react/primitives/CustomerPortalButton.d.ts.map +1 -0
  145. package/dist/react/primitives/CustomerPortalButton.js +21 -0
  146. package/dist/react/primitives/CustomerPortalButton.js.map +1 -0
  147. package/dist/react/primitives/NumberInput.d.ts +11 -0
  148. package/dist/react/primitives/NumberInput.d.ts.map +1 -0
  149. package/dist/react/primitives/NumberInput.js +18 -0
  150. package/dist/react/primitives/NumberInput.js.map +1 -0
  151. package/dist/react/primitives/OneTimeCheckoutButton.d.ts +11 -0
  152. package/dist/react/primitives/OneTimeCheckoutButton.d.ts.map +1 -0
  153. package/dist/react/primitives/OneTimeCheckoutButton.js +4 -0
  154. package/dist/react/primitives/OneTimeCheckoutButton.js.map +1 -0
  155. package/dist/react/primitives/OneTimePaymentStatusBadge.d.ts +6 -0
  156. package/dist/react/primitives/OneTimePaymentStatusBadge.d.ts.map +1 -0
  157. package/dist/react/primitives/OneTimePaymentStatusBadge.js +11 -0
  158. package/dist/react/primitives/OneTimePaymentStatusBadge.js.map +1 -0
  159. package/dist/react/primitives/PaymentWarningBanner.d.ts +7 -0
  160. package/dist/react/primitives/PaymentWarningBanner.d.ts.map +1 -0
  161. package/dist/react/primitives/PaymentWarningBanner.js +18 -0
  162. package/dist/react/primitives/PaymentWarningBanner.js.map +1 -0
  163. package/dist/react/primitives/PricingCard.d.ts +37 -0
  164. package/dist/react/primitives/PricingCard.d.ts.map +1 -0
  165. package/dist/react/primitives/PricingCard.js +125 -0
  166. package/dist/react/primitives/PricingCard.js.map +1 -0
  167. package/dist/react/primitives/PricingSection.d.ts +39 -0
  168. package/dist/react/primitives/PricingSection.d.ts.map +1 -0
  169. package/dist/react/primitives/PricingSection.js +24 -0
  170. package/dist/react/primitives/PricingSection.js.map +1 -0
  171. package/dist/react/primitives/ScheduledChangeBanner.d.ts +8 -0
  172. package/dist/react/primitives/ScheduledChangeBanner.d.ts.map +1 -0
  173. package/dist/react/primitives/ScheduledChangeBanner.js +13 -0
  174. package/dist/react/primitives/ScheduledChangeBanner.js.map +1 -0
  175. package/dist/react/primitives/SegmentControl.d.ts +11 -0
  176. package/dist/react/primitives/SegmentControl.d.ts.map +1 -0
  177. package/dist/react/primitives/SegmentControl.js +8 -0
  178. package/dist/react/primitives/SegmentControl.js.map +1 -0
  179. package/dist/react/primitives/SegmentGroup.d.ts +14 -0
  180. package/dist/react/primitives/SegmentGroup.d.ts.map +1 -0
  181. package/dist/react/primitives/SegmentGroup.js +11 -0
  182. package/dist/react/primitives/SegmentGroup.js.map +1 -0
  183. package/dist/react/primitives/TrialLimitBanner.d.ts +7 -0
  184. package/dist/react/primitives/TrialLimitBanner.d.ts.map +1 -0
  185. package/dist/react/primitives/TrialLimitBanner.js +14 -0
  186. package/dist/react/primitives/TrialLimitBanner.js.map +1 -0
  187. package/dist/react/shared.d.ts +28 -0
  188. package/dist/react/shared.d.ts.map +1 -0
  189. package/dist/react/shared.js +109 -0
  190. package/dist/react/shared.js.map +1 -0
  191. package/dist/react/widgets/BillingPortal.d.ts +9 -0
  192. package/dist/react/widgets/BillingPortal.d.ts.map +1 -0
  193. package/dist/react/widgets/BillingPortal.js +30 -0
  194. package/dist/react/widgets/BillingPortal.js.map +1 -0
  195. package/dist/react/widgets/ProductItem.d.ts +8 -0
  196. package/dist/react/widgets/ProductItem.d.ts.map +1 -0
  197. package/dist/react/widgets/ProductItem.js +14 -0
  198. package/dist/react/widgets/ProductItem.js.map +1 -0
  199. package/dist/react/widgets/ProductRoot.d.ts +16 -0
  200. package/dist/react/widgets/ProductRoot.d.ts.map +1 -0
  201. package/dist/react/widgets/ProductRoot.js +171 -0
  202. package/dist/react/widgets/ProductRoot.js.map +1 -0
  203. package/dist/react/widgets/SubscriptionItem.d.ts +27 -0
  204. package/dist/react/widgets/SubscriptionItem.d.ts.map +1 -0
  205. package/dist/react/widgets/SubscriptionItem.js +32 -0
  206. package/dist/react/widgets/SubscriptionItem.js.map +1 -0
  207. package/dist/react/widgets/SubscriptionRoot.d.ts +16 -0
  208. package/dist/react/widgets/SubscriptionRoot.d.ts.map +1 -0
  209. package/dist/react/widgets/SubscriptionRoot.js +405 -0
  210. package/dist/react/widgets/SubscriptionRoot.js.map +1 -0
  211. package/dist/react/widgets/index.d.ts +19 -0
  212. package/dist/react/widgets/index.d.ts.map +1 -0
  213. package/dist/react/widgets/index.js +16 -0
  214. package/dist/react/widgets/index.js.map +1 -0
  215. package/dist/react/widgets/productGroupContext.d.ts +6 -0
  216. package/dist/react/widgets/productGroupContext.d.ts.map +1 -0
  217. package/dist/react/widgets/productGroupContext.js +3 -0
  218. package/dist/react/widgets/productGroupContext.js.map +1 -0
  219. package/dist/react/widgets/subscriptionContext.d.ts +6 -0
  220. package/dist/react/widgets/subscriptionContext.d.ts.map +1 -0
  221. package/dist/react/widgets/subscriptionContext.js +3 -0
  222. package/dist/react/widgets/subscriptionContext.js.map +1 -0
  223. package/dist/react/widgets/types.d.ts +171 -0
  224. package/dist/react/widgets/types.d.ts.map +1 -0
  225. package/dist/react/widgets/types.js +2 -0
  226. package/dist/react/widgets/types.js.map +1 -0
  227. package/dist/svelte/index.d.ts +22 -0
  228. package/dist/svelte/index.d.ts.map +1 -0
  229. package/dist/svelte/index.js +20 -0
  230. package/dist/svelte/index.js.map +1 -0
  231. package/dist/svelte/primitives/BillingGate.svelte +28 -0
  232. package/dist/svelte/primitives/BillingToggle.svelte +27 -0
  233. package/dist/svelte/primitives/CheckoutButton.svelte +60 -0
  234. package/dist/svelte/primitives/CheckoutSuccessSummary.svelte +34 -0
  235. package/dist/svelte/primitives/CustomerPortalButton.svelte +60 -0
  236. package/dist/svelte/primitives/NumberInput.svelte +71 -0
  237. package/dist/svelte/primitives/OneTimeCheckoutButton.svelte +37 -0
  238. package/dist/svelte/primitives/OneTimePaymentStatusBadge.svelte +20 -0
  239. package/dist/svelte/primitives/PaymentWarningBanner.svelte +30 -0
  240. package/dist/svelte/primitives/PricingCard.svelte +356 -0
  241. package/dist/svelte/primitives/PricingSection.svelte +121 -0
  242. package/dist/svelte/primitives/ScheduledChangeBanner.svelte +46 -0
  243. package/dist/svelte/primitives/SegmentControl.svelte +38 -0
  244. package/dist/svelte/primitives/SegmentGroup.svelte +52 -0
  245. package/dist/svelte/primitives/TrialLimitBanner.svelte +32 -0
  246. package/dist/svelte/primitives/shared.d.ts +13 -0
  247. package/dist/svelte/primitives/shared.d.ts.map +1 -0
  248. package/dist/svelte/primitives/shared.js +87 -0
  249. package/dist/svelte/primitives/shared.js.map +1 -0
  250. package/dist/svelte/widgets/BillingPortal.svelte +55 -0
  251. package/dist/svelte/widgets/Product.svelte +35 -0
  252. package/dist/svelte/widgets/ProductRoot.svelte +428 -0
  253. package/dist/svelte/widgets/Subscription.svelte +52 -0
  254. package/dist/svelte/widgets/SubscriptionRoot.svelte +690 -0
  255. package/dist/svelte/widgets/index.d.ts +19 -0
  256. package/dist/svelte/widgets/index.d.ts.map +1 -0
  257. package/dist/svelte/widgets/index.js +16 -0
  258. package/dist/svelte/widgets/index.js.map +1 -0
  259. package/dist/svelte/widgets/productGroupContext.d.ts +6 -0
  260. package/dist/svelte/widgets/productGroupContext.d.ts.map +1 -0
  261. package/dist/svelte/widgets/productGroupContext.js +2 -0
  262. package/dist/svelte/widgets/productGroupContext.js.map +1 -0
  263. package/dist/svelte/widgets/subscriptionContext.d.ts +6 -0
  264. package/dist/svelte/widgets/subscriptionContext.d.ts.map +1 -0
  265. package/dist/svelte/widgets/subscriptionContext.js +2 -0
  266. package/dist/svelte/widgets/subscriptionContext.js.map +1 -0
  267. package/dist/svelte/widgets/types.d.ts +171 -0
  268. package/dist/svelte/widgets/types.d.ts.map +1 -0
  269. package/dist/svelte/widgets/types.js +2 -0
  270. package/dist/svelte/widgets/types.js.map +1 -0
  271. package/package.json +182 -0
  272. package/src/client/helpers.test.ts +139 -0
  273. package/src/client/helpers.ts +51 -0
  274. package/src/client/index.test.ts +1554 -0
  275. package/src/client/index.ts +1504 -0
  276. package/src/client/parsers.test.ts +1017 -0
  277. package/src/client/parsers.ts +182 -0
  278. package/src/client/polyfill.ts +2 -0
  279. package/src/component/_generated/api.ts +52 -0
  280. package/src/component/_generated/component.ts +619 -0
  281. package/src/component/_generated/dataModel.ts +60 -0
  282. package/src/component/_generated/server.ts +156 -0
  283. package/src/component/convex.config.ts +3 -0
  284. package/src/component/lib.test.ts +1359 -0
  285. package/src/component/lib.ts +726 -0
  286. package/src/component/schema.ts +112 -0
  287. package/src/component/util.test.ts +281 -0
  288. package/src/component/util.ts +228 -0
  289. package/src/core/catalog.test.ts +212 -0
  290. package/src/core/catalog.ts +119 -0
  291. package/src/core/index.ts +8 -0
  292. package/src/core/markdown.test.ts +43 -0
  293. package/src/core/markdown.ts +26 -0
  294. package/src/core/payments.test.ts +69 -0
  295. package/src/core/payments.ts +33 -0
  296. package/src/core/pendingCheckout.test.ts +44 -0
  297. package/src/core/pendingCheckout.ts +40 -0
  298. package/src/core/resolver.test.ts +283 -0
  299. package/src/core/resolver.ts +160 -0
  300. package/src/core/selectors.test.ts +119 -0
  301. package/src/core/selectors.ts +35 -0
  302. package/src/core/subscriptionUpdate.test.ts +164 -0
  303. package/src/core/subscriptionUpdate.ts +102 -0
  304. package/src/core/types.ts +220 -0
  305. package/src/design-system/README.md +40 -0
  306. package/src/design-system/base.css +27 -0
  307. package/src/design-system/colors/color-utils.ts +110 -0
  308. package/src/design-system/colors/config.ts +282 -0
  309. package/src/design-system/colors/index.ts +2 -0
  310. package/src/design-system/colors/utilities.css +2328 -0
  311. package/src/design-system/components/badges.css +65 -0
  312. package/src/design-system/components/buttons.css +256 -0
  313. package/src/design-system/components/dialog.css +218 -0
  314. package/src/design-system/components/icon-buttons.css +115 -0
  315. package/src/design-system/components/inputs.css +94 -0
  316. package/src/design-system/components/links.css +53 -0
  317. package/src/design-system/components/prose.css +67 -0
  318. package/src/design-system/components/segment-control.css +303 -0
  319. package/src/design-system/index.css +21 -0
  320. package/src/design-system/rounded/config.ts +91 -0
  321. package/src/design-system/rounded/index.ts +1 -0
  322. package/src/design-system/rounded/utilities.css +37 -0
  323. package/src/design-system/typography/config.ts +340 -0
  324. package/src/design-system/typography/index.ts +2 -0
  325. package/src/design-system/typography/tokens.ts +148 -0
  326. package/src/design-system/typography/utilities.css +728 -0
  327. package/src/library.css +20 -0
  328. package/src/react/hooks/useCheckoutSuccessParams.ts +7 -0
  329. package/src/react/index.tsx +47 -0
  330. package/src/react/primitives/BillingGate.tsx +26 -0
  331. package/src/react/primitives/BillingToggle.tsx +29 -0
  332. package/src/react/primitives/CheckoutButton.tsx +47 -0
  333. package/src/react/primitives/CheckoutSuccessSummary.tsx +36 -0
  334. package/src/react/primitives/CustomerPortalButton.tsx +50 -0
  335. package/src/react/primitives/NumberInput.tsx +83 -0
  336. package/src/react/primitives/OneTimeCheckoutButton.tsx +27 -0
  337. package/src/react/primitives/OneTimePaymentStatusBadge.tsx +18 -0
  338. package/src/react/primitives/PaymentWarningBanner.tsx +33 -0
  339. package/src/react/primitives/PricingCard.tsx +421 -0
  340. package/src/react/primitives/PricingSection.tsx +129 -0
  341. package/src/react/primitives/ScheduledChangeBanner.tsx +52 -0
  342. package/src/react/primitives/SegmentControl.tsx +32 -0
  343. package/src/react/primitives/SegmentGroup.tsx +53 -0
  344. package/src/react/primitives/TrialLimitBanner.tsx +32 -0
  345. package/src/react/shared.ts +138 -0
  346. package/src/react/widgets/BillingPortal.tsx +56 -0
  347. package/src/react/widgets/ProductItem.tsx +26 -0
  348. package/src/react/widgets/ProductRoot.tsx +441 -0
  349. package/src/react/widgets/SubscriptionItem.tsx +71 -0
  350. package/src/react/widgets/SubscriptionRoot.tsx +759 -0
  351. package/src/react/widgets/index.ts +36 -0
  352. package/src/react/widgets/productGroupContext.ts +10 -0
  353. package/src/react/widgets/subscriptionContext.ts +10 -0
  354. package/src/react/widgets/types.ts +179 -0
  355. package/src/svelte/index.ts +43 -0
  356. package/src/svelte/primitives/BillingGate.svelte +28 -0
  357. package/src/svelte/primitives/BillingToggle.svelte +27 -0
  358. package/src/svelte/primitives/CheckoutButton.svelte +60 -0
  359. package/src/svelte/primitives/CheckoutSuccessSummary.svelte +34 -0
  360. package/src/svelte/primitives/CustomerPortalButton.svelte +60 -0
  361. package/src/svelte/primitives/NumberInput.svelte +71 -0
  362. package/src/svelte/primitives/OneTimeCheckoutButton.svelte +37 -0
  363. package/src/svelte/primitives/OneTimePaymentStatusBadge.svelte +20 -0
  364. package/src/svelte/primitives/PaymentWarningBanner.svelte +30 -0
  365. package/src/svelte/primitives/PricingCard.svelte +356 -0
  366. package/src/svelte/primitives/PricingSection.svelte +121 -0
  367. package/src/svelte/primitives/ScheduledChangeBanner.svelte +46 -0
  368. package/src/svelte/primitives/SegmentControl.svelte +38 -0
  369. package/src/svelte/primitives/SegmentGroup.svelte +52 -0
  370. package/src/svelte/primitives/TrialLimitBanner.svelte +32 -0
  371. package/src/svelte/primitives/shared.ts +113 -0
  372. package/src/svelte/svelte.d.ts +6 -0
  373. package/src/svelte/widgets/BillingPortal.svelte +55 -0
  374. package/src/svelte/widgets/Product.svelte +35 -0
  375. package/src/svelte/widgets/ProductRoot.svelte +428 -0
  376. package/src/svelte/widgets/Subscription.svelte +52 -0
  377. package/src/svelte/widgets/SubscriptionRoot.svelte +690 -0
  378. package/src/svelte/widgets/index.ts +36 -0
  379. package/src/svelte/widgets/productGroupContext.ts +7 -0
  380. package/src/svelte/widgets/subscriptionContext.ts +7 -0
  381. package/src/svelte/widgets/types.ts +179 -0
  382. package/src/tailwind.css +6 -0
  383. package/src/test.ts +18 -0
@@ -0,0 +1,1017 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import {
3
+ getEventType,
4
+ getEventData,
5
+ getCustomerId,
6
+ getConvexEntityId,
7
+ parseSubscription,
8
+ manualParseSubscription,
9
+ parseCheckout,
10
+ parseProduct,
11
+ } from "./parsers.js";
12
+
13
+ // ── Real webhook payloads from Creem test mode ──────────────────────
14
+
15
+ const CHECKOUT_COMPLETED = {
16
+ id: "evt_3AEBNwl6xjy2VWoNy17RV4",
17
+ eventType: "checkout.completed",
18
+ created_at: 1772265126979,
19
+ object: {
20
+ id: "ch_6u5rCq19LEsXpltlDGtc9Y",
21
+ object: "checkout",
22
+ request_id: "6e75be93-e116-4f30-9d83-c6a5a0021f90",
23
+ order: {
24
+ object: "order",
25
+ id: "ord_7ZKxZkpnNcs0d1TxbBPcr0",
26
+ customer: "cust_6aVJrSJi8h9r7cGzWPVBF0",
27
+ product: "prod_35PR89LmiAjsR8JJwO7uM7",
28
+ amount: 2999,
29
+ currency: "USD",
30
+ sub_total: 2999,
31
+ tax_amount: 500,
32
+ amount_due: 2999,
33
+ amount_paid: 2999,
34
+ status: "paid",
35
+ type: "onetime",
36
+ transaction: "tran_78fYacdKnQu3Bw3wOwtG60",
37
+ created_at: "2026-02-28T07:52:06.979Z",
38
+ updated_at: "2026-02-28T07:52:06.979Z",
39
+ mode: "test",
40
+ },
41
+ product: {
42
+ id: "prod_35PR89LmiAjsR8JJwO7uM7",
43
+ object: "product",
44
+ name: "Test Product",
45
+ description: "A test product for webhook simulation",
46
+ price: 2999,
47
+ currency: "USD",
48
+ billing_type: "onetime",
49
+ billing_period: "once",
50
+ status: "active",
51
+ tax_mode: "exclusive",
52
+ tax_category: "saas",
53
+ default_success_url: null,
54
+ created_at: "2026-02-28T07:52:06.979Z",
55
+ updated_at: "2026-02-28T07:52:06.979Z",
56
+ mode: "test",
57
+ },
58
+ units: 1,
59
+ success_url: "https://example.com/success",
60
+ customer: {
61
+ id: "cust_6aVJrSJi8h9r7cGzWPVBF0",
62
+ object: "customer",
63
+ email: "test-customer@creem.io",
64
+ name: "Test Customer",
65
+ country: "US",
66
+ created_at: "2026-02-28T07:52:06.979Z",
67
+ updated_at: "2026-02-28T07:52:06.979Z",
68
+ mode: "test",
69
+ },
70
+ status: "completed",
71
+ mode: "test",
72
+ },
73
+ };
74
+
75
+ const SUBSCRIPTION_ACTIVE = {
76
+ id: "evt_3eqtj3OyzwBTBlXp1xjVu1",
77
+ eventType: "subscription.active",
78
+ created_at: 1772265324184,
79
+ object: {
80
+ id: "sub_35c5ooVUuIUtru83zXjrjC",
81
+ object: "subscription",
82
+ product: {
83
+ id: "prod_3prhYLElQzQaZMq7pePQqf",
84
+ object: "product",
85
+ name: "Test Subscription Product",
86
+ description: "A test product for webhook simulation",
87
+ price: 2999,
88
+ currency: "USD",
89
+ billing_type: "recurring",
90
+ billing_period: "once",
91
+ status: "active",
92
+ tax_mode: "exclusive",
93
+ tax_category: "saas",
94
+ default_success_url: null,
95
+ created_at: "2026-02-28T07:55:24.184Z",
96
+ updated_at: "2026-02-28T07:55:24.184Z",
97
+ mode: "test",
98
+ },
99
+ customer: {
100
+ id: "cust_50fklVtISQkWoAydmZ1LJb",
101
+ object: "customer",
102
+ email: "test-customer@creem.io",
103
+ name: "Test Customer",
104
+ country: "US",
105
+ created_at: "2026-02-28T07:55:24.184Z",
106
+ updated_at: "2026-02-28T07:55:24.184Z",
107
+ mode: "test",
108
+ },
109
+ collection_method: "charge_automatically",
110
+ status: "active",
111
+ last_transaction_id: "tran_2Fi2fZAyHUl9Q10l55uwUp",
112
+ last_transaction: {
113
+ id: "tran_2Fi2fZAyHUl9Q10l55uwUp",
114
+ object: "transaction",
115
+ amount: 2999,
116
+ amount_paid: 2999,
117
+ currency: "USD",
118
+ type: "invoice",
119
+ tax_country: "US",
120
+ tax_amount: 500,
121
+ status: "paid",
122
+ refunded_amount: null,
123
+ order: "ord_6lP07Pso8FLsMBpoorgVw",
124
+ subscription: "sub_35c5ooVUuIUtru83zXjrjC",
125
+ customer: null,
126
+ description: "Subscription payment",
127
+ period_start: 1772265324184,
128
+ period_end: 1774857324184,
129
+ created_at: 1772265324184,
130
+ mode: "test",
131
+ },
132
+ last_transaction_date: "2026-02-28T07:55:24.184Z",
133
+ next_transaction_date: "2026-03-30T07:55:24.184Z",
134
+ current_period_start_date: "2026-02-28T07:55:24.184Z",
135
+ current_period_end_date: "2026-03-30T07:55:24.184Z",
136
+ canceled_at: null,
137
+ created_at: "2026-02-28T07:55:24.184Z",
138
+ updated_at: "2026-02-28T07:55:24.184Z",
139
+ mode: "test",
140
+ },
141
+ };
142
+
143
+ const SUBSCRIPTION_TRIALING = {
144
+ id: "evt_3LfSrvVOs1SS686wPDeDnD",
145
+ eventType: "subscription.trialing",
146
+ created_at: 1772265341859,
147
+ object: {
148
+ id: "sub_21mGtm0fBnaCSyGpsczjr6",
149
+ object: "subscription",
150
+ product: {
151
+ id: "prod_6ZmY2cEp3XdlhhPiTrNKyj",
152
+ object: "product",
153
+ name: "Test Subscription Product",
154
+ description: "A test product for webhook simulation",
155
+ price: 2999,
156
+ currency: "USD",
157
+ billing_type: "recurring",
158
+ billing_period: "once",
159
+ status: "active",
160
+ tax_mode: "exclusive",
161
+ tax_category: "saas",
162
+ default_success_url: null,
163
+ created_at: "2026-02-28T07:55:41.859Z",
164
+ updated_at: "2026-02-28T07:55:41.859Z",
165
+ mode: "test",
166
+ },
167
+ customer: {
168
+ id: "cust_1aNp6ya9HlZjWCHr4guWkJ",
169
+ object: "customer",
170
+ email: "test-customer@creem.io",
171
+ name: "Test Customer",
172
+ country: "US",
173
+ created_at: "2026-02-28T07:55:41.859Z",
174
+ updated_at: "2026-02-28T07:55:41.859Z",
175
+ mode: "test",
176
+ },
177
+ collection_method: "charge_automatically",
178
+ status: "trialing",
179
+ last_transaction_id: "tran_5WE9hHhjebUOybbOeD5NH4",
180
+ last_transaction: {
181
+ id: "tran_5WE9hHhjebUOybbOeD5NH4",
182
+ object: "transaction",
183
+ amount: 2999,
184
+ amount_paid: 2999,
185
+ currency: "USD",
186
+ type: "invoice",
187
+ tax_country: "US",
188
+ tax_amount: 500,
189
+ status: "paid",
190
+ refunded_amount: null,
191
+ order: "ord_LftfECp6zxFPzrQQsOP2g",
192
+ subscription: "sub_21mGtm0fBnaCSyGpsczjr6",
193
+ customer: null,
194
+ description: "Subscription payment",
195
+ period_start: 1772265341859,
196
+ period_end: 1774857341859,
197
+ created_at: 1772265341859,
198
+ mode: "test",
199
+ },
200
+ last_transaction_date: "2026-02-28T07:55:41.859Z",
201
+ next_transaction_date: "2026-03-30T07:55:41.859Z",
202
+ current_period_start_date: "2026-02-28T07:55:41.859Z",
203
+ current_period_end_date: "2026-03-30T07:55:41.859Z",
204
+ canceled_at: null,
205
+ created_at: "2026-02-28T07:55:41.859Z",
206
+ updated_at: "2026-02-28T07:55:41.859Z",
207
+ mode: "test",
208
+ },
209
+ };
210
+
211
+ const SUBSCRIPTION_CANCELED = {
212
+ id: "evt_4Cdkj8fnfW9VbAYLDtV42A",
213
+ eventType: "subscription.canceled",
214
+ created_at: 1772265355057,
215
+ object: {
216
+ id: "sub_5PHyfaaDkDUmAsKdbH8mZZ",
217
+ object: "subscription",
218
+ product: {
219
+ id: "prod_3Hj6Hd2higtUp5f4gi50vR",
220
+ object: "product",
221
+ name: "Test Subscription Product",
222
+ description: "A test product for webhook simulation",
223
+ price: 2999,
224
+ currency: "USD",
225
+ billing_type: "recurring",
226
+ billing_period: "once",
227
+ status: "active",
228
+ tax_mode: "exclusive",
229
+ tax_category: "saas",
230
+ default_success_url: null,
231
+ created_at: "2026-02-28T07:55:55.057Z",
232
+ updated_at: "2026-02-28T07:55:55.057Z",
233
+ mode: "test",
234
+ },
235
+ customer: {
236
+ id: "cust_3TfrBYcWhn9oX22YP8B5Hc",
237
+ object: "customer",
238
+ email: "test-customer@creem.io",
239
+ name: "Test Customer",
240
+ country: "US",
241
+ created_at: "2026-02-28T07:55:55.057Z",
242
+ updated_at: "2026-02-28T07:55:55.057Z",
243
+ mode: "test",
244
+ },
245
+ collection_method: "charge_automatically",
246
+ status: "canceled",
247
+ last_transaction_id: "tran_53An6EaIasufdERUPSjWK0",
248
+ last_transaction: {
249
+ id: "tran_53An6EaIasufdERUPSjWK0",
250
+ object: "transaction",
251
+ amount: 2999,
252
+ amount_paid: 2999,
253
+ currency: "USD",
254
+ type: "invoice",
255
+ tax_country: "US",
256
+ tax_amount: 500,
257
+ status: "paid",
258
+ refunded_amount: null,
259
+ order: "ord_sIJBcoMp1MB1zlkQTHlFz",
260
+ subscription: "sub_5PHyfaaDkDUmAsKdbH8mZZ",
261
+ customer: null,
262
+ description: "Subscription payment",
263
+ period_start: 1772265355057,
264
+ period_end: 1774857355057,
265
+ created_at: 1772265355057,
266
+ mode: "test",
267
+ },
268
+ last_transaction_date: "2026-02-28T07:55:55.057Z",
269
+ current_period_start_date: "2026-02-28T07:55:55.057Z",
270
+ current_period_end_date: "2026-03-30T07:55:55.057Z",
271
+ canceled_at: "2026-02-28T07:55:55.057Z",
272
+ created_at: "2026-02-28T07:55:55.057Z",
273
+ updated_at: "2026-02-28T07:55:55.057Z",
274
+ mode: "test",
275
+ },
276
+ };
277
+
278
+ const SUBSCRIPTION_SCHEDULED_CANCEL = {
279
+ id: "evt_1o5z94w73NT0MFw8PUCuZt",
280
+ eventType: "subscription.scheduled_cancel",
281
+ created_at: 1772265367282,
282
+ object: {
283
+ id: "sub_5Nq3ne8vXuL0FvU356xKMc",
284
+ object: "subscription",
285
+ product: {
286
+ id: "prod_6naBoKRxbNHIWRhWFKe7ps",
287
+ object: "product",
288
+ name: "Test Subscription Product",
289
+ description: "A test product for webhook simulation",
290
+ price: 2999,
291
+ currency: "USD",
292
+ billing_type: "recurring",
293
+ billing_period: "once",
294
+ status: "active",
295
+ tax_mode: "exclusive",
296
+ tax_category: "saas",
297
+ default_success_url: null,
298
+ created_at: "2026-02-28T07:56:07.282Z",
299
+ updated_at: "2026-02-28T07:56:07.282Z",
300
+ mode: "test",
301
+ },
302
+ customer: {
303
+ id: "cust_1NymKnwUkMBmmA0uJJTy7t",
304
+ object: "customer",
305
+ email: "test-customer@creem.io",
306
+ name: "Test Customer",
307
+ country: "US",
308
+ created_at: "2026-02-28T07:56:07.282Z",
309
+ updated_at: "2026-02-28T07:56:07.282Z",
310
+ mode: "test",
311
+ },
312
+ collection_method: "charge_automatically",
313
+ status: "scheduled_cancel",
314
+ last_transaction_id: "tran_3jEKznrYGcci29IZ7de5TL",
315
+ last_transaction: {
316
+ id: "tran_3jEKznrYGcci29IZ7de5TL",
317
+ object: "transaction",
318
+ amount: 2999,
319
+ amount_paid: 2999,
320
+ currency: "USD",
321
+ type: "invoice",
322
+ tax_country: "US",
323
+ tax_amount: 500,
324
+ status: "paid",
325
+ refunded_amount: null,
326
+ order: "ord_xb6v7H75p0OT8dM25dmz5",
327
+ subscription: "sub_5Nq3ne8vXuL0FvU356xKMc",
328
+ customer: null,
329
+ description: "Subscription payment",
330
+ period_start: 1772265367282,
331
+ period_end: 1774857367282,
332
+ created_at: 1772265367282,
333
+ mode: "test",
334
+ },
335
+ last_transaction_date: "2026-02-28T07:56:07.282Z",
336
+ next_transaction_date: "2026-03-30T07:56:07.282Z",
337
+ current_period_start_date: "2026-02-28T07:56:07.282Z",
338
+ current_period_end_date: "2026-03-30T07:56:07.282Z",
339
+ canceled_at: null,
340
+ created_at: "2026-02-28T07:56:07.282Z",
341
+ updated_at: "2026-02-28T07:56:07.282Z",
342
+ mode: "test",
343
+ },
344
+ };
345
+
346
+ const SUBSCRIPTION_UNPAID = {
347
+ id: "evt_h9hBneNdvWvA8hQzIBzDx",
348
+ eventType: "subscription.unpaid",
349
+ created_at: 1772265400331,
350
+ object: {
351
+ id: "sub_3xx35QzxsnpFiJ3vRB9YKt",
352
+ object: "subscription",
353
+ product: {
354
+ id: "prod_L8mMzoYLOOZpMpTBwGw0k",
355
+ object: "product",
356
+ name: "Test Subscription Product",
357
+ description: "A test product for webhook simulation",
358
+ price: 2999,
359
+ currency: "USD",
360
+ billing_type: "recurring",
361
+ billing_period: "once",
362
+ status: "active",
363
+ tax_mode: "exclusive",
364
+ tax_category: "saas",
365
+ default_success_url: null,
366
+ created_at: "2026-02-28T07:56:40.331Z",
367
+ updated_at: "2026-02-28T07:56:40.331Z",
368
+ mode: "test",
369
+ },
370
+ customer: {
371
+ id: "cust_ubD9UtnJpafXNKQ5UaV0H",
372
+ object: "customer",
373
+ email: "test-customer@creem.io",
374
+ name: "Test Customer",
375
+ country: "US",
376
+ created_at: "2026-02-28T07:56:40.331Z",
377
+ updated_at: "2026-02-28T07:56:40.331Z",
378
+ mode: "test",
379
+ },
380
+ collection_method: "charge_automatically",
381
+ status: "unpaid",
382
+ last_transaction_id: "tran_6uBkPewvO7KHjfvGmpin82",
383
+ last_transaction: {
384
+ id: "tran_6uBkPewvO7KHjfvGmpin82",
385
+ object: "transaction",
386
+ amount: 2999,
387
+ amount_paid: 2999,
388
+ currency: "USD",
389
+ type: "invoice",
390
+ tax_country: "US",
391
+ tax_amount: 500,
392
+ status: "paid",
393
+ refunded_amount: null,
394
+ order: "ord_2vglgbdNf1LXb3jrzt2IF8",
395
+ subscription: "sub_3xx35QzxsnpFiJ3vRB9YKt",
396
+ customer: null,
397
+ description: "Subscription payment",
398
+ period_start: 1772265400331,
399
+ period_end: 1774857400331,
400
+ created_at: 1772265400331,
401
+ mode: "test",
402
+ },
403
+ last_transaction_date: "2026-02-28T07:56:40.331Z",
404
+ next_transaction_date: "2026-03-30T07:56:40.331Z",
405
+ current_period_start_date: "2026-02-28T07:56:40.331Z",
406
+ current_period_end_date: "2026-03-30T07:56:40.331Z",
407
+ canceled_at: null,
408
+ created_at: "2026-02-28T07:56:40.331Z",
409
+ updated_at: "2026-02-28T07:56:40.331Z",
410
+ mode: "test",
411
+ },
412
+ };
413
+
414
+ const SUBSCRIPTION_PAST_DUE = {
415
+ id: "evt_6hBqmAW43oDq570C4cg3JY",
416
+ eventType: "subscription.past_due",
417
+ created_at: 1772265419487,
418
+ object: {
419
+ id: "sub_1WRdOPrNNefOsSXDbPVi2d",
420
+ object: "subscription",
421
+ product: {
422
+ id: "prod_5Oq9fobvw8LxP8MvFQW70n",
423
+ object: "product",
424
+ name: "Test Subscription Product",
425
+ description: "A test product for webhook simulation",
426
+ price: 2999,
427
+ currency: "USD",
428
+ billing_type: "recurring",
429
+ billing_period: "once",
430
+ status: "active",
431
+ tax_mode: "exclusive",
432
+ tax_category: "saas",
433
+ default_success_url: null,
434
+ created_at: "2026-02-28T07:56:59.487Z",
435
+ updated_at: "2026-02-28T07:56:59.487Z",
436
+ mode: "test",
437
+ },
438
+ customer: {
439
+ id: "cust_66csf9rxiSwQhxOYIZCO4J",
440
+ object: "customer",
441
+ email: "test-customer@creem.io",
442
+ name: "Test Customer",
443
+ country: "US",
444
+ created_at: "2026-02-28T07:56:59.487Z",
445
+ updated_at: "2026-02-28T07:56:59.487Z",
446
+ mode: "test",
447
+ },
448
+ collection_method: "charge_automatically",
449
+ status: "past_due",
450
+ last_transaction_id: "tran_5OBGhff2drYg55J4LTdpC9",
451
+ last_transaction: {
452
+ id: "tran_5OBGhff2drYg55J4LTdpC9",
453
+ object: "transaction",
454
+ amount: 2999,
455
+ amount_paid: 2999,
456
+ currency: "USD",
457
+ type: "invoice",
458
+ tax_country: "US",
459
+ tax_amount: 500,
460
+ status: "paid",
461
+ refunded_amount: null,
462
+ order: "ord_5Afy6CLdSbAaKLLfAkr7rS",
463
+ subscription: "sub_1WRdOPrNNefOsSXDbPVi2d",
464
+ customer: null,
465
+ description: "Subscription payment",
466
+ period_start: 1772265419487,
467
+ period_end: 1774857419487,
468
+ created_at: 1772265419487,
469
+ mode: "test",
470
+ },
471
+ last_transaction_date: "2026-02-28T07:56:59.487Z",
472
+ next_transaction_date: "2026-03-30T07:56:59.487Z",
473
+ current_period_start_date: "2026-02-28T07:56:59.487Z",
474
+ current_period_end_date: "2026-03-30T07:56:59.487Z",
475
+ canceled_at: null,
476
+ created_at: "2026-02-28T07:56:59.487Z",
477
+ updated_at: "2026-02-28T07:56:59.487Z",
478
+ mode: "test",
479
+ },
480
+ };
481
+
482
+ const SUBSCRIPTION_PAUSED = {
483
+ id: "evt_390hPwy7T6xdnUat7LnL0V",
484
+ eventType: "subscription.paused",
485
+ created_at: 1772265436355,
486
+ object: {
487
+ id: "sub_4yINJXEk48ie8UrVXW6Hki",
488
+ object: "subscription",
489
+ product: {
490
+ id: "prod_4kwc63La2Hz9CW2lHYGuLT",
491
+ object: "product",
492
+ name: "Test Subscription Product",
493
+ description: "A test product for webhook simulation",
494
+ price: 2999,
495
+ currency: "USD",
496
+ billing_type: "recurring",
497
+ billing_period: "once",
498
+ status: "active",
499
+ tax_mode: "exclusive",
500
+ tax_category: "saas",
501
+ default_success_url: null,
502
+ created_at: "2026-02-28T07:57:16.355Z",
503
+ updated_at: "2026-02-28T07:57:16.355Z",
504
+ mode: "test",
505
+ },
506
+ customer: {
507
+ id: "cust_2tPZEqKOIuXW4ZVuO9aezw",
508
+ object: "customer",
509
+ email: "test-customer@creem.io",
510
+ name: "Test Customer",
511
+ country: "US",
512
+ created_at: "2026-02-28T07:57:16.355Z",
513
+ updated_at: "2026-02-28T07:57:16.355Z",
514
+ mode: "test",
515
+ },
516
+ collection_method: "charge_automatically",
517
+ status: "paused",
518
+ last_transaction_id: "tran_5IBycUgI0ffpwPJrynSsZE",
519
+ last_transaction: {
520
+ id: "tran_5IBycUgI0ffpwPJrynSsZE",
521
+ object: "transaction",
522
+ amount: 2999,
523
+ amount_paid: 2999,
524
+ currency: "USD",
525
+ type: "invoice",
526
+ tax_country: "US",
527
+ tax_amount: 500,
528
+ status: "paid",
529
+ refunded_amount: null,
530
+ order: "ord_7gBxgjGj76o6LO2iAlvqXd",
531
+ subscription: "sub_4yINJXEk48ie8UrVXW6Hki",
532
+ customer: null,
533
+ description: "Subscription payment",
534
+ period_start: 1772265436355,
535
+ period_end: 1774857436355,
536
+ created_at: 1772265436355,
537
+ mode: "test",
538
+ },
539
+ last_transaction_date: "2026-02-28T07:57:16.355Z",
540
+ next_transaction_date: "2026-03-30T07:57:16.355Z",
541
+ current_period_start_date: "2026-02-28T07:57:16.355Z",
542
+ current_period_end_date: "2026-03-30T07:57:16.355Z",
543
+ canceled_at: null,
544
+ created_at: "2026-02-28T07:57:16.355Z",
545
+ updated_at: "2026-02-28T07:57:16.355Z",
546
+ mode: "test",
547
+ },
548
+ };
549
+
550
+ const REFUND_CREATED = {
551
+ id: "evt_2IW5Ur2LYdq3AQDgMyeV3j",
552
+ eventType: "refund.created",
553
+ created_at: 1772265452403,
554
+ object: {
555
+ id: "ref_1mqUZAF4STf0CpHFzT5Qs0",
556
+ object: "refund",
557
+ status: "succeeded",
558
+ refund_amount: 2999,
559
+ refund_currency: "USD",
560
+ reason: "requested_by_customer",
561
+ transaction: {
562
+ id: "tran_3x6ImUPFqSe93cv2z40V2O",
563
+ object: "transaction",
564
+ amount: 2999,
565
+ amount_paid: 2999,
566
+ currency: "USD",
567
+ type: "payment",
568
+ tax_country: "US",
569
+ tax_amount: 500,
570
+ status: "paid",
571
+ refunded_amount: null,
572
+ order: "ord_49E2Q97ydLjZJbPoERGGWl",
573
+ customer: null,
574
+ description: "One-time payment",
575
+ created_at: 1772265452403,
576
+ mode: "test",
577
+ },
578
+ order: {
579
+ object: "order",
580
+ id: "ord_49E2Q97ydLjZJbPoERGGWl",
581
+ customer: "cust_5CApbQUEDoejRwCvbTUGy4",
582
+ product: "prod_7krsbs3yJ0j2SPYuzhlPs5",
583
+ amount: 2999,
584
+ currency: "USD",
585
+ sub_total: 2999,
586
+ tax_amount: 500,
587
+ amount_due: 2999,
588
+ amount_paid: 2999,
589
+ status: "paid",
590
+ type: "onetime",
591
+ transaction: "tran_3x6ImUPFqSe93cv2z40V2O",
592
+ created_at: "2026-02-28T07:57:32.403Z",
593
+ updated_at: "2026-02-28T07:57:32.403Z",
594
+ mode: "test",
595
+ },
596
+ customer: {
597
+ id: "cust_5CApbQUEDoejRwCvbTUGy4",
598
+ object: "customer",
599
+ email: "test-customer@creem.io",
600
+ name: "Test Customer",
601
+ country: "US",
602
+ created_at: "2026-02-28T07:57:32.403Z",
603
+ updated_at: "2026-02-28T07:57:32.403Z",
604
+ mode: "test",
605
+ },
606
+ created_at: 1772265452403,
607
+ mode: "test",
608
+ },
609
+ };
610
+
611
+ const DISPUTE_CREATED = {
612
+ id: "evt_13PSy7K8v9t2rOblwcse52",
613
+ eventType: "dispute.created",
614
+ created_at: 1772265464066,
615
+ object: {
616
+ id: "disp_26jKdaVrGiOQ0qsZ1CUckR",
617
+ object: "dispute",
618
+ amount: 2999,
619
+ currency: "USD",
620
+ transaction: {
621
+ id: "tran_125zeFXV7V3wQTOQcmwwUN",
622
+ object: "transaction",
623
+ amount: 2999,
624
+ amount_paid: 2999,
625
+ currency: "USD",
626
+ type: "payment",
627
+ tax_country: "US",
628
+ tax_amount: 500,
629
+ status: "paid",
630
+ refunded_amount: null,
631
+ order: "ord_21590VszfDxvEbtVRpzJrx",
632
+ customer: null,
633
+ description: "One-time payment",
634
+ created_at: 1772265464066,
635
+ mode: "test",
636
+ },
637
+ order: {
638
+ object: "order",
639
+ id: "ord_21590VszfDxvEbtVRpzJrx",
640
+ customer: "cust_41ejpMs1YtRqCVtu5QE6Mm",
641
+ product: "prod_2KXuN53r0sxlJFV4B6v44Y",
642
+ amount: 2999,
643
+ currency: "USD",
644
+ sub_total: 2999,
645
+ tax_amount: 500,
646
+ amount_due: 2999,
647
+ amount_paid: 2999,
648
+ status: "paid",
649
+ type: "onetime",
650
+ transaction: "tran_125zeFXV7V3wQTOQcmwwUN",
651
+ created_at: "2026-02-28T07:57:44.066Z",
652
+ updated_at: "2026-02-28T07:57:44.066Z",
653
+ mode: "test",
654
+ },
655
+ customer: {
656
+ id: "cust_41ejpMs1YtRqCVtu5QE6Mm",
657
+ object: "customer",
658
+ email: "test-customer@creem.io",
659
+ name: "Test Customer",
660
+ country: "US",
661
+ created_at: "2026-02-28T07:57:44.066Z",
662
+ updated_at: "2026-02-28T07:57:44.066Z",
663
+ mode: "test",
664
+ },
665
+ created_at: 1772265464066,
666
+ mode: "test",
667
+ },
668
+ };
669
+
670
+ // ── Tests ───────────────────────────────────────────────────────────
671
+
672
+ describe("getEventType", () => {
673
+ it("returns eventType from Creem webhook format", () => {
674
+ expect(getEventType(CHECKOUT_COMPLETED)).toBe("checkout.completed");
675
+ });
676
+
677
+ it("returns eventType from subscription events", () => {
678
+ expect(getEventType(SUBSCRIPTION_ACTIVE)).toBe("subscription.active");
679
+ expect(getEventType(SUBSCRIPTION_CANCELED)).toBe("subscription.canceled");
680
+ expect(getEventType(SUBSCRIPTION_PAUSED)).toBe("subscription.paused");
681
+ });
682
+
683
+ it("prefers type over eventType", () => {
684
+ expect(getEventType({ type: "a", eventType: "b" })).toBe("a");
685
+ });
686
+
687
+ it("returns empty string when both are missing", () => {
688
+ expect(getEventType({})).toBe("");
689
+ });
690
+
691
+ it("handles refund and dispute events", () => {
692
+ expect(getEventType(REFUND_CREATED)).toBe("refund.created");
693
+ expect(getEventType(DISPUTE_CREATED)).toBe("dispute.created");
694
+ });
695
+ });
696
+
697
+ describe("getEventData", () => {
698
+ it("returns object from Creem webhook format", () => {
699
+ const data = getEventData(CHECKOUT_COMPLETED);
700
+ expect(data).toBeDefined();
701
+ expect((data as Record<string, unknown>).id).toBe(
702
+ "ch_6u5rCq19LEsXpltlDGtc9Y",
703
+ );
704
+ });
705
+
706
+ it("prefers data over object", () => {
707
+ expect(getEventData({ data: "x", object: "y" })).toBe("x");
708
+ });
709
+
710
+ it("returns undefined when both are missing", () => {
711
+ expect(getEventData({})).toBeUndefined();
712
+ });
713
+
714
+ it("returns subscription object from subscription event", () => {
715
+ const data = getEventData(SUBSCRIPTION_ACTIVE) as Record<string, unknown>;
716
+ expect(data.id).toBe("sub_35c5ooVUuIUtru83zXjrjC");
717
+ expect(data.status).toBe("active");
718
+ });
719
+ });
720
+
721
+ describe("getCustomerId", () => {
722
+ it("returns null for null/undefined", () => {
723
+ expect(getCustomerId(null)).toBeNull();
724
+ expect(getCustomerId(undefined)).toBeNull();
725
+ });
726
+
727
+ it("returns string customer ID directly", () => {
728
+ expect(getCustomerId("cust_123")).toBe("cust_123");
729
+ });
730
+
731
+ it("extracts id from CustomerEntity object", () => {
732
+ const customer = {
733
+ id: "cust_abc",
734
+ object: "customer" as const,
735
+ email: "test@test.com",
736
+ name: "Test",
737
+ country: "US",
738
+ createdAt: new Date(),
739
+ updatedAt: new Date(),
740
+ mode: "test" as const,
741
+ };
742
+ expect(getCustomerId(customer)).toBe("cust_abc");
743
+ });
744
+
745
+ it("returns null for empty string (falsy)", () => {
746
+ expect(getCustomerId("")).toBeNull();
747
+ });
748
+ });
749
+
750
+ describe("getConvexEntityId", () => {
751
+ it("returns null for null/undefined", () => {
752
+ expect(getConvexEntityId(null)).toBeNull();
753
+ expect(getConvexEntityId(undefined)).toBeNull();
754
+ });
755
+
756
+ it("returns null for non-object", () => {
757
+ expect(getConvexEntityId("string")).toBeNull();
758
+ expect(getConvexEntityId(42)).toBeNull();
759
+ });
760
+
761
+ it("prefers convexBillingEntityId over convexUserId", () => {
762
+ expect(
763
+ getConvexEntityId({
764
+ convexBillingEntityId: "org_123",
765
+ convexUserId: "user_456",
766
+ }),
767
+ ).toBe("org_123");
768
+ });
769
+
770
+ it("falls back to convexUserId", () => {
771
+ expect(getConvexEntityId({ convexUserId: "user_789" })).toBe("user_789");
772
+ });
773
+
774
+ it("returns null for empty metadata", () => {
775
+ expect(getConvexEntityId({})).toBeNull();
776
+ });
777
+
778
+ it("returns null when keys have non-string values", () => {
779
+ expect(getConvexEntityId({ convexUserId: 42 })).toBeNull();
780
+ });
781
+ });
782
+
783
+ describe("parseCheckout", () => {
784
+ it("parses a real checkout.completed payload", () => {
785
+ const raw = CHECKOUT_COMPLETED.object as Record<string, unknown>;
786
+ const result = parseCheckout(raw);
787
+ expect(result).not.toBeNull();
788
+ expect(result!.id).toBe("ch_6u5rCq19LEsXpltlDGtc9Y");
789
+ expect(result!.status).toBe("completed");
790
+ expect(result!.mode).toBe("test");
791
+ });
792
+
793
+ it("extracts customer from checkout", () => {
794
+ const raw = CHECKOUT_COMPLETED.object as Record<string, unknown>;
795
+ const result = parseCheckout(raw);
796
+ expect(result).not.toBeNull();
797
+ expect(typeof result!.customer).toBe("object");
798
+ if (typeof result!.customer === "object") {
799
+ expect(result!.customer.id).toBe("cust_6aVJrSJi8h9r7cGzWPVBF0");
800
+ expect(result!.customer.email).toBe("test-customer@creem.io");
801
+ }
802
+ });
803
+
804
+ it("extracts product from checkout", () => {
805
+ const raw = CHECKOUT_COMPLETED.object as Record<string, unknown>;
806
+ const result = parseCheckout(raw);
807
+ expect(result).not.toBeNull();
808
+ if (typeof result!.product === "object") {
809
+ expect(result!.product.id).toBe("prod_35PR89LmiAjsR8JJwO7uM7");
810
+ expect(result!.product.name).toBe("Test Product");
811
+ }
812
+ });
813
+
814
+ it("returns null for invalid data", () => {
815
+ const result = parseCheckout({ id: 123 });
816
+ expect(result).toBeNull();
817
+ });
818
+ });
819
+
820
+ describe("parseProduct", () => {
821
+ it("parses a real product from checkout payload", () => {
822
+ const productData = (CHECKOUT_COMPLETED.object as Record<string, unknown>)
823
+ .product as Record<string, unknown>;
824
+ const result = parseProduct(productData);
825
+ expect(result).not.toBeNull();
826
+ expect(result!.id).toBe("prod_35PR89LmiAjsR8JJwO7uM7");
827
+ expect(result!.name).toBe("Test Product");
828
+ expect(result!.price).toBe(2999);
829
+ expect(result!.currency).toBe("USD");
830
+ expect(result!.billingType).toBe("onetime");
831
+ expect(result!.status).toBe("active");
832
+ });
833
+
834
+ it("parses a recurring product from subscription payload", () => {
835
+ const productData = (SUBSCRIPTION_ACTIVE.object as Record<string, unknown>)
836
+ .product as Record<string, unknown>;
837
+ const result = parseProduct(productData);
838
+ expect(result).not.toBeNull();
839
+ expect(result!.billingType).toBe("recurring");
840
+ });
841
+
842
+ it("returns null for invalid data", () => {
843
+ expect(parseProduct({ bad: "data" })).toBeNull();
844
+ });
845
+ });
846
+
847
+ describe("parseSubscription", () => {
848
+ it("parses active subscription from real webhook", () => {
849
+ const raw = SUBSCRIPTION_ACTIVE.object as Record<string, unknown>;
850
+ const result = parseSubscription(raw);
851
+ expect(result).not.toBeNull();
852
+ expect(result!.id).toBe("sub_35c5ooVUuIUtru83zXjrjC");
853
+ expect(result!.status).toBe("active");
854
+ expect(result!.collectionMethod).toBe("charge_automatically");
855
+ });
856
+
857
+ it("parses trialing subscription", () => {
858
+ const raw = SUBSCRIPTION_TRIALING.object as Record<string, unknown>;
859
+ const result = parseSubscription(raw);
860
+ expect(result).not.toBeNull();
861
+ expect(result!.status).toBe("trialing");
862
+ });
863
+
864
+ it("parses canceled subscription with canceledAt", () => {
865
+ const raw = SUBSCRIPTION_CANCELED.object as Record<string, unknown>;
866
+ const result = parseSubscription(raw);
867
+ expect(result).not.toBeNull();
868
+ expect(result!.status).toBe("canceled");
869
+ expect(result!.canceledAt).not.toBeNull();
870
+ });
871
+
872
+ it("parses scheduled_cancel subscription", () => {
873
+ const raw = SUBSCRIPTION_SCHEDULED_CANCEL.object as Record<string, unknown>;
874
+ const result = parseSubscription(raw);
875
+ expect(result).not.toBeNull();
876
+ // scheduled_cancel may be parsed by SDK or fall back to manual parser
877
+ expect(result!.id).toBe("sub_5Nq3ne8vXuL0FvU356xKMc");
878
+ });
879
+
880
+ it("parses past_due subscription", () => {
881
+ const raw = SUBSCRIPTION_PAST_DUE.object as Record<string, unknown>;
882
+ const result = parseSubscription(raw);
883
+ expect(result).not.toBeNull();
884
+ expect(result!.id).toBe("sub_1WRdOPrNNefOsSXDbPVi2d");
885
+ });
886
+
887
+ it("parses paused subscription", () => {
888
+ const raw = SUBSCRIPTION_PAUSED.object as Record<string, unknown>;
889
+ const result = parseSubscription(raw);
890
+ expect(result).not.toBeNull();
891
+ expect(result!.id).toBe("sub_4yINJXEk48ie8UrVXW6Hki");
892
+ });
893
+
894
+ it("extracts embedded customer object", () => {
895
+ const raw = SUBSCRIPTION_ACTIVE.object as Record<string, unknown>;
896
+ const result = parseSubscription(raw);
897
+ expect(result).not.toBeNull();
898
+ if (typeof result!.customer === "object") {
899
+ expect(result!.customer.id).toBe("cust_50fklVtISQkWoAydmZ1LJb");
900
+ }
901
+ });
902
+
903
+ it("extracts embedded product object", () => {
904
+ const raw = SUBSCRIPTION_ACTIVE.object as Record<string, unknown>;
905
+ const result = parseSubscription(raw);
906
+ expect(result).not.toBeNull();
907
+ if (typeof result!.product === "object") {
908
+ expect(result!.product.id).toBe("prod_3prhYLElQzQaZMq7pePQqf");
909
+ }
910
+ });
911
+
912
+ it("returns a fallback object for garbage data (manual parser is lenient)", () => {
913
+ // manualParseSubscription is intentionally lenient — it's a last-resort fallback
914
+ // for unknown statuses. It produces a partial object rather than null.
915
+ const result = parseSubscription({ random: "garbage" });
916
+ expect(result).not.toBeNull();
917
+ expect(result!.id).toBeUndefined();
918
+ });
919
+ });
920
+
921
+ describe("manualParseSubscription", () => {
922
+ it("parses a subscription with embedded product object", () => {
923
+ const raw = SUBSCRIPTION_ACTIVE.object as Record<string, unknown>;
924
+ const result = manualParseSubscription(raw);
925
+ expect(result).not.toBeNull();
926
+ expect(result!.id).toBe("sub_35c5ooVUuIUtru83zXjrjC");
927
+ expect(result!.status).toBe("active");
928
+ expect(result!.collectionMethod).toBe("charge_automatically");
929
+ });
930
+
931
+ it("handles string product ID", () => {
932
+ const result = manualParseSubscription({
933
+ id: "sub_test",
934
+ status: "active",
935
+ product: "prod_123",
936
+ customer: "cust_456",
937
+ collection_method: "charge_automatically",
938
+ current_period_start_date: "2026-01-01T00:00:00Z",
939
+ current_period_end_date: "2026-02-01T00:00:00Z",
940
+ created_at: "2026-01-01T00:00:00Z",
941
+ updated_at: "2026-01-01T00:00:00Z",
942
+ });
943
+ expect(result).not.toBeNull();
944
+ expect(result!.product).toBe("prod_123");
945
+ });
946
+
947
+ it("handles string customer ID", () => {
948
+ const result = manualParseSubscription({
949
+ id: "sub_test",
950
+ status: "active",
951
+ product: "prod_123",
952
+ customer: "cust_string",
953
+ created_at: "2026-01-01T00:00:00Z",
954
+ updated_at: "2026-01-01T00:00:00Z",
955
+ });
956
+ expect(result).not.toBeNull();
957
+ expect(result!.customer).toBe("cust_string");
958
+ });
959
+
960
+ it("parses customer object to extract id", () => {
961
+ const result = manualParseSubscription({
962
+ id: "sub_test",
963
+ status: "active",
964
+ product: "prod_123",
965
+ customer: { id: "cust_obj_id", email: "x@y.com" },
966
+ created_at: "2026-01-01T00:00:00Z",
967
+ updated_at: "2026-01-01T00:00:00Z",
968
+ });
969
+ expect(result).not.toBeNull();
970
+ expect(result!.customer).toBe("cust_obj_id");
971
+ });
972
+
973
+ it("parses canceledAt date", () => {
974
+ const raw = SUBSCRIPTION_CANCELED.object as Record<string, unknown>;
975
+ const result = manualParseSubscription(raw);
976
+ expect(result).not.toBeNull();
977
+ expect(result!.canceledAt).toBeInstanceOf(Date);
978
+ });
979
+
980
+ it("sets canceledAt to null when not present", () => {
981
+ const raw = SUBSCRIPTION_ACTIVE.object as Record<string, unknown>;
982
+ const result = manualParseSubscription(raw);
983
+ expect(result).not.toBeNull();
984
+ expect(result!.canceledAt).toBeNull();
985
+ });
986
+
987
+ it("defaults mode to test", () => {
988
+ const result = manualParseSubscription({
989
+ id: "sub_no_mode",
990
+ status: "active",
991
+ created_at: "2026-01-01T00:00:00Z",
992
+ updated_at: "2026-01-01T00:00:00Z",
993
+ });
994
+ expect(result).not.toBeNull();
995
+ expect(result!.mode).toBe("test");
996
+ });
997
+
998
+ it("defaults object to subscription", () => {
999
+ const result = manualParseSubscription({
1000
+ id: "sub_no_obj",
1001
+ status: "active",
1002
+ created_at: "2026-01-01T00:00:00Z",
1003
+ updated_at: "2026-01-01T00:00:00Z",
1004
+ });
1005
+ expect(result).not.toBeNull();
1006
+ expect(result!.object).toBe("subscription");
1007
+ });
1008
+
1009
+ it("parses unpaid subscription (SDK may not know this status)", () => {
1010
+ const raw = SUBSCRIPTION_UNPAID.object as Record<string, unknown>;
1011
+ const result = manualParseSubscription(raw);
1012
+ expect(result).not.toBeNull();
1013
+ expect(result!.id).toBe("sub_3xx35QzxsnpFiJ3vRB9YKt");
1014
+ // Status is passed through even if SDK doesn't recognize it
1015
+ expect(result!.status).toBe("unpaid");
1016
+ });
1017
+ });