@coursebuilder/adapter-drizzle 2.1.1 → 3.0.0-canary.0.0c9beb2ae

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 (296) hide show
  1. package/dist/{chunk-KIQLUFJX.js → chunk-2GW3BJE2.js} +2 -2
  2. package/dist/{chunk-M3WM5Q7W.js → chunk-33ZN7FN7.js} +2 -2
  3. package/dist/chunk-4VZJQAPV.js +74 -0
  4. package/dist/chunk-5FLS4ZBX.js +109 -0
  5. package/dist/chunk-5YR5M22S.js +147 -0
  6. package/dist/chunk-6XQ6C3IX.js +18 -0
  7. package/dist/chunk-AIDM2CH6.js +146 -0
  8. package/dist/chunk-ALTODDUX.js +173 -0
  9. package/dist/chunk-AZY2VRAE.js +63 -0
  10. package/dist/chunk-BS7T27VL.js +239 -0
  11. package/dist/chunk-CN3FCRLR.js +55 -0
  12. package/dist/chunk-EAXC4CQD.js +245 -0
  13. package/dist/chunk-EDDG2YFS.js +249 -0
  14. package/dist/chunk-EDKWCMA7.js +210 -0
  15. package/dist/{chunk-7FBSP3VD.js → chunk-EDM6DKA5.js} +2 -2
  16. package/dist/{chunk-EZEQQJNH.js → chunk-EIQBTF5J.js} +2 -2
  17. package/dist/chunk-FYT7RTVW.js +248 -0
  18. package/dist/{chunk-F56AI52P.js → chunk-INZTF6QV.js} +2 -2
  19. package/dist/{chunk-AP5HBIV4.js → chunk-JVHX4JHA.js} +2 -2
  20. package/dist/chunk-LWEKWDLC.js +300 -0
  21. package/dist/chunk-OJPEBGGE.js +139 -0
  22. package/dist/chunk-QKOSV34V.js +16268 -0
  23. package/dist/chunk-QO3QE3LE.js +330 -0
  24. package/dist/chunk-QQKNNFFB.js +399 -0
  25. package/dist/chunk-RBHCV3UW.js +20 -0
  26. package/dist/chunk-TNH2C27F.js +252 -0
  27. package/dist/chunk-TNWNW666.js +133 -0
  28. package/dist/chunk-VBEXYQ6L.js +156 -0
  29. package/dist/chunk-W3ILNMLD.js +265 -0
  30. package/dist/chunk-WJ2ZUXL3.js +71 -0
  31. package/dist/{chunk-PJORQUQP.js → chunk-WUMDFFN3.js} +2 -2
  32. package/dist/{chunk-2AW5KYAK.js → chunk-X7DRYXB7.js} +2 -2
  33. package/dist/{chunk-PNPTKVJ4.js → chunk-XUBZKYH6.js} +24 -15
  34. package/dist/{chunk-WWBSNLRC.js → chunk-ZY4AFKUQ.js} +2 -2
  35. package/dist/index.d.ts +5 -11
  36. package/dist/index.js +41 -18
  37. package/dist/lib/mysql/cms-resource-actions.cjs +15912 -4127
  38. package/dist/lib/mysql/cms-resource-actions.cjs.map +1 -1
  39. package/dist/lib/mysql/cms-resource-actions.d.cts +8 -38
  40. package/dist/lib/mysql/cms-resource-actions.d.ts +8 -38
  41. package/dist/lib/mysql/cms-resource-actions.js +2 -1
  42. package/dist/lib/mysql/context.cjs +3380 -0
  43. package/dist/lib/mysql/context.cjs.map +1 -0
  44. package/dist/lib/mysql/context.d.cts +15 -0
  45. package/dist/lib/mysql/context.d.ts +15 -0
  46. package/dist/lib/mysql/context.js +34 -0
  47. package/dist/lib/mysql/context.js.map +1 -0
  48. package/dist/lib/mysql/index.cjs +21875 -15261
  49. package/dist/lib/mysql/index.cjs.map +1 -1
  50. package/dist/lib/mysql/index.d.cts +24 -14300
  51. package/dist/lib/mysql/index.d.ts +24 -14300
  52. package/dist/lib/mysql/index.js +46 -22
  53. package/dist/lib/mysql/ports/auth-storage.cjs +84 -0
  54. package/dist/lib/mysql/ports/auth-storage.cjs.map +1 -0
  55. package/dist/lib/mysql/ports/auth-storage.d.cts +22 -0
  56. package/dist/lib/mysql/ports/auth-storage.d.ts +22 -0
  57. package/dist/lib/mysql/ports/auth-storage.js +8 -0
  58. package/dist/lib/mysql/ports/auth-storage.js.map +1 -0
  59. package/dist/lib/mysql/ports/catalog.cjs +16688 -0
  60. package/dist/lib/mysql/ports/catalog.cjs.map +1 -0
  61. package/dist/lib/mysql/ports/catalog.d.cts +24 -0
  62. package/dist/lib/mysql/ports/catalog.d.ts +24 -0
  63. package/dist/lib/mysql/ports/catalog.js +9 -0
  64. package/dist/lib/mysql/ports/catalog.js.map +1 -0
  65. package/dist/lib/mysql/ports/content-resource.cjs +321 -0
  66. package/dist/lib/mysql/ports/content-resource.cjs.map +1 -0
  67. package/dist/lib/mysql/ports/content-resource.d.cts +23 -0
  68. package/dist/lib/mysql/ports/content-resource.d.ts +23 -0
  69. package/dist/lib/mysql/ports/content-resource.js +8 -0
  70. package/dist/lib/mysql/ports/content-resource.js.map +1 -0
  71. package/dist/lib/mysql/ports/coupon.cjs +16524 -0
  72. package/dist/lib/mysql/ports/coupon.cjs.map +1 -0
  73. package/dist/lib/mysql/ports/coupon.d.cts +22 -0
  74. package/dist/lib/mysql/ports/coupon.d.ts +22 -0
  75. package/dist/lib/mysql/ports/coupon.js +9 -0
  76. package/dist/lib/mysql/ports/coupon.js.map +1 -0
  77. package/dist/lib/mysql/ports/entitlement.cjs +16350 -0
  78. package/dist/lib/mysql/ports/entitlement.cjs.map +1 -0
  79. package/dist/lib/mysql/ports/entitlement.d.cts +22 -0
  80. package/dist/lib/mysql/ports/entitlement.d.ts +22 -0
  81. package/dist/lib/mysql/ports/entitlement.js +9 -0
  82. package/dist/lib/mysql/ports/entitlement.js.map +1 -0
  83. package/dist/lib/mysql/ports/identity.cjs +154 -0
  84. package/dist/lib/mysql/ports/identity.cjs.map +1 -0
  85. package/dist/lib/mysql/ports/identity.d.cts +22 -0
  86. package/dist/lib/mysql/ports/identity.d.ts +22 -0
  87. package/dist/lib/mysql/ports/identity.js +8 -0
  88. package/dist/lib/mysql/ports/identity.js.map +1 -0
  89. package/dist/lib/mysql/ports/merchant.cjs +194 -0
  90. package/dist/lib/mysql/ports/merchant.cjs.map +1 -0
  91. package/dist/lib/mysql/ports/merchant.d.cts +22 -0
  92. package/dist/lib/mysql/ports/merchant.d.ts +22 -0
  93. package/dist/lib/mysql/ports/merchant.js +8 -0
  94. package/dist/lib/mysql/ports/merchant.js.map +1 -0
  95. package/dist/lib/mysql/ports/organization.cjs +16426 -0
  96. package/dist/lib/mysql/ports/organization.cjs.map +1 -0
  97. package/dist/lib/mysql/ports/organization.d.cts +22 -0
  98. package/dist/lib/mysql/ports/organization.d.ts +22 -0
  99. package/dist/lib/mysql/ports/organization.js +9 -0
  100. package/dist/lib/mysql/ports/organization.js.map +1 -0
  101. package/dist/lib/mysql/ports/progress.cjs +16544 -0
  102. package/dist/lib/mysql/ports/progress.cjs.map +1 -0
  103. package/dist/lib/mysql/ports/progress.d.cts +22 -0
  104. package/dist/lib/mysql/ports/progress.d.ts +22 -0
  105. package/dist/lib/mysql/ports/progress.js +9 -0
  106. package/dist/lib/mysql/ports/progress.js.map +1 -0
  107. package/dist/lib/mysql/ports/purchase.cjs +16609 -0
  108. package/dist/lib/mysql/ports/purchase.cjs.map +1 -0
  109. package/dist/lib/mysql/ports/purchase.d.cts +23 -0
  110. package/dist/lib/mysql/ports/purchase.d.ts +23 -0
  111. package/dist/lib/mysql/ports/purchase.js +9 -0
  112. package/dist/lib/mysql/ports/purchase.js.map +1 -0
  113. package/dist/lib/mysql/ports/subscription.cjs +76 -0
  114. package/dist/lib/mysql/ports/subscription.cjs.map +1 -0
  115. package/dist/lib/mysql/ports/subscription.d.cts +22 -0
  116. package/dist/lib/mysql/ports/subscription.d.ts +22 -0
  117. package/dist/lib/mysql/ports/subscription.js +8 -0
  118. package/dist/lib/mysql/ports/subscription.js.map +1 -0
  119. package/dist/lib/mysql/schema.cjs +3373 -0
  120. package/dist/lib/mysql/schema.cjs.map +1 -0
  121. package/dist/lib/mysql/schema.d.cts +14385 -0
  122. package/dist/lib/mysql/schema.d.ts +14385 -0
  123. package/dist/lib/mysql/schema.js +35 -0
  124. package/dist/lib/mysql/schema.js.map +1 -0
  125. package/dist/lib/mysql/schemas/auth/accounts.cjs.map +1 -1
  126. package/dist/lib/mysql/schemas/auth/accounts.js +4 -4
  127. package/dist/lib/mysql/schemas/auth/device-access-token.cjs.map +1 -1
  128. package/dist/lib/mysql/schemas/auth/device-access-token.js +5 -5
  129. package/dist/lib/mysql/schemas/auth/device-verification.cjs.map +1 -1
  130. package/dist/lib/mysql/schemas/auth/device-verification.js +5 -5
  131. package/dist/lib/mysql/schemas/auth/personal-access-token.cjs.map +1 -1
  132. package/dist/lib/mysql/schemas/auth/personal-access-token.js +5 -5
  133. package/dist/lib/mysql/schemas/auth/profiles.cjs.map +1 -1
  134. package/dist/lib/mysql/schemas/auth/profiles.js +4 -4
  135. package/dist/lib/mysql/schemas/auth/role-permissions.cjs.map +1 -1
  136. package/dist/lib/mysql/schemas/auth/role-permissions.js +5 -5
  137. package/dist/lib/mysql/schemas/auth/roles.cjs.map +1 -1
  138. package/dist/lib/mysql/schemas/auth/roles.js +4 -4
  139. package/dist/lib/mysql/schemas/auth/sessions.cjs.map +1 -1
  140. package/dist/lib/mysql/schemas/auth/sessions.js +5 -5
  141. package/dist/lib/mysql/schemas/auth/user-permissions.cjs.map +1 -1
  142. package/dist/lib/mysql/schemas/auth/user-permissions.js +4 -4
  143. package/dist/lib/mysql/schemas/auth/user-prefs.cjs.map +1 -1
  144. package/dist/lib/mysql/schemas/auth/user-prefs.js +4 -4
  145. package/dist/lib/mysql/schemas/auth/user-roles.cjs.map +1 -1
  146. package/dist/lib/mysql/schemas/auth/user-roles.js +4 -4
  147. package/dist/lib/mysql/schemas/auth/users.cjs +5 -1
  148. package/dist/lib/mysql/schemas/auth/users.cjs.map +1 -1
  149. package/dist/lib/mysql/schemas/auth/users.js +4 -4
  150. package/dist/lib/mysql/schemas/commerce/coupon.cjs.map +1 -1
  151. package/dist/lib/mysql/schemas/commerce/coupon.js +4 -4
  152. package/dist/lib/mysql/schemas/commerce/merchant-charge.cjs.map +1 -1
  153. package/dist/lib/mysql/schemas/commerce/merchant-charge.js +4 -4
  154. package/dist/lib/mysql/schemas/commerce/merchant-subscription.cjs.map +1 -1
  155. package/dist/lib/mysql/schemas/commerce/merchant-subscription.js +4 -4
  156. package/dist/lib/mysql/schemas/commerce/price.cjs.map +1 -1
  157. package/dist/lib/mysql/schemas/commerce/price.js +4 -4
  158. package/dist/lib/mysql/schemas/commerce/product.cjs.map +1 -1
  159. package/dist/lib/mysql/schemas/commerce/product.js +4 -4
  160. package/dist/lib/mysql/schemas/commerce/purchase-user-transfer.cjs.map +1 -1
  161. package/dist/lib/mysql/schemas/commerce/purchase-user-transfer.js +5 -5
  162. package/dist/lib/mysql/schemas/commerce/purchase.cjs +11 -2
  163. package/dist/lib/mysql/schemas/commerce/purchase.cjs.map +1 -1
  164. package/dist/lib/mysql/schemas/commerce/purchase.js +4 -4
  165. package/dist/lib/mysql/schemas/commerce/subscription.cjs +6 -1
  166. package/dist/lib/mysql/schemas/commerce/subscription.cjs.map +1 -1
  167. package/dist/lib/mysql/schemas/commerce/subscription.js +4 -4
  168. package/dist/lib/mysql/schemas/commerce/upgradable-products.cjs.map +1 -1
  169. package/dist/lib/mysql/schemas/commerce/upgradable-products.js +5 -5
  170. package/dist/lib/mysql/schemas/communication/comment.cjs +5 -1
  171. package/dist/lib/mysql/schemas/communication/comment.cjs.map +1 -1
  172. package/dist/lib/mysql/schemas/communication/comment.js +4 -4
  173. package/dist/lib/mysql/schemas/communication/communication-preferences.cjs +5 -1
  174. package/dist/lib/mysql/schemas/communication/communication-preferences.cjs.map +1 -1
  175. package/dist/lib/mysql/schemas/communication/communication-preferences.js +4 -4
  176. package/dist/lib/mysql/schemas/communication/question-response.cjs.map +1 -1
  177. package/dist/lib/mysql/schemas/communication/question-response.js +5 -5
  178. package/dist/lib/mysql/schemas/content/content-contributions.cjs +5 -1
  179. package/dist/lib/mysql/schemas/content/content-contributions.cjs.map +1 -1
  180. package/dist/lib/mysql/schemas/content/content-contributions.js +4 -4
  181. package/dist/lib/mysql/schemas/content/content-resource-product.cjs.map +1 -1
  182. package/dist/lib/mysql/schemas/content/content-resource-product.js +4 -4
  183. package/dist/lib/mysql/schemas/content/content-resource-resource.cjs.map +1 -1
  184. package/dist/lib/mysql/schemas/content/content-resource-resource.js +4 -4
  185. package/dist/lib/mysql/schemas/content/content-resource-tag.cjs.map +1 -1
  186. package/dist/lib/mysql/schemas/content/content-resource-tag.js +4 -4
  187. package/dist/lib/mysql/schemas/content/content-resource-version.cjs.map +1 -1
  188. package/dist/lib/mysql/schemas/content/content-resource-version.js +4 -4
  189. package/dist/lib/mysql/schemas/content/content-resource.cjs +5 -1
  190. package/dist/lib/mysql/schemas/content/content-resource.cjs.map +1 -1
  191. package/dist/lib/mysql/schemas/content/content-resource.js +4 -4
  192. package/dist/lib/mysql/schemas/content/tag-tag.cjs.map +1 -1
  193. package/dist/lib/mysql/schemas/content/tag-tag.js +4 -4
  194. package/dist/lib/mysql/schemas/content/tag.cjs.map +1 -1
  195. package/dist/lib/mysql/schemas/content/tag.js +4 -4
  196. package/dist/lib/mysql/schemas/entitlements/entitlement.cjs +11 -2
  197. package/dist/lib/mysql/schemas/entitlements/entitlement.cjs.map +1 -1
  198. package/dist/lib/mysql/schemas/entitlements/entitlement.js +5 -5
  199. package/dist/lib/mysql/schemas/org/organization-membership-roles.cjs +11 -2
  200. package/dist/lib/mysql/schemas/org/organization-membership-roles.cjs.map +1 -1
  201. package/dist/lib/mysql/schemas/org/organization-membership-roles.js +4 -4
  202. package/dist/lib/mysql/schemas/org/organization-memberships.cjs +11 -2
  203. package/dist/lib/mysql/schemas/org/organization-memberships.cjs.map +1 -1
  204. package/dist/lib/mysql/schemas/org/organization-memberships.d.cts +16 -0
  205. package/dist/lib/mysql/schemas/org/organization-memberships.d.ts +16 -0
  206. package/dist/lib/mysql/schemas/org/organization-memberships.js +4 -4
  207. package/dist/lib/mysql/schemas/org/organizations.cjs +11 -2
  208. package/dist/lib/mysql/schemas/org/organizations.cjs.map +1 -1
  209. package/dist/lib/mysql/schemas/org/organizations.d.cts +16 -0
  210. package/dist/lib/mysql/schemas/org/organizations.d.ts +16 -0
  211. package/dist/lib/mysql/schemas/org/organizations.js +4 -4
  212. package/dist/lib/mysql/workflows/content-resource-workflow-utils.cjs +40 -0
  213. package/dist/lib/mysql/workflows/content-resource-workflow-utils.cjs.map +1 -0
  214. package/dist/lib/mysql/workflows/content-resource-workflow-utils.d.cts +4 -0
  215. package/dist/lib/mysql/workflows/content-resource-workflow-utils.d.ts +4 -0
  216. package/dist/lib/mysql/workflows/content-resource-workflow-utils.js +10 -0
  217. package/dist/lib/mysql/workflows/content-resource-workflow-utils.js.map +1 -0
  218. package/dist/lib/mysql/workflows/create-cohort.cjs +184 -0
  219. package/dist/lib/mysql/workflows/create-cohort.cjs.map +1 -0
  220. package/dist/lib/mysql/workflows/create-cohort.d.cts +24 -0
  221. package/dist/lib/mysql/workflows/create-cohort.d.ts +24 -0
  222. package/dist/lib/mysql/workflows/create-cohort.js +9 -0
  223. package/dist/lib/mysql/workflows/create-cohort.js.map +1 -0
  224. package/dist/lib/mysql/workflows/create-event-series.cjs +283 -0
  225. package/dist/lib/mysql/workflows/create-event-series.cjs.map +1 -0
  226. package/dist/lib/mysql/workflows/create-event-series.d.cts +24 -0
  227. package/dist/lib/mysql/workflows/create-event-series.d.ts +24 -0
  228. package/dist/lib/mysql/workflows/create-event-series.js +9 -0
  229. package/dist/lib/mysql/workflows/create-event-series.js.map +1 -0
  230. package/dist/lib/mysql/workflows/create-event.cjs +194 -0
  231. package/dist/lib/mysql/workflows/create-event.cjs.map +1 -0
  232. package/dist/lib/mysql/workflows/create-event.d.cts +24 -0
  233. package/dist/lib/mysql/workflows/create-event.d.ts +24 -0
  234. package/dist/lib/mysql/workflows/create-event.js +9 -0
  235. package/dist/lib/mysql/workflows/create-event.js.map +1 -0
  236. package/dist/lib/mysql/workflows/create-merchant-charge-and-purchase.cjs +231 -0
  237. package/dist/lib/mysql/workflows/create-merchant-charge-and-purchase.cjs.map +1 -0
  238. package/dist/lib/mysql/workflows/create-merchant-charge-and-purchase.d.cts +22 -0
  239. package/dist/lib/mysql/workflows/create-merchant-charge-and-purchase.d.ts +22 -0
  240. package/dist/lib/mysql/workflows/create-merchant-charge-and-purchase.js +8 -0
  241. package/dist/lib/mysql/workflows/create-merchant-charge-and-purchase.js.map +1 -0
  242. package/dist/lib/mysql/workflows/create-workshop.cjs +286 -0
  243. package/dist/lib/mysql/workflows/create-workshop.cjs.map +1 -0
  244. package/dist/lib/mysql/workflows/create-workshop.d.cts +24 -0
  245. package/dist/lib/mysql/workflows/create-workshop.d.ts +24 -0
  246. package/dist/lib/mysql/workflows/create-workshop.js +9 -0
  247. package/dist/lib/mysql/workflows/create-workshop.js.map +1 -0
  248. package/dist/lib/mysql/workflows/redeem-full-price-coupon.cjs +130 -0
  249. package/dist/lib/mysql/workflows/redeem-full-price-coupon.cjs.map +1 -0
  250. package/dist/lib/mysql/workflows/redeem-full-price-coupon.d.cts +24 -0
  251. package/dist/lib/mysql/workflows/redeem-full-price-coupon.d.ts +24 -0
  252. package/dist/lib/mysql/workflows/redeem-full-price-coupon.js +8 -0
  253. package/dist/lib/mysql/workflows/redeem-full-price-coupon.js.map +1 -0
  254. package/dist/lib/mysql/workflows/transfer-purchase-to-user.cjs +95 -0
  255. package/dist/lib/mysql/workflows/transfer-purchase-to-user.cjs.map +1 -0
  256. package/dist/lib/mysql/workflows/transfer-purchase-to-user.d.cts +30 -0
  257. package/dist/lib/mysql/workflows/transfer-purchase-to-user.d.ts +30 -0
  258. package/dist/lib/mysql/workflows/transfer-purchase-to-user.js +8 -0
  259. package/dist/lib/mysql/workflows/transfer-purchase-to-user.js.map +1 -0
  260. package/dist/lib/utils.d.cts +1 -10
  261. package/dist/lib/utils.d.ts +1 -10
  262. package/dist/types.d.ts +14 -0
  263. package/dist/types.js +1 -0
  264. package/package.json +9 -5
  265. package/src/index.ts +3 -2
  266. package/src/lib/mysql/context.ts +20 -0
  267. package/src/lib/mysql/index.ts +118 -4873
  268. package/src/lib/mysql/ports/auth-storage.ts +139 -0
  269. package/src/lib/mysql/ports/catalog.ts +610 -0
  270. package/src/lib/mysql/ports/content-resource.ts +442 -0
  271. package/src/lib/mysql/ports/coupon.ts +387 -0
  272. package/src/lib/mysql/ports/entitlement.ts +96 -0
  273. package/src/lib/mysql/ports/identity.ts +160 -0
  274. package/src/lib/mysql/ports/merchant.ts +274 -0
  275. package/src/lib/mysql/ports/organization.ts +216 -0
  276. package/src/lib/mysql/ports/progress.ts +366 -0
  277. package/src/lib/mysql/ports/purchase.ts +517 -0
  278. package/src/lib/mysql/ports/subscription.ts +93 -0
  279. package/src/lib/mysql/schema.ts +374 -0
  280. package/src/lib/mysql/schemas/org/organization-memberships.ts +7 -0
  281. package/src/lib/mysql/schemas/org/organizations.ts +8 -0
  282. package/src/lib/mysql/workflows/content-resource-workflow-utils.ts +22 -0
  283. package/src/lib/mysql/workflows/create-cohort.ts +239 -0
  284. package/src/lib/mysql/workflows/create-event-series.ts +384 -0
  285. package/src/lib/mysql/workflows/create-event.ts +232 -0
  286. package/src/lib/mysql/workflows/create-merchant-charge-and-purchase.ts +365 -0
  287. package/src/lib/mysql/workflows/create-workshop.ts +372 -0
  288. package/src/lib/mysql/workflows/redeem-full-price-coupon.ts +179 -0
  289. package/src/lib/mysql/workflows/transfer-purchase-to-user.ts +142 -0
  290. package/src/types.ts +33 -0
  291. package/dist/authjs.d.ts +0 -27
  292. package/dist/authjs.js +0 -1
  293. package/dist/chunk-62VSIVME.js +0 -4714
  294. package/dist/chunk-RVXF3JZ7.js +0 -8732
  295. package/dist/chunk-WEJL5OFI.js +0 -8732
  296. package/src/authjs.ts +0 -34
@@ -0,0 +1,517 @@
1
+ import { and, asc, eq, inArray, isNull, not, or } from 'drizzle-orm'
2
+ import { v4 } from 'uuid'
3
+ import { z } from 'zod'
4
+
5
+ import type { PurchasePort } from '@coursebuilder/core/ports'
6
+ import {
7
+ merchantChargeSchema,
8
+ purchaseSchema,
9
+ purchaseUserTransferSchema,
10
+ upgradableProductSchema,
11
+ userSchema,
12
+ type Purchase,
13
+ type PurchaseUserTransfer,
14
+ type PurchaseUserTransferState,
15
+ type UpgradableProduct,
16
+ type User,
17
+ } from '@coursebuilder/core/schemas'
18
+ import { logger as baseLogger } from '@coursebuilder/utils/logger'
19
+
20
+ import type { MySqlContext } from '../context.js'
21
+
22
+ const logger = baseLogger as unknown as {
23
+ debug: (message: string, attrs?: Record<string, unknown>) => void
24
+ error: (error: Error, attrs?: Record<string, unknown>) => void
25
+ }
26
+
27
+ type OrdinaryPurchasePort = Omit<
28
+ PurchasePort,
29
+ | 'createMerchantChargeAndPurchase'
30
+ | 'redeemFullPriceCoupon'
31
+ | 'transferPurchaseToUser'
32
+ >
33
+
34
+ function toError(error: unknown): Error {
35
+ return error instanceof Error ? error : new Error(String(error))
36
+ }
37
+
38
+ /**
39
+ * Creates the MySQL implementation of purchase persistence operations.
40
+ *
41
+ * @param context - The shared MySQL client and table set.
42
+ * @returns All fifteen PurchasePort operations.
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * const purchasePort = createPurchasePort(context)
47
+ * ```
48
+ */
49
+ export function createPurchasePort({
50
+ client,
51
+ tables,
52
+ }: MySqlContext): OrdinaryPurchasePort {
53
+ const {
54
+ purchases: purchaseTable,
55
+ purchaseUserTransfer,
56
+ merchantCharge,
57
+ merchantCustomer,
58
+ merchantSession,
59
+ upgradableProducts,
60
+ } = tables
61
+
62
+ const purchasePort: OrdinaryPurchasePort = {
63
+ createPurchaseTransfer: async (options) => {
64
+ const id = `put_${v4()}`
65
+ await client.insert(purchaseUserTransfer).values({
66
+ id,
67
+ purchaseId: options.purchaseId,
68
+ sourceUserId: options.sourceUserId,
69
+ expiresAt: options.expiresAt,
70
+ })
71
+ },
72
+
73
+ getExistingNonBulkValidPurchasesOfProduct: async ({
74
+ userId,
75
+ productId,
76
+ }) => {
77
+ const existingPurchases = await client.query.purchases.findMany({
78
+ where: and(
79
+ eq(purchaseTable.userId, userId),
80
+ productId ? eq(purchaseTable.productId, productId) : undefined,
81
+ eq(purchaseTable.status, 'Valid'),
82
+ isNull(purchaseTable.bulkCouponId),
83
+ ),
84
+ })
85
+
86
+ return z.array(purchaseSchema).parse(existingPurchases)
87
+ },
88
+ async getPurchasesForBulkCouponId(
89
+ bulkCouponId: string,
90
+ ): Promise<(Purchase & { user: User })[]> {
91
+ return z.array(purchaseSchema.extend({ user: userSchema })).parse(
92
+ await client.query.purchases.findMany({
93
+ where: eq(purchaseTable.bulkCouponId, bulkCouponId),
94
+ with: {
95
+ user: true,
96
+ },
97
+ }),
98
+ )
99
+ },
100
+
101
+ async getPurchaseCountForProduct(productId: string): Promise<number> {
102
+ return await client.query.purchases
103
+ .findMany({
104
+ where: and(
105
+ eq(purchaseTable.productId, productId),
106
+ inArray(purchaseTable.status, ['Valid', 'Restricted']),
107
+ ),
108
+ })
109
+ .then((res) => res.length)
110
+ },
111
+ async getPurchase(purchaseId: string): Promise<Purchase | null> {
112
+ const purchase = await client.query.purchases.findFirst({
113
+ where: eq(purchaseTable.id, purchaseId),
114
+ })
115
+
116
+ return purchase ? purchaseSchema.parse(purchase) : null
117
+ },
118
+ async createPurchase(options): Promise<Purchase> {
119
+ const newPurchaseId = options.id || `purchase-${v4()}`
120
+ await client.insert(purchaseTable).values({
121
+ ...options,
122
+ id: newPurchaseId,
123
+ })
124
+
125
+ const newPurchase = await purchasePort.getPurchase(newPurchaseId)
126
+
127
+ return purchaseSchema.parse(newPurchase)
128
+ },
129
+
130
+ async getPurchaseForStripeCharge(
131
+ stripeChargeId: string,
132
+ ): Promise<Purchase | null> {
133
+ logger.debug('getPurchaseForStripeCharge', { stripeChargeId })
134
+
135
+ const chargeForPurchase = merchantChargeSchema.nullable().parse(
136
+ (await client.query.merchantCharge.findFirst({
137
+ where: eq(merchantCharge.identifier, stripeChargeId),
138
+ })) || null,
139
+ )
140
+
141
+ if (!chargeForPurchase) {
142
+ // Expected during initial checkout processing — the merchant
143
+ // charge hasn't been created yet. Also expected when the
144
+ // payment-status-checker passes a PaymentIntent ID (pi_xxx)
145
+ // instead of a Charge ID (ch_xxx).
146
+ logger.debug('getPurchaseForStripeCharge: no matching charge', {
147
+ stripeChargeId,
148
+ idPrefix: stripeChargeId?.substring(0, 3),
149
+ })
150
+ return null
151
+ }
152
+
153
+ const purchase = purchaseSchema.safeParse(
154
+ await client.query.purchases.findFirst({
155
+ where: eq(purchaseTable.merchantChargeId, chargeForPurchase.id),
156
+ with: {
157
+ user: true,
158
+ product: true,
159
+ bulkCoupon: true,
160
+ },
161
+ }),
162
+ )
163
+
164
+ if (!purchase.success) {
165
+ return null
166
+ }
167
+
168
+ return purchase.data
169
+ },
170
+ async getPurchaseByCheckoutSessionId(
171
+ checkoutSessionId: string,
172
+ ): Promise<Purchase | null> {
173
+ logger.debug('getPurchaseByCheckoutSessionId', { checkoutSessionId })
174
+
175
+ // Find the MerchantSession by its identifier (the Stripe checkout session ID)
176
+ const session = await client.query.merchantSession.findFirst({
177
+ where: eq(merchantSession.identifier, checkoutSessionId),
178
+ })
179
+
180
+ if (!session) {
181
+ logger.debug('getPurchaseByCheckoutSessionId: no matching session', {
182
+ checkoutSessionId,
183
+ })
184
+ return null
185
+ }
186
+
187
+ const purchase = purchaseSchema.safeParse(
188
+ await client.query.purchases.findFirst({
189
+ where: eq(purchaseTable.merchantSessionId, String(session.id)),
190
+ with: {
191
+ user: true,
192
+ product: true,
193
+ bulkCoupon: true,
194
+ },
195
+ }),
196
+ )
197
+
198
+ if (!purchase.success) {
199
+ return null
200
+ }
201
+
202
+ return purchase.data
203
+ },
204
+ async getPurchaseUserTransferById(options: { id: string }): Promise<
205
+ | (PurchaseUserTransfer & {
206
+ sourceUser: User
207
+ targetUser: User | null
208
+ purchase: Purchase
209
+ })
210
+ | null
211
+ > {
212
+ const purchaseTransferData =
213
+ await client.query.purchaseUserTransfer.findFirst({
214
+ where: eq(purchaseUserTransfer.id, options.id),
215
+ with: {
216
+ sourceUser: true,
217
+ targetUser: true,
218
+ purchase: true,
219
+ },
220
+ })
221
+
222
+ return purchaseUserTransferSchema
223
+ .merge(
224
+ z.object({
225
+ sourceUser: userSchema,
226
+ targetUser: userSchema.nullable(),
227
+ purchase: purchaseSchema,
228
+ organizationId: z.string().nullable(),
229
+ }),
230
+ )
231
+ .nullable()
232
+ .parse(purchaseTransferData)
233
+ },
234
+ async getPurchaseWithUser(purchaseId: string): Promise<
235
+ | (Purchase & {
236
+ user: User
237
+ })
238
+ | null
239
+ > {
240
+ const purchaseData =
241
+ (await client.query.purchases.findFirst({
242
+ where: eq(purchaseTable.id, purchaseId),
243
+ with: {
244
+ user: true,
245
+ },
246
+ })) || null
247
+
248
+ const parsedPurchase = purchaseSchema
249
+ .merge(z.object({ user: userSchema }))
250
+ .nullable()
251
+ .safeParse(purchaseData)
252
+
253
+ if (!parsedPurchase.success) {
254
+ logger.error(parsedPurchase.error, {
255
+ event: 'adapter.purchase.parse_failed',
256
+ purchaseId,
257
+ })
258
+ return null
259
+ }
260
+ return parsedPurchase.data
261
+ },
262
+ async getPurchasesForUser(userId?: string): Promise<Purchase[]> {
263
+ if (!userId) {
264
+ return []
265
+ }
266
+
267
+ const visiblePurchaseStates = ['Valid', 'Refunded', 'Restricted']
268
+
269
+ const userPurchases = await client.query.purchases.findMany({
270
+ where: and(
271
+ eq(purchaseTable.userId, userId),
272
+ inArray(purchaseTable.status, visiblePurchaseStates),
273
+ ),
274
+ with: {
275
+ user: true,
276
+ product: true,
277
+ bulkCoupon: true,
278
+ },
279
+ orderBy: asc(purchaseTable.createdAt),
280
+ })
281
+
282
+ const parsedPurchases = z.array(purchaseSchema).safeParse(userPurchases)
283
+
284
+ if (!parsedPurchases.success) {
285
+ logger.error(parsedPurchases.error, {
286
+ event: 'adapter.purchase.list.parse_failed',
287
+ purchaseCount: userPurchases.length,
288
+ userId,
289
+ })
290
+ return []
291
+ }
292
+
293
+ return parsedPurchases.data
294
+ },
295
+ async getPurchaseDetails(
296
+ purchaseId: string,
297
+ userId: string,
298
+ ): Promise<{
299
+ purchase?: Purchase
300
+ existingPurchase?: Purchase | null
301
+ availableUpgrades: UpgradableProduct[]
302
+ }> {
303
+ const visiblePurchaseStates = ['Valid', 'Refunded', 'Restricted']
304
+
305
+ const userPurchases = await client.query.purchases.findMany({
306
+ where: and(
307
+ eq(purchaseTable.userId, userId),
308
+ inArray(purchaseTable.status, visiblePurchaseStates),
309
+ ),
310
+ with: {
311
+ user: true,
312
+ product: true,
313
+ bulkCoupon: true,
314
+ },
315
+ orderBy: asc(purchaseTable.createdAt),
316
+ })
317
+
318
+ const parsedPurchases = z.array(purchaseSchema).safeParse(userPurchases)
319
+
320
+ const allPurchases = parsedPurchases.success ? parsedPurchases.data : []
321
+
322
+ const thePurchase = await client.query.purchases.findFirst({
323
+ where: and(
324
+ eq(purchaseTable.id, purchaseId),
325
+ eq(purchaseTable.userId, userId),
326
+ ),
327
+ with: {
328
+ user: true,
329
+ product: true,
330
+ bulkCoupon: true,
331
+ },
332
+ })
333
+
334
+ const parsedPurchase = purchaseSchema.safeParse(thePurchase)
335
+
336
+ if (!parsedPurchase.success) {
337
+ logger.error(parsedPurchase.error, {
338
+ event: 'adapter.purchase.details.parse_failed',
339
+ purchaseId,
340
+ userId,
341
+ })
342
+ return {
343
+ availableUpgrades: [],
344
+ }
345
+ }
346
+
347
+ const purchaseCanUpgrade = ['Valid', 'Restricted'].includes(
348
+ parsedPurchase.data.state || '',
349
+ )
350
+
351
+ let availableUpgrades: any[] = []
352
+
353
+ if (purchaseCanUpgrade) {
354
+ availableUpgrades = await client.query.upgradableProducts.findMany({
355
+ where: and(
356
+ eq(
357
+ upgradableProducts.upgradableFromId,
358
+ parsedPurchase.data.product?.id as string,
359
+ ),
360
+ not(
361
+ inArray(
362
+ upgradableProducts.upgradableToId,
363
+ allPurchases.map((p) => p.product?.id as string),
364
+ ),
365
+ ),
366
+ ),
367
+ with: {
368
+ upgradableTo: true,
369
+ upgradableFrom: true,
370
+ },
371
+ })
372
+ }
373
+
374
+ const existingPurchase = purchaseSchema
375
+ .optional()
376
+ .nullable()
377
+ .parse(
378
+ (await client.query.purchases.findFirst({
379
+ where: and(
380
+ eq(purchaseTable.userId, userId),
381
+ eq(purchaseTable.productId, parsedPurchase.data.productId),
382
+ inArray(purchaseTable.status, ['Valid', 'Restricted']),
383
+ isNull(purchaseTable.bulkCouponId),
384
+ not(eq(purchaseTable.id, parsedPurchase.data.id)),
385
+ ),
386
+ with: {
387
+ user: true,
388
+ product: true,
389
+ bulkCoupon: true,
390
+ },
391
+ })) || null,
392
+ )
393
+
394
+ return {
395
+ availableUpgrades: z
396
+ .array(upgradableProductSchema)
397
+ .parse(availableUpgrades),
398
+ existingPurchase,
399
+ purchase: parsedPurchase.data,
400
+ }
401
+ },
402
+
403
+ async transferPurchasesToNewUser(options: {
404
+ merchantCustomerId: string
405
+ userId: string
406
+ }): Promise<void> {
407
+ await client.transaction(async (trx) => {
408
+ const charges = await trx
409
+ .select({ id: merchantCharge.id })
410
+ .from(merchantCharge)
411
+ .where(
412
+ eq(merchantCharge.merchantCustomerId, options.merchantCustomerId),
413
+ )
414
+ const chargeIds = charges.map(({ id }) => id)
415
+ if (chargeIds.length > 0) {
416
+ await trx
417
+ .update(purchaseTable)
418
+ .set({ userId: options.userId })
419
+ .where(inArray(purchaseTable.merchantChargeId, chargeIds))
420
+ await trx
421
+ .update(merchantCharge)
422
+ .set({ userId: options.userId })
423
+ .where(inArray(merchantCharge.id, chargeIds))
424
+ }
425
+ await trx
426
+ .update(merchantCustomer)
427
+ .set({ userId: options.userId })
428
+ .where(eq(merchantCustomer.id, options.merchantCustomerId))
429
+ })
430
+ },
431
+ async updatePurchaseStatusForCharge(
432
+ chargeId: string,
433
+ status: 'Valid' | 'Refunded' | 'Disputed' | 'Banned' | 'Restricted',
434
+ ): Promise<Purchase | undefined> {
435
+ try {
436
+ logger.debug('adapter.purchase.status_update.started', {
437
+ chargeId,
438
+ status,
439
+ })
440
+ const merchantChargeForPurchase =
441
+ await client.query.merchantCharge.findFirst({
442
+ where: or(
443
+ eq(merchantCharge.identifier, chargeId),
444
+ eq(merchantCharge.id, chargeId),
445
+ ),
446
+ })
447
+
448
+ logger.debug('adapter.purchase.status_update.charge_resolved', {
449
+ chargeId,
450
+ merchantChargeId: merchantChargeForPurchase?.id ?? null,
451
+ merchantChargeIdentifier:
452
+ merchantChargeForPurchase?.identifier ?? null,
453
+ })
454
+
455
+ const parsedMerchantChargeForPurchase = merchantChargeSchema.parse(
456
+ merchantChargeForPurchase,
457
+ )
458
+
459
+ if (!parsedMerchantChargeForPurchase)
460
+ throw new Error(`no-charge-found-for-purchase ${chargeId}`)
461
+
462
+ const purchase = await client.query.purchases.findFirst({
463
+ where: eq(
464
+ purchaseTable.merchantChargeId,
465
+ parsedMerchantChargeForPurchase.id,
466
+ ),
467
+ })
468
+
469
+ const parsedPurchase = purchaseSchema.nullable().parse(purchase)
470
+
471
+ if (parsedPurchase) {
472
+ await client
473
+ .update(purchaseTable)
474
+ .set({ status: status })
475
+ .where(eq(purchaseTable.id, parsedPurchase.id))
476
+
477
+ const newPurchase = await client.query.purchases.findFirst({
478
+ where: eq(purchaseTable.id, parsedPurchase.id),
479
+ })
480
+
481
+ return purchaseSchema.optional().parse(newPurchase)
482
+ } else {
483
+ throw new Error(`no-purchase-found-for-charge ${chargeId}`)
484
+ }
485
+ } catch (e) {
486
+ logger.error(toError(e), {
487
+ chargeId,
488
+ event: 'adapter.purchase.status_update.failed',
489
+ status,
490
+ })
491
+ throw e
492
+ }
493
+ },
494
+ async updatePurchaseUserTransferTransferState(options: {
495
+ id: string
496
+ transferState: PurchaseUserTransferState
497
+ }): Promise<PurchaseUserTransfer | null> {
498
+ await client
499
+ .update(purchaseUserTransfer)
500
+ .set({
501
+ transferState: options.transferState,
502
+ })
503
+ .where(eq(purchaseUserTransfer.id, options.id))
504
+
505
+ const purchaseUserTransferData =
506
+ (await client.query.purchaseUserTransfer.findFirst({
507
+ where: eq(purchaseUserTransfer.id, options.id),
508
+ })) || null
509
+
510
+ return purchaseUserTransferSchema
511
+ .nullable()
512
+ .parse(purchaseUserTransferData)
513
+ },
514
+ }
515
+
516
+ return purchasePort
517
+ }
@@ -0,0 +1,93 @@
1
+ import { eq } from 'drizzle-orm'
2
+
3
+ import type { SubscriptionPort } from '@coursebuilder/core/ports'
4
+ import { MerchantSubscriptionSchema } from '@coursebuilder/core/schemas/merchant-subscription'
5
+ import { SubscriptionSchema } from '@coursebuilder/core/schemas/subscription'
6
+
7
+ import type { MySqlContext } from '../context.js'
8
+
9
+ /**
10
+ * Creates the MySQL implementation of product-level subscription operations.
11
+ *
12
+ * @param context - The shared MySQL client and table set.
13
+ * @returns All three SubscriptionPort operations.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * const subscriptionPort = createSubscriptionPort(context)
18
+ * ```
19
+ */
20
+ export function createSubscriptionPort({
21
+ client,
22
+ tables,
23
+ }: MySqlContext): SubscriptionPort {
24
+ const {
25
+ merchantSubscription: merchantSubscriptionTable,
26
+ subscription: subscriptionTable,
27
+ } = tables
28
+
29
+ return {
30
+ createSubscription: async (options: {
31
+ organizationId: string
32
+ merchantSubscriptionId: string
33
+ productId: string
34
+ }) => {
35
+ const id = `sub_${crypto.randomUUID()}`
36
+ await client.insert(subscriptionTable).values({
37
+ ...options,
38
+ id,
39
+ })
40
+
41
+ return SubscriptionSchema.parse(
42
+ await client.query.subscription.findFirst({
43
+ where: eq(subscriptionTable.id, id),
44
+ with: {
45
+ product: true,
46
+ },
47
+ }),
48
+ )
49
+ },
50
+ getSubscriptionForStripeId: async (stripeSubscriptionId: string) => {
51
+ const merchantSubscriptionParsed = MerchantSubscriptionSchema.safeParse(
52
+ await client.query.merchantSubscription.findFirst({
53
+ where: eq(merchantSubscriptionTable.identifier, stripeSubscriptionId),
54
+ }),
55
+ )
56
+
57
+ if (!merchantSubscriptionParsed.success) {
58
+ throw new Error(
59
+ `No merchant subscription found for stripe id ${stripeSubscriptionId} ${merchantSubscriptionParsed.error}`,
60
+ )
61
+ }
62
+
63
+ const subscriptionParsed = SubscriptionSchema.safeParse(
64
+ await client.query.subscription.findFirst({
65
+ where: eq(
66
+ subscriptionTable.merchantSubscriptionId,
67
+ merchantSubscriptionParsed.data.id,
68
+ ),
69
+ with: {
70
+ product: true,
71
+ },
72
+ }),
73
+ )
74
+
75
+ if (!subscriptionParsed.success) {
76
+ throw new Error(
77
+ `No subscription found for merchant subscription ${merchantSubscriptionParsed.data.id} ${subscriptionParsed.error}`,
78
+ )
79
+ }
80
+
81
+ return subscriptionParsed.data
82
+ },
83
+ updateSubscriptionStatus: async (
84
+ subscriptionId: string,
85
+ status: string,
86
+ ) => {
87
+ await client
88
+ .update(subscriptionTable)
89
+ .set({ status })
90
+ .where(eq(subscriptionTable.id, subscriptionId))
91
+ },
92
+ }
93
+ }