@porulle/core 0.7.0 → 0.8.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 (289) hide show
  1. package/dist/auth/setup.d.ts.map +1 -1
  2. package/dist/auth/setup.js +1 -0
  3. package/dist/config/defaults.d.ts.map +1 -1
  4. package/dist/config/defaults.js +5 -0
  5. package/dist/config/types.d.ts +17 -1
  6. package/dist/config/types.d.ts.map +1 -1
  7. package/dist/generated/plugin-manifest.d.ts +3 -0
  8. package/dist/generated/plugin-manifest.d.ts.map +1 -1
  9. package/dist/generated/plugin-manifest.js +1 -0
  10. package/dist/hooks/checkout.d.ts.map +1 -1
  11. package/dist/hooks/checkout.js +16 -1
  12. package/dist/index.d.ts +1 -0
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +1 -0
  15. package/dist/interfaces/rest/index.d.ts.map +1 -1
  16. package/dist/interfaces/rest/index.js +12 -0
  17. package/dist/interfaces/rest/routes/admin/staff.d.ts +12 -0
  18. package/dist/interfaces/rest/routes/admin/staff.d.ts.map +1 -0
  19. package/dist/interfaces/rest/routes/admin/staff.js +172 -0
  20. package/dist/interfaces/rest/routes/analytics.d.ts +5 -0
  21. package/dist/interfaces/rest/routes/analytics.d.ts.map +1 -0
  22. package/dist/interfaces/rest/routes/analytics.js +24 -0
  23. package/dist/interfaces/rest/routes/carts.d.ts.map +1 -1
  24. package/dist/interfaces/rest/routes/carts.js +39 -2
  25. package/dist/interfaces/rest/routes/catalog.d.ts.map +1 -1
  26. package/dist/interfaces/rest/routes/catalog.js +17 -1
  27. package/dist/interfaces/rest/routes/checkout.d.ts.map +1 -1
  28. package/dist/interfaces/rest/routes/checkout.js +17 -0
  29. package/dist/interfaces/rest/routes/documents.d.ts +10 -0
  30. package/dist/interfaces/rest/routes/documents.d.ts.map +1 -0
  31. package/dist/interfaces/rest/routes/documents.js +40 -0
  32. package/dist/interfaces/rest/routes/orders.d.ts.map +1 -1
  33. package/dist/interfaces/rest/routes/orders.js +98 -1
  34. package/dist/interfaces/rest/routes/pricing.d.ts.map +1 -1
  35. package/dist/interfaces/rest/routes/pricing.js +36 -1
  36. package/dist/interfaces/rest/routes/settings.d.ts +5 -0
  37. package/dist/interfaces/rest/routes/settings.d.ts.map +1 -0
  38. package/dist/interfaces/rest/routes/settings.js +50 -0
  39. package/dist/interfaces/rest/routes/shipping.d.ts +5 -0
  40. package/dist/interfaces/rest/routes/shipping.d.ts.map +1 -0
  41. package/dist/interfaces/rest/routes/shipping.js +68 -0
  42. package/dist/interfaces/rest/routes/tax.d.ts +5 -0
  43. package/dist/interfaces/rest/routes/tax.d.ts.map +1 -0
  44. package/dist/interfaces/rest/routes/tax.js +68 -0
  45. package/dist/interfaces/rest/schemas/admin-staff.d.ts +899 -0
  46. package/dist/interfaces/rest/schemas/admin-staff.d.ts.map +1 -0
  47. package/dist/interfaces/rest/schemas/admin-staff.js +98 -0
  48. package/dist/interfaces/rest/schemas/analytics.d.ts +252 -0
  49. package/dist/interfaces/rest/schemas/analytics.d.ts.map +1 -0
  50. package/dist/interfaces/rest/schemas/analytics.js +33 -0
  51. package/dist/interfaces/rest/schemas/carts.d.ts +656 -0
  52. package/dist/interfaces/rest/schemas/carts.d.ts.map +1 -1
  53. package/dist/interfaces/rest/schemas/carts.js +38 -0
  54. package/dist/interfaces/rest/schemas/catalog.d.ts +1218 -1
  55. package/dist/interfaces/rest/schemas/catalog.d.ts.map +1 -1
  56. package/dist/interfaces/rest/schemas/catalog.js +48 -0
  57. package/dist/interfaces/rest/schemas/checkout.d.ts +2 -0
  58. package/dist/interfaces/rest/schemas/checkout.d.ts.map +1 -1
  59. package/dist/interfaces/rest/schemas/checkout.js +4 -0
  60. package/dist/interfaces/rest/schemas/customer-portal.d.ts +35 -0
  61. package/dist/interfaces/rest/schemas/customer-portal.d.ts.map +1 -1
  62. package/dist/interfaces/rest/schemas/documents.d.ts +509 -0
  63. package/dist/interfaces/rest/schemas/documents.d.ts.map +1 -0
  64. package/dist/interfaces/rest/schemas/documents.js +65 -0
  65. package/dist/interfaces/rest/schemas/orders.d.ts +3110 -43
  66. package/dist/interfaces/rest/schemas/orders.d.ts.map +1 -1
  67. package/dist/interfaces/rest/schemas/orders.js +233 -0
  68. package/dist/interfaces/rest/schemas/pricing.d.ts +405 -0
  69. package/dist/interfaces/rest/schemas/pricing.d.ts.map +1 -1
  70. package/dist/interfaces/rest/schemas/pricing.js +70 -0
  71. package/dist/interfaces/rest/schemas/responses.d.ts +156 -0
  72. package/dist/interfaces/rest/schemas/responses.d.ts.map +1 -1
  73. package/dist/interfaces/rest/schemas/search.d.ts +17 -0
  74. package/dist/interfaces/rest/schemas/search.d.ts.map +1 -1
  75. package/dist/interfaces/rest/schemas/settings.d.ts +397 -0
  76. package/dist/interfaces/rest/schemas/settings.d.ts.map +1 -0
  77. package/dist/interfaces/rest/schemas/settings.js +79 -0
  78. package/dist/interfaces/rest/schemas/shipping.d.ts +1093 -0
  79. package/dist/interfaces/rest/schemas/shipping.d.ts.map +1 -0
  80. package/dist/interfaces/rest/schemas/shipping.js +146 -0
  81. package/dist/interfaces/rest/schemas/tax.d.ts +1074 -0
  82. package/dist/interfaces/rest/schemas/tax.d.ts.map +1 -0
  83. package/dist/interfaces/rest/schemas/tax.js +132 -0
  84. package/dist/kernel/database/schema.d.ts +4 -0
  85. package/dist/kernel/database/schema.d.ts.map +1 -1
  86. package/dist/kernel/database/schema.js +8 -0
  87. package/dist/kernel/hooks/types.d.ts +1 -1
  88. package/dist/kernel/hooks/types.d.ts.map +1 -1
  89. package/dist/kernel/plugin/manifest.d.ts +23 -0
  90. package/dist/kernel/plugin/manifest.d.ts.map +1 -1
  91. package/dist/kernel/plugin/manifest.js +17 -2
  92. package/dist/modules/analytics/module.d.ts.map +1 -1
  93. package/dist/modules/analytics/module.js +6 -0
  94. package/dist/modules/analytics/reports.d.ts +58 -0
  95. package/dist/modules/analytics/reports.d.ts.map +1 -0
  96. package/dist/modules/analytics/reports.js +249 -0
  97. package/dist/modules/analytics/service.d.ts +10 -0
  98. package/dist/modules/analytics/service.d.ts.map +1 -1
  99. package/dist/modules/analytics/service.js +14 -0
  100. package/dist/modules/cart/repository/index.d.ts +7 -0
  101. package/dist/modules/cart/repository/index.d.ts.map +1 -1
  102. package/dist/modules/cart/repository/index.js +25 -1
  103. package/dist/modules/cart/schema.d.ts +17 -0
  104. package/dist/modules/cart/schema.d.ts.map +1 -1
  105. package/dist/modules/cart/schema.js +2 -0
  106. package/dist/modules/cart/schemas.d.ts +1 -0
  107. package/dist/modules/cart/schemas.d.ts.map +1 -1
  108. package/dist/modules/cart/schemas.js +1 -0
  109. package/dist/modules/cart/service.d.ts +32 -0
  110. package/dist/modules/cart/service.d.ts.map +1 -1
  111. package/dist/modules/cart/service.js +58 -0
  112. package/dist/modules/catalog/entity-service.d.ts +21 -0
  113. package/dist/modules/catalog/entity-service.d.ts.map +1 -1
  114. package/dist/modules/catalog/entity-service.js +132 -3
  115. package/dist/modules/catalog/schema.d.ts +34 -0
  116. package/dist/modules/catalog/schema.d.ts.map +1 -1
  117. package/dist/modules/catalog/schema.js +5 -0
  118. package/dist/modules/catalog/schemas.d.ts +3 -0
  119. package/dist/modules/catalog/schemas.d.ts.map +1 -1
  120. package/dist/modules/catalog/schemas.js +3 -0
  121. package/dist/modules/catalog/service.d.ts +36 -0
  122. package/dist/modules/catalog/service.d.ts.map +1 -1
  123. package/dist/modules/catalog/service.js +6 -0
  124. package/dist/modules/documents/module.d.ts +7 -0
  125. package/dist/modules/documents/module.d.ts.map +1 -0
  126. package/dist/modules/documents/module.js +12 -0
  127. package/dist/modules/documents/pdf.d.ts +25 -0
  128. package/dist/modules/documents/pdf.d.ts.map +1 -0
  129. package/dist/modules/documents/pdf.js +80 -0
  130. package/dist/modules/documents/render.d.ts +32 -0
  131. package/dist/modules/documents/render.d.ts.map +1 -0
  132. package/dist/modules/documents/render.js +190 -0
  133. package/dist/modules/documents/repository/index.d.ts +25 -0
  134. package/dist/modules/documents/repository/index.d.ts.map +1 -0
  135. package/dist/modules/documents/repository/index.js +47 -0
  136. package/dist/modules/documents/schema.d.ts +214 -0
  137. package/dist/modules/documents/schema.d.ts.map +1 -0
  138. package/dist/modules/documents/schema.js +42 -0
  139. package/dist/modules/documents/service.d.ts +51 -0
  140. package/dist/modules/documents/service.d.ts.map +1 -0
  141. package/dist/modules/documents/service.js +119 -0
  142. package/dist/modules/fulfillment/service.d.ts +18 -0
  143. package/dist/modules/fulfillment/service.d.ts.map +1 -1
  144. package/dist/modules/fulfillment/service.js +85 -1
  145. package/dist/modules/orders/repository/index.d.ts +18 -1
  146. package/dist/modules/orders/repository/index.d.ts.map +1 -1
  147. package/dist/modules/orders/repository/index.js +86 -1
  148. package/dist/modules/orders/schema.d.ts +374 -0
  149. package/dist/modules/orders/schema.d.ts.map +1 -1
  150. package/dist/modules/orders/schema.js +52 -1
  151. package/dist/modules/orders/service.d.ts +86 -1
  152. package/dist/modules/orders/service.d.ts.map +1 -1
  153. package/dist/modules/orders/service.js +447 -20
  154. package/dist/modules/pricing/repository/index.d.ts +5 -0
  155. package/dist/modules/pricing/repository/index.d.ts.map +1 -1
  156. package/dist/modules/pricing/repository/index.js +19 -0
  157. package/dist/modules/pricing/service.d.ts +18 -0
  158. package/dist/modules/pricing/service.d.ts.map +1 -1
  159. package/dist/modules/pricing/service.js +38 -0
  160. package/dist/modules/settings/module.d.ts +6 -0
  161. package/dist/modules/settings/module.d.ts.map +1 -0
  162. package/dist/modules/settings/module.js +11 -0
  163. package/dist/modules/settings/repository/index.d.ts +16 -0
  164. package/dist/modules/settings/repository/index.d.ts.map +1 -0
  165. package/dist/modules/settings/repository/index.js +41 -0
  166. package/dist/modules/settings/schema.d.ts +121 -0
  167. package/dist/modules/settings/schema.d.ts.map +1 -0
  168. package/dist/modules/settings/schema.js +24 -0
  169. package/dist/modules/settings/service.d.ts +26 -0
  170. package/dist/modules/settings/service.d.ts.map +1 -0
  171. package/dist/modules/settings/service.js +51 -0
  172. package/dist/modules/shipping/calculator.d.ts +5 -0
  173. package/dist/modules/shipping/calculator.d.ts.map +1 -1
  174. package/dist/modules/shipping/calculator.js +10 -0
  175. package/dist/modules/shipping/module.d.ts +5 -1
  176. package/dist/modules/shipping/module.d.ts.map +1 -1
  177. package/dist/modules/shipping/module.js +4 -1
  178. package/dist/modules/shipping/repository/index.d.ts +27 -2
  179. package/dist/modules/shipping/repository/index.d.ts.map +1 -1
  180. package/dist/modules/shipping/repository/index.js +105 -1
  181. package/dist/modules/shipping/schema.d.ts +419 -0
  182. package/dist/modules/shipping/schema.d.ts.map +1 -0
  183. package/dist/modules/shipping/schema.js +56 -0
  184. package/dist/modules/shipping/service.d.ts +54 -0
  185. package/dist/modules/shipping/service.d.ts.map +1 -1
  186. package/dist/modules/shipping/service.js +161 -2
  187. package/dist/modules/tax/adapter.d.ts +13 -0
  188. package/dist/modules/tax/adapter.d.ts.map +1 -1
  189. package/dist/modules/tax/module.d.ts +5 -1
  190. package/dist/modules/tax/module.d.ts.map +1 -1
  191. package/dist/modules/tax/module.js +4 -1
  192. package/dist/modules/tax/repository/index.d.ts +38 -2
  193. package/dist/modules/tax/repository/index.d.ts.map +1 -1
  194. package/dist/modules/tax/repository/index.js +138 -1
  195. package/dist/modules/tax/schema.d.ts +355 -0
  196. package/dist/modules/tax/schema.d.ts.map +1 -0
  197. package/dist/modules/tax/schema.js +55 -0
  198. package/dist/modules/tax/service.d.ts +60 -1
  199. package/dist/modules/tax/service.d.ts.map +1 -1
  200. package/dist/modules/tax/service.js +227 -2
  201. package/dist/runtime/kernel-modules.d.ts +15 -2
  202. package/dist/runtime/kernel-modules.d.ts.map +1 -1
  203. package/dist/runtime/kernel-modules.js +4 -0
  204. package/dist/runtime/kernel-types.d.ts +5 -1
  205. package/dist/runtime/kernel-types.d.ts.map +1 -1
  206. package/dist/runtime/kernel-types.js +2 -0
  207. package/dist/runtime/server.js +1 -1
  208. package/dist/test-utils/create-plugin-test-app.d.ts +3 -0
  209. package/dist/test-utils/create-plugin-test-app.d.ts.map +1 -1
  210. package/dist/test-utils/create-plugin-test-app.js +15 -3
  211. package/dist/test-utils/rest-api-test-utils.d.ts.map +1 -1
  212. package/dist/test-utils/rest-api-test-utils.js +6 -0
  213. package/package.json +3 -3
  214. package/src/auth/setup.ts +1 -0
  215. package/src/config/defaults.ts +5 -0
  216. package/src/config/types.ts +17 -1
  217. package/src/generated/plugin-capabilities.d.ts +1 -0
  218. package/src/generated/plugin-manifest.ts +1 -0
  219. package/src/generated/plugin-repositories.d.ts +1 -0
  220. package/src/hooks/checkout.ts +25 -3
  221. package/src/index.ts +1 -0
  222. package/src/interfaces/rest/index.ts +12 -0
  223. package/src/interfaces/rest/routes/admin/staff.ts +218 -0
  224. package/src/interfaces/rest/routes/analytics.ts +33 -0
  225. package/src/interfaces/rest/routes/carts.ts +52 -2
  226. package/src/interfaces/rest/routes/catalog.ts +34 -0
  227. package/src/interfaces/rest/routes/checkout.ts +22 -0
  228. package/src/interfaces/rest/routes/documents.ts +61 -0
  229. package/src/interfaces/rest/routes/orders.ts +137 -1
  230. package/src/interfaces/rest/routes/pricing.ts +44 -1
  231. package/src/interfaces/rest/routes/settings.ts +61 -0
  232. package/src/interfaces/rest/routes/shipping.ts +92 -0
  233. package/src/interfaces/rest/routes/tax.ts +90 -0
  234. package/src/interfaces/rest/schemas/admin-staff.ts +110 -0
  235. package/src/interfaces/rest/schemas/analytics.ts +39 -0
  236. package/src/interfaces/rest/schemas/carts.ts +40 -0
  237. package/src/interfaces/rest/schemas/catalog.ts +53 -0
  238. package/src/interfaces/rest/schemas/checkout.ts +4 -0
  239. package/src/interfaces/rest/schemas/documents.ts +72 -0
  240. package/src/interfaces/rest/schemas/orders.ts +257 -0
  241. package/src/interfaces/rest/schemas/pricing.ts +75 -0
  242. package/src/interfaces/rest/schemas/settings.ts +93 -0
  243. package/src/interfaces/rest/schemas/shipping.ts +164 -0
  244. package/src/interfaces/rest/schemas/tax.ts +148 -0
  245. package/src/kernel/database/schema.ts +12 -0
  246. package/src/kernel/hooks/types.ts +1 -0
  247. package/src/kernel/plugin/manifest.ts +38 -2
  248. package/src/modules/analytics/module.ts +10 -0
  249. package/src/modules/analytics/reports.ts +311 -0
  250. package/src/modules/analytics/service.ts +23 -0
  251. package/src/modules/cart/repository/index.ts +30 -1
  252. package/src/modules/cart/schema.ts +2 -0
  253. package/src/modules/cart/schemas.ts +1 -0
  254. package/src/modules/cart/service.ts +101 -0
  255. package/src/modules/catalog/entity-service.ts +168 -3
  256. package/src/modules/catalog/schema.ts +5 -0
  257. package/src/modules/catalog/schemas.ts +3 -0
  258. package/src/modules/catalog/service.ts +30 -0
  259. package/src/modules/documents/module.ts +19 -0
  260. package/src/modules/documents/pdf.ts +101 -0
  261. package/src/modules/documents/render.ts +231 -0
  262. package/src/modules/documents/repository/index.ts +68 -0
  263. package/src/modules/documents/schema.ts +44 -0
  264. package/src/modules/documents/service.ts +176 -0
  265. package/src/modules/fulfillment/service.ts +139 -1
  266. package/src/modules/orders/repository/index.ts +127 -1
  267. package/src/modules/orders/schema.ts +54 -1
  268. package/src/modules/orders/service.ts +654 -22
  269. package/src/modules/pricing/repository/index.ts +34 -0
  270. package/src/modules/pricing/service.ts +71 -0
  271. package/src/modules/settings/module.ts +18 -0
  272. package/src/modules/settings/repository/index.ts +59 -0
  273. package/src/modules/settings/schema.ts +25 -0
  274. package/src/modules/settings/service.ts +82 -0
  275. package/src/modules/shipping/calculator.ts +27 -0
  276. package/src/modules/shipping/module.ts +6 -2
  277. package/src/modules/shipping/repository/index.ts +136 -2
  278. package/src/modules/shipping/schema.ts +58 -0
  279. package/src/modules/shipping/service.ts +260 -1
  280. package/src/modules/tax/adapter.ts +9 -0
  281. package/src/modules/tax/module.ts +6 -2
  282. package/src/modules/tax/repository/index.ts +169 -2
  283. package/src/modules/tax/schema.ts +57 -0
  284. package/src/modules/tax/service.ts +313 -2
  285. package/src/runtime/kernel-modules.ts +4 -0
  286. package/src/runtime/kernel-types.ts +6 -0
  287. package/src/runtime/server.ts +1 -1
  288. package/src/test-utils/create-plugin-test-app.ts +17 -4
  289. package/src/test-utils/rest-api-test-utils.ts +6 -0
@@ -1,6 +1,6 @@
1
1
  import { OpenAPIHono } from "@hono/zod-openapi";
2
2
  import type { Kernel } from "../../../runtime/kernel.js";
3
- import { changeOrderStatusRoute, listOrdersRoute, orderLookupRoute, getOrderRoute, getOrderFulfillmentsRoute, createOrderRoute, refundOrderRoute, captureOrderRoute } from "../schemas/orders.js";
3
+ import { changeOrderStatusRoute, listOrdersRoute, orderLookupRoute, getOrderRoute, getOrderFulfillmentsRoute, createOrderRoute, refundOrderRoute, captureOrderRoute, createOrderFulfillmentRoute, addOrderLineItemRoute, updateOrderLineItemRoute, removeOrderLineItemRoute, refundOrderLinesRoute, undoOrderRefundRoute, listOrderRefundsRoute, refundCapStatusRoute, createOrderNoteRoute, listOrderNotesRoute, deleteOrderNoteRoute, orderTimelineRoute } from "../schemas/orders.js";
4
4
  import { type AppEnv, isUUID, mapErrorToResponse, mapErrorToStatus, parsePagination } from "../utils.js";
5
5
  import type { CreateOrderInput } from "../../../modules/orders/service.js";
6
6
 
@@ -82,6 +82,80 @@ export function orderRoutes(kernel: Kernel) {
82
82
  return c.json({ data: result.value });
83
83
  });
84
84
 
85
+ // ── Notes + activity timeline (issue #56) ──────────────────────────
86
+
87
+ // @ts-expect-error -- openapi handler union return type
88
+ router.openapi(createOrderNoteRoute, async (c) => {
89
+ const body = c.req.valid("json") as { body: string; pinned?: boolean };
90
+ const result = await kernel.services.orders.addNote(c.req.param("id"), body, c.get("actor"));
91
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
92
+ return c.json({ data: result.value }, 201);
93
+ });
94
+
95
+ // @ts-expect-error -- openapi handler union return type
96
+ router.openapi(listOrderNotesRoute, async (c) => {
97
+ const result = await kernel.services.orders.listNotes(c.req.param("id"), c.get("actor"));
98
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
99
+ return c.json({ data: result.value });
100
+ });
101
+
102
+ // @ts-expect-error -- openapi handler union return type
103
+ router.openapi(deleteOrderNoteRoute, async (c) => {
104
+ const result = await kernel.services.orders.deleteNote(
105
+ c.req.param("id"),
106
+ c.req.param("noteId"),
107
+ c.get("actor"),
108
+ );
109
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
110
+ return c.json({ data: result.value });
111
+ });
112
+
113
+ // @ts-expect-error -- openapi handler union return type
114
+ router.openapi(orderTimelineRoute, async (c) => {
115
+ const result = await kernel.services.orders.timeline(c.req.param("id"), c.get("actor"));
116
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
117
+ return c.json({ data: result.value });
118
+ });
119
+
120
+ // ── Line-level refunds (issue #52) ─────────────────────────────────
121
+
122
+ // @ts-expect-error -- openapi handler union return type
123
+ router.openapi(refundCapStatusRoute, async (c) => {
124
+ const result = await kernel.services.orders.refundCapStatus(c.get("actor"));
125
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
126
+ return c.json({ data: result.value });
127
+ });
128
+
129
+ // @ts-expect-error -- openapi handler union return type
130
+ router.openapi(refundOrderLinesRoute, async (c) => {
131
+ const body = c.req.valid("json") as { lines: Array<{ lineItemId: string; quantity: number }>; reason?: string };
132
+ const result = await kernel.services.orders.refundLines(
133
+ c.req.param("id"),
134
+ body,
135
+ c.get("actor"),
136
+ );
137
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
138
+ return c.json({ data: result.value }, 201);
139
+ });
140
+
141
+ // @ts-expect-error -- openapi handler union return type
142
+ router.openapi(undoOrderRefundRoute, async (c) => {
143
+ const result = await kernel.services.orders.undoRefund(
144
+ c.req.param("id"),
145
+ c.req.param("refundId"),
146
+ c.get("actor"),
147
+ );
148
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
149
+ return c.json({ data: result.value });
150
+ });
151
+
152
+ // @ts-expect-error -- openapi handler union return type
153
+ router.openapi(listOrderRefundsRoute, async (c) => {
154
+ const result = await kernel.services.orders.listRefunds(c.req.param("id"), c.get("actor"));
155
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
156
+ return c.json({ data: result.value });
157
+ });
158
+
85
159
  // @ts-expect-error -- openapi handler union return type
86
160
  router.openapi(captureOrderRoute, async (c) => {
87
161
  const body = c.req.valid("json") as { amount?: number } | undefined;
@@ -94,6 +168,42 @@ export function orderRoutes(kernel: Kernel) {
94
168
  return c.json({ data: result.value });
95
169
  });
96
170
 
171
+ // @ts-expect-error -- openapi handler union return type
172
+ router.openapi(addOrderLineItemRoute, async (c) => {
173
+ const body = c.req.valid("json");
174
+ const result = await kernel.services.orders.addLineItem(
175
+ c.req.param("id"),
176
+ body,
177
+ c.get("actor"),
178
+ );
179
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
180
+ return c.json({ data: result.value }, 201);
181
+ });
182
+
183
+ // @ts-expect-error -- openapi handler union return type
184
+ router.openapi(updateOrderLineItemRoute, async (c) => {
185
+ const body = c.req.valid("json");
186
+ const result = await kernel.services.orders.updateOrderLineItem(
187
+ c.req.param("id"),
188
+ c.req.param("lineItemId"),
189
+ body,
190
+ c.get("actor"),
191
+ );
192
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
193
+ return c.json({ data: result.value });
194
+ });
195
+
196
+ // @ts-expect-error -- openapi handler union return type
197
+ router.openapi(removeOrderLineItemRoute, async (c) => {
198
+ const result = await kernel.services.orders.removeLineItem(
199
+ c.req.param("id"),
200
+ c.req.param("lineItemId"),
201
+ c.get("actor"),
202
+ );
203
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
204
+ return c.json({ data: result.value });
205
+ });
206
+
97
207
  // @ts-expect-error -- openapi() enforces strict response typing but our handler
98
208
  // returns union responses (200 | 400 | 404). The route definition documents the
99
209
  // contract; the handler returns dynamic status.
@@ -112,6 +222,32 @@ export function orderRoutes(kernel: Kernel) {
112
222
  return c.json({ data: result.value });
113
223
  });
114
224
 
225
+ // @ts-expect-error -- openapi handler union return type
226
+ router.openapi(createOrderFulfillmentRoute, async (c) => {
227
+ const actor = c.get("actor");
228
+ const orderId = c.req.param("id");
229
+
230
+ // Verify the order exists and the actor has access before recording
231
+ const orderResult = await kernel.services.orders.getById(orderId, actor);
232
+ if (!orderResult.ok) return c.json(mapErrorToResponse(orderResult.error), mapErrorToStatus(orderResult.error));
233
+
234
+ const body = c.req.valid("json") as {
235
+ lineItems: Array<{ orderLineItemId: string; quantity: number }>;
236
+ carrier?: string;
237
+ trackingNumber?: string;
238
+ trackingUrl?: string;
239
+ type?: string;
240
+ status?: string;
241
+ metadata?: Record<string, unknown>;
242
+ };
243
+ const result = await kernel.services.fulfillment.createFulfillment(
244
+ { ...body, orderId: orderResult.value.id },
245
+ actor,
246
+ );
247
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
248
+ return c.json({ data: result.value }, 201);
249
+ });
250
+
115
251
  // @ts-expect-error -- openapi handler union return type
116
252
  router.openapi(getOrderFulfillmentsRoute, async (c) => {
117
253
  const orderId = c.req.param("id");
@@ -4,7 +4,7 @@ import { CommerceValidationError } from "../../../kernel/errors.js";
4
4
  import type { DrizzleDatabase } from "../../../kernel/database/drizzle-db.js";
5
5
  import { CatalogRepository } from "../../../modules/catalog/repository/index.js";
6
6
  import type { Kernel } from "../../../runtime/kernel.js";
7
- import { setBasePriceRoute, createModifierRoute, listPricesRoute } from "../schemas/pricing.js";
7
+ import { setBasePriceRoute, createModifierRoute, listPricesRoute, listModifiersRoute, updateModifierRoute, deleteModifierRoute } from "../schemas/pricing.js";
8
8
  import { type AppEnv, mapErrorToResponse, mapErrorToStatus, requirePerm } from "../utils.js";
9
9
 
10
10
  export function pricingRoutes(kernel: Kernel) {
@@ -45,6 +45,49 @@ export function pricingRoutes(kernel: Kernel) {
45
45
  });
46
46
 
47
47
  router.use("/modifiers", requirePerm("pricing:manage"));
48
+ router.use("/modifiers/:id", requirePerm("pricing:manage"));
49
+
50
+ // @ts-expect-error -- openapi handler union return type
51
+ router.openapi(listModifiersRoute, async (c) => {
52
+ const actor = c.get("actor");
53
+ const entityId = c.req.query("entityId");
54
+ const currency = c.req.query("currency");
55
+ const active = c.req.query("active");
56
+
57
+ const result = await kernel.services.pricing.listModifiers({
58
+ ...(entityId !== undefined ? { entityId } : {}),
59
+ ...(currency !== undefined ? { currency } : {}),
60
+ ...(active === "true" ? { active: true } : {}),
61
+ }, actor);
62
+ if (!result.ok) {
63
+ return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
64
+ }
65
+ return c.json({ data: result.value });
66
+ });
67
+
68
+ // @ts-expect-error -- openapi handler union return type
69
+ router.openapi(updateModifierRoute, async (c) => {
70
+ const actor = c.get("actor");
71
+ const result = await kernel.services.pricing.updateModifier(
72
+ c.req.param("id"),
73
+ c.req.valid("json"),
74
+ actor,
75
+ );
76
+ if (!result.ok) {
77
+ return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
78
+ }
79
+ return c.json({ data: result.value });
80
+ });
81
+
82
+ // @ts-expect-error -- openapi handler union return type
83
+ router.openapi(deleteModifierRoute, async (c) => {
84
+ const actor = c.get("actor");
85
+ const result = await kernel.services.pricing.deleteModifier(c.req.param("id"), actor);
86
+ if (!result.ok) {
87
+ return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
88
+ }
89
+ return c.json({ data: result.value });
90
+ });
48
91
 
49
92
  // @ts-expect-error -- openapi() enforces strict response typing but our handler
50
93
  // returns union responses (201 | 400 | 422). The route definition documents the
@@ -0,0 +1,61 @@
1
+ import { OpenAPIHono } from "@hono/zod-openapi";
2
+ import type { Kernel } from "../../../runtime/kernel.js";
3
+ import {
4
+ KNOWN_GROUP_SCHEMAS,
5
+ getAllSettingsRoute,
6
+ getSettingsGroupRoute,
7
+ patchSettingsGroupRoute,
8
+ } from "../schemas/settings.js";
9
+ import { type AppEnv, mapErrorToResponse, mapErrorToStatus, requirePerm } from "../utils.js";
10
+
11
+ export function settingsRoutes(kernel: Kernel) {
12
+ const router = new OpenAPIHono<AppEnv>();
13
+
14
+ router.use("/", requirePerm("settings:manage"));
15
+ router.use("/:group", requirePerm("settings:manage"));
16
+
17
+ // @ts-expect-error -- openapi handler union return type
18
+ router.openapi(getAllSettingsRoute, async (c) => {
19
+ const result = await kernel.services.settings.getAll(c.get("actor"));
20
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
21
+ return c.json({ data: result.value });
22
+ });
23
+
24
+ // @ts-expect-error -- openapi handler union return type
25
+ router.openapi(getSettingsGroupRoute, async (c) => {
26
+ const result = await kernel.services.settings.getGroup(c.req.param("group"), c.get("actor"));
27
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
28
+ return c.json({ data: result.value });
29
+ });
30
+
31
+ // @ts-expect-error -- openapi handler union return type
32
+ router.openapi(patchSettingsGroupRoute, async (c) => {
33
+ const group = c.req.param("group");
34
+ const body = c.req.valid("json");
35
+
36
+ // Known groups get typed validation; unknown groups accept any object.
37
+ const groupSchema = KNOWN_GROUP_SCHEMAS[group];
38
+ if (groupSchema) {
39
+ const parsed = groupSchema.safeParse(body);
40
+ if (!parsed.success) {
41
+ const isProd = process.env.NODE_ENV === "production";
42
+ return c.json({
43
+ error: {
44
+ code: "VALIDATION_FAILED",
45
+ message: isProd
46
+ ? "Invalid input."
47
+ : parsed.error.issues
48
+ .map((i) => `${i.path.join(".")}: ${i.message}`)
49
+ .join("; "),
50
+ },
51
+ }, 422);
52
+ }
53
+ }
54
+
55
+ const result = await kernel.services.settings.updateGroup(group, body, c.get("actor"));
56
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
57
+ return c.json({ data: result.value });
58
+ });
59
+
60
+ return router;
61
+ }
@@ -0,0 +1,92 @@
1
+ import { OpenAPIHono } from "@hono/zod-openapi";
2
+ import type { Kernel } from "../../../runtime/kernel.js";
3
+ import {
4
+ createShippingZoneRoute,
5
+ listShippingZonesRoute,
6
+ updateShippingZoneRoute,
7
+ deleteShippingZoneRoute,
8
+ createShippingRateRoute,
9
+ listShippingRatesRoute,
10
+ updateShippingRateRoute,
11
+ deleteShippingRateRoute,
12
+ } from "../schemas/shipping.js";
13
+ import { type AppEnv, mapErrorToResponse, mapErrorToStatus, requirePerm } from "../utils.js";
14
+
15
+ export function shippingRoutes(kernel: Kernel) {
16
+ const router = new OpenAPIHono<AppEnv>();
17
+
18
+ router.use("/zones", requirePerm("shipping:manage"));
19
+ router.use("/zones/:id", requirePerm("shipping:manage"));
20
+ router.use("/rates", requirePerm("shipping:manage"));
21
+ router.use("/rates/:id", requirePerm("shipping:manage"));
22
+
23
+ // @ts-expect-error -- openapi handler union return type
24
+ router.openapi(createShippingZoneRoute, async (c) => {
25
+ const result = await kernel.services.shipping.createZone(c.req.valid("json"), c.get("actor"));
26
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
27
+ return c.json({ data: result.value }, 201);
28
+ });
29
+
30
+ // @ts-expect-error -- openapi handler union return type
31
+ router.openapi(listShippingZonesRoute, async (c) => {
32
+ const result = await kernel.services.shipping.listZones(c.get("actor"));
33
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
34
+ return c.json({ data: result.value });
35
+ });
36
+
37
+ // @ts-expect-error -- openapi handler union return type
38
+ router.openapi(updateShippingZoneRoute, async (c) => {
39
+ const result = await kernel.services.shipping.updateZone(
40
+ c.req.param("id"),
41
+ c.req.valid("json"),
42
+ c.get("actor"),
43
+ );
44
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
45
+ return c.json({ data: result.value });
46
+ });
47
+
48
+ // @ts-expect-error -- openapi handler union return type
49
+ router.openapi(deleteShippingZoneRoute, async (c) => {
50
+ const result = await kernel.services.shipping.deleteZone(c.req.param("id"), c.get("actor"));
51
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
52
+ return c.json({ data: result.value });
53
+ });
54
+
55
+ // @ts-expect-error -- openapi handler union return type
56
+ router.openapi(createShippingRateRoute, async (c) => {
57
+ const result = await kernel.services.shipping.createRate(c.req.valid("json"), c.get("actor"));
58
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
59
+ return c.json({ data: result.value }, 201);
60
+ });
61
+
62
+ // @ts-expect-error -- openapi handler union return type
63
+ router.openapi(listShippingRatesRoute, async (c) => {
64
+ const zoneId = c.req.query("zoneId");
65
+ const result = await kernel.services.shipping.listRates(
66
+ zoneId !== undefined ? { zoneId } : undefined,
67
+ c.get("actor"),
68
+ );
69
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
70
+ return c.json({ data: result.value });
71
+ });
72
+
73
+ // @ts-expect-error -- openapi handler union return type
74
+ router.openapi(updateShippingRateRoute, async (c) => {
75
+ const result = await kernel.services.shipping.updateRate(
76
+ c.req.param("id"),
77
+ c.req.valid("json"),
78
+ c.get("actor"),
79
+ );
80
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
81
+ return c.json({ data: result.value });
82
+ });
83
+
84
+ // @ts-expect-error -- openapi handler union return type
85
+ router.openapi(deleteShippingRateRoute, async (c) => {
86
+ const result = await kernel.services.shipping.deleteRate(c.req.param("id"), c.get("actor"));
87
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
88
+ return c.json({ data: result.value });
89
+ });
90
+
91
+ return router;
92
+ }
@@ -0,0 +1,90 @@
1
+ import { OpenAPIHono } from "@hono/zod-openapi";
2
+ import type { Kernel } from "../../../runtime/kernel.js";
3
+ import {
4
+ createTaxRateRoute,
5
+ listTaxRatesRoute,
6
+ updateTaxRateRoute,
7
+ deleteTaxRateRoute,
8
+ createTaxClassRoute,
9
+ listTaxClassesRoute,
10
+ updateTaxClassRoute,
11
+ deleteTaxClassRoute,
12
+ } from "../schemas/tax.js";
13
+ import { type AppEnv, mapErrorToResponse, mapErrorToStatus, requirePerm } from "../utils.js";
14
+
15
+ export function taxRoutes(kernel: Kernel) {
16
+ const router = new OpenAPIHono<AppEnv>();
17
+
18
+ router.use("/rates", requirePerm("tax:manage"));
19
+ router.use("/rates/:id", requirePerm("tax:manage"));
20
+ router.use("/classes", requirePerm("tax:manage"));
21
+ router.use("/classes/:id", requirePerm("tax:manage"));
22
+
23
+ // @ts-expect-error -- openapi handler union return type
24
+ router.openapi(createTaxRateRoute, async (c) => {
25
+ const result = await kernel.services.tax.createTaxRate(c.req.valid("json"), c.get("actor"));
26
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
27
+ return c.json({ data: result.value }, 201);
28
+ });
29
+
30
+ // @ts-expect-error -- openapi handler union return type
31
+ router.openapi(listTaxRatesRoute, async (c) => {
32
+ const result = await kernel.services.tax.listTaxRates(c.get("actor"));
33
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
34
+ return c.json({ data: result.value });
35
+ });
36
+
37
+ // @ts-expect-error -- openapi handler union return type
38
+ router.openapi(updateTaxRateRoute, async (c) => {
39
+ const result = await kernel.services.tax.updateTaxRate(
40
+ c.req.param("id"),
41
+ c.req.valid("json"),
42
+ c.get("actor"),
43
+ );
44
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
45
+ return c.json({ data: result.value });
46
+ });
47
+
48
+ // @ts-expect-error -- openapi handler union return type
49
+ router.openapi(deleteTaxRateRoute, async (c) => {
50
+ const result = await kernel.services.tax.deleteTaxRate(c.req.param("id"), c.get("actor"));
51
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
52
+ return c.json({ data: result.value });
53
+ });
54
+
55
+ // ── Product tax classes (issue #57) ───────────────────────────────
56
+
57
+ // @ts-expect-error -- openapi handler union return type
58
+ router.openapi(createTaxClassRoute, async (c) => {
59
+ const result = await kernel.services.tax.createTaxClass(c.req.valid("json"), c.get("actor"));
60
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
61
+ return c.json({ data: result.value }, 201);
62
+ });
63
+
64
+ // @ts-expect-error -- openapi handler union return type
65
+ router.openapi(listTaxClassesRoute, async (c) => {
66
+ const result = await kernel.services.tax.listTaxClasses(c.get("actor"));
67
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
68
+ return c.json({ data: result.value });
69
+ });
70
+
71
+ // @ts-expect-error -- openapi handler union return type
72
+ router.openapi(updateTaxClassRoute, async (c) => {
73
+ const result = await kernel.services.tax.updateTaxClass(
74
+ c.req.param("id"),
75
+ c.req.valid("json"),
76
+ c.get("actor"),
77
+ );
78
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
79
+ return c.json({ data: result.value });
80
+ });
81
+
82
+ // @ts-expect-error -- openapi handler union return type
83
+ router.openapi(deleteTaxClassRoute, async (c) => {
84
+ const result = await kernel.services.tax.deleteTaxClass(c.req.param("id"), c.get("actor"));
85
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
86
+ return c.json({ data: result.value });
87
+ });
88
+
89
+ return router;
90
+ }
@@ -0,0 +1,110 @@
1
+ import { z, createRoute } from "@hono/zod-openapi";
2
+ import { errorResponses } from "./shared.js";
3
+
4
+ export const CreateStaffBodySchema = z.object({
5
+ userId: z.string().min(1).openapi({ example: "user_abc123", description: "Better Auth user id of an existing user" }),
6
+ role: z.string().min(1).openapi({ example: "manager" }),
7
+ }).openapi("CreateStaffRequest");
8
+
9
+ export const InviteStaffBodySchema = z.object({
10
+ email: z.email().openapi({ example: "newhire@example.com" }),
11
+ role: z.string().min(1).openapi({ example: "manager" }),
12
+ }).openapi("InviteStaffRequest");
13
+
14
+ export const UpdateStaffRoleBodySchema = z.object({
15
+ role: z.string().min(1).openapi({ example: "admin" }),
16
+ }).openapi("UpdateStaffRoleRequest");
17
+
18
+ const MemberIdParam = z.object({
19
+ id: z.string().min(1).openapi({ example: "mem_abc123" }),
20
+ });
21
+
22
+ const DataResponse = z.object({ data: z.any() }).openapi("AdminStaffResponse");
23
+
24
+ export const listStaffRoute = createRoute({
25
+ method: "get",
26
+ path: "/staff",
27
+ tags: ["Admin"],
28
+ summary: "List staff members with their roles",
29
+ responses: {
30
+ 200: { content: { "application/json": { schema: DataResponse } }, description: "Staff members" },
31
+ ...errorResponses,
32
+ },
33
+ });
34
+
35
+ export const createStaffRoute = createRoute({
36
+ method: "post",
37
+ path: "/staff",
38
+ tags: ["Admin"],
39
+ summary: "Add an existing user as a staff member with a role",
40
+ request: {
41
+ body: { content: { "application/json": { schema: CreateStaffBodySchema } }, required: true },
42
+ },
43
+ responses: {
44
+ 201: { content: { "application/json": { schema: DataResponse } }, description: "Staff member created." },
45
+ ...errorResponses,
46
+ },
47
+ });
48
+
49
+ export const inviteStaffRoute = createRoute({
50
+ method: "post",
51
+ path: "/staff/invitations",
52
+ tags: ["Admin"],
53
+ summary: "Invite a teammate by email with a role",
54
+ request: {
55
+ body: { content: { "application/json": { schema: InviteStaffBodySchema } }, required: true },
56
+ },
57
+ responses: {
58
+ 201: { content: { "application/json": { schema: DataResponse } }, description: "Invitation created." },
59
+ ...errorResponses,
60
+ },
61
+ });
62
+
63
+ export const listStaffInvitationsRoute = createRoute({
64
+ method: "get",
65
+ path: "/staff/invitations",
66
+ tags: ["Admin"],
67
+ summary: "List pending staff invitations",
68
+ responses: {
69
+ 200: { content: { "application/json": { schema: DataResponse } }, description: "Invitations" },
70
+ ...errorResponses,
71
+ },
72
+ });
73
+
74
+ export const listStaffRolesRoute = createRoute({
75
+ method: "get",
76
+ path: "/staff/roles",
77
+ tags: ["Admin"],
78
+ summary: "Role → permission mapping staff roles resolve to",
79
+ responses: {
80
+ 200: { content: { "application/json": { schema: DataResponse } }, description: "Roles" },
81
+ ...errorResponses,
82
+ },
83
+ });
84
+
85
+ export const updateStaffRoleRoute = createRoute({
86
+ method: "patch",
87
+ path: "/staff/{id}",
88
+ tags: ["Admin"],
89
+ summary: "Change a staff member's role",
90
+ request: {
91
+ params: MemberIdParam,
92
+ body: { content: { "application/json": { schema: UpdateStaffRoleBodySchema } }, required: true },
93
+ },
94
+ responses: {
95
+ 200: { content: { "application/json": { schema: DataResponse } }, description: "Role updated." },
96
+ ...errorResponses,
97
+ },
98
+ });
99
+
100
+ export const revokeStaffRoute = createRoute({
101
+ method: "delete",
102
+ path: "/staff/{id}",
103
+ tags: ["Admin"],
104
+ summary: "Revoke a staff member's membership",
105
+ request: { params: MemberIdParam },
106
+ responses: {
107
+ 200: { content: { "application/json": { schema: DataResponse } }, description: "Membership revoked." },
108
+ ...errorResponses,
109
+ },
110
+ });
@@ -0,0 +1,39 @@
1
+ import { z, createRoute } from "@hono/zod-openapi";
2
+ import { errorResponses } from "./shared.js";
3
+
4
+ const DATE = /^\d{4}-\d{2}-\d{2}$/;
5
+
6
+ const ReportParamsSchema = z.object({
7
+ name: z.string().regex(/^[a-z][a-z0-9-]*$/).openapi({ example: "daily-journal" }),
8
+ });
9
+
10
+ const ReportQuerySchema = z.object({
11
+ date: z.string().regex(DATE).optional().openapi({ description: "Local calendar date (single-day reports)." }),
12
+ from: z.string().regex(DATE).optional().openapi({ description: "Local range start, inclusive." }),
13
+ to: z.string().regex(DATE).optional().openapi({ description: "Local range end, inclusive." }),
14
+ });
15
+
16
+ const DataResponse = z.object({ data: z.any() }).openapi("AnalyticsReportResponse");
17
+
18
+ export const listReportsRoute = createRoute({
19
+ method: "get",
20
+ path: "/reports",
21
+ tags: ["Analytics"],
22
+ summary: "List the canned retail reports",
23
+ responses: {
24
+ 200: { content: { "application/json": { schema: DataResponse } }, description: "Available reports." },
25
+ ...errorResponses,
26
+ },
27
+ });
28
+
29
+ export const getReportRoute = createRoute({
30
+ method: "get",
31
+ path: "/reports/{name}",
32
+ tags: ["Analytics"],
33
+ summary: "Run a canned retail report (calendar math in the store's timezone)",
34
+ request: { params: ReportParamsSchema, query: ReportQuerySchema },
35
+ responses: {
36
+ 200: { content: { "application/json": { schema: DataResponse } }, description: "Report result." },
37
+ ...errorResponses,
38
+ },
39
+ });
@@ -26,6 +26,46 @@ const CartItemParams = z.object({
26
26
 
27
27
  // ─── Route Definitions ──────────────────────────────────────────────────────
28
28
 
29
+ export const listCartsRoute = createRoute({
30
+ method: "get",
31
+ path: "/",
32
+ tags: ["Carts"],
33
+ summary: "List carts (admin) — abandoned-checkout recovery filters",
34
+ description: "Lists carts with status/olderThan/hasCustomer filters and pagination. Includes shopper identity (cart email + linked customer email). Requires cart:manage.",
35
+ request: {
36
+ query: z.object({
37
+ status: z.enum(["active", "checking_out", "merged", "checked_out", "abandoned"]).optional(),
38
+ olderThan: z.string().optional().openapi({ example: "2026-06-30T00:00:00Z", description: "Only carts not updated since this ISO timestamp" }),
39
+ hasCustomer: z.string().optional().openapi({ example: "true" }),
40
+ page: z.string().optional(),
41
+ limit: z.string().optional(),
42
+ }),
43
+ },
44
+ responses: {
45
+ 200: {
46
+ content: { "application/json": { schema: CartResponse } },
47
+ description: "Carts",
48
+ },
49
+ ...errorResponses,
50
+ },
51
+ });
52
+
53
+ export const recoverCartRoute = createRoute({
54
+ method: "post",
55
+ path: "/{id}/recover",
56
+ tags: ["Carts"],
57
+ summary: "Recover an abandoned cart (admin)",
58
+ description: "Reactivates the cart, extends its expiry, and returns a resume secret suitable for a recovery email's resume/checkout link. Fires the cart.afterRecover hook. Requires cart:manage.",
59
+ request: { params: CartIdParam },
60
+ responses: {
61
+ 200: {
62
+ content: { "application/json": { schema: CartResponse } },
63
+ description: "Recovery payload (cartId, secret, expiresAt, shopper identity).",
64
+ },
65
+ ...errorResponses,
66
+ },
67
+ });
68
+
29
69
  export const getCartRoute = createRoute({
30
70
  method: "get",
31
71
  path: "/{id}",