@unifiedcommerce/core 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (245) hide show
  1. package/dist/auth/setup.d.ts.map +1 -1
  2. package/dist/auth/setup.js +8 -3
  3. package/dist/config/types.d.ts +3 -1
  4. package/dist/config/types.d.ts.map +1 -1
  5. package/dist/index.d.ts +1 -0
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +1 -0
  8. package/dist/interfaces/mcp/server.d.ts +3 -5
  9. package/dist/interfaces/mcp/server.d.ts.map +1 -1
  10. package/dist/interfaces/mcp/server.js +25 -510
  11. package/dist/interfaces/mcp/tool-builder.d.ts +120 -0
  12. package/dist/interfaces/mcp/tool-builder.d.ts.map +1 -0
  13. package/dist/interfaces/mcp/tool-builder.js +224 -0
  14. package/dist/interfaces/mcp/tools/analytics.d.ts +42 -0
  15. package/dist/interfaces/mcp/tools/analytics.d.ts.map +1 -0
  16. package/dist/interfaces/mcp/tools/analytics.js +70 -0
  17. package/dist/interfaces/mcp/tools/cart.d.ts +14 -0
  18. package/dist/interfaces/mcp/tools/cart.d.ts.map +1 -0
  19. package/dist/interfaces/mcp/tools/cart.js +47 -0
  20. package/dist/interfaces/mcp/tools/catalog.d.ts +53 -0
  21. package/dist/interfaces/mcp/tools/catalog.d.ts.map +1 -0
  22. package/dist/interfaces/mcp/tools/catalog.js +284 -0
  23. package/dist/interfaces/mcp/tools/index.d.ts +3 -0
  24. package/dist/interfaces/mcp/tools/index.d.ts.map +1 -0
  25. package/dist/interfaces/mcp/tools/index.js +20 -0
  26. package/dist/interfaces/mcp/tools/inventory.d.ts +27 -0
  27. package/dist/interfaces/mcp/tools/inventory.d.ts.map +1 -0
  28. package/dist/interfaces/mcp/tools/inventory.js +143 -0
  29. package/dist/interfaces/mcp/tools/orders.d.ts +18 -0
  30. package/dist/interfaces/mcp/tools/orders.d.ts.map +1 -0
  31. package/dist/interfaces/mcp/tools/orders.js +82 -0
  32. package/dist/interfaces/mcp/tools/pricing.d.ts +29 -0
  33. package/dist/interfaces/mcp/tools/pricing.d.ts.map +1 -0
  34. package/dist/interfaces/mcp/tools/pricing.js +90 -0
  35. package/dist/interfaces/mcp/tools/promotions.d.ts +44 -0
  36. package/dist/interfaces/mcp/tools/promotions.d.ts.map +1 -0
  37. package/dist/interfaces/mcp/tools/promotions.js +109 -0
  38. package/dist/interfaces/mcp/tools/registry.d.ts +32 -0
  39. package/dist/interfaces/mcp/tools/registry.d.ts.map +1 -0
  40. package/dist/interfaces/mcp/tools/registry.js +55 -0
  41. package/dist/interfaces/mcp/tools/search.d.ts +14 -0
  42. package/dist/interfaces/mcp/tools/search.d.ts.map +1 -0
  43. package/dist/interfaces/mcp/tools/search.js +39 -0
  44. package/dist/interfaces/mcp/tools/webhooks.d.ts +15 -0
  45. package/dist/interfaces/mcp/tools/webhooks.d.ts.map +1 -0
  46. package/dist/interfaces/mcp/tools/webhooks.js +48 -0
  47. package/dist/interfaces/mcp/transport.d.ts +17 -2
  48. package/dist/interfaces/mcp/transport.d.ts.map +1 -1
  49. package/dist/interfaces/mcp/transport.js +91 -44
  50. package/dist/interfaces/rest/router.d.ts.map +1 -1
  51. package/dist/interfaces/rest/routes/checkout.d.ts.map +1 -1
  52. package/dist/interfaces/rest/routes/checkout.js +1 -1
  53. package/dist/interfaces/rest/routes/promotions.d.ts.map +1 -1
  54. package/dist/interfaces/rest/routes/promotions.js +3 -2
  55. package/dist/kernel/database/adapter.d.ts +8 -0
  56. package/dist/kernel/database/adapter.d.ts.map +1 -1
  57. package/dist/kernel/factory/repository-factory.d.ts.map +1 -1
  58. package/dist/kernel/factory/repository-factory.js +3 -1
  59. package/dist/kernel/local-api.d.ts.map +1 -1
  60. package/dist/kernel/local-api.js +2 -0
  61. package/dist/kernel/plugin/manifest.d.ts +3 -3
  62. package/dist/kernel/plugin/manifest.d.ts.map +1 -1
  63. package/dist/kernel/plugin/manifest.js +36 -7
  64. package/dist/runtime/kernel.d.ts +1 -2
  65. package/dist/runtime/kernel.d.ts.map +1 -1
  66. package/dist/runtime/kernel.js +16 -8
  67. package/dist/runtime/server.d.ts.map +1 -1
  68. package/dist/runtime/server.js +8 -3
  69. package/dist/test-utils/create-pglite-adapter.d.ts.map +1 -1
  70. package/dist/test-utils/create-pglite-adapter.js +7 -6
  71. package/dist/tsconfig.tsbuildinfo +1 -0
  72. package/package.json +2 -2
  73. package/src/adapters/console-email.ts +0 -43
  74. package/src/auth/access.ts +0 -187
  75. package/src/auth/auth-schema.ts +0 -139
  76. package/src/auth/middleware.ts +0 -161
  77. package/src/auth/org.ts +0 -41
  78. package/src/auth/permissions.ts +0 -28
  79. package/src/auth/setup.ts +0 -169
  80. package/src/auth/system-actor.ts +0 -19
  81. package/src/auth/types.ts +0 -10
  82. package/src/config/defaults.ts +0 -82
  83. package/src/config/define-config.ts +0 -53
  84. package/src/config/types.ts +0 -299
  85. package/src/generated/plugin-capabilities.d.ts +0 -20
  86. package/src/generated/plugin-manifest.ts +0 -23
  87. package/src/generated/plugin-repositories.d.ts +0 -20
  88. package/src/hooks/checkout-completion.ts +0 -262
  89. package/src/hooks/checkout.ts +0 -677
  90. package/src/hooks/order-emails.ts +0 -62
  91. package/src/index.ts +0 -214
  92. package/src/interfaces/mcp/agent-prompt.ts +0 -174
  93. package/src/interfaces/mcp/context-enrichment.ts +0 -177
  94. package/src/interfaces/mcp/server.ts +0 -617
  95. package/src/interfaces/mcp/transport.ts +0 -68
  96. package/src/interfaces/rest/customer-portal.ts +0 -299
  97. package/src/interfaces/rest/index.ts +0 -74
  98. package/src/interfaces/rest/router.ts +0 -334
  99. package/src/interfaces/rest/routes/admin-jobs.ts +0 -58
  100. package/src/interfaces/rest/routes/audit.ts +0 -50
  101. package/src/interfaces/rest/routes/carts.ts +0 -89
  102. package/src/interfaces/rest/routes/catalog.ts +0 -493
  103. package/src/interfaces/rest/routes/checkout.ts +0 -283
  104. package/src/interfaces/rest/routes/inventory.ts +0 -70
  105. package/src/interfaces/rest/routes/media.ts +0 -86
  106. package/src/interfaces/rest/routes/orders.ts +0 -78
  107. package/src/interfaces/rest/routes/payments.ts +0 -60
  108. package/src/interfaces/rest/routes/pricing.ts +0 -57
  109. package/src/interfaces/rest/routes/promotions.ts +0 -92
  110. package/src/interfaces/rest/routes/search.ts +0 -71
  111. package/src/interfaces/rest/routes/webhooks.ts +0 -46
  112. package/src/interfaces/rest/schemas/admin-jobs.ts +0 -40
  113. package/src/interfaces/rest/schemas/audit.ts +0 -46
  114. package/src/interfaces/rest/schemas/carts.ts +0 -125
  115. package/src/interfaces/rest/schemas/catalog.ts +0 -450
  116. package/src/interfaces/rest/schemas/checkout.ts +0 -66
  117. package/src/interfaces/rest/schemas/customer-portal.ts +0 -195
  118. package/src/interfaces/rest/schemas/inventory.ts +0 -138
  119. package/src/interfaces/rest/schemas/media.ts +0 -75
  120. package/src/interfaces/rest/schemas/orders.ts +0 -104
  121. package/src/interfaces/rest/schemas/pricing.ts +0 -80
  122. package/src/interfaces/rest/schemas/promotions.ts +0 -110
  123. package/src/interfaces/rest/schemas/responses.ts +0 -85
  124. package/src/interfaces/rest/schemas/search.ts +0 -58
  125. package/src/interfaces/rest/schemas/shared.ts +0 -62
  126. package/src/interfaces/rest/schemas/webhooks.ts +0 -68
  127. package/src/interfaces/rest/utils.ts +0 -104
  128. package/src/interfaces/rest/webhook-router.ts +0 -50
  129. package/src/kernel/compensation/executor.ts +0 -61
  130. package/src/kernel/compensation/types.ts +0 -26
  131. package/src/kernel/database/adapter.ts +0 -13
  132. package/src/kernel/database/drizzle-db.ts +0 -56
  133. package/src/kernel/database/migrate.ts +0 -76
  134. package/src/kernel/database/plugin-types.ts +0 -34
  135. package/src/kernel/database/schema.ts +0 -49
  136. package/src/kernel/database/scoped-db.ts +0 -68
  137. package/src/kernel/database/tx-context.ts +0 -46
  138. package/src/kernel/error-mapper.ts +0 -15
  139. package/src/kernel/errors.ts +0 -89
  140. package/src/kernel/factory/repository-factory.ts +0 -242
  141. package/src/kernel/hooks/create-context.ts +0 -43
  142. package/src/kernel/hooks/executor.ts +0 -88
  143. package/src/kernel/hooks/registry.ts +0 -74
  144. package/src/kernel/hooks/types.ts +0 -52
  145. package/src/kernel/http-error.ts +0 -44
  146. package/src/kernel/jobs/adapter.ts +0 -36
  147. package/src/kernel/jobs/drizzle-adapter.ts +0 -58
  148. package/src/kernel/jobs/runner.ts +0 -153
  149. package/src/kernel/jobs/schema.ts +0 -46
  150. package/src/kernel/jobs/types.ts +0 -30
  151. package/src/kernel/local-api.ts +0 -185
  152. package/src/kernel/plugin/manifest.ts +0 -253
  153. package/src/kernel/query/executor.ts +0 -184
  154. package/src/kernel/query/registry.ts +0 -46
  155. package/src/kernel/result.ts +0 -33
  156. package/src/kernel/schema/extra-columns.ts +0 -37
  157. package/src/kernel/service-registry.ts +0 -76
  158. package/src/kernel/service-timing.ts +0 -89
  159. package/src/kernel/state-machine/machine.ts +0 -101
  160. package/src/modules/analytics/drizzle-adapter.ts +0 -426
  161. package/src/modules/analytics/hooks.ts +0 -11
  162. package/src/modules/analytics/models.ts +0 -125
  163. package/src/modules/analytics/repository/index.ts +0 -6
  164. package/src/modules/analytics/service.ts +0 -245
  165. package/src/modules/analytics/types.ts +0 -180
  166. package/src/modules/audit/hooks.ts +0 -78
  167. package/src/modules/audit/schema.ts +0 -33
  168. package/src/modules/audit/service.ts +0 -151
  169. package/src/modules/cart/access.ts +0 -27
  170. package/src/modules/cart/matcher.ts +0 -26
  171. package/src/modules/cart/repository/index.ts +0 -234
  172. package/src/modules/cart/schema.ts +0 -42
  173. package/src/modules/cart/schemas.ts +0 -38
  174. package/src/modules/cart/service.ts +0 -541
  175. package/src/modules/catalog/repository/index.ts +0 -772
  176. package/src/modules/catalog/schema.ts +0 -203
  177. package/src/modules/catalog/schemas.ts +0 -104
  178. package/src/modules/catalog/service.ts +0 -1544
  179. package/src/modules/customers/repository/index.ts +0 -327
  180. package/src/modules/customers/schema.ts +0 -64
  181. package/src/modules/customers/service.ts +0 -171
  182. package/src/modules/fulfillment/repository/index.ts +0 -426
  183. package/src/modules/fulfillment/schema.ts +0 -101
  184. package/src/modules/fulfillment/service.ts +0 -555
  185. package/src/modules/fulfillment/types.ts +0 -59
  186. package/src/modules/inventory/repository/index.ts +0 -509
  187. package/src/modules/inventory/schema.ts +0 -94
  188. package/src/modules/inventory/schemas.ts +0 -38
  189. package/src/modules/inventory/service.ts +0 -490
  190. package/src/modules/media/adapter.ts +0 -17
  191. package/src/modules/media/repository/index.ts +0 -274
  192. package/src/modules/media/schema.ts +0 -41
  193. package/src/modules/media/service.ts +0 -151
  194. package/src/modules/orders/repository/index.ts +0 -287
  195. package/src/modules/orders/schema.ts +0 -66
  196. package/src/modules/orders/service.ts +0 -619
  197. package/src/modules/orders/stale-order-cleanup.ts +0 -76
  198. package/src/modules/organization/service.ts +0 -191
  199. package/src/modules/payments/adapter.ts +0 -47
  200. package/src/modules/payments/repository/index.ts +0 -6
  201. package/src/modules/payments/service.ts +0 -107
  202. package/src/modules/pricing/repository/index.ts +0 -291
  203. package/src/modules/pricing/schema.ts +0 -71
  204. package/src/modules/pricing/schemas.ts +0 -38
  205. package/src/modules/pricing/service.ts +0 -494
  206. package/src/modules/promotions/repository/index.ts +0 -325
  207. package/src/modules/promotions/schema.ts +0 -62
  208. package/src/modules/promotions/schemas.ts +0 -38
  209. package/src/modules/promotions/service.ts +0 -598
  210. package/src/modules/search/adapter.ts +0 -57
  211. package/src/modules/search/hooks.ts +0 -12
  212. package/src/modules/search/repository/index.ts +0 -6
  213. package/src/modules/search/service.ts +0 -315
  214. package/src/modules/shipping/calculator.ts +0 -188
  215. package/src/modules/shipping/repository/index.ts +0 -6
  216. package/src/modules/shipping/service.ts +0 -51
  217. package/src/modules/tax/adapter.ts +0 -60
  218. package/src/modules/tax/repository/index.ts +0 -6
  219. package/src/modules/tax/service.ts +0 -53
  220. package/src/modules/webhooks/hook.ts +0 -34
  221. package/src/modules/webhooks/repository/index.ts +0 -278
  222. package/src/modules/webhooks/schema.ts +0 -56
  223. package/src/modules/webhooks/service.ts +0 -117
  224. package/src/modules/webhooks/signing.ts +0 -6
  225. package/src/modules/webhooks/ssrf-guard.ts +0 -71
  226. package/src/modules/webhooks/tasks.ts +0 -52
  227. package/src/modules/webhooks/worker.ts +0 -134
  228. package/src/runtime/commerce.ts +0 -145
  229. package/src/runtime/kernel.ts +0 -419
  230. package/src/runtime/logger.ts +0 -36
  231. package/src/runtime/server.ts +0 -349
  232. package/src/runtime/shutdown.ts +0 -43
  233. package/src/test-utils/create-pglite-adapter.ts +0 -129
  234. package/src/test-utils/create-plugin-test-app.ts +0 -128
  235. package/src/test-utils/create-repository-test-harness.ts +0 -16
  236. package/src/test-utils/create-test-config.ts +0 -190
  237. package/src/test-utils/create-test-kernel.ts +0 -7
  238. package/src/test-utils/create-test-plugin-context.ts +0 -75
  239. package/src/test-utils/rest-api-test-utils.ts +0 -265
  240. package/src/test-utils/test-actors.ts +0 -62
  241. package/src/test-utils/typed-hooks.ts +0 -54
  242. package/src/types/commerce-types.ts +0 -34
  243. package/src/utils/id.ts +0 -3
  244. package/src/utils/logger.ts +0 -18
  245. package/src/utils/pagination.ts +0 -22
@@ -1,283 +0,0 @@
1
- import { OpenAPIHono } from "@hono/zod-openapi";
2
- import type { Kernel } from "../../../runtime/kernel.js";
3
- import { checkoutRoute } from "../schemas/checkout.js";
4
- import {
5
- applyPromotionCodes,
6
- authorizePayment,
7
- calculateShipping,
8
- calculateTax,
9
- checkInventoryAvailability,
10
- completeCheckout,
11
- recordAnalyticsEvent,
12
- resolveCurrentPrices,
13
- validateCartNotEmpty,
14
- validatePaymentMethod,
15
- type CheckoutData,
16
- type OrderResult,
17
- } from "../../../hooks/checkout.js";
18
- import { runAfterHooks, runBeforeHooks } from "../../../kernel/hooks/executor.js";
19
- import { createHookContext } from "../../../kernel/hooks/create-context.js";
20
- import type { AfterHook, BeforeHook, ServiceContainer } from "../../../kernel/hooks/types.js";
21
- import { type AppEnv, mapErrorToResponse, mapErrorToStatus } from "../utils.js";
22
- import { isCommerceError } from "../../../kernel/errors.js";
23
- import { makeId } from "../../../utils/id.js";
24
- import type { ShippingAddress } from "../../../modules/shipping/calculator.js";
25
-
26
- export function checkoutRoutes(kernel: Kernel) {
27
- const router = new OpenAPIHono<AppEnv>();
28
-
29
- // @ts-expect-error -- openapi() enforces strict response typing but our handler
30
- // returns union responses (201 | 400 | 422). The route definition documents the
31
- // contract; the defaultHook handles Zod validation; the handler returns dynamic status.
32
- router.openapi(checkoutRoute, async (c) => {
33
- const body = c.req.valid("json");
34
-
35
- const actor = c.get("actor");
36
- const checkoutData: CheckoutData = {
37
- checkoutId: makeId(),
38
- cartId: body.cartId,
39
- currency: body.currency ?? "USD",
40
- paymentMethodId: body.paymentMethodId,
41
- lineItems: [],
42
- subtotal: 0,
43
- discountTotal: 0,
44
- taxTotal: 0,
45
- shippingTotal: 0,
46
- total: 0,
47
- ...(body.customerId !== undefined ? { customerId: body.customerId } : {}),
48
- ...(body.customerGroupIds !== undefined
49
- ? { customerGroupIds: body.customerGroupIds }
50
- : {}),
51
- ...(body.promotionCodes !== undefined
52
- ? { promotionCodes: body.promotionCodes }
53
- : {}),
54
- ...(body.shippingAddress != null
55
- ? {
56
- shippingAddress: {
57
- line1: body.shippingAddress.line1,
58
- city: body.shippingAddress.city,
59
- postalCode: body.shippingAddress.postalCode,
60
- country: body.shippingAddress.country,
61
- ...(body.shippingAddress.line2 != null ? { line2: body.shippingAddress.line2 } : {}),
62
- ...(body.shippingAddress.state != null ? { state: body.shippingAddress.state } : {}),
63
- },
64
- }
65
- : {}),
66
- };
67
-
68
- // ── Phase 1: Validate & Calculate (inside DB transaction — fast SQL only) ──
69
- const validationHooks: BeforeHook<CheckoutData>[] = [
70
- validateCartNotEmpty,
71
- resolveCurrentPrices,
72
- checkInventoryAvailability,
73
- applyPromotionCodes,
74
- calculateTax,
75
- calculateShipping,
76
- ...(kernel.hooks.resolve("checkout.beforePayment") as BeforeHook<CheckoutData>[]),
77
- validatePaymentMethod,
78
- ];
79
-
80
- // ── Phase 2: Payment Authorization (outside transaction — external API call) ──
81
- const paymentHooks: BeforeHook<CheckoutData>[] = [
82
- authorizePayment,
83
- ...(kernel.hooks.resolve("checkout.beforeCreate") as BeforeHook<CheckoutData>[]),
84
- ];
85
-
86
- const afterHooks: AfterHook<OrderResult>[] = [
87
- completeCheckout,
88
- recordAnalyticsEvent,
89
- ...(kernel.hooks.resolve("checkout.afterCreate") as AfterHook<OrderResult>[]),
90
- ];
91
-
92
- const context = createHookContext({
93
- actor,
94
- logger: kernel.logger,
95
- services: kernel.services as ServiceContainer,
96
- context: { moduleName: "checkout" },
97
- origin: "rest",
98
- kernel: kernel as unknown as { database: { db: import("../../../kernel/database/plugin-types.js").PluginDb } },
99
- });
100
-
101
- try {
102
- // Phase 1: DB transaction for validation — releases connection immediately after
103
- const validated = await kernel.database.transaction(async (_tx) => {
104
- context.tx = _tx;
105
- return runBeforeHooks(
106
- validationHooks,
107
- checkoutData,
108
- "create",
109
- context,
110
- );
111
- });
112
-
113
- // Phase 2: Payment authorization — NO DB connection held while calling Stripe/etc.
114
- // If Stripe takes 5s, the DB connection pool is not affected.
115
- context.tx = null;
116
- const processed = await runBeforeHooks(
117
- paymentHooks,
118
- validated,
119
- "create",
120
- context,
121
- );
122
-
123
- // Resolve customer profile UUID from customerId (may be a profile UUID or a Better Auth user_id)
124
- let customerUuid: string | undefined = undefined;
125
- if (processed.customerId) {
126
- const uuidRe = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
127
- if (uuidRe.test(processed.customerId)) {
128
- // Looks like a profile UUID — try direct lookup (no auto-create)
129
- const byIdResult = await kernel.services.customers.getById(
130
- processed.customerId,
131
- actor,
132
- );
133
- if (byIdResult.ok) {
134
- customerUuid = byIdResult.value.id;
135
- }
136
- }
137
- if (!customerUuid) {
138
- // Fall back to user_id lookup (auto-creates customer profile if needed)
139
- const byUserIdResult = await kernel.services.customers.getByUserId(
140
- processed.customerId,
141
- actor,
142
- );
143
- if (byUserIdResult.ok) {
144
- customerUuid = byUserIdResult.value.id;
145
- }
146
- }
147
- // If both lookups fail, we still allow guest checkout (customerUuid remains undefined)
148
- }
149
-
150
- const orderPayload = {
151
- currency: processed.currency,
152
- subtotal: processed.subtotal,
153
- taxTotal: processed.taxTotal,
154
- shippingTotal: processed.shippingTotal,
155
- discountTotal: processed.discountTotal,
156
- grandTotal: processed.total,
157
- paymentIntentId: processed.paymentIntentId,
158
- paymentMethodId: processed.paymentMethodId,
159
- metadata: {
160
- // H2 fix: Merge hook-injected metadata (e.g., BNPL fee) before core fields
161
- ...(typeof processed.metadata === "object" && processed.metadata !== null
162
- ? processed.metadata
163
- : {}),
164
- cartId: processed.cartId,
165
- paymentIntentId: processed.paymentIntentId,
166
- checkoutId: processed.checkoutId,
167
- promotionCodes: processed.promotionCodes,
168
- appliedPromotions: processed.appliedPromotions,
169
- shippingAddress: processed.shippingAddress,
170
- },
171
- lineItems: processed.lineItems.map((lineItem) => {
172
- const payload = {
173
- entityId: lineItem.entityId,
174
- entityType: lineItem.entityType ?? "product",
175
- title: lineItem.title ?? lineItem.entityId,
176
- quantity: lineItem.quantity,
177
- unitPrice: lineItem.resolvedUnitPrice ?? 0,
178
- totalPrice: lineItem.resolvedTotal ?? 0,
179
- };
180
- return lineItem.variantId !== undefined
181
- ? { ...payload, variantId: lineItem.variantId }
182
- : payload;
183
- }),
184
- ...(customerUuid !== undefined
185
- ? { customerId: customerUuid }
186
- : {}),
187
- };
188
-
189
- const order = await kernel.services.orders.create(orderPayload, actor);
190
-
191
- if (!order.ok) {
192
- return c.json(
193
- mapErrorToResponse(order.error),
194
- mapErrorToStatus(order.error),
195
- );
196
- }
197
-
198
- if (order.ok && (processed.appliedPromotions?.length ?? 0) > 0) {
199
- await kernel.services.promotions.recordUsage({
200
- promotions: processed.appliedPromotions ?? [],
201
- orderId: order.value.id,
202
- ...(customerUuid !== undefined
203
- ? { customerId: customerUuid }
204
- : {}),
205
- });
206
- }
207
-
208
- if (order.ok) {
209
- await kernel.services.tax.reportTransaction({
210
- transactionId: order.value.id,
211
- transactionDate: new Date(),
212
- currency: processed.currency,
213
- amount:
214
- processed.subtotal -
215
- processed.discountTotal +
216
- processed.shippingTotal,
217
- shipping: processed.shippingTotal,
218
- salesTax: processed.taxTotal,
219
- lineItems: processed.lineItems.map((lineItem, index) => ({
220
- id: lineItem.id ?? `${order.value.id}-${index + 1}`,
221
- entityId: lineItem.entityId,
222
- description: lineItem.title ?? lineItem.entityId,
223
- quantity: lineItem.quantity,
224
- unitPrice: lineItem.resolvedUnitPrice ?? 0,
225
- ...(lineItem.discountAmount !== undefined
226
- ? { discount: lineItem.discountAmount }
227
- : {}),
228
- })),
229
- ...(customerUuid !== undefined
230
- ? { customerId: customerUuid }
231
- : {}),
232
- ...(processed.shippingAddress !== undefined
233
- ? { toAddress: processed.shippingAddress }
234
- : {}),
235
- });
236
- }
237
-
238
- // Stash paymentMethodId for completeCheckout compensation chain
239
- context.context.paymentMethodId = processed.paymentMethodId;
240
-
241
- const afterReport = await runAfterHooks(
242
- afterHooks,
243
- null,
244
- order.value,
245
- "create",
246
- context,
247
- );
248
-
249
- await kernel.services.cart.markAsCheckedOut(body.cartId, actor);
250
-
251
- return c.json(
252
- {
253
- data: {
254
- ...order.value,
255
- // Stripe Elements requires clientSecret to collect card details on the frontend
256
- ...(processed.paymentClientSecret
257
- ? { paymentClientSecret: processed.paymentClientSecret }
258
- : {}),
259
- },
260
- meta: afterReport.hasErrors
261
- ? { hookErrors: afterReport.errors }
262
- : undefined,
263
- },
264
- 201,
265
- );
266
- } catch (error) {
267
- const message = isCommerceError(error)
268
- ? error.message
269
- : "Checkout failed.";
270
- return c.json(
271
- {
272
- error: {
273
- code: "CHECKOUT_FAILED",
274
- message,
275
- },
276
- },
277
- 422,
278
- );
279
- }
280
- });
281
-
282
- return router;
283
- }
@@ -1,70 +0,0 @@
1
- import { OpenAPIHono } from "@hono/zod-openapi";
2
- import type { Kernel } from "../../../runtime/kernel.js";
3
- import {
4
- inventoryAdjustRoute,
5
- inventoryReserveRoute,
6
- inventoryReleaseRoute,
7
- createWarehouseRoute,
8
- inventoryCheckRoute,
9
- listWarehousesRoute,
10
- } from "../schemas/inventory.js";
11
- import { type AppEnv, mapErrorToResponse, mapErrorToStatus } from "../utils.js";
12
-
13
- export function inventoryRoutes(kernel: Kernel) {
14
- const router = new OpenAPIHono<AppEnv>();
15
-
16
- // @ts-expect-error -- openapi handler union return type
17
- router.openapi(inventoryCheckRoute, async (c) => {
18
- const entityIds = (c.req.query("entityIds") ?? "")
19
- .split(",")
20
- .map((item) => item.trim())
21
- .filter(Boolean);
22
-
23
- const result = await kernel.services.inventory.checkMultiple(entityIds);
24
- if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
25
- return c.json({ data: result.value });
26
- });
27
-
28
- // @ts-expect-error -- openapi handler union return type
29
- router.openapi(inventoryAdjustRoute, async (c) => {
30
- const body = c.req.valid("json");
31
- const result = await kernel.services.inventory.adjust(body, c.get("actor"));
32
- if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
33
- return c.json({ data: result.value });
34
- });
35
-
36
- // @ts-expect-error -- openapi handler union return type
37
- router.openapi(inventoryReserveRoute, async (c) => {
38
- const body = c.req.valid("json");
39
- const result = await kernel.services.inventory.reserve(body, c.get("actor"));
40
- if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
41
- return c.json({ data: { reserved: true } });
42
- });
43
-
44
- // @ts-expect-error -- openapi handler union return type
45
- router.openapi(inventoryReleaseRoute, async (c) => {
46
- const body = c.req.valid("json");
47
- const result = await kernel.services.inventory.release(body, c.get("actor"));
48
- if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
49
- return c.json({ data: { released: true } });
50
- });
51
-
52
- // @ts-expect-error -- openapi handler union return type
53
- router.openapi(createWarehouseRoute, async (c) => {
54
- const body = c.req.valid("json") as Parameters<typeof kernel.services.inventory.createWarehouse>[0];
55
- const actor = c.get("actor");
56
- const result = await kernel.services.inventory.createWarehouse(body, actor);
57
- if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
58
- return c.json({ data: result.value }, 201);
59
- });
60
-
61
- // @ts-expect-error -- openapi handler union return type
62
- router.openapi(listWarehousesRoute, async (c) => {
63
- const actor = c.get("actor");
64
- const result = await kernel.services.inventory.listWarehouses(actor);
65
- if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
66
- return c.json({ data: result.value });
67
- });
68
-
69
- return router;
70
- }
@@ -1,86 +0,0 @@
1
- import { OpenAPIHono } from "@hono/zod-openapi";
2
- import type { Kernel } from "../../../runtime/kernel.js";
3
- import type { AttachMediaInput } from "../../../modules/media/service.js";
4
- import { attachMediaRoute, getMediaRoute, deleteMediaRoute } from "../schemas/media.js";
5
- import { type AppEnv, mapErrorToResponse, mapErrorToStatus, requirePerm } from "../utils.js";
6
-
7
- export function mediaRoutes(kernel: Kernel) {
8
- const router = new OpenAPIHono<AppEnv>();
9
-
10
- // Upload requires authentication + media:write permission
11
- router.use("/upload", requirePerm("media:write"));
12
-
13
- router.post("/upload", async (c) => {
14
- const body = await c.req.parseBody();
15
- const file = body.file as File;
16
-
17
- if (!file) {
18
- return c.json({ error: { code: "VALIDATION_FAILED", message: "file is required" } }, 422);
19
- }
20
-
21
- const buffer = await file.arrayBuffer();
22
- const actor = c.get("actor");
23
- const result = await kernel.services.media.upload({
24
- filename: file.name,
25
- contentType: file.type,
26
- data: buffer,
27
- alt: String(body.alt ?? ""),
28
- }, actor);
29
-
30
- if (!result.ok) {
31
- return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
32
- }
33
-
34
- return c.json({ data: result.value }, 201);
35
- });
36
-
37
- router.openapi(getMediaRoute, async (c) => {
38
- const signed = c.req.query("signed") === "true";
39
-
40
- // Signed URLs require authentication
41
- if (signed && !c.get("actor")) {
42
- return c.json(
43
- { error: { code: "UNAUTHORIZED", message: "Authentication required for signed URLs." } },
44
- 401,
45
- );
46
- }
47
-
48
- const result = signed
49
- ? await kernel.services.media.getSignedUrl(c.req.param("id"))
50
- : await kernel.services.media.getUrl(c.req.param("id"));
51
-
52
- if (!result.ok) {
53
- return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
54
- }
55
-
56
- return c.redirect(result.value, 302);
57
- });
58
-
59
- // @ts-expect-error -- openapi handler union return type
60
- router.openapi(deleteMediaRoute, async (c) => {
61
- const actor = c.get("actor");
62
- if (!actor || (!actor.permissions.includes("media:write") && !actor.permissions.includes("*:*"))) {
63
- return c.json({ error: { code: "FORBIDDEN", message: "media:write permission required." } }, 403);
64
- }
65
- const result = await kernel.services.media.delete(c.req.param("id"));
66
- if (!result.ok) {
67
- return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
68
- }
69
- return c.json({ data: { deleted: true } });
70
- });
71
-
72
- // Attach requires media:write
73
- router.use("/attach", requirePerm("media:write"));
74
-
75
- // @ts-expect-error -- openapi handler union return type
76
- router.openapi(attachMediaRoute, async (c) => {
77
- const body = c.req.valid("json") as AttachMediaInput;
78
- const result = await kernel.services.media.attachToEntity(body);
79
- if (!result.ok) {
80
- return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
81
- }
82
- return c.json({ data: { attached: true } }, 201);
83
- });
84
-
85
- return router;
86
- }
@@ -1,78 +0,0 @@
1
- import { OpenAPIHono } from "@hono/zod-openapi";
2
- import type { Kernel } from "../../../runtime/kernel.js";
3
- import { changeOrderStatusRoute, listOrdersRoute, getOrderRoute, getOrderFulfillmentsRoute } from "../schemas/orders.js";
4
- import { type AppEnv, isUUID, mapErrorToResponse, mapErrorToStatus, parsePagination } from "../utils.js";
5
-
6
- export function orderRoutes(kernel: Kernel) {
7
- const router = new OpenAPIHono<AppEnv>();
8
-
9
- // @ts-expect-error -- openapi handler union return type
10
- router.openapi(listOrdersRoute, async (c) => {
11
- const pagination = parsePagination(c.req.query());
12
- const status = c.req.query("status");
13
- const result = await kernel.services.orders.list(
14
- {
15
- page: pagination.page,
16
- limit: pagination.limit,
17
- ...(status !== undefined ? { status } : {}),
18
- },
19
- c.get("actor"),
20
- );
21
-
22
- if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
23
- return c.json({
24
- data: result.value.items,
25
- meta: {
26
- pagination: result.value.pagination,
27
- },
28
- });
29
- });
30
-
31
- // @ts-expect-error -- openapi handler union return type
32
- router.openapi(getOrderRoute, async (c) => {
33
- const idOrNumber = c.req.param("idOrNumber");
34
- const result = isUUID(idOrNumber)
35
- ? await kernel.services.orders.getById(idOrNumber, c.get("actor"))
36
- : await kernel.services.orders.getByNumber(idOrNumber, c.get("actor"));
37
-
38
- if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
39
- return c.json({ data: result.value });
40
- });
41
-
42
- // @ts-expect-error -- openapi() enforces strict response typing but our handler
43
- // returns union responses (200 | 400 | 404). The route definition documents the
44
- // contract; the handler returns dynamic status.
45
- router.openapi(changeOrderStatusRoute, async (c) => {
46
- const body = c.req.valid("json");
47
- const result = await kernel.services.orders.changeStatus(
48
- {
49
- orderId: c.req.param("id"),
50
- newStatus: body.status,
51
- ...(body.reason !== undefined ? { reason: body.reason } : {}),
52
- },
53
- c.get("actor"),
54
- );
55
-
56
- if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
57
- return c.json({ data: result.value });
58
- });
59
-
60
- // @ts-expect-error -- openapi handler union return type
61
- router.openapi(getOrderFulfillmentsRoute, async (c) => {
62
- const orderId = c.req.param("id");
63
- const actor = c.get("actor");
64
-
65
- // Verify the order exists and the actor has access before returning fulfillments
66
- const orderResult = isUUID(orderId)
67
- ? await kernel.services.orders.getById(orderId, actor)
68
- : await kernel.services.orders.getByNumber(orderId, actor);
69
-
70
- if (!orderResult.ok) return c.json(mapErrorToResponse(orderResult.error), mapErrorToStatus(orderResult.error));
71
-
72
- const result = await kernel.services.fulfillment.getByOrderId(orderResult.value.id);
73
- if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
74
- return c.json({ data: result.value });
75
- });
76
-
77
- return router;
78
- }
@@ -1,60 +0,0 @@
1
- import { OpenAPIHono } from "@hono/zod-openapi";
2
- import type { PgDatabase, PgQueryResultHKT } from "drizzle-orm/pg-core";
3
- import type { Kernel } from "../../../runtime/kernel.js";
4
- import { type AppEnv, mapErrorToResponse, mapErrorToStatus } from "../utils.js";
5
- import { processedWebhookEvents } from "../../../modules/webhooks/schema.js";
6
-
7
- type Db = PgDatabase<PgQueryResultHKT, Record<string, unknown>>;
8
-
9
- export function paymentRoutes(kernel: Kernel) {
10
- const router = new OpenAPIHono<AppEnv>();
11
-
12
- router.post("/webhook", async (c) => {
13
- const result = await kernel.services.payments.verifyWebhook(c.req.raw);
14
- if (!result.ok) {
15
- return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
16
- }
17
-
18
- const event = result.value;
19
- const db = kernel.database.db as Db;
20
-
21
- // Atomic idempotency: INSERT ... ON CONFLICT DO NOTHING ... RETURNING
22
- // If the row already exists, RETURNING yields zero rows → duplicate.
23
- // If the row is new, RETURNING yields one row → process the event.
24
- // No TOCTOU race: the UNIQUE constraint on event_id is the single source of truth.
25
- const [inserted] = await db
26
- .insert(processedWebhookEvents)
27
- .values({
28
- eventId: event.id,
29
- provider: "stripe",
30
- eventType: event.type,
31
- })
32
- .onConflictDoNothing()
33
- .returning({ id: processedWebhookEvents.id });
34
-
35
- if (!inserted) {
36
- // Row already existed — this is a duplicate delivery
37
- return c.json({ data: { received: true, duplicate: true } });
38
- }
39
-
40
- // Process the event (first time only)
41
- if (event.type === "payment_intent.succeeded") {
42
- const data = event.data as Record<string, unknown> | undefined;
43
- const metadata = data?.metadata as Record<string, unknown> | undefined;
44
- if (typeof metadata?.orderId === "string") {
45
- await kernel.services.orders.changeStatus(
46
- {
47
- orderId: metadata.orderId,
48
- newStatus: "confirmed",
49
- reason: "stripe_webhook_payment_intent_succeeded",
50
- },
51
- null,
52
- );
53
- }
54
- }
55
-
56
- return c.json({ data: { received: true } });
57
- });
58
-
59
- return router;
60
- }
@@ -1,57 +0,0 @@
1
- import { OpenAPIHono } from "@hono/zod-openapi";
2
- import type { Kernel } from "../../../runtime/kernel.js";
3
- import { setBasePriceRoute, createModifierRoute, listPricesRoute } from "../schemas/pricing.js";
4
- import { type AppEnv, mapErrorToResponse, mapErrorToStatus, requirePerm } from "../utils.js";
5
-
6
- export function pricingRoutes(kernel: Kernel) {
7
- const router = new OpenAPIHono<AppEnv>();
8
-
9
- router.use("/prices", requirePerm("pricing:manage"));
10
-
11
- // @ts-expect-error -- openapi() enforces strict response typing but our handler
12
- // returns union responses (201 | 400 | 422). The route definition documents the
13
- // contract; the handler returns dynamic status.
14
- router.openapi(setBasePriceRoute, async (c) => {
15
- const actor = c.get("actor");
16
- const result = await kernel.services.pricing.setBasePrice(c.req.valid("json"), actor);
17
- if (!result.ok) {
18
- return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
19
- }
20
- return c.json({ data: result.value }, 201);
21
- });
22
-
23
- // @ts-expect-error -- openapi handler union return type
24
- router.openapi(listPricesRoute, async (c) => {
25
- const entityId = c.req.query("entityId");
26
- const variantId = c.req.query("variantId");
27
- const currency = c.req.query("currency");
28
- const customerGroupId = c.req.query("customerGroupId");
29
-
30
- const result = await kernel.services.pricing.listPrices({
31
- ...(entityId !== undefined ? { entityId } : {}),
32
- ...(variantId !== undefined ? { variantId } : {}),
33
- ...(currency !== undefined ? { currency } : {}),
34
- ...(customerGroupId !== undefined ? { customerGroupId } : {}),
35
- });
36
- if (!result.ok) {
37
- return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
38
- }
39
- return c.json({ data: result.value });
40
- });
41
-
42
- router.use("/modifiers", requirePerm("pricing:manage"));
43
-
44
- // @ts-expect-error -- openapi() enforces strict response typing but our handler
45
- // returns union responses (201 | 400 | 422). The route definition documents the
46
- // contract; the handler returns dynamic status.
47
- router.openapi(createModifierRoute, async (c) => {
48
- const actor = c.get("actor");
49
- const result = await kernel.services.pricing.createModifier(c.req.valid("json"), actor);
50
- if (!result.ok) {
51
- return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
52
- }
53
- return c.json({ data: result.value }, 201);
54
- });
55
-
56
- return router;
57
- }