@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,387 @@
1
+ import { and, desc, eq, gte, inArray, isNull, or, sql } from 'drizzle-orm'
2
+ import { customAlphabet } from 'nanoid'
3
+ import { v4 } from 'uuid'
4
+ import { z } from 'zod'
5
+
6
+ import type { CouponPort } from '@coursebuilder/core/ports'
7
+ import {
8
+ couponSchema,
9
+ merchantCouponSchema,
10
+ purchaseSchema,
11
+ type Coupon,
12
+ type MerchantCoupon,
13
+ type Purchase,
14
+ } from '@coursebuilder/core/schemas'
15
+ import { logger as baseLogger } from '@coursebuilder/utils/logger'
16
+
17
+ import type { MySqlContext } from '../context.js'
18
+
19
+ const guid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 5)
20
+
21
+ const logger = baseLogger as unknown as {
22
+ debug: (message: string, attrs?: Record<string, unknown>) => void
23
+ error: (error: Error, attrs?: Record<string, unknown>) => void
24
+ }
25
+
26
+ /**
27
+ * Creates the MySQL implementation of coupon operations.
28
+ *
29
+ * @param context - The shared MySQL client and table set.
30
+ * @returns All eleven CouponPort operations.
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * const couponPort = createCouponPort(context)
35
+ * ```
36
+ */
37
+ export function createCouponPort({ client, tables }: MySqlContext): CouponPort {
38
+ const { coupon, merchantCoupon, purchases: purchaseTable } = tables
39
+
40
+ return {
41
+ incrementCouponUsedCount: async (couponId) => {
42
+ await client
43
+ .update(coupon)
44
+ .set({ usedCount: sql`${coupon.usedCount} + 1` })
45
+ .where(eq(coupon.id, couponId))
46
+ },
47
+ createMerchantCoupon: async (options) => {
48
+ await client.insert(merchantCoupon).values({
49
+ id: `mcoupon_${v4()}`,
50
+ identifier: options.identifier,
51
+ merchantAccountId: options.merchantAccountId,
52
+ type: options.type,
53
+ amountDiscount: Math.floor(options.amountDiscount),
54
+ status: 1,
55
+ })
56
+ return merchantCouponSchema.parse(
57
+ await client.query.merchantCoupon.findFirst({
58
+ where: eq(merchantCoupon.identifier, options.identifier),
59
+ }),
60
+ )
61
+ },
62
+ async couponForIdOrCode(options: {
63
+ code?: string
64
+ couponId?: string
65
+ }): Promise<(Coupon & { merchantCoupon: MerchantCoupon }) | null> {
66
+ if (!options.couponId && !options.code) return null
67
+ const couponForIdOrCode = await client.query.coupon.findFirst({
68
+ where: or(
69
+ and(
70
+ or(
71
+ options.code ? eq(coupon.code, options.code) : undefined,
72
+ options.couponId ? eq(coupon.id, options.couponId) : undefined,
73
+ ),
74
+ gte(coupon.expires, new Date()),
75
+ ),
76
+ and(
77
+ or(
78
+ options.code ? eq(coupon.code, options.code) : undefined,
79
+ options.couponId ? eq(coupon.id, options.couponId) : undefined,
80
+ ),
81
+ isNull(coupon.expires),
82
+ ),
83
+ ),
84
+ with: {
85
+ merchantCoupon: true,
86
+ },
87
+ })
88
+
89
+ if (!couponForIdOrCode) return null
90
+
91
+ const parsedCoupon = couponSchema
92
+ .extend({
93
+ merchantCoupon: merchantCouponSchema,
94
+ })
95
+ .safeParse(couponForIdOrCode)
96
+
97
+ if (!parsedCoupon.success) {
98
+ logger.error(parsedCoupon.error, {
99
+ code: options.code,
100
+ couponId: options.couponId,
101
+ event: 'adapter.coupon.parse_failed',
102
+ })
103
+ return null
104
+ }
105
+
106
+ return parsedCoupon.data
107
+ },
108
+ async getCoupon(couponIdOrCode: string): Promise<Coupon | null> {
109
+ const loadedCoupon =
110
+ (await client.query.coupon.findFirst({
111
+ where: or(
112
+ eq(coupon.id, couponIdOrCode),
113
+ eq(coupon.code, couponIdOrCode),
114
+ ),
115
+ })) || null
116
+
117
+ logger.debug('getCoupon', {
118
+ couponIdOrCode,
119
+ found: !!loadedCoupon,
120
+ })
121
+
122
+ return couponSchema.nullable().parse(loadedCoupon)
123
+ },
124
+ async getCouponWithBulkPurchases(couponId: string): Promise<
125
+ | (Coupon & {
126
+ bulkPurchases?: Purchase[] | null
127
+ redeemedBulkCouponPurchases: { bulkCouponId?: string | null }[]
128
+ })
129
+ | null
130
+ > {
131
+ logger.debug('getCouponWithBulkPurchases', { couponId })
132
+ let couponData
133
+ let bulkCouponPurchases: unknown[] = []
134
+ try {
135
+ couponData =
136
+ (await client.query.coupon.findFirst({
137
+ where: eq(coupon.id, couponId),
138
+ with: {
139
+ bulkPurchases: true,
140
+ redeemedBulkCouponPurchases: true,
141
+ },
142
+ })) || null
143
+ } catch (e) {
144
+ logger.error(e as Error, {
145
+ method: 'getCouponWithBulkPurchases',
146
+ couponId,
147
+ step: 'load-coupon',
148
+ })
149
+ }
150
+
151
+ try {
152
+ bulkCouponPurchases = await client.query.purchases.findMany({
153
+ where: eq(purchaseTable.redeemedBulkCouponId, couponId),
154
+ with: {
155
+ user: true,
156
+ },
157
+ })
158
+ logger.debug('getCouponWithBulkPurchases.redeemed', {
159
+ couponId,
160
+ count: bulkCouponPurchases?.length ?? 0,
161
+ })
162
+ } catch (e) {
163
+ logger.error(e as Error, {
164
+ method: 'getCouponWithBulkPurchases',
165
+ couponId,
166
+ step: 'load-redeemed-purchases',
167
+ })
168
+ }
169
+
170
+ if (!couponData) {
171
+ logger.debug('getCouponWithBulkPurchases', {
172
+ couponId,
173
+ error: 'no coupon found',
174
+ })
175
+ return null
176
+ }
177
+
178
+ const couponWithBulkPurchases = {
179
+ ...couponData,
180
+ redeemedBulkCouponPurchases: bulkCouponPurchases || [],
181
+ }
182
+
183
+ const parsedCoupon = couponSchema
184
+ .merge(
185
+ z.object({
186
+ redeemedBulkCouponPurchases: z.array(purchaseSchema),
187
+ }),
188
+ )
189
+ .nullable()
190
+ .safeParse(couponWithBulkPurchases)
191
+
192
+ if (!parsedCoupon.success) {
193
+ logger.error(parsedCoupon.error, {
194
+ couponId: couponData?.id ?? couponId,
195
+ event: 'adapter.coupon.parse_failed',
196
+ redeemedBulkCouponPurchasesCount: bulkCouponPurchases?.length ?? 0,
197
+ })
198
+ return null
199
+ }
200
+
201
+ return parsedCoupon.data
202
+ },
203
+ async getDefaultCoupon(productIds?: string[]): Promise<{
204
+ defaultMerchantCoupon: MerchantCoupon
205
+ defaultCoupon: Coupon
206
+ } | null> {
207
+ const activeSaleCoupon = await client.query.coupon.findFirst({
208
+ where: and(
209
+ eq(coupon.status, 1),
210
+ eq(coupon.default, true),
211
+ gte(coupon.expires, new Date()),
212
+ or(
213
+ productIds
214
+ ? inArray(coupon.restrictedToProductId, productIds)
215
+ : undefined,
216
+ isNull(coupon.restrictedToProductId),
217
+ ),
218
+ ),
219
+ orderBy: desc(coupon.percentageDiscount),
220
+ with: {
221
+ merchantCoupon: true,
222
+ product: true,
223
+ },
224
+ })
225
+ if (activeSaleCoupon) {
226
+ const { restrictedToProductId } = activeSaleCoupon
227
+ const validForProdcutId = restrictedToProductId
228
+ ? productIds?.includes(restrictedToProductId as string)
229
+ : true
230
+
231
+ const { merchantCoupon: defaultMerchantCoupon, ...defaultCoupon } =
232
+ activeSaleCoupon
233
+
234
+ if (validForProdcutId) {
235
+ return {
236
+ defaultMerchantCoupon: merchantCouponSchema.parse(
237
+ defaultMerchantCoupon,
238
+ ),
239
+ defaultCoupon: couponSchema.parse(defaultCoupon),
240
+ }
241
+ }
242
+ }
243
+ return null
244
+ },
245
+ async getMerchantCouponsForTypeAndPercent(params: {
246
+ type: string
247
+ percentageDiscount: number
248
+ }): Promise<MerchantCoupon[]> {
249
+ return z.array(merchantCouponSchema).parse(
250
+ await client.query.merchantCoupon.findMany({
251
+ where: and(
252
+ eq(merchantCoupon.type, params.type),
253
+ eq(
254
+ merchantCoupon.percentageDiscount,
255
+ params.percentageDiscount.toString(),
256
+ ),
257
+ ),
258
+ }),
259
+ )
260
+ },
261
+ async getMerchantCouponForTypeAndPercent(params: {
262
+ type: string
263
+ percentageDiscount: number
264
+ }): Promise<MerchantCoupon | null> {
265
+ const foundMerchantCoupon = await client.query.merchantCoupon.findFirst({
266
+ where: and(
267
+ eq(merchantCoupon.type, params.type),
268
+ eq(
269
+ merchantCoupon.percentageDiscount,
270
+ params.percentageDiscount.toString(),
271
+ ),
272
+ ),
273
+ })
274
+
275
+ const parsed = merchantCouponSchema
276
+ .nullable()
277
+ .safeParse(foundMerchantCoupon)
278
+ if (parsed.success) {
279
+ return parsed.data
280
+ }
281
+
282
+ return null
283
+ },
284
+ async getMerchantCouponForTypeAndAmount(params: {
285
+ type: string
286
+ amountDiscount: number
287
+ }): Promise<MerchantCoupon | null> {
288
+ const foundMerchantCoupon = await client.query.merchantCoupon.findFirst({
289
+ where: and(
290
+ eq(merchantCoupon.type, params.type),
291
+ eq(merchantCoupon.amountDiscount, params.amountDiscount),
292
+ ),
293
+ })
294
+
295
+ const parsed = merchantCouponSchema
296
+ .nullable()
297
+ .safeParse(foundMerchantCoupon)
298
+ if (parsed.success) {
299
+ return parsed.data
300
+ }
301
+
302
+ return null
303
+ },
304
+ async getMerchantCoupon(
305
+ merchantCouponId: string,
306
+ ): Promise<MerchantCoupon | null> {
307
+ const foundMerchantCoupon = await client.query.merchantCoupon.findFirst({
308
+ where: eq(merchantCoupon.id, merchantCouponId),
309
+ })
310
+
311
+ const parsed = merchantCouponSchema
312
+ .nullable()
313
+ .safeParse(foundMerchantCoupon)
314
+ if (parsed.success) {
315
+ return parsed.data
316
+ }
317
+
318
+ return null
319
+ },
320
+ async createCoupon(input: {
321
+ quantity: string
322
+ maxUses: number
323
+ expires: Date | null
324
+ restrictedToProductId: string | null
325
+ percentageDiscount: string
326
+ status: number
327
+ default: boolean
328
+ fields: Record<string, any>
329
+ }) {
330
+ function findClosestDiscount(percentOff: number) {
331
+ // we want a fraction so if it is whole number, we make it fractional
332
+ percentOff = percentOff <= 1 ? percentOff : percentOff / 100
333
+ return [1, 0.95, 0.9, 0.75, 0.6, 0.5, 0.4, 0.25, 0.1].reduce((a, b) => {
334
+ let aDiff = Math.abs(a - percentOff)
335
+ let bDiff = Math.abs(b - percentOff)
336
+
337
+ if (aDiff === bDiff) {
338
+ // Choose largest vs smallest (> vs <)
339
+ return a > b ? a : b
340
+ } else {
341
+ return bDiff < aDiff ? b : a
342
+ }
343
+ })
344
+ }
345
+
346
+ const percentageDiscount = findClosestDiscount(
347
+ Number(input.percentageDiscount) * 100,
348
+ )
349
+
350
+ const merchantCouponResult =
351
+ percentageDiscount < 1
352
+ ? await client.query.merchantCoupon.findFirst({
353
+ where: and(
354
+ eq(
355
+ merchantCoupon.percentageDiscount,
356
+ percentageDiscount.toString(),
357
+ ),
358
+ eq(merchantCoupon.type, 'special'),
359
+ ),
360
+ })
361
+ : null
362
+
363
+ if (!merchantCouponResult) {
364
+ return []
365
+ }
366
+
367
+ const codesArray: string[] = []
368
+ const requestedQuantity = Number(input.quantity)
369
+ const couponQuantity = requestedQuantity < 0 ? 1 : requestedQuantity
370
+
371
+ await client.transaction(async (trx) => {
372
+ // insert coupon for CouponInput quantity
373
+ for (let i = 0; i < couponQuantity; i++) {
374
+ const id = `coupon_${guid()}`
375
+ await trx.insert(coupon).values({
376
+ ...input,
377
+ merchantCouponId: merchantCouponResult.id as string,
378
+ id,
379
+ })
380
+ codesArray.push(id)
381
+ }
382
+ })
383
+
384
+ return codesArray
385
+ },
386
+ }
387
+ }
@@ -0,0 +1,96 @@
1
+ import { and, asc, eq, gte, isNull, or, sql } from 'drizzle-orm'
2
+ import { z } from 'zod'
3
+
4
+ import type { EntitlementPort } from '@coursebuilder/core/ports'
5
+ import { entitlementSchema } from '@coursebuilder/core/schemas'
6
+ import { logger as baseLogger } from '@coursebuilder/utils/logger'
7
+
8
+ import type { MySqlContext } from '../context.js'
9
+
10
+ const logger = baseLogger as unknown as {
11
+ error: (error: Error, attrs?: Record<string, unknown>) => void
12
+ }
13
+
14
+ /**
15
+ * Creates the MySQL implementation of entitlement lookup operations.
16
+ *
17
+ * @param context - The shared MySQL client and table set.
18
+ * @returns Both entitlement capability operations.
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * const entitlementPort = createEntitlementPort(context)
23
+ * ```
24
+ */
25
+ export function createEntitlementPort({
26
+ client,
27
+ tables,
28
+ }: MySqlContext): EntitlementPort {
29
+ const { entitlements: entitlementTable, entitlementTypes } = tables
30
+
31
+ return {
32
+ async getEntitlementsForUser(params) {
33
+ const { userId, sourceType, entitlementType } = params
34
+
35
+ if (!userId) {
36
+ return []
37
+ }
38
+
39
+ const conditions = [eq(entitlementTable.userId, userId)]
40
+
41
+ if (sourceType) {
42
+ conditions.push(eq(entitlementTable.sourceType, sourceType))
43
+ }
44
+
45
+ if (entitlementType) {
46
+ conditions.push(eq(entitlementTable.entitlementType, entitlementType))
47
+ }
48
+
49
+ // Only return active entitlements (not deleted, not expired)
50
+ conditions.push(isNull(entitlementTable.deletedAt))
51
+ const expiresCondition = or(
52
+ isNull(entitlementTable.expiresAt),
53
+ gte(entitlementTable.expiresAt, sql`CURRENT_TIMESTAMP`),
54
+ )
55
+ if (expiresCondition) {
56
+ conditions.push(expiresCondition)
57
+ }
58
+
59
+ const userEntitlements = await client.query.entitlements.findMany({
60
+ where: conditions.length > 0 ? and(...conditions) : undefined,
61
+ orderBy: asc(entitlementTable.createdAt),
62
+ })
63
+
64
+ const parsedEntitlements = z
65
+ .array(entitlementSchema)
66
+ .safeParse(userEntitlements)
67
+
68
+ if (!parsedEntitlements.success) {
69
+ logger.error(parsedEntitlements.error, {
70
+ entitlementCount: userEntitlements.length,
71
+ entitlementType,
72
+ event: 'adapter.entitlement.list.parse_failed',
73
+ sourceType,
74
+ userId,
75
+ })
76
+ return []
77
+ }
78
+
79
+ return parsedEntitlements.data
80
+ },
81
+ async getEntitlementTypeByName(name) {
82
+ const entitlementType = await client.query.entitlementTypes.findFirst({
83
+ where: eq(entitlementTypes.name, name),
84
+ })
85
+
86
+ if (!entitlementType) {
87
+ return null
88
+ }
89
+
90
+ return {
91
+ id: String(entitlementType.id),
92
+ name: String(entitlementType.name),
93
+ }
94
+ },
95
+ }
96
+ }
@@ -0,0 +1,160 @@
1
+ import { and, eq } from 'drizzle-orm'
2
+ import { v4 } from 'uuid'
3
+
4
+ import type { IdentityPort } from '@coursebuilder/core/ports'
5
+ import { userSchema, type User } from '@coursebuilder/core/schemas'
6
+ import { logger as baseLogger } from '@coursebuilder/utils/logger'
7
+
8
+ import type { MySqlContext } from '../context.js'
9
+
10
+ const logger = baseLogger as unknown as {
11
+ error: (error: Error, attrs?: Record<string, unknown>) => void
12
+ info: (message: string, attrs?: Record<string, unknown>) => void
13
+ }
14
+
15
+ function toError(error: unknown) {
16
+ return error instanceof Error ? error : new Error(String(error))
17
+ }
18
+
19
+ /**
20
+ * Creates the MySQL implementation of Course Builder identity persistence.
21
+ *
22
+ * @param context - The shared MySQL client and table set.
23
+ * @returns User and external-identity-link operations.
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * const identity = createIdentityPort(context)
28
+ * ```
29
+ */
30
+ export function createIdentityPort({
31
+ client,
32
+ tables,
33
+ }: MySqlContext): Required<IdentityPort> {
34
+ const { accounts, users, verificationTokens } = tables
35
+
36
+ const getUser: NonNullable<IdentityPort['getUser']> = async (data) =>
37
+ (await client
38
+ .select()
39
+ .from(users)
40
+ .where(eq(users.id, data))
41
+ .then((res) => res[0])) ?? null
42
+
43
+ const getUserByEmail: NonNullable<IdentityPort['getUserByEmail']> = async (
44
+ data,
45
+ ) =>
46
+ (await client
47
+ .select()
48
+ .from(users)
49
+ .where(eq(users.email, data))
50
+ .then((res) => res[0])) ?? null
51
+
52
+ const createUser: NonNullable<IdentityPort['createUser']> = async (data) => {
53
+ const id = crypto.randomUUID()
54
+ try {
55
+ await client.insert(users).values({ ...data, id })
56
+
57
+ const user = await getUser(id)
58
+ if (!user) throw new Error('Failed to get user')
59
+ return user
60
+ } catch (error) {
61
+ logger.error(toError(error), {
62
+ email: data.email,
63
+ event: 'adapter.user.create.failed',
64
+ userId: id,
65
+ })
66
+ throw error
67
+ }
68
+ }
69
+
70
+ return {
71
+ createUser,
72
+ getUser,
73
+ getUserByEmail,
74
+ updateUser: async (data) => {
75
+ if (!data.id) throw new Error('No user id.')
76
+ await client.update(users).set(data).where(eq(users.id, data.id))
77
+ return await client
78
+ .select()
79
+ .from(users)
80
+ .where(eq(users.id, data.id))
81
+ .then((res) => res[0])
82
+ },
83
+ linkAccount: async (rawAccount) => {
84
+ await client.insert(accounts).values(rawAccount)
85
+ },
86
+ getUserByAccount: async (account) => {
87
+ const dbAccount =
88
+ (await client
89
+ .select()
90
+ .from(accounts)
91
+ .where(
92
+ and(
93
+ eq(accounts.providerAccountId, account.providerAccountId),
94
+ eq(accounts.provider, account.provider),
95
+ ),
96
+ )
97
+ .leftJoin(users, eq(accounts.userId, users.id))
98
+ .then((res) => res[0])) ?? null
99
+ return dbAccount?.User ?? null
100
+ },
101
+ unlinkAccount: async (account) => {
102
+ await client
103
+ .delete(accounts)
104
+ .where(
105
+ and(
106
+ eq(accounts.providerAccountId, account.providerAccountId),
107
+ eq(accounts.provider, account.provider),
108
+ ),
109
+ )
110
+ return undefined
111
+ },
112
+ createVerificationToken: async (token) => {
113
+ await client.insert(verificationTokens).values(token)
114
+ return await client
115
+ .select()
116
+ .from(verificationTokens)
117
+ .where(eq(verificationTokens.identifier, token.identifier))
118
+ .then((res) => res[0])
119
+ },
120
+ findOrCreateUser: async (email, name) => {
121
+ const user = await getUserByEmail(email)
122
+ if (!user) {
123
+ const newUser = await createUser({
124
+ id: `u_${v4()}`,
125
+ email,
126
+ name,
127
+ emailVerified: null,
128
+ })
129
+ logger.info('adapter.user.created', { email, userId: newUser.id })
130
+ return { user: newUser as User, isNewUser: true }
131
+ }
132
+ return { user: user as User, isNewUser: false }
133
+ },
134
+ getUserWithPurchasersByEmail: async (email) =>
135
+ client.query.users.findFirst({
136
+ where: eq(users.email, email.trim().toLowerCase()),
137
+ with: {
138
+ roles: { with: { role: true } },
139
+ purchases: {
140
+ with: {
141
+ bulkCoupon: true,
142
+ merchantCharge: true,
143
+ product: { columns: { id: true, name: true, key: true } },
144
+ },
145
+ },
146
+ },
147
+ }),
148
+ getUserById: async (userId) => {
149
+ const user = await client.query.users
150
+ .findFirst({
151
+ where: eq(users.id, userId),
152
+ with: { roles: { with: { role: true } } },
153
+ })
154
+ .then(async (res) =>
155
+ res ? { ...res, roles: res.roles.map((r) => r.role) } : undefined,
156
+ )
157
+ return userSchema.nullable().parse(user ?? null)
158
+ },
159
+ }
160
+ }