@porulle/core 0.7.0 → 0.9.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 (325) hide show
  1. package/dist/auth/middleware.d.ts.map +1 -1
  2. package/dist/auth/middleware.js +26 -7
  3. package/dist/auth/setup.d.ts.map +1 -1
  4. package/dist/auth/setup.js +8 -5
  5. package/dist/config/defaults.d.ts.map +1 -1
  6. package/dist/config/defaults.js +7 -0
  7. package/dist/config/types.d.ts +26 -1
  8. package/dist/config/types.d.ts.map +1 -1
  9. package/dist/generated/plugin-manifest.d.ts +3 -0
  10. package/dist/generated/plugin-manifest.d.ts.map +1 -1
  11. package/dist/generated/plugin-manifest.js +1 -0
  12. package/dist/hooks/checkout-completion.d.ts.map +1 -1
  13. package/dist/hooks/checkout-completion.js +2 -2
  14. package/dist/hooks/checkout.d.ts.map +1 -1
  15. package/dist/hooks/checkout.js +17 -2
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +1 -0
  19. package/dist/interfaces/rest/index.d.ts.map +1 -1
  20. package/dist/interfaces/rest/index.js +12 -0
  21. package/dist/interfaces/rest/routes/admin/staff.d.ts +12 -0
  22. package/dist/interfaces/rest/routes/admin/staff.d.ts.map +1 -0
  23. package/dist/interfaces/rest/routes/admin/staff.js +206 -0
  24. package/dist/interfaces/rest/routes/analytics.d.ts +5 -0
  25. package/dist/interfaces/rest/routes/analytics.d.ts.map +1 -0
  26. package/dist/interfaces/rest/routes/analytics.js +24 -0
  27. package/dist/interfaces/rest/routes/carts.d.ts.map +1 -1
  28. package/dist/interfaces/rest/routes/carts.js +39 -2
  29. package/dist/interfaces/rest/routes/catalog.d.ts.map +1 -1
  30. package/dist/interfaces/rest/routes/catalog.js +17 -1
  31. package/dist/interfaces/rest/routes/checkout.d.ts +9 -0
  32. package/dist/interfaces/rest/routes/checkout.d.ts.map +1 -1
  33. package/dist/interfaces/rest/routes/checkout.js +97 -21
  34. package/dist/interfaces/rest/routes/documents.d.ts +10 -0
  35. package/dist/interfaces/rest/routes/documents.d.ts.map +1 -0
  36. package/dist/interfaces/rest/routes/documents.js +40 -0
  37. package/dist/interfaces/rest/routes/inventory.d.ts.map +1 -1
  38. package/dist/interfaces/rest/routes/inventory.js +13 -0
  39. package/dist/interfaces/rest/routes/orders.d.ts.map +1 -1
  40. package/dist/interfaces/rest/routes/orders.js +111 -1
  41. package/dist/interfaces/rest/routes/pricing.d.ts.map +1 -1
  42. package/dist/interfaces/rest/routes/pricing.js +36 -1
  43. package/dist/interfaces/rest/routes/settings.d.ts +5 -0
  44. package/dist/interfaces/rest/routes/settings.d.ts.map +1 -0
  45. package/dist/interfaces/rest/routes/settings.js +50 -0
  46. package/dist/interfaces/rest/routes/shipping.d.ts +5 -0
  47. package/dist/interfaces/rest/routes/shipping.d.ts.map +1 -0
  48. package/dist/interfaces/rest/routes/shipping.js +68 -0
  49. package/dist/interfaces/rest/routes/tax.d.ts +5 -0
  50. package/dist/interfaces/rest/routes/tax.d.ts.map +1 -0
  51. package/dist/interfaces/rest/routes/tax.js +68 -0
  52. package/dist/interfaces/rest/schemas/admin-staff.d.ts +899 -0
  53. package/dist/interfaces/rest/schemas/admin-staff.d.ts.map +1 -0
  54. package/dist/interfaces/rest/schemas/admin-staff.js +98 -0
  55. package/dist/interfaces/rest/schemas/analytics.d.ts +252 -0
  56. package/dist/interfaces/rest/schemas/analytics.d.ts.map +1 -0
  57. package/dist/interfaces/rest/schemas/analytics.js +33 -0
  58. package/dist/interfaces/rest/schemas/carts.d.ts +656 -0
  59. package/dist/interfaces/rest/schemas/carts.d.ts.map +1 -1
  60. package/dist/interfaces/rest/schemas/carts.js +38 -0
  61. package/dist/interfaces/rest/schemas/catalog.d.ts +1218 -1
  62. package/dist/interfaces/rest/schemas/catalog.d.ts.map +1 -1
  63. package/dist/interfaces/rest/schemas/catalog.js +48 -0
  64. package/dist/interfaces/rest/schemas/checkout.d.ts +2 -0
  65. package/dist/interfaces/rest/schemas/checkout.d.ts.map +1 -1
  66. package/dist/interfaces/rest/schemas/checkout.js +4 -0
  67. package/dist/interfaces/rest/schemas/customer-portal.d.ts +35 -0
  68. package/dist/interfaces/rest/schemas/customer-portal.d.ts.map +1 -1
  69. package/dist/interfaces/rest/schemas/documents.d.ts +509 -0
  70. package/dist/interfaces/rest/schemas/documents.d.ts.map +1 -0
  71. package/dist/interfaces/rest/schemas/documents.js +65 -0
  72. package/dist/interfaces/rest/schemas/orders.d.ts +3110 -43
  73. package/dist/interfaces/rest/schemas/orders.d.ts.map +1 -1
  74. package/dist/interfaces/rest/schemas/orders.js +234 -1
  75. package/dist/interfaces/rest/schemas/pricing.d.ts +405 -0
  76. package/dist/interfaces/rest/schemas/pricing.d.ts.map +1 -1
  77. package/dist/interfaces/rest/schemas/pricing.js +70 -0
  78. package/dist/interfaces/rest/schemas/responses.d.ts +190 -0
  79. package/dist/interfaces/rest/schemas/responses.d.ts.map +1 -1
  80. package/dist/interfaces/rest/schemas/search.d.ts +17 -0
  81. package/dist/interfaces/rest/schemas/search.d.ts.map +1 -1
  82. package/dist/interfaces/rest/schemas/settings.d.ts +397 -0
  83. package/dist/interfaces/rest/schemas/settings.d.ts.map +1 -0
  84. package/dist/interfaces/rest/schemas/settings.js +79 -0
  85. package/dist/interfaces/rest/schemas/shipping.d.ts +1093 -0
  86. package/dist/interfaces/rest/schemas/shipping.d.ts.map +1 -0
  87. package/dist/interfaces/rest/schemas/shipping.js +146 -0
  88. package/dist/interfaces/rest/schemas/tax.d.ts +1074 -0
  89. package/dist/interfaces/rest/schemas/tax.d.ts.map +1 -0
  90. package/dist/interfaces/rest/schemas/tax.js +132 -0
  91. package/dist/kernel/database/schema.d.ts +4 -0
  92. package/dist/kernel/database/schema.d.ts.map +1 -1
  93. package/dist/kernel/database/schema.js +8 -0
  94. package/dist/kernel/database/scoped-db.d.ts +2 -1
  95. package/dist/kernel/database/scoped-db.d.ts.map +1 -1
  96. package/dist/kernel/database/scoped-db.js +62 -1
  97. package/dist/kernel/hooks/types.d.ts +1 -1
  98. package/dist/kernel/hooks/types.d.ts.map +1 -1
  99. package/dist/kernel/plugin/manifest.d.ts +26 -0
  100. package/dist/kernel/plugin/manifest.d.ts.map +1 -1
  101. package/dist/kernel/plugin/manifest.js +17 -2
  102. package/dist/modules/analytics/drizzle-adapter.d.ts.map +1 -1
  103. package/dist/modules/analytics/drizzle-adapter.js +16 -3
  104. package/dist/modules/analytics/module.d.ts.map +1 -1
  105. package/dist/modules/analytics/module.js +6 -0
  106. package/dist/modules/analytics/reports.d.ts +58 -0
  107. package/dist/modules/analytics/reports.d.ts.map +1 -0
  108. package/dist/modules/analytics/reports.js +249 -0
  109. package/dist/modules/analytics/service.d.ts +10 -0
  110. package/dist/modules/analytics/service.d.ts.map +1 -1
  111. package/dist/modules/analytics/service.js +14 -0
  112. package/dist/modules/cart/repository/index.d.ts +7 -0
  113. package/dist/modules/cart/repository/index.d.ts.map +1 -1
  114. package/dist/modules/cart/repository/index.js +25 -1
  115. package/dist/modules/cart/schema.d.ts +17 -0
  116. package/dist/modules/cart/schema.d.ts.map +1 -1
  117. package/dist/modules/cart/schema.js +2 -0
  118. package/dist/modules/cart/schemas.d.ts +1 -0
  119. package/dist/modules/cart/schemas.d.ts.map +1 -1
  120. package/dist/modules/cart/schemas.js +1 -0
  121. package/dist/modules/cart/service.d.ts +32 -0
  122. package/dist/modules/cart/service.d.ts.map +1 -1
  123. package/dist/modules/cart/service.js +61 -2
  124. package/dist/modules/catalog/entity-service.d.ts +21 -0
  125. package/dist/modules/catalog/entity-service.d.ts.map +1 -1
  126. package/dist/modules/catalog/entity-service.js +166 -5
  127. package/dist/modules/catalog/schema.d.ts +34 -0
  128. package/dist/modules/catalog/schema.d.ts.map +1 -1
  129. package/dist/modules/catalog/schema.js +5 -0
  130. package/dist/modules/catalog/schemas.d.ts +3 -0
  131. package/dist/modules/catalog/schemas.d.ts.map +1 -1
  132. package/dist/modules/catalog/schemas.js +3 -0
  133. package/dist/modules/catalog/service.d.ts +36 -0
  134. package/dist/modules/catalog/service.d.ts.map +1 -1
  135. package/dist/modules/catalog/service.js +6 -0
  136. package/dist/modules/customers/service.d.ts.map +1 -1
  137. package/dist/modules/customers/service.js +6 -0
  138. package/dist/modules/documents/module.d.ts +7 -0
  139. package/dist/modules/documents/module.d.ts.map +1 -0
  140. package/dist/modules/documents/module.js +12 -0
  141. package/dist/modules/documents/pdf.d.ts +25 -0
  142. package/dist/modules/documents/pdf.d.ts.map +1 -0
  143. package/dist/modules/documents/pdf.js +80 -0
  144. package/dist/modules/documents/render.d.ts +32 -0
  145. package/dist/modules/documents/render.d.ts.map +1 -0
  146. package/dist/modules/documents/render.js +190 -0
  147. package/dist/modules/documents/repository/index.d.ts +25 -0
  148. package/dist/modules/documents/repository/index.d.ts.map +1 -0
  149. package/dist/modules/documents/repository/index.js +47 -0
  150. package/dist/modules/documents/schema.d.ts +214 -0
  151. package/dist/modules/documents/schema.d.ts.map +1 -0
  152. package/dist/modules/documents/schema.js +42 -0
  153. package/dist/modules/documents/service.d.ts +51 -0
  154. package/dist/modules/documents/service.d.ts.map +1 -0
  155. package/dist/modules/documents/service.js +119 -0
  156. package/dist/modules/fulfillment/service.d.ts +18 -0
  157. package/dist/modules/fulfillment/service.d.ts.map +1 -1
  158. package/dist/modules/fulfillment/service.js +85 -1
  159. package/dist/modules/orders/repository/index.d.ts +18 -1
  160. package/dist/modules/orders/repository/index.d.ts.map +1 -1
  161. package/dist/modules/orders/repository/index.js +86 -1
  162. package/dist/modules/orders/schema.d.ts +391 -0
  163. package/dist/modules/orders/schema.d.ts.map +1 -1
  164. package/dist/modules/orders/schema.js +56 -1
  165. package/dist/modules/orders/service.d.ts +102 -3
  166. package/dist/modules/orders/service.d.ts.map +1 -1
  167. package/dist/modules/orders/service.js +653 -29
  168. package/dist/modules/orders/stale-order-cleanup.d.ts.map +1 -1
  169. package/dist/modules/orders/stale-order-cleanup.js +13 -1
  170. package/dist/modules/pricing/repository/index.d.ts +5 -0
  171. package/dist/modules/pricing/repository/index.d.ts.map +1 -1
  172. package/dist/modules/pricing/repository/index.js +19 -0
  173. package/dist/modules/pricing/service.d.ts +18 -0
  174. package/dist/modules/pricing/service.d.ts.map +1 -1
  175. package/dist/modules/pricing/service.js +43 -5
  176. package/dist/modules/settings/module.d.ts +6 -0
  177. package/dist/modules/settings/module.d.ts.map +1 -0
  178. package/dist/modules/settings/module.js +11 -0
  179. package/dist/modules/settings/repository/index.d.ts +16 -0
  180. package/dist/modules/settings/repository/index.d.ts.map +1 -0
  181. package/dist/modules/settings/repository/index.js +41 -0
  182. package/dist/modules/settings/schema.d.ts +121 -0
  183. package/dist/modules/settings/schema.d.ts.map +1 -0
  184. package/dist/modules/settings/schema.js +24 -0
  185. package/dist/modules/settings/service.d.ts +26 -0
  186. package/dist/modules/settings/service.d.ts.map +1 -0
  187. package/dist/modules/settings/service.js +51 -0
  188. package/dist/modules/shipping/calculator.d.ts +5 -0
  189. package/dist/modules/shipping/calculator.d.ts.map +1 -1
  190. package/dist/modules/shipping/calculator.js +10 -0
  191. package/dist/modules/shipping/module.d.ts +5 -1
  192. package/dist/modules/shipping/module.d.ts.map +1 -1
  193. package/dist/modules/shipping/module.js +4 -1
  194. package/dist/modules/shipping/repository/index.d.ts +27 -2
  195. package/dist/modules/shipping/repository/index.d.ts.map +1 -1
  196. package/dist/modules/shipping/repository/index.js +105 -1
  197. package/dist/modules/shipping/schema.d.ts +419 -0
  198. package/dist/modules/shipping/schema.d.ts.map +1 -0
  199. package/dist/modules/shipping/schema.js +56 -0
  200. package/dist/modules/shipping/service.d.ts +54 -0
  201. package/dist/modules/shipping/service.d.ts.map +1 -1
  202. package/dist/modules/shipping/service.js +161 -2
  203. package/dist/modules/tax/adapter.d.ts +13 -0
  204. package/dist/modules/tax/adapter.d.ts.map +1 -1
  205. package/dist/modules/tax/module.d.ts +5 -1
  206. package/dist/modules/tax/module.d.ts.map +1 -1
  207. package/dist/modules/tax/module.js +4 -1
  208. package/dist/modules/tax/repository/index.d.ts +38 -2
  209. package/dist/modules/tax/repository/index.d.ts.map +1 -1
  210. package/dist/modules/tax/repository/index.js +138 -1
  211. package/dist/modules/tax/schema.d.ts +355 -0
  212. package/dist/modules/tax/schema.d.ts.map +1 -0
  213. package/dist/modules/tax/schema.js +55 -0
  214. package/dist/modules/tax/service.d.ts +60 -1
  215. package/dist/modules/tax/service.d.ts.map +1 -1
  216. package/dist/modules/tax/service.js +227 -2
  217. package/dist/runtime/kernel-modules.d.ts +15 -2
  218. package/dist/runtime/kernel-modules.d.ts.map +1 -1
  219. package/dist/runtime/kernel-modules.js +4 -0
  220. package/dist/runtime/kernel-types.d.ts +5 -1
  221. package/dist/runtime/kernel-types.d.ts.map +1 -1
  222. package/dist/runtime/kernel-types.js +2 -0
  223. package/dist/runtime/server.js +1 -1
  224. package/dist/test-utils/create-plugin-test-app.d.ts +3 -0
  225. package/dist/test-utils/create-plugin-test-app.d.ts.map +1 -1
  226. package/dist/test-utils/create-plugin-test-app.js +15 -3
  227. package/dist/test-utils/create-test-config.d.ts.map +1 -1
  228. package/dist/test-utils/create-test-config.js +2 -0
  229. package/dist/test-utils/order-test-helpers.d.ts +12 -0
  230. package/dist/test-utils/order-test-helpers.d.ts.map +1 -0
  231. package/dist/test-utils/order-test-helpers.js +18 -0
  232. package/dist/test-utils/rest-api-test-utils.d.ts.map +1 -1
  233. package/dist/test-utils/rest-api-test-utils.js +7 -0
  234. package/dist/test-utils/test-actors.js +1 -1
  235. package/dist/testing.d.ts +1 -0
  236. package/dist/testing.d.ts.map +1 -1
  237. package/dist/testing.js +1 -0
  238. package/package.json +3 -3
  239. package/src/auth/middleware.ts +26 -7
  240. package/src/auth/setup.ts +11 -4
  241. package/src/config/defaults.ts +7 -0
  242. package/src/config/types.ts +26 -1
  243. package/src/generated/plugin-capabilities.d.ts +1 -0
  244. package/src/generated/plugin-manifest.ts +1 -0
  245. package/src/generated/plugin-repositories.d.ts +1 -0
  246. package/src/hooks/checkout-completion.ts +22 -16
  247. package/src/hooks/checkout.ts +36 -11
  248. package/src/index.ts +1 -0
  249. package/src/interfaces/rest/index.ts +12 -0
  250. package/src/interfaces/rest/routes/admin/staff.ts +260 -0
  251. package/src/interfaces/rest/routes/analytics.ts +33 -0
  252. package/src/interfaces/rest/routes/carts.ts +52 -2
  253. package/src/interfaces/rest/routes/catalog.ts +34 -0
  254. package/src/interfaces/rest/routes/checkout.ts +114 -27
  255. package/src/interfaces/rest/routes/documents.ts +61 -0
  256. package/src/interfaces/rest/routes/inventory.ts +14 -0
  257. package/src/interfaces/rest/routes/orders.ts +148 -1
  258. package/src/interfaces/rest/routes/pricing.ts +44 -1
  259. package/src/interfaces/rest/routes/settings.ts +61 -0
  260. package/src/interfaces/rest/routes/shipping.ts +92 -0
  261. package/src/interfaces/rest/routes/tax.ts +90 -0
  262. package/src/interfaces/rest/schemas/admin-staff.ts +110 -0
  263. package/src/interfaces/rest/schemas/analytics.ts +39 -0
  264. package/src/interfaces/rest/schemas/carts.ts +40 -0
  265. package/src/interfaces/rest/schemas/catalog.ts +53 -0
  266. package/src/interfaces/rest/schemas/checkout.ts +4 -0
  267. package/src/interfaces/rest/schemas/documents.ts +72 -0
  268. package/src/interfaces/rest/schemas/orders.ts +258 -1
  269. package/src/interfaces/rest/schemas/pricing.ts +75 -0
  270. package/src/interfaces/rest/schemas/settings.ts +93 -0
  271. package/src/interfaces/rest/schemas/shipping.ts +164 -0
  272. package/src/interfaces/rest/schemas/tax.ts +148 -0
  273. package/src/kernel/database/schema.ts +12 -0
  274. package/src/kernel/database/scoped-db.ts +70 -1
  275. package/src/kernel/hooks/types.ts +1 -0
  276. package/src/kernel/plugin/manifest.ts +41 -2
  277. package/src/modules/analytics/drizzle-adapter.ts +19 -3
  278. package/src/modules/analytics/module.ts +10 -0
  279. package/src/modules/analytics/reports.ts +311 -0
  280. package/src/modules/analytics/service.ts +23 -0
  281. package/src/modules/cart/repository/index.ts +30 -1
  282. package/src/modules/cart/schema.ts +2 -0
  283. package/src/modules/cart/schemas.ts +1 -0
  284. package/src/modules/cart/service.ts +104 -2
  285. package/src/modules/catalog/entity-service.ts +196 -4
  286. package/src/modules/catalog/schema.ts +5 -0
  287. package/src/modules/catalog/schemas.ts +3 -0
  288. package/src/modules/catalog/service.ts +30 -0
  289. package/src/modules/customers/service.ts +6 -0
  290. package/src/modules/documents/module.ts +19 -0
  291. package/src/modules/documents/pdf.ts +101 -0
  292. package/src/modules/documents/render.ts +231 -0
  293. package/src/modules/documents/repository/index.ts +68 -0
  294. package/src/modules/documents/schema.ts +44 -0
  295. package/src/modules/documents/service.ts +176 -0
  296. package/src/modules/fulfillment/service.ts +139 -1
  297. package/src/modules/orders/repository/index.ts +127 -1
  298. package/src/modules/orders/schema.ts +58 -1
  299. package/src/modules/orders/service.ts +1041 -65
  300. package/src/modules/orders/stale-order-cleanup.ts +18 -4
  301. package/src/modules/pricing/repository/index.ts +34 -0
  302. package/src/modules/pricing/service.ts +76 -6
  303. package/src/modules/settings/module.ts +18 -0
  304. package/src/modules/settings/repository/index.ts +59 -0
  305. package/src/modules/settings/schema.ts +25 -0
  306. package/src/modules/settings/service.ts +82 -0
  307. package/src/modules/shipping/calculator.ts +27 -0
  308. package/src/modules/shipping/module.ts +6 -2
  309. package/src/modules/shipping/repository/index.ts +136 -2
  310. package/src/modules/shipping/schema.ts +58 -0
  311. package/src/modules/shipping/service.ts +260 -1
  312. package/src/modules/tax/adapter.ts +9 -0
  313. package/src/modules/tax/module.ts +6 -2
  314. package/src/modules/tax/repository/index.ts +169 -2
  315. package/src/modules/tax/schema.ts +57 -0
  316. package/src/modules/tax/service.ts +313 -2
  317. package/src/runtime/kernel-modules.ts +4 -0
  318. package/src/runtime/kernel-types.ts +6 -0
  319. package/src/runtime/server.ts +1 -1
  320. package/src/test-utils/create-plugin-test-app.ts +17 -4
  321. package/src/test-utils/create-test-config.ts +2 -0
  322. package/src/test-utils/order-test-helpers.ts +26 -0
  323. package/src/test-utils/rest-api-test-utils.ts +7 -0
  324. package/src/test-utils/test-actors.ts +1 -1
  325. package/src/testing.ts +1 -0
@@ -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}",
@@ -477,3 +477,56 @@ export const generateVariantsRoute = createRoute({
477
477
  ...errorResponses,
478
478
  },
479
479
  });
480
+
481
+ // ── One-call variant creation (issue #50) ───────────────────────────────────
482
+
483
+ export const QuickVariantBodySchema = z.object({
484
+ options: z.record(z.string().min(1), z.string().min(1)).openapi({
485
+ example: { color: "Red", size: "M" },
486
+ description: "Axis name → value. Option types/values are upserted inline.",
487
+ }),
488
+ sku: z.string().min(1).optional(),
489
+ barcode: z.string().min(1).optional(),
490
+ }).openapi("QuickVariantRequest");
491
+
492
+ export const BulkVariantsBodySchema = z.object({
493
+ axes: z.array(z.object({
494
+ name: z.string().min(1),
495
+ values: z.array(z.string().min(1)).min(1),
496
+ })).openapi({
497
+ example: [{ name: "color", values: ["Red", "Blue"] }, { name: "size", values: ["S", "M"] }],
498
+ description: "Matrix axes; an empty array creates one option-less variant.",
499
+ }),
500
+ skuPrefix: z.string().min(1).optional(),
501
+ }).openapi("BulkVariantsRequest");
502
+
503
+ export const quickVariantRoute = createRoute({
504
+ method: "post",
505
+ path: "/entities/{id}/variants/quick",
506
+ tags: ["Catalog"],
507
+ summary: "Create one sellable variant in a single call (upserts axes, seeds inventory)",
508
+ request: {
509
+ params: EntityIdParam,
510
+ body: { content: { "application/json": { schema: QuickVariantBodySchema } }, required: true },
511
+ },
512
+ responses: {
513
+ 201: { content: { "application/json": { schema: DataResponse } }, description: "Variant created and inventory seeded." },
514
+ 200: { content: { "application/json": { schema: DataResponse } }, description: "The combination already existed; the existing variant is returned." },
515
+ ...errorResponses,
516
+ },
517
+ });
518
+
519
+ export const bulkVariantsRoute = createRoute({
520
+ method: "post",
521
+ path: "/entities/{id}/variants/bulk",
522
+ tags: ["Catalog"],
523
+ summary: "Create a variant matrix in a single call (upserts axes, seeds inventory, skips existing)",
524
+ request: {
525
+ params: EntityIdParam,
526
+ body: { content: { "application/json": { schema: BulkVariantsBodySchema } }, required: true },
527
+ },
528
+ responses: {
529
+ 201: { content: { "application/json": { schema: DataResponse } }, description: "Variants created (existing combinations skipped)." },
530
+ ...errorResponses,
531
+ },
532
+ });
@@ -6,6 +6,10 @@ import { ErrorSchema, errorResponses } from "./shared.js";
6
6
  export const CheckoutBodySchema = z.object({
7
7
  cartId: z.uuid().openapi({ example: "550e8400-e29b-41d4-a716-446655440000" }),
8
8
  paymentMethodId: z.string().openapi({ example: "pm_card_visa" }),
9
+ idempotencyKey: z.string().min(8).max(255).optional().openapi({
10
+ example: "checkout-8f14e45f-1738312200",
11
+ description: "Client-supplied retry key. A re-submitted checkout with the same key returns the already-created order without re-authorizing payment (safe offline-queue replay).",
12
+ }),
9
13
  customerId: z.string().optional().openapi({ example: "customer-uuid-or-user-id" }),
10
14
  customerGroupIds: z.array(z.string()).optional(),
11
15
  currency: z.string().length(3).optional().openapi({ example: "USD" }),
@@ -0,0 +1,72 @@
1
+ import { z, createRoute } from "@hono/zod-openapi";
2
+ import { errorResponses } from "./shared.js";
3
+
4
+ const IdParam = z.object({
5
+ id: z.uuid().openapi({ example: "550e8400-e29b-41d4-a716-446655440000" }),
6
+ });
7
+
8
+ export const EmailInvoiceBodySchema = z.object({
9
+ to: z.email().openapi({ example: "customer@example.com" }),
10
+ }).openapi("EmailInvoiceRequest");
11
+
12
+ const DataResponse = z.object({ data: z.any() }).openapi("DocumentActionResponse");
13
+
14
+ export const invoicePdfRoute = createRoute({
15
+ method: "get",
16
+ path: "/{id}/invoice.pdf",
17
+ tags: ["Documents"],
18
+ summary: "Render the order's invoice as PDF (issues a fiscal invoice number on first render)",
19
+ request: { params: IdParam },
20
+ responses: {
21
+ 200: {
22
+ content: { "application/pdf": { schema: z.any().openapi({ type: "string", format: "binary" }) } },
23
+ description: "The rendered PDF invoice.",
24
+ },
25
+ ...errorResponses,
26
+ },
27
+ });
28
+
29
+ export const invoiceHtmlRoute = createRoute({
30
+ method: "get",
31
+ path: "/{id}/invoice.html",
32
+ tags: ["Documents"],
33
+ summary: "Render the order's invoice as HTML",
34
+ request: { params: IdParam },
35
+ responses: {
36
+ 200: {
37
+ content: { "text/html": { schema: z.string() } },
38
+ description: "The rendered HTML invoice.",
39
+ },
40
+ ...errorResponses,
41
+ },
42
+ });
43
+
44
+ export const receiptHtmlRoute = createRoute({
45
+ method: "get",
46
+ path: "/{id}/receipt.html",
47
+ tags: ["Documents"],
48
+ summary: "Render the order's receipt as HTML",
49
+ request: { params: IdParam },
50
+ responses: {
51
+ 200: {
52
+ content: { "text/html": { schema: z.string() } },
53
+ description: "The rendered HTML receipt.",
54
+ },
55
+ ...errorResponses,
56
+ },
57
+ });
58
+
59
+ export const emailInvoiceRoute = createRoute({
60
+ method: "post",
61
+ path: "/{id}/invoice/email",
62
+ tags: ["Documents"],
63
+ summary: "Email the order's invoice via the configured email adapter",
64
+ request: {
65
+ params: IdParam,
66
+ body: { content: { "application/json": { schema: EmailInvoiceBodySchema } }, required: true },
67
+ },
68
+ responses: {
69
+ 200: { content: { "application/json": { schema: DataResponse } }, description: "Invoice emailed." },
70
+ ...errorResponses,
71
+ },
72
+ });