@unifiedcommerce/core 0.4.4 → 0.4.5

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 (556) hide show
  1. package/dist/adapters/console-email.d.ts +23 -0
  2. package/dist/adapters/console-email.d.ts.map +1 -0
  3. package/dist/adapters/console-email.js +38 -0
  4. package/dist/auth/access.d.ts +101 -0
  5. package/dist/auth/access.d.ts.map +1 -0
  6. package/dist/auth/access.js +128 -0
  7. package/dist/auth/auth-schema.d.ts +1475 -0
  8. package/dist/auth/auth-schema.d.ts.map +1 -0
  9. package/dist/auth/auth-schema.js +124 -0
  10. package/dist/auth/middleware.d.ts +5 -0
  11. package/dist/auth/middleware.d.ts.map +1 -0
  12. package/dist/auth/middleware.js +141 -0
  13. package/dist/auth/org.d.ts +22 -0
  14. package/dist/auth/org.d.ts.map +1 -0
  15. package/dist/auth/org.js +36 -0
  16. package/dist/auth/permissions.d.ts +4 -0
  17. package/dist/auth/permissions.d.ts.map +1 -0
  18. package/dist/auth/permissions.js +24 -0
  19. package/dist/auth/setup.d.ts +29 -0
  20. package/dist/auth/setup.d.ts.map +1 -0
  21. package/dist/auth/setup.js +117 -0
  22. package/dist/auth/system-actor.d.ts +7 -0
  23. package/dist/auth/system-actor.d.ts.map +1 -0
  24. package/dist/auth/system-actor.js +17 -0
  25. package/dist/auth/types.d.ts +11 -0
  26. package/dist/auth/types.d.ts.map +1 -0
  27. package/dist/auth/types.js +1 -0
  28. package/dist/config/defaults.d.ts +3 -0
  29. package/dist/config/defaults.d.ts.map +1 -0
  30. package/dist/config/defaults.js +80 -0
  31. package/dist/config/define-config.d.ts +9 -0
  32. package/dist/config/define-config.d.ts.map +1 -0
  33. package/dist/config/define-config.js +44 -0
  34. package/dist/config/types.d.ts +327 -0
  35. package/dist/config/types.d.ts.map +1 -0
  36. package/dist/config/types.js +1 -0
  37. package/dist/generated/plugin-manifest.d.ts +48 -0
  38. package/dist/generated/plugin-manifest.d.ts.map +1 -0
  39. package/dist/generated/plugin-manifest.js +20 -0
  40. package/dist/hooks/checkout-completion.d.ts +58 -0
  41. package/dist/hooks/checkout-completion.d.ts.map +1 -0
  42. package/dist/hooks/checkout-completion.js +137 -0
  43. package/dist/hooks/checkout.d.ts +99 -0
  44. package/dist/hooks/checkout.d.ts.map +1 -0
  45. package/dist/hooks/checkout.js +317 -0
  46. package/dist/hooks/order-emails.d.ts +16 -0
  47. package/dist/hooks/order-emails.d.ts.map +1 -0
  48. package/dist/hooks/order-emails.js +44 -0
  49. package/dist/index.d.ts +77 -0
  50. package/dist/index.d.ts.map +1 -0
  51. package/dist/index.js +45 -0
  52. package/dist/interfaces/mcp/agent-prompt.d.ts +16 -0
  53. package/dist/interfaces/mcp/agent-prompt.d.ts.map +1 -0
  54. package/dist/interfaces/mcp/agent-prompt.js +172 -0
  55. package/dist/interfaces/mcp/context-enrichment.d.ts +39 -0
  56. package/dist/interfaces/mcp/context-enrichment.d.ts.map +1 -0
  57. package/dist/interfaces/mcp/context-enrichment.js +119 -0
  58. package/dist/interfaces/mcp/server.d.ts +5 -0
  59. package/dist/interfaces/mcp/server.d.ts.map +1 -0
  60. package/dist/interfaces/mcp/server.js +30 -0
  61. package/dist/interfaces/mcp/tool-builder.d.ts +120 -0
  62. package/dist/interfaces/mcp/tool-builder.d.ts.map +1 -0
  63. package/dist/interfaces/mcp/tool-builder.js +224 -0
  64. package/dist/interfaces/mcp/tools/analytics.d.ts +42 -0
  65. package/dist/interfaces/mcp/tools/analytics.d.ts.map +1 -0
  66. package/dist/interfaces/mcp/tools/analytics.js +70 -0
  67. package/dist/interfaces/mcp/tools/cart.d.ts +14 -0
  68. package/dist/interfaces/mcp/tools/cart.d.ts.map +1 -0
  69. package/dist/interfaces/mcp/tools/cart.js +47 -0
  70. package/dist/interfaces/mcp/tools/catalog.d.ts +53 -0
  71. package/dist/interfaces/mcp/tools/catalog.d.ts.map +1 -0
  72. package/dist/interfaces/mcp/tools/catalog.js +284 -0
  73. package/dist/interfaces/mcp/tools/index.d.ts +3 -0
  74. package/dist/interfaces/mcp/tools/index.d.ts.map +1 -0
  75. package/dist/interfaces/mcp/tools/index.js +20 -0
  76. package/dist/interfaces/mcp/tools/inventory.d.ts +27 -0
  77. package/dist/interfaces/mcp/tools/inventory.d.ts.map +1 -0
  78. package/dist/interfaces/mcp/tools/inventory.js +143 -0
  79. package/dist/interfaces/mcp/tools/orders.d.ts +18 -0
  80. package/dist/interfaces/mcp/tools/orders.d.ts.map +1 -0
  81. package/dist/interfaces/mcp/tools/orders.js +82 -0
  82. package/dist/interfaces/mcp/tools/pricing.d.ts +29 -0
  83. package/dist/interfaces/mcp/tools/pricing.d.ts.map +1 -0
  84. package/dist/interfaces/mcp/tools/pricing.js +90 -0
  85. package/dist/interfaces/mcp/tools/promotions.d.ts +44 -0
  86. package/dist/interfaces/mcp/tools/promotions.d.ts.map +1 -0
  87. package/dist/interfaces/mcp/tools/promotions.js +109 -0
  88. package/dist/interfaces/mcp/tools/registry.d.ts +32 -0
  89. package/dist/interfaces/mcp/tools/registry.d.ts.map +1 -0
  90. package/dist/interfaces/mcp/tools/registry.js +55 -0
  91. package/dist/interfaces/mcp/tools/search.d.ts +14 -0
  92. package/dist/interfaces/mcp/tools/search.d.ts.map +1 -0
  93. package/dist/interfaces/mcp/tools/search.js +39 -0
  94. package/dist/interfaces/mcp/tools/webhooks.d.ts +15 -0
  95. package/dist/interfaces/mcp/tools/webhooks.d.ts.map +1 -0
  96. package/dist/interfaces/mcp/tools/webhooks.js +48 -0
  97. package/dist/interfaces/mcp/transport.d.ts +20 -0
  98. package/dist/interfaces/mcp/transport.d.ts.map +1 -0
  99. package/dist/interfaces/mcp/transport.js +99 -0
  100. package/dist/interfaces/rest/customer-portal.d.ts +5 -0
  101. package/dist/interfaces/rest/customer-portal.d.ts.map +1 -0
  102. package/dist/interfaces/rest/customer-portal.js +206 -0
  103. package/dist/interfaces/rest/router.d.ts +164 -0
  104. package/dist/interfaces/rest/router.d.ts.map +1 -0
  105. package/dist/interfaces/rest/router.js +259 -0
  106. package/dist/interfaces/rest/routes/admin-jobs.d.ts +5 -0
  107. package/dist/interfaces/rest/routes/admin-jobs.d.ts.map +1 -0
  108. package/dist/interfaces/rest/routes/admin-jobs.js +48 -0
  109. package/dist/interfaces/rest/routes/audit.d.ts +5 -0
  110. package/dist/interfaces/rest/routes/audit.d.ts.map +1 -0
  111. package/dist/interfaces/rest/routes/audit.js +43 -0
  112. package/dist/interfaces/rest/routes/carts.d.ts +5 -0
  113. package/dist/interfaces/rest/routes/carts.d.ts.map +1 -0
  114. package/dist/interfaces/rest/routes/carts.js +55 -0
  115. package/dist/interfaces/rest/routes/catalog.d.ts +5 -0
  116. package/dist/interfaces/rest/routes/catalog.d.ts.map +1 -0
  117. package/dist/interfaces/rest/routes/catalog.js +256 -0
  118. package/dist/interfaces/rest/routes/checkout.d.ts +5 -0
  119. package/dist/interfaces/rest/routes/checkout.d.ts.map +1 -0
  120. package/dist/interfaces/rest/routes/checkout.js +216 -0
  121. package/dist/interfaces/rest/routes/customers.d.ts +5 -0
  122. package/dist/interfaces/rest/routes/customers.d.ts.map +1 -0
  123. package/dist/interfaces/rest/routes/customers.js +74 -0
  124. package/dist/interfaces/rest/routes/entity-aliases.d.ts +18 -0
  125. package/dist/interfaces/rest/routes/entity-aliases.d.ts.map +1 -0
  126. package/dist/interfaces/rest/routes/entity-aliases.js +39 -0
  127. package/dist/interfaces/rest/routes/inventory.d.ts +5 -0
  128. package/dist/interfaces/rest/routes/inventory.d.ts.map +1 -0
  129. package/dist/interfaces/rest/routes/inventory.js +72 -0
  130. package/dist/interfaces/rest/routes/media.d.ts +5 -0
  131. package/dist/interfaces/rest/routes/media.d.ts.map +1 -0
  132. package/dist/interfaces/rest/routes/media.js +65 -0
  133. package/dist/interfaces/rest/routes/orders.d.ts +5 -0
  134. package/dist/interfaces/rest/routes/orders.d.ts.map +1 -0
  135. package/dist/interfaces/rest/routes/orders.js +64 -0
  136. package/dist/interfaces/rest/routes/payments.d.ts +5 -0
  137. package/dist/interfaces/rest/routes/payments.d.ts.map +1 -0
  138. package/dist/interfaces/rest/routes/payments.js +45 -0
  139. package/dist/interfaces/rest/routes/pricing.d.ts +5 -0
  140. package/dist/interfaces/rest/routes/pricing.d.ts.map +1 -0
  141. package/dist/interfaces/rest/routes/pricing.js +48 -0
  142. package/dist/interfaces/rest/routes/promotions.d.ts +5 -0
  143. package/dist/interfaces/rest/routes/promotions.d.ts.map +1 -0
  144. package/dist/interfaces/rest/routes/promotions.js +76 -0
  145. package/dist/interfaces/rest/routes/search.d.ts +5 -0
  146. package/dist/interfaces/rest/routes/search.d.ts.map +1 -0
  147. package/dist/interfaces/rest/routes/search.js +60 -0
  148. package/dist/interfaces/rest/routes/webhooks.d.ts +5 -0
  149. package/dist/interfaces/rest/routes/webhooks.d.ts.map +1 -0
  150. package/dist/interfaces/rest/routes/webhooks.js +39 -0
  151. package/dist/interfaces/rest/schemas/admin-jobs.d.ts +327 -0
  152. package/dist/interfaces/rest/schemas/admin-jobs.d.ts.map +1 -0
  153. package/dist/interfaces/rest/schemas/admin-jobs.js +37 -0
  154. package/dist/interfaces/rest/schemas/audit.d.ts +59 -0
  155. package/dist/interfaces/rest/schemas/audit.d.ts.map +1 -0
  156. package/dist/interfaces/rest/schemas/audit.js +43 -0
  157. package/dist/interfaces/rest/schemas/carts.d.ts +1456 -0
  158. package/dist/interfaces/rest/schemas/carts.d.ts.map +1 -0
  159. package/dist/interfaces/rest/schemas/carts.js +109 -0
  160. package/dist/interfaces/rest/schemas/catalog.d.ts +5452 -0
  161. package/dist/interfaces/rest/schemas/catalog.d.ts.map +1 -0
  162. package/dist/interfaces/rest/schemas/catalog.js +397 -0
  163. package/dist/interfaces/rest/schemas/checkout.d.ts +160 -0
  164. package/dist/interfaces/rest/schemas/checkout.d.ts.map +1 -0
  165. package/dist/interfaces/rest/schemas/checkout.js +60 -0
  166. package/dist/interfaces/rest/schemas/customer-portal.d.ts +2203 -0
  167. package/dist/interfaces/rest/schemas/customer-portal.d.ts.map +1 -0
  168. package/dist/interfaces/rest/schemas/customer-portal.js +177 -0
  169. package/dist/interfaces/rest/schemas/customers.d.ts +422 -0
  170. package/dist/interfaces/rest/schemas/customers.d.ts.map +1 -0
  171. package/dist/interfaces/rest/schemas/customers.js +150 -0
  172. package/dist/interfaces/rest/schemas/inventory.d.ts +561 -0
  173. package/dist/interfaces/rest/schemas/inventory.d.ts.map +1 -0
  174. package/dist/interfaces/rest/schemas/inventory.js +148 -0
  175. package/dist/interfaces/rest/schemas/media.d.ts +303 -0
  176. package/dist/interfaces/rest/schemas/media.d.ts.map +1 -0
  177. package/dist/interfaces/rest/schemas/media.js +69 -0
  178. package/dist/interfaces/rest/schemas/orders.d.ts +1792 -0
  179. package/dist/interfaces/rest/schemas/orders.d.ts.map +1 -0
  180. package/dist/interfaces/rest/schemas/orders.js +93 -0
  181. package/dist/interfaces/rest/schemas/pricing.d.ts +256 -0
  182. package/dist/interfaces/rest/schemas/pricing.d.ts.map +1 -0
  183. package/dist/interfaces/rest/schemas/pricing.js +72 -0
  184. package/dist/interfaces/rest/schemas/promotions.d.ts +363 -0
  185. package/dist/interfaces/rest/schemas/promotions.d.ts.map +1 -0
  186. package/dist/interfaces/rest/schemas/promotions.js +98 -0
  187. package/dist/interfaces/rest/schemas/responses.d.ts +4086 -0
  188. package/dist/interfaces/rest/schemas/responses.d.ts.map +1 -0
  189. package/dist/interfaces/rest/schemas/responses.js +74 -0
  190. package/dist/interfaces/rest/schemas/search.d.ts +247 -0
  191. package/dist/interfaces/rest/schemas/search.d.ts.map +1 -0
  192. package/dist/interfaces/rest/schemas/search.js +55 -0
  193. package/dist/interfaces/rest/schemas/shared.d.ts +95 -0
  194. package/dist/interfaces/rest/schemas/shared.d.ts.map +1 -0
  195. package/dist/interfaces/rest/schemas/shared.js +51 -0
  196. package/dist/interfaces/rest/schemas/webhooks.d.ts +221 -0
  197. package/dist/interfaces/rest/schemas/webhooks.d.ts.map +1 -0
  198. package/dist/interfaces/rest/schemas/webhooks.js +62 -0
  199. package/dist/interfaces/rest/utils.d.ts +45 -0
  200. package/dist/interfaces/rest/utils.d.ts.map +1 -0
  201. package/dist/interfaces/rest/utils.js +71 -0
  202. package/dist/interfaces/rest/webhook-router.d.ts +41 -0
  203. package/dist/interfaces/rest/webhook-router.d.ts.map +1 -0
  204. package/dist/interfaces/rest/webhook-router.js +36 -0
  205. package/dist/kernel/compensation/executor.d.ts +21 -0
  206. package/dist/kernel/compensation/executor.d.ts.map +1 -0
  207. package/dist/kernel/compensation/executor.js +36 -0
  208. package/dist/kernel/compensation/types.d.ts +25 -0
  209. package/dist/kernel/compensation/types.d.ts.map +1 -0
  210. package/dist/kernel/compensation/types.js +1 -0
  211. package/dist/kernel/database/adapter.d.ts +18 -0
  212. package/dist/kernel/database/adapter.d.ts.map +1 -0
  213. package/dist/kernel/database/adapter.js +3 -0
  214. package/dist/kernel/database/drizzle-db.d.ts +49 -0
  215. package/dist/kernel/database/drizzle-db.d.ts.map +1 -0
  216. package/dist/kernel/database/drizzle-db.js +20 -0
  217. package/dist/kernel/database/migrate.d.ts +38 -0
  218. package/dist/kernel/database/migrate.d.ts.map +1 -0
  219. package/dist/kernel/database/migrate.js +61 -0
  220. package/dist/kernel/database/plugin-types.d.ts +32 -0
  221. package/dist/kernel/database/plugin-types.d.ts.map +1 -0
  222. package/dist/kernel/database/plugin-types.js +10 -0
  223. package/dist/kernel/database/schema.d.ts +24 -0
  224. package/dist/kernel/database/schema.d.ts.map +1 -0
  225. package/dist/kernel/database/schema.js +36 -0
  226. package/dist/kernel/database/scoped-db.d.ts +20 -0
  227. package/dist/kernel/database/scoped-db.d.ts.map +1 -0
  228. package/dist/kernel/database/scoped-db.js +62 -0
  229. package/dist/kernel/database/tx-context.d.ts +15 -0
  230. package/dist/kernel/database/tx-context.d.ts.map +1 -0
  231. package/dist/kernel/database/tx-context.js +19 -0
  232. package/dist/kernel/error-mapper.d.ts +3 -0
  233. package/dist/kernel/error-mapper.d.ts.map +1 -0
  234. package/dist/kernel/error-mapper.js +12 -0
  235. package/dist/kernel/errors.d.ts +38 -0
  236. package/dist/kernel/errors.d.ts.map +1 -0
  237. package/dist/kernel/errors.js +69 -0
  238. package/dist/kernel/factory/repository-factory.d.ts +71 -0
  239. package/dist/kernel/factory/repository-factory.d.ts.map +1 -0
  240. package/dist/kernel/factory/repository-factory.js +138 -0
  241. package/dist/kernel/hooks/create-context.d.ts +25 -0
  242. package/dist/kernel/hooks/create-context.d.ts.map +1 -0
  243. package/dist/kernel/hooks/create-context.js +22 -0
  244. package/dist/kernel/hooks/executor.d.ts +12 -0
  245. package/dist/kernel/hooks/executor.d.ts.map +1 -0
  246. package/dist/kernel/hooks/executor.js +50 -0
  247. package/dist/kernel/hooks/registry.d.ts +28 -0
  248. package/dist/kernel/hooks/registry.d.ts.map +1 -0
  249. package/dist/kernel/hooks/registry.js +58 -0
  250. package/dist/kernel/hooks/types.d.ts +37 -0
  251. package/dist/kernel/hooks/types.d.ts.map +1 -0
  252. package/dist/kernel/hooks/types.js +1 -0
  253. package/dist/kernel/http-error.d.ts +30 -0
  254. package/dist/kernel/http-error.d.ts.map +1 -0
  255. package/dist/kernel/http-error.js +35 -0
  256. package/dist/kernel/jobs/adapter.d.ts +25 -0
  257. package/dist/kernel/jobs/adapter.d.ts.map +1 -0
  258. package/dist/kernel/jobs/adapter.js +9 -0
  259. package/dist/kernel/jobs/drizzle-adapter.d.ts +15 -0
  260. package/dist/kernel/jobs/drizzle-adapter.d.ts.map +1 -0
  261. package/dist/kernel/jobs/drizzle-adapter.js +42 -0
  262. package/dist/kernel/jobs/runner.d.ts +24 -0
  263. package/dist/kernel/jobs/runner.d.ts.map +1 -0
  264. package/dist/kernel/jobs/runner.js +114 -0
  265. package/dist/kernel/jobs/schema.d.ts +280 -0
  266. package/dist/kernel/jobs/schema.d.ts.map +1 -0
  267. package/dist/kernel/jobs/schema.js +37 -0
  268. package/dist/kernel/jobs/types.d.ts +30 -0
  269. package/dist/kernel/jobs/types.d.ts.map +1 -0
  270. package/dist/kernel/jobs/types.js +1 -0
  271. package/dist/kernel/local-api.d.ts +103 -0
  272. package/dist/kernel/local-api.d.ts.map +1 -0
  273. package/dist/kernel/local-api.js +89 -0
  274. package/dist/kernel/plugin/manifest.d.ts +90 -0
  275. package/dist/kernel/plugin/manifest.d.ts.map +1 -0
  276. package/dist/kernel/plugin/manifest.js +169 -0
  277. package/dist/kernel/query/executor.d.ts +21 -0
  278. package/dist/kernel/query/executor.d.ts.map +1 -0
  279. package/dist/kernel/query/executor.js +128 -0
  280. package/dist/kernel/query/registry.d.ts +33 -0
  281. package/dist/kernel/query/registry.d.ts.map +1 -0
  282. package/dist/kernel/query/registry.js +20 -0
  283. package/dist/kernel/result.d.ts +36 -0
  284. package/dist/kernel/result.d.ts.map +1 -0
  285. package/dist/kernel/result.js +16 -0
  286. package/dist/kernel/schema/extra-columns.d.ts +23 -0
  287. package/dist/kernel/schema/extra-columns.d.ts.map +1 -0
  288. package/dist/kernel/schema/extra-columns.js +10 -0
  289. package/dist/kernel/service-registry.d.ts +109 -0
  290. package/dist/kernel/service-registry.d.ts.map +1 -0
  291. package/dist/kernel/service-registry.js +26 -0
  292. package/dist/kernel/service-timing.d.ts +25 -0
  293. package/dist/kernel/service-timing.d.ts.map +1 -0
  294. package/dist/kernel/service-timing.js +62 -0
  295. package/dist/kernel/state-machine/machine.d.ts +24 -0
  296. package/dist/kernel/state-machine/machine.d.ts.map +1 -0
  297. package/dist/kernel/state-machine/machine.js +70 -0
  298. package/dist/modules/analytics/drizzle-adapter.d.ts +13 -0
  299. package/dist/modules/analytics/drizzle-adapter.d.ts.map +1 -0
  300. package/dist/modules/analytics/drizzle-adapter.js +358 -0
  301. package/dist/modules/analytics/hooks.d.ts +13 -0
  302. package/dist/modules/analytics/hooks.d.ts.map +1 -0
  303. package/dist/modules/analytics/hooks.js +12 -0
  304. package/dist/modules/analytics/models.d.ts +14 -0
  305. package/dist/modules/analytics/models.d.ts.map +1 -0
  306. package/dist/modules/analytics/models.js +118 -0
  307. package/dist/modules/analytics/repository/index.d.ts +5 -0
  308. package/dist/modules/analytics/repository/index.d.ts.map +1 -0
  309. package/dist/modules/analytics/repository/index.js +1 -0
  310. package/dist/modules/analytics/service.d.ts +45 -0
  311. package/dist/modules/analytics/service.d.ts.map +1 -0
  312. package/dist/modules/analytics/service.js +196 -0
  313. package/dist/modules/analytics/types.d.ts +119 -0
  314. package/dist/modules/analytics/types.d.ts.map +1 -0
  315. package/dist/modules/analytics/types.js +25 -0
  316. package/dist/modules/audit/hooks.d.ts +7 -0
  317. package/dist/modules/audit/hooks.d.ts.map +1 -0
  318. package/dist/modules/audit/hooks.js +67 -0
  319. package/dist/modules/audit/schema.d.ts +178 -0
  320. package/dist/modules/audit/schema.d.ts.map +1 -0
  321. package/dist/modules/audit/schema.js +21 -0
  322. package/dist/modules/audit/service.d.ts +38 -0
  323. package/dist/modules/audit/service.d.ts.map +1 -0
  324. package/dist/modules/audit/service.js +109 -0
  325. package/dist/modules/cart/access.d.ts +11 -0
  326. package/dist/modules/cart/access.d.ts.map +1 -0
  327. package/dist/modules/cart/access.js +18 -0
  328. package/dist/modules/cart/matcher.d.ts +20 -0
  329. package/dist/modules/cart/matcher.d.ts.map +1 -0
  330. package/dist/modules/cart/matcher.js +2 -0
  331. package/dist/modules/cart/repository/index.d.ts +45 -0
  332. package/dist/modules/cart/repository/index.d.ts.map +1 -0
  333. package/dist/modules/cart/repository/index.js +158 -0
  334. package/dist/modules/cart/schema.d.ts +359 -0
  335. package/dist/modules/cart/schema.d.ts.map +1 -0
  336. package/dist/modules/cart/schema.js +40 -0
  337. package/dist/modules/cart/schemas.d.ts +29 -0
  338. package/dist/modules/cart/schemas.d.ts.map +1 -0
  339. package/dist/modules/cart/schemas.js +14 -0
  340. package/dist/modules/cart/service.d.ts +63 -0
  341. package/dist/modules/cart/service.d.ts.map +1 -0
  342. package/dist/modules/cart/service.js +339 -0
  343. package/dist/modules/catalog/repository/index.d.ts +106 -0
  344. package/dist/modules/catalog/repository/index.d.ts.map +1 -0
  345. package/dist/modules/catalog/repository/index.js +455 -0
  346. package/dist/modules/catalog/schema.d.ts +1193 -0
  347. package/dist/modules/catalog/schema.d.ts.map +1 -0
  348. package/dist/modules/catalog/schema.js +149 -0
  349. package/dist/modules/catalog/schemas.d.ts +81 -0
  350. package/dist/modules/catalog/schemas.d.ts.map +1 -0
  351. package/dist/modules/catalog/schemas.js +62 -0
  352. package/dist/modules/catalog/service.d.ts +160 -0
  353. package/dist/modules/catalog/service.d.ts.map +1 -0
  354. package/dist/modules/catalog/service.js +759 -0
  355. package/dist/modules/customers/repository/index.d.ts +47 -0
  356. package/dist/modules/customers/repository/index.d.ts.map +1 -0
  357. package/dist/modules/customers/repository/index.js +206 -0
  358. package/dist/modules/customers/schema.d.ts +560 -0
  359. package/dist/modules/customers/schema.d.ts.map +1 -0
  360. package/dist/modules/customers/schema.js +60 -0
  361. package/dist/modules/customers/service.d.ts +27 -0
  362. package/dist/modules/customers/service.d.ts.map +1 -0
  363. package/dist/modules/customers/service.js +106 -0
  364. package/dist/modules/fulfillment/repository/index.d.ts +63 -0
  365. package/dist/modules/fulfillment/repository/index.d.ts.map +1 -0
  366. package/dist/modules/fulfillment/repository/index.js +268 -0
  367. package/dist/modules/fulfillment/schema.d.ts +655 -0
  368. package/dist/modules/fulfillment/schema.d.ts.map +1 -0
  369. package/dist/modules/fulfillment/schema.js +83 -0
  370. package/dist/modules/fulfillment/service.d.ts +58 -0
  371. package/dist/modules/fulfillment/service.d.ts.map +1 -0
  372. package/dist/modules/fulfillment/service.js +338 -0
  373. package/dist/modules/fulfillment/types.d.ts +44 -0
  374. package/dist/modules/fulfillment/types.d.ts.map +1 -0
  375. package/dist/modules/fulfillment/types.js +1 -0
  376. package/dist/modules/inventory/repository/index.d.ts +81 -0
  377. package/dist/modules/inventory/repository/index.d.ts.map +1 -0
  378. package/dist/modules/inventory/repository/index.js +310 -0
  379. package/dist/modules/inventory/schema.d.ts +570 -0
  380. package/dist/modules/inventory/schema.d.ts.map +1 -0
  381. package/dist/modules/inventory/schema.js +69 -0
  382. package/dist/modules/inventory/schemas.d.ts +31 -0
  383. package/dist/modules/inventory/schemas.d.ts.map +1 -0
  384. package/dist/modules/inventory/schemas.js +28 -0
  385. package/dist/modules/inventory/service.d.ts +69 -0
  386. package/dist/modules/inventory/service.d.ts.map +1 -0
  387. package/dist/modules/inventory/service.js +283 -0
  388. package/dist/modules/media/adapter.d.ts +16 -0
  389. package/dist/modules/media/adapter.d.ts.map +1 -0
  390. package/dist/modules/media/adapter.js +1 -0
  391. package/dist/modules/media/repository/index.d.ts +35 -0
  392. package/dist/modules/media/repository/index.d.ts.map +1 -0
  393. package/dist/modules/media/repository/index.js +176 -0
  394. package/dist/modules/media/schema.d.ts +289 -0
  395. package/dist/modules/media/schema.d.ts.map +1 -0
  396. package/dist/modules/media/schema.js +35 -0
  397. package/dist/modules/media/service.d.ts +42 -0
  398. package/dist/modules/media/service.d.ts.map +1 -0
  399. package/dist/modules/media/service.js +89 -0
  400. package/dist/modules/orders/repository/index.d.ts +48 -0
  401. package/dist/modules/orders/repository/index.d.ts.map +1 -0
  402. package/dist/modules/orders/repository/index.js +199 -0
  403. package/dist/modules/orders/schema.d.ts +672 -0
  404. package/dist/modules/orders/schema.d.ts.map +1 -0
  405. package/dist/modules/orders/schema.js +63 -0
  406. package/dist/modules/orders/service.d.ts +85 -0
  407. package/dist/modules/orders/service.d.ts.map +1 -0
  408. package/dist/modules/orders/service.js +313 -0
  409. package/dist/modules/orders/stale-order-cleanup.d.ts +27 -0
  410. package/dist/modules/orders/stale-order-cleanup.d.ts.map +1 -0
  411. package/dist/modules/orders/stale-order-cleanup.js +55 -0
  412. package/dist/modules/organization/service.d.ts +53 -0
  413. package/dist/modules/organization/service.d.ts.map +1 -0
  414. package/dist/modules/organization/service.js +151 -0
  415. package/dist/modules/payments/adapter.d.ts +42 -0
  416. package/dist/modules/payments/adapter.d.ts.map +1 -0
  417. package/dist/modules/payments/adapter.js +1 -0
  418. package/dist/modules/payments/repository/index.d.ts +5 -0
  419. package/dist/modules/payments/repository/index.d.ts.map +1 -0
  420. package/dist/modules/payments/repository/index.js +1 -0
  421. package/dist/modules/payments/service.d.ts +23 -0
  422. package/dist/modules/payments/service.d.ts.map +1 -0
  423. package/dist/modules/payments/service.js +72 -0
  424. package/dist/modules/pricing/repository/index.d.ts +34 -0
  425. package/dist/modules/pricing/repository/index.d.ts.map +1 -0
  426. package/dist/modules/pricing/repository/index.js +176 -0
  427. package/dist/modules/pricing/schema.d.ts +565 -0
  428. package/dist/modules/pricing/schema.d.ts.map +1 -0
  429. package/dist/modules/pricing/schema.js +57 -0
  430. package/dist/modules/pricing/schemas.d.ts +37 -0
  431. package/dist/modules/pricing/schemas.d.ts.map +1 -0
  432. package/dist/modules/pricing/schemas.js +30 -0
  433. package/dist/modules/pricing/service.d.ts +62 -0
  434. package/dist/modules/pricing/service.d.ts.map +1 -0
  435. package/dist/modules/pricing/service.js +308 -0
  436. package/dist/modules/promotions/repository/index.d.ts +41 -0
  437. package/dist/modules/promotions/repository/index.d.ts.map +1 -0
  438. package/dist/modules/promotions/repository/index.js +204 -0
  439. package/dist/modules/promotions/schema.d.ts +427 -0
  440. package/dist/modules/promotions/schema.d.ts.map +1 -0
  441. package/dist/modules/promotions/schema.js +52 -0
  442. package/dist/modules/promotions/schemas.d.ts +33 -0
  443. package/dist/modules/promotions/schemas.d.ts.map +1 -0
  444. package/dist/modules/promotions/schemas.js +32 -0
  445. package/dist/modules/promotions/service.d.ts +80 -0
  446. package/dist/modules/promotions/service.d.ts.map +1 -0
  447. package/dist/modules/promotions/service.js +347 -0
  448. package/dist/modules/search/adapter.d.ts +51 -0
  449. package/dist/modules/search/adapter.d.ts.map +1 -0
  450. package/dist/modules/search/adapter.js +1 -0
  451. package/dist/modules/search/hooks.d.ts +8 -0
  452. package/dist/modules/search/hooks.d.ts.map +1 -0
  453. package/dist/modules/search/hooks.js +6 -0
  454. package/dist/modules/search/repository/index.d.ts +5 -0
  455. package/dist/modules/search/repository/index.d.ts.map +1 -0
  456. package/dist/modules/search/repository/index.js +1 -0
  457. package/dist/modules/search/service.d.ts +24 -0
  458. package/dist/modules/search/service.d.ts.map +1 -0
  459. package/dist/modules/search/service.js +217 -0
  460. package/dist/modules/shipping/calculator.d.ts +42 -0
  461. package/dist/modules/shipping/calculator.d.ts.map +1 -0
  462. package/dist/modules/shipping/calculator.js +91 -0
  463. package/dist/modules/shipping/repository/index.d.ts +5 -0
  464. package/dist/modules/shipping/repository/index.d.ts.map +1 -0
  465. package/dist/modules/shipping/repository/index.js +1 -0
  466. package/dist/modules/shipping/service.d.ts +28 -0
  467. package/dist/modules/shipping/service.d.ts.map +1 -0
  468. package/dist/modules/shipping/service.js +20 -0
  469. package/dist/modules/tax/adapter.d.ts +58 -0
  470. package/dist/modules/tax/adapter.d.ts.map +1 -0
  471. package/dist/modules/tax/adapter.js +1 -0
  472. package/dist/modules/tax/repository/index.d.ts +5 -0
  473. package/dist/modules/tax/repository/index.d.ts.map +1 -0
  474. package/dist/modules/tax/repository/index.js +1 -0
  475. package/dist/modules/tax/service.d.ts +19 -0
  476. package/dist/modules/tax/service.d.ts.map +1 -0
  477. package/dist/modules/tax/service.js +34 -0
  478. package/dist/modules/webhooks/hook.d.ts +13 -0
  479. package/dist/modules/webhooks/hook.d.ts.map +1 -0
  480. package/dist/modules/webhooks/hook.js +29 -0
  481. package/dist/modules/webhooks/repository/index.d.ts +40 -0
  482. package/dist/modules/webhooks/repository/index.d.ts.map +1 -0
  483. package/dist/modules/webhooks/repository/index.js +175 -0
  484. package/dist/modules/webhooks/schema.d.ts +404 -0
  485. package/dist/modules/webhooks/schema.d.ts.map +1 -0
  486. package/dist/modules/webhooks/schema.js +40 -0
  487. package/dist/modules/webhooks/service.d.ts +23 -0
  488. package/dist/modules/webhooks/service.d.ts.map +1 -0
  489. package/dist/modules/webhooks/service.js +92 -0
  490. package/dist/modules/webhooks/signing.d.ts +2 -0
  491. package/dist/modules/webhooks/signing.d.ts.map +1 -0
  492. package/dist/modules/webhooks/signing.js +5 -0
  493. package/dist/modules/webhooks/ssrf-guard.d.ts +19 -0
  494. package/dist/modules/webhooks/ssrf-guard.d.ts.map +1 -0
  495. package/dist/modules/webhooks/ssrf-guard.js +79 -0
  496. package/dist/modules/webhooks/tasks.d.ts +16 -0
  497. package/dist/modules/webhooks/tasks.d.ts.map +1 -0
  498. package/dist/modules/webhooks/tasks.js +35 -0
  499. package/dist/modules/webhooks/worker.d.ts +21 -0
  500. package/dist/modules/webhooks/worker.d.ts.map +1 -0
  501. package/dist/modules/webhooks/worker.js +113 -0
  502. package/dist/runtime/commerce.d.ts +110 -0
  503. package/dist/runtime/commerce.d.ts.map +1 -0
  504. package/dist/runtime/commerce.js +37 -0
  505. package/dist/runtime/kernel.d.ts +71 -0
  506. package/dist/runtime/kernel.d.ts.map +1 -0
  507. package/dist/runtime/kernel.js +306 -0
  508. package/dist/runtime/logger.d.ts +11 -0
  509. package/dist/runtime/logger.d.ts.map +1 -0
  510. package/dist/runtime/logger.js +32 -0
  511. package/dist/runtime/shutdown.d.ts +15 -0
  512. package/dist/runtime/shutdown.d.ts.map +1 -0
  513. package/dist/runtime/shutdown.js +34 -0
  514. package/dist/test-utils/create-pglite-adapter.d.ts +32 -0
  515. package/dist/test-utils/create-pglite-adapter.d.ts.map +1 -0
  516. package/dist/test-utils/create-pglite-adapter.js +107 -0
  517. package/dist/test-utils/create-plugin-test-app.d.ts +50 -0
  518. package/dist/test-utils/create-plugin-test-app.d.ts.map +1 -0
  519. package/dist/test-utils/create-plugin-test-app.js +74 -0
  520. package/dist/test-utils/create-repository-test-harness.d.ts +8 -0
  521. package/dist/test-utils/create-repository-test-harness.d.ts.map +1 -0
  522. package/dist/test-utils/create-repository-test-harness.js +7 -0
  523. package/dist/test-utils/create-test-config.d.ts +18 -0
  524. package/dist/test-utils/create-test-config.d.ts.map +1 -0
  525. package/dist/test-utils/create-test-config.js +172 -0
  526. package/dist/test-utils/create-test-kernel.d.ts +3 -0
  527. package/dist/test-utils/create-test-kernel.d.ts.map +1 -0
  528. package/dist/test-utils/create-test-kernel.js +5 -0
  529. package/dist/test-utils/create-test-plugin-context.d.ts +42 -0
  530. package/dist/test-utils/create-test-plugin-context.d.ts.map +1 -0
  531. package/dist/test-utils/create-test-plugin-context.js +46 -0
  532. package/dist/test-utils/rest-api-test-utils.d.ts +64 -0
  533. package/dist/test-utils/rest-api-test-utils.d.ts.map +1 -0
  534. package/dist/test-utils/rest-api-test-utils.js +207 -0
  535. package/dist/test-utils/test-actors.d.ts +15 -0
  536. package/dist/test-utils/test-actors.d.ts.map +1 -0
  537. package/dist/test-utils/test-actors.js +57 -0
  538. package/dist/test-utils/typed-hooks.d.ts +43 -0
  539. package/dist/test-utils/typed-hooks.d.ts.map +1 -0
  540. package/dist/test-utils/typed-hooks.js +35 -0
  541. package/dist/testing.d.ts +14 -0
  542. package/dist/testing.d.ts.map +1 -0
  543. package/dist/testing.js +13 -0
  544. package/dist/types/commerce-types.d.ts +34 -0
  545. package/dist/types/commerce-types.d.ts.map +1 -0
  546. package/dist/types/commerce-types.js +1 -0
  547. package/dist/utils/id.d.ts +2 -0
  548. package/dist/utils/id.d.ts.map +1 -0
  549. package/dist/utils/id.js +3 -0
  550. package/dist/utils/logger.d.ts +3 -0
  551. package/dist/utils/logger.d.ts.map +1 -0
  552. package/dist/utils/logger.js +16 -0
  553. package/dist/utils/pagination.d.ts +11 -0
  554. package/dist/utils/pagination.d.ts.map +1 -0
  555. package/dist/utils/pagination.js +15 -0
  556. package/package.json +1 -1
@@ -0,0 +1,30 @@
1
+ import { z } from "@hono/zod-openapi";
2
+ // ─── Zod Body Schemas (single source of truth) ─────────────────────────────
3
+ export const SetBasePriceBodySchema = z.object({
4
+ entityId: z.string().openapi({ example: "product-uuid" }),
5
+ variantId: z.string().optional().openapi({ example: "variant-uuid" }),
6
+ currency: z.string().length(3).openapi({ example: "USD" }),
7
+ amount: z.number().openapi({ example: 29.99 }),
8
+ customerGroupId: z.string().optional(),
9
+ minQuantity: z.number().int().optional(),
10
+ maxQuantity: z.number().int().optional(),
11
+ validFrom: z.coerce.date().optional(),
12
+ validUntil: z.coerce.date().optional(),
13
+ metadata: z.record(z.string(), z.unknown()).optional(),
14
+ }).openapi("SetBasePriceRequest");
15
+ export const CreateModifierBodySchema = z.object({
16
+ name: z.string().openapi({ example: "Summer Sale" }),
17
+ type: z.enum(["percentage_discount", "fixed_discount", "markup", "override"]).openapi({ example: "percentage_discount" }),
18
+ value: z.number().openapi({ example: 10 }),
19
+ priority: z.number().int().optional(),
20
+ entityId: z.string().optional(),
21
+ variantId: z.string().optional(),
22
+ customerGroupId: z.string().optional(),
23
+ currency: z.string().length(3).optional().openapi({ example: "USD" }),
24
+ minQuantity: z.number().int().optional(),
25
+ maxQuantity: z.number().int().optional(),
26
+ conditions: z.record(z.string(), z.unknown()).optional(),
27
+ validFrom: z.coerce.date().optional(),
28
+ validUntil: z.coerce.date().optional(),
29
+ metadata: z.record(z.string(), z.unknown()).optional(),
30
+ }).openapi("CreateModifierRequest");
@@ -0,0 +1,62 @@
1
+ import type { Actor } from "../../auth/types.js";
2
+ import { type Result } from "../../kernel/result.js";
3
+ import type { TxContext } from "../../kernel/database/tx-context.js";
4
+ import type { PricingRepository, Price, PriceModifier } from "./repository/index.js";
5
+ import type { CatalogRepository } from "../catalog/repository/index.js";
6
+ export type PriceModifierType = "percentage_discount" | "fixed_discount" | "markup" | "override";
7
+ interface PricingServiceDeps {
8
+ repository: PricingRepository;
9
+ catalogRepository: CatalogRepository;
10
+ }
11
+ export interface PriceResolutionContext {
12
+ entityId: string;
13
+ variantId?: string;
14
+ currency: string;
15
+ quantity: number;
16
+ customerId?: string;
17
+ customerGroupIds?: string[];
18
+ timestamp?: Date;
19
+ }
20
+ export interface PriceBreakdownStep {
21
+ label: string;
22
+ amountBefore: number;
23
+ delta: number;
24
+ amountAfter: number;
25
+ metadata?: Record<string, unknown>;
26
+ }
27
+ export interface ResolvedPrice {
28
+ baseAmount: number;
29
+ finalAmount: number;
30
+ currency: string;
31
+ appliedModifiers: Array<{
32
+ id: string;
33
+ name: string;
34
+ type: PriceModifierType;
35
+ delta: number;
36
+ value: number;
37
+ priority: number;
38
+ }>;
39
+ breakdown: PriceBreakdownStep[];
40
+ basePriceId: string;
41
+ }
42
+ export type { SetBasePriceInput, CreatePriceModifierInput } from "./schemas.js";
43
+ import type { SetBasePriceInput, CreatePriceModifierInput } from "./schemas.js";
44
+ export declare class PricingService {
45
+ private deps;
46
+ private readonly repo;
47
+ private readonly catalogRepo;
48
+ constructor(deps: PricingServiceDeps);
49
+ setBasePrice(input: SetBasePriceInput, actor?: Actor | null, ctx?: TxContext): Promise<Result<Price>>;
50
+ createModifier(input: CreatePriceModifierInput, actor?: Actor | null, ctx?: TxContext): Promise<Result<PriceModifier>>;
51
+ listPrices(filter?: {
52
+ entityId?: string;
53
+ variantId?: string;
54
+ currency?: string;
55
+ customerGroupId?: string;
56
+ }, ctx?: TxContext): Promise<Result<{
57
+ prices: Price[];
58
+ modifiers: PriceModifier[];
59
+ }>>;
60
+ resolve(input: PriceResolutionContext, ctx?: TxContext): Promise<Result<ResolvedPrice>>;
61
+ }
62
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/modules/pricing/service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,308 @@
1
+ import { resolveOrgId } from "../../auth/org.js";
2
+ import { CommerceNotFoundError, CommerceValidationError, } from "../../kernel/errors.js";
3
+ import { Err, Ok } from "../../kernel/result.js";
4
+ function matchesQuantity(min, max, quantity) {
5
+ if (min != null && quantity < min)
6
+ return false;
7
+ if (max != null && quantity > max)
8
+ return false;
9
+ return true;
10
+ }
11
+ function matchesWindow(validFrom, validUntil, timestamp) {
12
+ if (validFrom && timestamp < validFrom)
13
+ return false;
14
+ if (validUntil && timestamp > validUntil)
15
+ return false;
16
+ return true;
17
+ }
18
+ function durationScore(validFrom, validUntil) {
19
+ if (validFrom && validUntil)
20
+ return validUntil.getTime() - validFrom.getTime();
21
+ if (validFrom || validUntil)
22
+ return Number.MAX_SAFE_INTEGER - 1;
23
+ return Number.MAX_SAFE_INTEGER;
24
+ }
25
+ function quantityRangeWidth(min, max) {
26
+ if (min != null && max != null)
27
+ return Math.max(0, max - min);
28
+ if (min != null || max != null)
29
+ return Number.MAX_SAFE_INTEGER - 1;
30
+ return Number.MAX_SAFE_INTEGER;
31
+ }
32
+ function compareBasePriceSpecificity(a, b, context) {
33
+ const aVariant = context.variantId !== undefined && a.variantId === context.variantId
34
+ ? 1
35
+ : 0;
36
+ const bVariant = context.variantId !== undefined && b.variantId === context.variantId
37
+ ? 1
38
+ : 0;
39
+ if (aVariant !== bVariant)
40
+ return bVariant - aVariant;
41
+ const hasGroupA = a.customerGroupId ? 1 : 0;
42
+ const hasGroupB = b.customerGroupId ? 1 : 0;
43
+ if (hasGroupA !== hasGroupB)
44
+ return hasGroupB - hasGroupA;
45
+ const aRange = quantityRangeWidth(a.minQuantity, a.maxQuantity);
46
+ const bRange = quantityRangeWidth(b.minQuantity, b.maxQuantity);
47
+ if (aRange !== bRange)
48
+ return aRange - bRange;
49
+ const aDuration = durationScore(a.validFrom, a.validUntil);
50
+ const bDuration = durationScore(b.validFrom, b.validUntil);
51
+ if (aDuration !== bDuration)
52
+ return aDuration - bDuration;
53
+ return b.createdAt.getTime() - a.createdAt.getTime();
54
+ }
55
+ function resolveModifierDelta(type, value, amountBefore) {
56
+ switch (type) {
57
+ case "percentage_discount":
58
+ return -Math.round((amountBefore * value) / 100);
59
+ case "fixed_discount":
60
+ return -value;
61
+ case "markup":
62
+ return value;
63
+ case "override":
64
+ return value - amountBefore;
65
+ default:
66
+ return 0;
67
+ }
68
+ }
69
+ function toGroupSet(context) {
70
+ return new Set(context.customerGroupIds ?? []);
71
+ }
72
+ function normalizeCurrency(currency) {
73
+ return currency.trim().toUpperCase();
74
+ }
75
+ export class PricingService {
76
+ deps;
77
+ repo;
78
+ catalogRepo;
79
+ constructor(deps) {
80
+ this.deps = deps;
81
+ this.repo = deps.repository;
82
+ this.catalogRepo = deps.catalogRepository;
83
+ }
84
+ async setBasePrice(input, actor, ctx) {
85
+ if (input.amount < 0) {
86
+ return Err(new CommerceValidationError("Base price amount cannot be negative."));
87
+ }
88
+ const entity = await this.catalogRepo.findEntityById(input.entityId, ctx);
89
+ if (!entity) {
90
+ return Err(new CommerceNotFoundError("Entity not found for price assignment."));
91
+ }
92
+ const orgId = resolveOrgId(actor ?? ctx?.actor ?? null);
93
+ const priceData = {
94
+ organizationId: orgId,
95
+ entityId: input.entityId,
96
+ currency: normalizeCurrency(input.currency),
97
+ amount: input.amount,
98
+ metadata: input.metadata ?? {},
99
+ variantId: input.variantId ?? null,
100
+ customerGroupId: input.customerGroupId ?? null,
101
+ minQuantity: input.minQuantity ?? null,
102
+ maxQuantity: input.maxQuantity ?? null,
103
+ validFrom: input.validFrom ?? null,
104
+ validUntil: input.validUntil ?? null,
105
+ };
106
+ const record = await this.repo.createPrice(priceData, ctx);
107
+ return Ok(record);
108
+ }
109
+ async createModifier(input, actor, ctx) {
110
+ if (!input.name) {
111
+ return Err(new CommerceValidationError("Modifier name is required."));
112
+ }
113
+ // Validate modifier type
114
+ const validTypes = [
115
+ "percentage_discount",
116
+ "fixed_discount",
117
+ "markup",
118
+ "override",
119
+ ];
120
+ if (!validTypes.includes(input.type)) {
121
+ return Err(new CommerceValidationError(`Invalid modifier type "${input.type}". Must be one of: ${validTypes.join(", ")}`));
122
+ }
123
+ const orgId = resolveOrgId(actor ?? ctx?.actor ?? null);
124
+ const modifierData = {
125
+ organizationId: orgId,
126
+ name: input.name,
127
+ type: input.type,
128
+ value: input.value,
129
+ priority: input.priority ?? 100,
130
+ conditions: input.conditions ?? {},
131
+ metadata: input.metadata ?? {},
132
+ entityId: input.entityId ?? null,
133
+ variantId: input.variantId ?? null,
134
+ customerGroupId: input.customerGroupId ?? null,
135
+ currency: input.currency ? normalizeCurrency(input.currency) : null,
136
+ minQuantity: input.minQuantity ?? null,
137
+ maxQuantity: input.maxQuantity ?? null,
138
+ validFrom: input.validFrom ?? null,
139
+ validUntil: input.validUntil ?? null,
140
+ };
141
+ const modifier = await this.repo.createModifier(modifierData, ctx);
142
+ return Ok(modifier);
143
+ }
144
+ async listPrices(filter, ctx) {
145
+ // Get all prices for the entity if specified, or filter after retrieval
146
+ let prices = [];
147
+ if (filter?.entityId) {
148
+ prices = await this.repo.findPricesByEntityId(filter.entityId, ctx);
149
+ }
150
+ // Note: For full list without entityId, we'd need a findAll method
151
+ // For now, entityId is typically required for practical use
152
+ // Apply additional filters
153
+ if (filter?.variantId !== undefined) {
154
+ prices = prices.filter((p) => p.variantId === filter.variantId);
155
+ }
156
+ if (filter?.currency !== undefined) {
157
+ const normalizedCurrency = normalizeCurrency(filter.currency);
158
+ prices = prices.filter((p) => p.currency === normalizedCurrency);
159
+ }
160
+ if (filter?.customerGroupId !== undefined) {
161
+ prices = prices.filter((p) => p.customerGroupId === filter.customerGroupId);
162
+ }
163
+ // Get modifiers for the entity
164
+ let modifiers = [];
165
+ if (filter?.entityId) {
166
+ modifiers = await this.repo.findModifiersByEntityId(filter.entityId, ctx);
167
+ }
168
+ // Apply additional filters
169
+ if (filter?.variantId !== undefined) {
170
+ modifiers = modifiers.filter((m) => m.variantId === filter.variantId);
171
+ }
172
+ if (filter?.currency !== undefined) {
173
+ const normalizedCurrency = normalizeCurrency(filter.currency);
174
+ modifiers = modifiers.filter((m) => m.currency === null || m.currency === normalizedCurrency);
175
+ }
176
+ if (filter?.customerGroupId !== undefined) {
177
+ modifiers = modifiers.filter((m) => m.customerGroupId === null ||
178
+ m.customerGroupId === filter.customerGroupId);
179
+ }
180
+ return Ok({ prices, modifiers });
181
+ }
182
+ async resolve(input, ctx) {
183
+ const entity = await this.catalogRepo.findEntityById(input.entityId, ctx);
184
+ if (!entity) {
185
+ return Err(new CommerceNotFoundError(`Entity ${input.entityId} not found.`));
186
+ }
187
+ if (input.quantity <= 0) {
188
+ return Err(new CommerceValidationError("Quantity must be greater than zero for price resolution."));
189
+ }
190
+ const timestamp = input.timestamp ?? new Date();
191
+ const currency = normalizeCurrency(input.currency);
192
+ const groupSet = toGroupSet(input);
193
+ // Get matching prices from repository
194
+ const allPrices = await this.repo.findPricesByEntityId(input.entityId, ctx);
195
+ const matchingPrices = allPrices.filter((price) => {
196
+ if (input.variantId !== undefined) {
197
+ if (price.variantId !== null && price.variantId !== input.variantId)
198
+ return false;
199
+ }
200
+ else if (price.variantId !== null) {
201
+ return false;
202
+ }
203
+ if (price.currency !== currency)
204
+ return false;
205
+ if (!matchesQuantity(price.minQuantity, price.maxQuantity, input.quantity))
206
+ return false;
207
+ if (!matchesWindow(price.validFrom, price.validUntil, timestamp))
208
+ return false;
209
+ if (price.customerGroupId !== null &&
210
+ !groupSet.has(price.customerGroupId))
211
+ return false;
212
+ return true;
213
+ });
214
+ if (matchingPrices.length === 0) {
215
+ const metadataPrice = typeof entity.metadata?.basePrice === "number"
216
+ ? Math.round(entity.metadata.basePrice)
217
+ : undefined;
218
+ if (metadataPrice === undefined) {
219
+ return Err(new CommerceNotFoundError(`No base price configured for ${entity.slug} (${currency}).`));
220
+ }
221
+ const fallback = {
222
+ baseAmount: metadataPrice,
223
+ finalAmount: metadataPrice,
224
+ currency,
225
+ basePriceId: "metadata:basePrice",
226
+ appliedModifiers: [],
227
+ breakdown: [
228
+ {
229
+ label: "Base price (entity metadata)",
230
+ amountBefore: metadataPrice,
231
+ delta: 0,
232
+ amountAfter: metadataPrice,
233
+ },
234
+ ],
235
+ };
236
+ return Ok(fallback);
237
+ }
238
+ const selectedBase = [...matchingPrices].sort((a, b) => compareBasePriceSpecificity(a, b, input))[0];
239
+ if (!selectedBase) {
240
+ return Err(new CommerceNotFoundError("No matching base price could be selected."));
241
+ }
242
+ let runningAmount = selectedBase.amount;
243
+ const breakdown = [
244
+ {
245
+ label: "Base price",
246
+ amountBefore: selectedBase.amount,
247
+ delta: 0,
248
+ amountAfter: selectedBase.amount,
249
+ metadata: {
250
+ priceId: selectedBase.id,
251
+ customerGroupId: selectedBase.customerGroupId,
252
+ minQuantity: selectedBase.minQuantity,
253
+ maxQuantity: selectedBase.maxQuantity,
254
+ },
255
+ },
256
+ ];
257
+ const appliedModifiers = [];
258
+ // Get matching modifiers (includes both entity-specific and global)
259
+ const firstGroupId = groupSet.size > 0 ? [...groupSet][0] : undefined;
260
+ const activeModifiers = await this.repo.findActiveModifiers(input.entityId, input.variantId, firstGroupId, currency, input.quantity, ctx);
261
+ // Additional filtering for multiple customer groups and conditions
262
+ const modifiers = activeModifiers
263
+ .filter((modifier) => {
264
+ // Re-check customer group for multi-group support
265
+ if (modifier.customerGroupId !== null &&
266
+ !groupSet.has(modifier.customerGroupId))
267
+ return false;
268
+ const conditions = modifier.conditions;
269
+ const minSubtotal = conditions?.minSubtotal;
270
+ if (typeof minSubtotal === "number" && runningAmount < minSubtotal)
271
+ return false;
272
+ return true;
273
+ })
274
+ .sort((a, b) => a.priority - b.priority);
275
+ for (const modifier of modifiers) {
276
+ const amountBefore = runningAmount;
277
+ const rawDelta = resolveModifierDelta(modifier.type, modifier.value, amountBefore);
278
+ const delta = Math.max(-amountBefore, rawDelta);
279
+ runningAmount = Math.max(0, amountBefore + delta);
280
+ appliedModifiers.push({
281
+ id: modifier.id,
282
+ name: modifier.name,
283
+ type: modifier.type,
284
+ delta,
285
+ value: modifier.value,
286
+ priority: modifier.priority,
287
+ });
288
+ breakdown.push({
289
+ label: `Modifier: ${modifier.name}`,
290
+ amountBefore,
291
+ delta,
292
+ amountAfter: runningAmount,
293
+ metadata: {
294
+ type: modifier.type,
295
+ priority: modifier.priority,
296
+ },
297
+ });
298
+ }
299
+ return Ok({
300
+ baseAmount: selectedBase.amount,
301
+ finalAmount: runningAmount,
302
+ currency,
303
+ basePriceId: selectedBase.id,
304
+ appliedModifiers,
305
+ breakdown,
306
+ });
307
+ }
308
+ }
@@ -0,0 +1,41 @@
1
+ import type { TxContext } from "../../../kernel/database/tx-context.js";
2
+ import type { DrizzleDatabase } from "../../../kernel/database/drizzle-db.js";
3
+ import { promotions, promotionUsages } from "../schema.js";
4
+ export type Promotion = typeof promotions.$inferSelect;
5
+ export type PromotionInsert = typeof promotions.$inferInsert;
6
+ export type PromotionUsage = typeof promotionUsages.$inferSelect;
7
+ export type PromotionUsageInsert = typeof promotionUsages.$inferInsert;
8
+ /**
9
+ * PromotionsRepository provides type-safe database operations for promotions.
10
+ *
11
+ * This repository manages promotions and their usage tracking.
12
+ * All methods support an optional TxContext parameter for transaction participation.
13
+ */
14
+ export declare class PromotionsRepository {
15
+ private readonly db;
16
+ constructor(db: DrizzleDatabase);
17
+ private getDb;
18
+ findById(orgId: string, id: string, ctx?: TxContext): Promise<Promotion | undefined>;
19
+ findByCode(orgId: string, code: string, ctx?: TxContext): Promise<Promotion | undefined>;
20
+ findAll(orgId: string, ctx?: TxContext): Promise<Promotion[]>;
21
+ findActive(orgId: string, ctx?: TxContext): Promise<Promotion[]>;
22
+ findAutomatic(orgId: string, ctx?: TxContext): Promise<Promotion[]>;
23
+ create(data: PromotionInsert, ctx?: TxContext): Promise<Promotion>;
24
+ update(id: string, data: Partial<Omit<PromotionInsert, "id">>, ctx?: TxContext): Promise<Promotion | undefined>;
25
+ delete(id: string, ctx?: TxContext): Promise<boolean>;
26
+ activate(id: string, ctx?: TxContext): Promise<Promotion | undefined>;
27
+ deactivate(id: string, ctx?: TxContext): Promise<Promotion | undefined>;
28
+ findUsageById(id: string, ctx?: TxContext): Promise<PromotionUsage | undefined>;
29
+ findUsagesByPromotionId(promotionId: string, ctx?: TxContext): Promise<PromotionUsage[]>;
30
+ findUsagesByCustomerId(customerId: string, ctx?: TxContext): Promise<PromotionUsage[]>;
31
+ createUsage(data: PromotionUsageInsert, ctx?: TxContext): Promise<PromotionUsage>;
32
+ countUsages(promotionId: string, ctx?: TxContext): Promise<number>;
33
+ countUsagesByCustomer(promotionId: string, customerId: string, ctx?: TxContext): Promise<number>;
34
+ isUsageLimitReached(orgId: string, promotionId: string, ctx?: TxContext): Promise<boolean>;
35
+ isCustomerUsageLimitReached(orgId: string, promotionId: string, customerId: string, ctx?: TxContext): Promise<boolean>;
36
+ isPromotionValid(orgId: string, promotionId: string, customerId?: string, ctx?: TxContext): Promise<{
37
+ valid: boolean;
38
+ reason?: string;
39
+ }>;
40
+ }
41
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modules/promotions/repository/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,204 @@
1
+ import { eq, and, lte, gte, or, isNull, desc, sql } from "drizzle-orm";
2
+ import { promotions, promotionUsages } from "../schema.js";
3
+ /**
4
+ * PromotionsRepository provides type-safe database operations for promotions.
5
+ *
6
+ * This repository manages promotions and their usage tracking.
7
+ * All methods support an optional TxContext parameter for transaction participation.
8
+ */
9
+ export class PromotionsRepository {
10
+ db;
11
+ constructor(db) {
12
+ this.db = db;
13
+ }
14
+ getDb(ctx) {
15
+ return ctx?.tx ?? this.db;
16
+ }
17
+ // ─────────────────────────────────────────────────────────────────────────────
18
+ // Promotions
19
+ // ─────────────────────────────────────────────────────────────────────────────
20
+ async findById(orgId, id, ctx) {
21
+ const db = this.getDb(ctx);
22
+ const rows = await db
23
+ .select()
24
+ .from(promotions)
25
+ .where(and(eq(promotions.organizationId, orgId), eq(promotions.id, id)));
26
+ return rows[0];
27
+ }
28
+ async findByCode(orgId, code, ctx) {
29
+ const db = this.getDb(ctx);
30
+ const rows = await db
31
+ .select()
32
+ .from(promotions)
33
+ .where(and(eq(promotions.organizationId, orgId), eq(promotions.code, code)));
34
+ return rows[0];
35
+ }
36
+ async findAll(orgId, ctx) {
37
+ const db = this.getDb(ctx);
38
+ return db
39
+ .select()
40
+ .from(promotions)
41
+ .where(eq(promotions.organizationId, orgId))
42
+ .orderBy(desc(promotions.priority));
43
+ }
44
+ async findActive(orgId, ctx) {
45
+ const db = this.getDb(ctx);
46
+ const now = new Date();
47
+ return db
48
+ .select()
49
+ .from(promotions)
50
+ .where(and(eq(promotions.organizationId, orgId), eq(promotions.isActive, true), or(isNull(promotions.validFrom), lte(promotions.validFrom, now)), or(isNull(promotions.validUntil), gte(promotions.validUntil, now))))
51
+ .orderBy(desc(promotions.priority));
52
+ }
53
+ async findAutomatic(orgId, ctx) {
54
+ const db = this.getDb(ctx);
55
+ const now = new Date();
56
+ return db
57
+ .select()
58
+ .from(promotions)
59
+ .where(and(eq(promotions.organizationId, orgId), eq(promotions.isActive, true), eq(promotions.isAutomatic, true), or(isNull(promotions.validFrom), lte(promotions.validFrom, now)), or(isNull(promotions.validUntil), gte(promotions.validUntil, now))))
60
+ .orderBy(desc(promotions.priority));
61
+ }
62
+ async create(data, ctx) {
63
+ const db = this.getDb(ctx);
64
+ const rows = await db.insert(promotions).values(data).returning();
65
+ return rows[0];
66
+ }
67
+ async update(id, data, ctx) {
68
+ const db = this.getDb(ctx);
69
+ const rows = await db
70
+ .update(promotions)
71
+ .set({ ...data, updatedAt: new Date() })
72
+ .where(eq(promotions.id, id))
73
+ .returning();
74
+ return rows[0];
75
+ }
76
+ async delete(id, ctx) {
77
+ const db = this.getDb(ctx);
78
+ const result = await db
79
+ .delete(promotions)
80
+ .where(eq(promotions.id, id))
81
+ .returning();
82
+ return result.length > 0;
83
+ }
84
+ async activate(id, ctx) {
85
+ return this.update(id, { isActive: true }, ctx);
86
+ }
87
+ async deactivate(id, ctx) {
88
+ return this.update(id, { isActive: false }, ctx);
89
+ }
90
+ // ─────────────────────────────────────────────────────────────────────────────
91
+ // Promotion Usages
92
+ // ─────────────────────────────────────────────────────────────────────────────
93
+ async findUsageById(id, ctx) {
94
+ const db = this.getDb(ctx);
95
+ const rows = await db
96
+ .select()
97
+ .from(promotionUsages)
98
+ .where(eq(promotionUsages.id, id));
99
+ return rows[0];
100
+ }
101
+ async findUsagesByPromotionId(promotionId, ctx) {
102
+ const db = this.getDb(ctx);
103
+ return db
104
+ .select()
105
+ .from(promotionUsages)
106
+ .where(eq(promotionUsages.promotionId, promotionId));
107
+ }
108
+ async findUsagesByCustomerId(customerId, ctx) {
109
+ const db = this.getDb(ctx);
110
+ return db
111
+ .select()
112
+ .from(promotionUsages)
113
+ .where(eq(promotionUsages.customerId, customerId));
114
+ }
115
+ async createUsage(data, ctx) {
116
+ const db = this.getDb(ctx);
117
+ // Atomic guard: use INSERT ... SELECT WHERE count < limit to prevent
118
+ // race conditions between concurrent checkouts using the same coupon.
119
+ // A plain count-then-insert has a TOCTOU gap; this single statement
120
+ // ensures the insert only succeeds if the limit has not been reached.
121
+ const promo = await db
122
+ .select({ usageLimitTotal: promotions.usageLimitTotal })
123
+ .from(promotions)
124
+ .where(eq(promotions.id, data.promotionId));
125
+ const limit = promo[0]?.usageLimitTotal;
126
+ if (limit != null) {
127
+ // Atomic guard: lock the promotion row and check usage count in the
128
+ // same statement sequence. This prevents two concurrent checkouts
129
+ // from both passing the count check (TOCTOU race).
130
+ //
131
+ // SELECT ... FOR UPDATE acquires a row-level lock that serializes
132
+ // concurrent callers. If the caller is already inside a transaction
133
+ // (ctx.tx), the lock is held until that transaction commits.
134
+ await db.execute(sql `SELECT id FROM promotions WHERE id = ${data.promotionId} FOR UPDATE`);
135
+ const currentCount = await this.countUsages(data.promotionId, ctx);
136
+ if (currentCount >= limit) {
137
+ throw new Error(`Promotion usage limit reached (${currentCount}/${limit})`);
138
+ }
139
+ const rows = await db.insert(promotionUsages).values(data).returning();
140
+ return rows[0];
141
+ }
142
+ const rows = await db.insert(promotionUsages).values(data).returning();
143
+ return rows[0];
144
+ }
145
+ async countUsages(promotionId, ctx) {
146
+ const db = this.getDb(ctx);
147
+ const result = await db
148
+ .select({ count: sql `count(*)::int` })
149
+ .from(promotionUsages)
150
+ .where(eq(promotionUsages.promotionId, promotionId));
151
+ return result[0]?.count ?? 0;
152
+ }
153
+ async countUsagesByCustomer(promotionId, customerId, ctx) {
154
+ const db = this.getDb(ctx);
155
+ const result = await db
156
+ .select({ count: sql `count(*)::int` })
157
+ .from(promotionUsages)
158
+ .where(and(eq(promotionUsages.promotionId, promotionId), eq(promotionUsages.customerId, customerId)));
159
+ return result[0]?.count ?? 0;
160
+ }
161
+ // ─────────────────────────────────────────────────────────────────────────────
162
+ // Validation Helpers
163
+ // ─────────────────────────────────────────────────────────────────────────────
164
+ async isUsageLimitReached(orgId, promotionId, ctx) {
165
+ const promotion = await this.findById(orgId, promotionId, ctx);
166
+ if (!promotion || promotion.usageLimitTotal === null) {
167
+ return false;
168
+ }
169
+ const count = await this.countUsages(promotionId, ctx);
170
+ return count >= promotion.usageLimitTotal;
171
+ }
172
+ async isCustomerUsageLimitReached(orgId, promotionId, customerId, ctx) {
173
+ const promotion = await this.findById(orgId, promotionId, ctx);
174
+ if (!promotion || promotion.usageLimitPerCustomer === null) {
175
+ return false;
176
+ }
177
+ const count = await this.countUsagesByCustomer(promotionId, customerId, ctx);
178
+ return count >= promotion.usageLimitPerCustomer;
179
+ }
180
+ async isPromotionValid(orgId, promotionId, customerId, ctx) {
181
+ const promotion = await this.findById(orgId, promotionId, ctx);
182
+ if (!promotion) {
183
+ return { valid: false, reason: "Promotion not found" };
184
+ }
185
+ if (!promotion.isActive) {
186
+ return { valid: false, reason: "Promotion is not active" };
187
+ }
188
+ const now = new Date();
189
+ if (promotion.validFrom && promotion.validFrom > now) {
190
+ return { valid: false, reason: "Promotion has not started yet" };
191
+ }
192
+ if (promotion.validUntil && promotion.validUntil < now) {
193
+ return { valid: false, reason: "Promotion has expired" };
194
+ }
195
+ if (await this.isUsageLimitReached(orgId, promotionId, ctx)) {
196
+ return { valid: false, reason: "Promotion usage limit reached" };
197
+ }
198
+ if (customerId &&
199
+ (await this.isCustomerUsageLimitReached(orgId, promotionId, customerId, ctx))) {
200
+ return { valid: false, reason: "Customer usage limit reached" };
201
+ }
202
+ return { valid: true };
203
+ }
204
+ }