@porulle/core 0.6.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 (301) 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 +122 -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 +1230 -2
  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 +4467 -7
  66. package/dist/interfaces/rest/schemas/orders.d.ts.map +1 -1
  67. package/dist/interfaces/rest/schemas/orders.js +330 -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/error-mapper.d.ts.map +1 -1
  88. package/dist/kernel/error-mapper.js +1 -0
  89. package/dist/kernel/errors.d.ts +5 -0
  90. package/dist/kernel/errors.d.ts.map +1 -1
  91. package/dist/kernel/errors.js +9 -0
  92. package/dist/kernel/hooks/types.d.ts +1 -1
  93. package/dist/kernel/hooks/types.d.ts.map +1 -1
  94. package/dist/kernel/plugin/manifest.d.ts +23 -0
  95. package/dist/kernel/plugin/manifest.d.ts.map +1 -1
  96. package/dist/kernel/plugin/manifest.js +17 -2
  97. package/dist/modules/analytics/module.d.ts.map +1 -1
  98. package/dist/modules/analytics/module.js +6 -0
  99. package/dist/modules/analytics/reports.d.ts +58 -0
  100. package/dist/modules/analytics/reports.d.ts.map +1 -0
  101. package/dist/modules/analytics/reports.js +249 -0
  102. package/dist/modules/analytics/service.d.ts +10 -0
  103. package/dist/modules/analytics/service.d.ts.map +1 -1
  104. package/dist/modules/analytics/service.js +14 -0
  105. package/dist/modules/cart/repository/index.d.ts +7 -0
  106. package/dist/modules/cart/repository/index.d.ts.map +1 -1
  107. package/dist/modules/cart/repository/index.js +25 -1
  108. package/dist/modules/cart/schema.d.ts +17 -0
  109. package/dist/modules/cart/schema.d.ts.map +1 -1
  110. package/dist/modules/cart/schema.js +2 -0
  111. package/dist/modules/cart/schemas.d.ts +1 -0
  112. package/dist/modules/cart/schemas.d.ts.map +1 -1
  113. package/dist/modules/cart/schemas.js +1 -0
  114. package/dist/modules/cart/service.d.ts +32 -0
  115. package/dist/modules/cart/service.d.ts.map +1 -1
  116. package/dist/modules/cart/service.js +58 -0
  117. package/dist/modules/catalog/entity-service.d.ts +21 -0
  118. package/dist/modules/catalog/entity-service.d.ts.map +1 -1
  119. package/dist/modules/catalog/entity-service.js +151 -6
  120. package/dist/modules/catalog/schema.d.ts +34 -0
  121. package/dist/modules/catalog/schema.d.ts.map +1 -1
  122. package/dist/modules/catalog/schema.js +5 -0
  123. package/dist/modules/catalog/schemas.d.ts +15 -1
  124. package/dist/modules/catalog/schemas.d.ts.map +1 -1
  125. package/dist/modules/catalog/schemas.js +17 -1
  126. package/dist/modules/catalog/service.d.ts +43 -1
  127. package/dist/modules/catalog/service.d.ts.map +1 -1
  128. package/dist/modules/catalog/service.js +6 -0
  129. package/dist/modules/documents/module.d.ts +7 -0
  130. package/dist/modules/documents/module.d.ts.map +1 -0
  131. package/dist/modules/documents/module.js +12 -0
  132. package/dist/modules/documents/pdf.d.ts +25 -0
  133. package/dist/modules/documents/pdf.d.ts.map +1 -0
  134. package/dist/modules/documents/pdf.js +80 -0
  135. package/dist/modules/documents/render.d.ts +32 -0
  136. package/dist/modules/documents/render.d.ts.map +1 -0
  137. package/dist/modules/documents/render.js +190 -0
  138. package/dist/modules/documents/repository/index.d.ts +25 -0
  139. package/dist/modules/documents/repository/index.d.ts.map +1 -0
  140. package/dist/modules/documents/repository/index.js +47 -0
  141. package/dist/modules/documents/schema.d.ts +214 -0
  142. package/dist/modules/documents/schema.d.ts.map +1 -0
  143. package/dist/modules/documents/schema.js +42 -0
  144. package/dist/modules/documents/service.d.ts +51 -0
  145. package/dist/modules/documents/service.d.ts.map +1 -0
  146. package/dist/modules/documents/service.js +119 -0
  147. package/dist/modules/fulfillment/service.d.ts +18 -0
  148. package/dist/modules/fulfillment/service.d.ts.map +1 -1
  149. package/dist/modules/fulfillment/service.js +85 -1
  150. package/dist/modules/media/service.d.ts +18 -0
  151. package/dist/modules/media/service.d.ts.map +1 -1
  152. package/dist/modules/media/service.js +26 -0
  153. package/dist/modules/orders/repository/index.d.ts +18 -1
  154. package/dist/modules/orders/repository/index.d.ts.map +1 -1
  155. package/dist/modules/orders/repository/index.js +86 -1
  156. package/dist/modules/orders/schema.d.ts +374 -0
  157. package/dist/modules/orders/schema.d.ts.map +1 -1
  158. package/dist/modules/orders/schema.js +52 -1
  159. package/dist/modules/orders/service.d.ts +102 -2
  160. package/dist/modules/orders/service.d.ts.map +1 -1
  161. package/dist/modules/orders/service.js +494 -23
  162. package/dist/modules/pricing/repository/index.d.ts +5 -0
  163. package/dist/modules/pricing/repository/index.d.ts.map +1 -1
  164. package/dist/modules/pricing/repository/index.js +19 -0
  165. package/dist/modules/pricing/service.d.ts +18 -0
  166. package/dist/modules/pricing/service.d.ts.map +1 -1
  167. package/dist/modules/pricing/service.js +57 -1
  168. package/dist/modules/settings/module.d.ts +6 -0
  169. package/dist/modules/settings/module.d.ts.map +1 -0
  170. package/dist/modules/settings/module.js +11 -0
  171. package/dist/modules/settings/repository/index.d.ts +16 -0
  172. package/dist/modules/settings/repository/index.d.ts.map +1 -0
  173. package/dist/modules/settings/repository/index.js +41 -0
  174. package/dist/modules/settings/schema.d.ts +121 -0
  175. package/dist/modules/settings/schema.d.ts.map +1 -0
  176. package/dist/modules/settings/schema.js +24 -0
  177. package/dist/modules/settings/service.d.ts +26 -0
  178. package/dist/modules/settings/service.d.ts.map +1 -0
  179. package/dist/modules/settings/service.js +51 -0
  180. package/dist/modules/shipping/calculator.d.ts +5 -0
  181. package/dist/modules/shipping/calculator.d.ts.map +1 -1
  182. package/dist/modules/shipping/calculator.js +10 -0
  183. package/dist/modules/shipping/module.d.ts +5 -1
  184. package/dist/modules/shipping/module.d.ts.map +1 -1
  185. package/dist/modules/shipping/module.js +4 -1
  186. package/dist/modules/shipping/repository/index.d.ts +27 -2
  187. package/dist/modules/shipping/repository/index.d.ts.map +1 -1
  188. package/dist/modules/shipping/repository/index.js +105 -1
  189. package/dist/modules/shipping/schema.d.ts +419 -0
  190. package/dist/modules/shipping/schema.d.ts.map +1 -0
  191. package/dist/modules/shipping/schema.js +56 -0
  192. package/dist/modules/shipping/service.d.ts +54 -0
  193. package/dist/modules/shipping/service.d.ts.map +1 -1
  194. package/dist/modules/shipping/service.js +161 -2
  195. package/dist/modules/tax/adapter.d.ts +13 -0
  196. package/dist/modules/tax/adapter.d.ts.map +1 -1
  197. package/dist/modules/tax/module.d.ts +5 -1
  198. package/dist/modules/tax/module.d.ts.map +1 -1
  199. package/dist/modules/tax/module.js +4 -1
  200. package/dist/modules/tax/repository/index.d.ts +38 -2
  201. package/dist/modules/tax/repository/index.d.ts.map +1 -1
  202. package/dist/modules/tax/repository/index.js +138 -1
  203. package/dist/modules/tax/schema.d.ts +355 -0
  204. package/dist/modules/tax/schema.d.ts.map +1 -0
  205. package/dist/modules/tax/schema.js +55 -0
  206. package/dist/modules/tax/service.d.ts +60 -1
  207. package/dist/modules/tax/service.d.ts.map +1 -1
  208. package/dist/modules/tax/service.js +227 -2
  209. package/dist/runtime/kernel-modules.d.ts +15 -2
  210. package/dist/runtime/kernel-modules.d.ts.map +1 -1
  211. package/dist/runtime/kernel-modules.js +4 -0
  212. package/dist/runtime/kernel-types.d.ts +5 -1
  213. package/dist/runtime/kernel-types.d.ts.map +1 -1
  214. package/dist/runtime/kernel-types.js +2 -0
  215. package/dist/runtime/server.d.ts.map +1 -1
  216. package/dist/runtime/server.js +35 -3
  217. package/dist/test-utils/create-plugin-test-app.d.ts +3 -0
  218. package/dist/test-utils/create-plugin-test-app.d.ts.map +1 -1
  219. package/dist/test-utils/create-plugin-test-app.js +15 -3
  220. package/dist/test-utils/rest-api-test-utils.d.ts.map +1 -1
  221. package/dist/test-utils/rest-api-test-utils.js +6 -0
  222. package/package.json +3 -3
  223. package/src/auth/setup.ts +1 -0
  224. package/src/config/defaults.ts +5 -0
  225. package/src/config/types.ts +17 -1
  226. package/src/generated/plugin-capabilities.d.ts +1 -0
  227. package/src/generated/plugin-manifest.ts +1 -0
  228. package/src/generated/plugin-repositories.d.ts +1 -0
  229. package/src/hooks/checkout.ts +25 -3
  230. package/src/index.ts +1 -0
  231. package/src/interfaces/rest/index.ts +12 -0
  232. package/src/interfaces/rest/routes/admin/staff.ts +218 -0
  233. package/src/interfaces/rest/routes/analytics.ts +33 -0
  234. package/src/interfaces/rest/routes/carts.ts +52 -2
  235. package/src/interfaces/rest/routes/catalog.ts +34 -0
  236. package/src/interfaces/rest/routes/checkout.ts +22 -0
  237. package/src/interfaces/rest/routes/documents.ts +61 -0
  238. package/src/interfaces/rest/routes/orders.ts +172 -1
  239. package/src/interfaces/rest/routes/pricing.ts +44 -1
  240. package/src/interfaces/rest/routes/settings.ts +61 -0
  241. package/src/interfaces/rest/routes/shipping.ts +92 -0
  242. package/src/interfaces/rest/routes/tax.ts +90 -0
  243. package/src/interfaces/rest/schemas/admin-staff.ts +110 -0
  244. package/src/interfaces/rest/schemas/analytics.ts +39 -0
  245. package/src/interfaces/rest/schemas/carts.ts +40 -0
  246. package/src/interfaces/rest/schemas/catalog.ts +53 -0
  247. package/src/interfaces/rest/schemas/checkout.ts +4 -0
  248. package/src/interfaces/rest/schemas/documents.ts +72 -0
  249. package/src/interfaces/rest/schemas/orders.ts +361 -0
  250. package/src/interfaces/rest/schemas/pricing.ts +75 -0
  251. package/src/interfaces/rest/schemas/settings.ts +93 -0
  252. package/src/interfaces/rest/schemas/shipping.ts +164 -0
  253. package/src/interfaces/rest/schemas/tax.ts +148 -0
  254. package/src/kernel/database/schema.ts +12 -0
  255. package/src/kernel/error-mapper.ts +1 -0
  256. package/src/kernel/errors.ts +11 -0
  257. package/src/kernel/hooks/types.ts +1 -0
  258. package/src/kernel/plugin/manifest.ts +38 -2
  259. package/src/modules/analytics/module.ts +10 -0
  260. package/src/modules/analytics/reports.ts +311 -0
  261. package/src/modules/analytics/service.ts +23 -0
  262. package/src/modules/cart/repository/index.ts +30 -1
  263. package/src/modules/cart/schema.ts +2 -0
  264. package/src/modules/cart/schemas.ts +1 -0
  265. package/src/modules/cart/service.ts +101 -0
  266. package/src/modules/catalog/entity-service.ts +195 -6
  267. package/src/modules/catalog/schema.ts +5 -0
  268. package/src/modules/catalog/schemas.ts +18 -1
  269. package/src/modules/catalog/service.ts +32 -2
  270. package/src/modules/documents/module.ts +19 -0
  271. package/src/modules/documents/pdf.ts +101 -0
  272. package/src/modules/documents/render.ts +231 -0
  273. package/src/modules/documents/repository/index.ts +68 -0
  274. package/src/modules/documents/schema.ts +44 -0
  275. package/src/modules/documents/service.ts +176 -0
  276. package/src/modules/fulfillment/service.ts +139 -1
  277. package/src/modules/media/service.ts +40 -0
  278. package/src/modules/orders/repository/index.ts +127 -1
  279. package/src/modules/orders/schema.ts +54 -1
  280. package/src/modules/orders/service.ts +739 -24
  281. package/src/modules/pricing/repository/index.ts +34 -0
  282. package/src/modules/pricing/service.ts +100 -1
  283. package/src/modules/settings/module.ts +18 -0
  284. package/src/modules/settings/repository/index.ts +59 -0
  285. package/src/modules/settings/schema.ts +25 -0
  286. package/src/modules/settings/service.ts +82 -0
  287. package/src/modules/shipping/calculator.ts +27 -0
  288. package/src/modules/shipping/module.ts +6 -2
  289. package/src/modules/shipping/repository/index.ts +136 -2
  290. package/src/modules/shipping/schema.ts +58 -0
  291. package/src/modules/shipping/service.ts +260 -1
  292. package/src/modules/tax/adapter.ts +9 -0
  293. package/src/modules/tax/module.ts +6 -2
  294. package/src/modules/tax/repository/index.ts +169 -2
  295. package/src/modules/tax/schema.ts +57 -0
  296. package/src/modules/tax/service.ts +313 -2
  297. package/src/runtime/kernel-modules.ts +4 -0
  298. package/src/runtime/kernel-types.ts +6 -0
  299. package/src/runtime/server.ts +36 -3
  300. package/src/test-utils/create-plugin-test-app.ts +17 -4
  301. package/src/test-utils/rest-api-test-utils.ts +6 -0
@@ -1,11 +1,53 @@
1
- import { CommerceValidationError } from "../../kernel/errors.js";
1
+ import { resolveOrgId } from "../../auth/org.js";
2
+ import { CommerceNotFoundError, CommerceValidationError } from "../../kernel/errors.js";
2
3
  import { Err, Ok } from "../../kernel/result.js";
3
4
  export class TaxService {
4
5
  adapter;
6
+ repository;
5
7
  constructor(deps) {
6
8
  this.adapter = deps.adapter;
9
+ this.repository = deps.repository;
7
10
  }
8
- async calculate(params) {
11
+ /**
12
+ * Calculates tax. Resolution order:
13
+ * 1. Product tax classes (issue #57) — when the org defines active classes,
14
+ * each line taxes by its class (variant taxClass beats entity taxClass;
15
+ * unclassed lines use the default class or 0), with `orderDiscount`
16
+ * pro-rated across lines first. Returns per-line results in `lines`.
17
+ * 2. Runtime tax rates for the org matching `toAddress` (issue #45) —
18
+ * state-specific matches beat country-level; matches at the winning
19
+ * specificity are summed (e.g. GST + PST).
20
+ * 3. The configured tax adapter.
21
+ * 4. Zero tax.
22
+ */
23
+ async calculate(params, orgId, ctx) {
24
+ if (this.repository && orgId && params.lineItems.length > 0) {
25
+ const classes = await this.repository.findActiveClasses(orgId, ctx);
26
+ if (classes.length > 0) {
27
+ return Ok(await this.calculateByClasses(params, classes, ctx));
28
+ }
29
+ }
30
+ if (this.repository && orgId && params.toAddress) {
31
+ const matched = await this.matchRuntimeRates(orgId, params.toAddress, ctx);
32
+ if (matched.length > 0) {
33
+ const taxableAmount = params.lineItems.reduce((sum, lineItem) => sum + lineItem.unitPrice * lineItem.quantity - (lineItem.discount ?? 0), 0);
34
+ let amountToCollect = 0;
35
+ const breakdown = {};
36
+ for (const rate of matched) {
37
+ const base = taxableAmount + (rate.appliesToShipping ? params.shippingAmount : 0);
38
+ const amount = Math.max(0, Math.round((base * rate.rateBps) / 10_000));
39
+ amountToCollect += amount;
40
+ breakdown[rate.name] = { rateBps: rate.rateBps, amount };
41
+ }
42
+ const totalBps = matched.reduce((sum, r) => sum + r.rateBps, 0);
43
+ return Ok({
44
+ amountToCollect,
45
+ taxableAmount,
46
+ rate: totalBps / 10_000,
47
+ breakdown,
48
+ });
49
+ }
50
+ }
9
51
  if (!this.adapter) {
10
52
  return Ok({
11
53
  amountToCollect: 0,
@@ -15,6 +57,123 @@ export class TaxService {
15
57
  }
16
58
  return this.adapter.calculateTax(params);
17
59
  }
60
+ /** Per-line class-based tax with cart-discount pro-ration (issue #57). */
61
+ async calculateByClasses(params, classes, ctx) {
62
+ const byName = new Map(classes.map((c) => [c.name, c]));
63
+ const defaultClass = classes.find((c) => c.isDefault) ?? null;
64
+ const { byEntity, byVariant } = await this.repository.resolveCatalogTaxClasses(params.lineItems.map((li) => ({ entityId: li.entityId, variantId: li.variantId })), ctx);
65
+ // Pro-rate the cart-level discount across lines by value share; the last
66
+ // line absorbs the rounding remainder so the sum stays exact.
67
+ const lineValues = params.lineItems.map((li) => Math.max(0, li.unitPrice * li.quantity - (li.discount ?? 0)));
68
+ const totalValue = lineValues.reduce((sum, v) => sum + v, 0);
69
+ const orderDiscount = Math.min(params.orderDiscount ?? 0, totalValue);
70
+ const prorated = lineValues.map((value) => totalValue > 0 ? Math.round((orderDiscount * value) / totalValue) : 0);
71
+ if (lineValues.length > 0) {
72
+ const drift = orderDiscount - prorated.reduce((sum, v) => sum + v, 0);
73
+ prorated[prorated.length - 1] += drift;
74
+ }
75
+ let amountToCollect = 0;
76
+ let taxableAmount = 0;
77
+ const breakdown = {};
78
+ const lines = [];
79
+ for (const [i, lineItem] of params.lineItems.entries()) {
80
+ const className = (lineItem.variantId ? byVariant.get(lineItem.variantId) : null) ??
81
+ byEntity.get(lineItem.entityId) ??
82
+ defaultClass?.name ??
83
+ null;
84
+ const taxClass = className ? (byName.get(className) ?? defaultClass) : defaultClass;
85
+ const base = Math.max(0, lineValues[i] - prorated[i]);
86
+ const taxAmount = taxClass
87
+ ? Math.max(0, Math.round((base * taxClass.rateBps) / 10_000))
88
+ : 0;
89
+ amountToCollect += taxAmount;
90
+ taxableAmount += base;
91
+ lines.push({ id: lineItem.id, taxClass: taxClass?.name ?? null, taxAmount });
92
+ if (taxClass) {
93
+ const entry = breakdown[taxClass.name] ?? {
94
+ rateBps: taxClass.rateBps,
95
+ amount: 0,
96
+ };
97
+ entry.amount += taxAmount;
98
+ breakdown[taxClass.name] = entry;
99
+ }
100
+ }
101
+ return {
102
+ amountToCollect,
103
+ taxableAmount,
104
+ rate: taxableAmount > 0 ? amountToCollect / taxableAmount : 0,
105
+ breakdown,
106
+ lines,
107
+ };
108
+ }
109
+ // ── Tax class management (issue #57) ─────────────────────────────────────
110
+ async createTaxClass(input, actor, ctx) {
111
+ const repo = this.requireRepository();
112
+ if (!repo.ok)
113
+ return repo;
114
+ if (input.rateBps < 0) {
115
+ return Err(new CommerceValidationError("rateBps must be non-negative."));
116
+ }
117
+ const orgId = resolveOrgId(actor ?? ctx?.actor ?? null);
118
+ if (input.isDefault)
119
+ await repo.value.clearDefaultClass(orgId, ctx);
120
+ const created = await repo.value.createClass({
121
+ organizationId: orgId,
122
+ name: input.name,
123
+ rateBps: input.rateBps,
124
+ isDefault: input.isDefault ?? false,
125
+ isActive: input.isActive ?? true,
126
+ }, ctx);
127
+ return Ok(created);
128
+ }
129
+ async listTaxClasses(actor, ctx) {
130
+ const repo = this.requireRepository();
131
+ if (!repo.ok)
132
+ return repo;
133
+ const orgId = resolveOrgId(actor ?? ctx?.actor ?? null);
134
+ return Ok(await repo.value.findAllClasses(orgId, ctx));
135
+ }
136
+ async updateTaxClass(id, patch, actor, ctx) {
137
+ const repo = this.requireRepository();
138
+ if (!repo.ok)
139
+ return repo;
140
+ const orgId = resolveOrgId(actor ?? ctx?.actor ?? null);
141
+ const existing = await repo.value.findClassById(orgId, id, ctx);
142
+ if (!existing)
143
+ return Err(new CommerceNotFoundError("Tax class not found."));
144
+ if (patch.isDefault)
145
+ await repo.value.clearDefaultClass(orgId, ctx);
146
+ const updated = await repo.value.updateClass(id, {
147
+ ...(patch.name !== undefined ? { name: patch.name } : {}),
148
+ ...(patch.rateBps !== undefined ? { rateBps: patch.rateBps } : {}),
149
+ ...(patch.isDefault !== undefined ? { isDefault: patch.isDefault } : {}),
150
+ ...(patch.isActive !== undefined ? { isActive: patch.isActive } : {}),
151
+ }, ctx);
152
+ return Ok(updated);
153
+ }
154
+ async deleteTaxClass(id, actor, ctx) {
155
+ const repo = this.requireRepository();
156
+ if (!repo.ok)
157
+ return repo;
158
+ const orgId = resolveOrgId(actor ?? ctx?.actor ?? null);
159
+ const existing = await repo.value.findClassById(orgId, id, ctx);
160
+ if (!existing)
161
+ return Err(new CommerceNotFoundError("Tax class not found."));
162
+ await repo.value.deleteClass(id, ctx);
163
+ return Ok({ deleted: true });
164
+ }
165
+ async matchRuntimeRates(orgId, toAddress, ctx) {
166
+ const rates = await this.repository.findActive(orgId, ctx);
167
+ if (rates.length === 0)
168
+ return [];
169
+ const country = toAddress.country?.toUpperCase();
170
+ const state = toAddress.state?.toUpperCase();
171
+ const countryMatches = rates.filter((r) => r.country === "*" || r.country.toUpperCase() === country);
172
+ const stateMatches = countryMatches.filter((r) => r.state != null && state != null && r.state.toUpperCase() === state);
173
+ if (stateMatches.length > 0)
174
+ return stateMatches;
175
+ return countryMatches.filter((r) => r.state == null);
176
+ }
18
177
  async reportTransaction(params) {
19
178
  if (!this.adapter)
20
179
  return Ok({ transactionId: params.transactionId });
@@ -31,4 +190,70 @@ export class TaxService {
31
190
  }
32
191
  return Ok(this.adapter);
33
192
  }
193
+ // ── Runtime tax-rate management (issue #45) ────────────────────────────
194
+ requireRepository() {
195
+ if (!this.repository) {
196
+ return Err(new CommerceValidationError("Tax rate persistence is not available."));
197
+ }
198
+ return Ok(this.repository);
199
+ }
200
+ async createTaxRate(input, actor, ctx) {
201
+ const repo = this.requireRepository();
202
+ if (!repo.ok)
203
+ return repo;
204
+ if (input.rateBps < 0) {
205
+ return Err(new CommerceValidationError("rateBps must be non-negative."));
206
+ }
207
+ const orgId = resolveOrgId(actor ?? ctx?.actor ?? null);
208
+ const rate = await repo.value.create({
209
+ organizationId: orgId,
210
+ name: input.name,
211
+ country: input.country.toUpperCase(),
212
+ state: input.state?.toUpperCase() ?? null,
213
+ rateBps: input.rateBps,
214
+ appliesToShipping: input.appliesToShipping ?? true,
215
+ priority: input.priority ?? 100,
216
+ isActive: input.isActive ?? true,
217
+ }, ctx);
218
+ return Ok(rate);
219
+ }
220
+ async listTaxRates(actor, ctx) {
221
+ const repo = this.requireRepository();
222
+ if (!repo.ok)
223
+ return repo;
224
+ const orgId = resolveOrgId(actor ?? ctx?.actor ?? null);
225
+ return Ok(await repo.value.findAll(orgId, ctx));
226
+ }
227
+ async updateTaxRate(id, patch, actor, ctx) {
228
+ const repo = this.requireRepository();
229
+ if (!repo.ok)
230
+ return repo;
231
+ const orgId = resolveOrgId(actor ?? ctx?.actor ?? null);
232
+ const existing = await repo.value.findById(orgId, id, ctx);
233
+ if (!existing)
234
+ return Err(new CommerceNotFoundError("Tax rate not found."));
235
+ const updated = await repo.value.update(id, {
236
+ ...(patch.name !== undefined ? { name: patch.name } : {}),
237
+ ...(patch.country !== undefined ? { country: patch.country.toUpperCase() } : {}),
238
+ ...(patch.state !== undefined ? { state: patch.state?.toUpperCase() ?? null } : {}),
239
+ ...(patch.rateBps !== undefined ? { rateBps: patch.rateBps } : {}),
240
+ ...(patch.appliesToShipping !== undefined
241
+ ? { appliesToShipping: patch.appliesToShipping }
242
+ : {}),
243
+ ...(patch.priority !== undefined ? { priority: patch.priority } : {}),
244
+ ...(patch.isActive !== undefined ? { isActive: patch.isActive } : {}),
245
+ }, ctx);
246
+ return Ok(updated);
247
+ }
248
+ async deleteTaxRate(id, actor, ctx) {
249
+ const repo = this.requireRepository();
250
+ if (!repo.ok)
251
+ return repo;
252
+ const orgId = resolveOrgId(actor ?? ctx?.actor ?? null);
253
+ const existing = await repo.value.findById(orgId, id, ctx);
254
+ if (!existing)
255
+ return Err(new CommerceNotFoundError("Tax rate not found."));
256
+ await repo.value.delete(id, ctx);
257
+ return Ok({ deleted: true });
258
+ }
34
259
  }
@@ -179,6 +179,13 @@ export declare const KERNEL_ALL_MODULES: {
179
179
  dialect: "pg";
180
180
  }>;
181
181
  }, import("../index.js").AuditService, Record<string, unknown>>;
182
+ readonly settings: AppModule<{
183
+ storeSettings: typeof import("../modules/settings/schema.js").storeSettings;
184
+ }, import("../modules/settings/service.js").SettingsService, Record<string, never>>;
185
+ readonly documents: AppModule<{
186
+ invoiceSequences: typeof import("../modules/documents/schema.js").invoiceSequences;
187
+ orderDocuments: typeof import("../modules/documents/schema.js").orderDocuments;
188
+ }, import("../modules/documents/service.js").DocumentsService, Record<string, never>>;
182
189
  readonly webhooks: AppModule<{
183
190
  webhookEndpoints: import("drizzle-orm/pg-core").PgTableWithColumns<{
184
191
  name: "webhook_endpoints";
@@ -1701,10 +1708,16 @@ export declare const KERNEL_ALL_MODULES: {
1701
1708
  readonly search: AppModule<Record<string, never>, import("../modules/search/service.js").SearchService, {
1702
1709
  catalog: import("../modules/catalog/service.js").CatalogServiceImpl;
1703
1710
  }>;
1704
- readonly shipping: AppModule<Record<string, never>, import("../modules/shipping/service.js").ShippingService, {
1711
+ readonly shipping: AppModule<{
1712
+ shippingZones: typeof import("../modules/shipping/schema.js").shippingZones;
1713
+ shippingRates: typeof import("../modules/shipping/schema.js").shippingRates;
1714
+ }, import("../modules/shipping/service.js").ShippingService, {
1705
1715
  catalog: import("../modules/catalog/service.js").CatalogServiceImpl;
1706
1716
  }>;
1707
- readonly tax: AppModule<Record<string, never>, import("../modules/tax/service.js").TaxService, Record<string, never>>;
1717
+ readonly tax: AppModule<{
1718
+ taxRates: typeof import("../modules/tax/schema.js").taxRates;
1719
+ taxClasses: typeof import("../modules/tax/schema.js").taxClasses;
1720
+ }, import("../modules/tax/service.js").TaxService, Record<string, never>>;
1708
1721
  readonly payments: AppModule<Record<string, never>, import("../modules/payments/service.js").PaymentsService, Record<string, never>>;
1709
1722
  readonly analytics: AppModule<Record<string, never>, import("../modules/analytics/service.js").AnalyticsService, Record<string, never>>;
1710
1723
  };
@@ -1 +1 @@
1
- {"version":3,"file":"kernel-modules.d.ts","sourceRoot":"","sources":["../../src/runtime/kernel-modules.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAoB3D,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkBrB,CAAC;AAEX,2FAA2F;AAC3F,wBAAgB,wBAAwB,IAAI,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAoBpE;AAED,wBAAgB,8BAA8B,IAAI,SAAS,MAAM,EAAE,CAElE"}
1
+ {"version":3,"file":"kernel-modules.d.ts","sourceRoot":"","sources":["../../src/runtime/kernel-modules.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAsB3D,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoBrB,CAAC;AAEX,2FAA2F;AAC3F,wBAAgB,wBAAwB,IAAI,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAoBpE;AAED,wBAAgB,8BAA8B,IAAI,SAAS,MAAM,EAAE,CAElE"}
@@ -4,6 +4,7 @@ import { analyticsModule } from "../modules/analytics/module.js";
4
4
  import { cartModule } from "../modules/cart/module.js";
5
5
  import { catalogModule } from "../modules/catalog/module.js";
6
6
  import { customersModule } from "../modules/customers/index.js";
7
+ import { documentsModule } from "../modules/documents/module.js";
7
8
  import { fulfillmentModule } from "../modules/fulfillment/module.js";
8
9
  import { inventoryModule } from "../modules/inventory/module.js";
9
10
  import { mediaModule } from "../modules/media/index.js";
@@ -13,11 +14,14 @@ import { paymentsModule } from "../modules/payments/module.js";
13
14
  import { pricingModule } from "../modules/pricing/index.js";
14
15
  import { promotionsModule } from "../modules/promotions/module.js";
15
16
  import { searchModule } from "../modules/search/module.js";
17
+ import { settingsModule } from "../modules/settings/module.js";
16
18
  import { shippingModule } from "../modules/shipping/module.js";
17
19
  import { taxModule } from "../modules/tax/module.js";
18
20
  import { webhooksModule } from "../modules/webhooks/index.js";
19
21
  export const KERNEL_ALL_MODULES = {
20
22
  audit: auditModule,
23
+ settings: settingsModule,
24
+ documents: documentsModule,
21
25
  webhooks: webhooksModule,
22
26
  media: mediaModule,
23
27
  organization: organizationModule,
@@ -16,6 +16,8 @@ import { PromotionService } from "../modules/promotions/service.js";
16
16
  import { TaxService } from "../modules/tax/service.js";
17
17
  import { ShippingService } from "../modules/shipping/service.js";
18
18
  import { SearchService } from "../modules/search/service.js";
19
+ import { SettingsService } from "../modules/settings/service.js";
20
+ import { DocumentsService } from "../modules/documents/service.js";
19
21
  import type { AuditService } from "../modules/audit/service.js";
20
22
  import { OrganizationService } from "../modules/organization/service.js";
21
23
  import { createLogger } from "../utils/logger.js";
@@ -51,6 +53,8 @@ export interface Kernel {
51
53
  tax: TaxService;
52
54
  shipping: ShippingService;
53
55
  search: SearchService;
56
+ settings: SettingsService;
57
+ documents: DocumentsService;
54
58
  audit: AuditService;
55
59
  compensationFailures: CompensationFailuresRepository;
56
60
  email: CommerceConfig["email"];
@@ -59,7 +63,7 @@ export interface Kernel {
59
63
  pluginPermissions: PluginPermission[];
60
64
  logger: ReturnType<typeof createLogger>;
61
65
  }
62
- export declare const KERNEL_REQUIRED_SERVICE_KEYS: ["catalog", "inventory", "media", "cart", "orders", "payments", "fulfillment", "customers", "webhooks", "analytics", "pricing", "promotions", "tax", "shipping", "search", "audit", "compensationFailures"];
66
+ export declare const KERNEL_REQUIRED_SERVICE_KEYS: ["catalog", "inventory", "media", "cart", "orders", "payments", "fulfillment", "customers", "webhooks", "analytics", "pricing", "promotions", "tax", "shipping", "search", "settings", "documents", "audit", "compensationFailures"];
63
67
  export declare function assertKernelServicesReady(services: Partial<Kernel["services"]>): asserts services is Kernel["services"];
64
68
  export declare function assertSortedBefore(topo: readonly string[], a: string, b: string): void;
65
69
  //# sourceMappingURL=kernel-types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"kernel-types.d.ts","sourceRoot":"","sources":["../../src/runtime/kernel-types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"kernel-types.d.ts","sourceRoot":"","sources":["../../src/runtime/kernel-types.ts"],"names":[],"mappings":"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"}
@@ -14,6 +14,8 @@ export const KERNEL_REQUIRED_SERVICE_KEYS = [
14
14
  "tax",
15
15
  "shipping",
16
16
  "search",
17
+ "settings",
18
+ "documents",
17
19
  "audit",
18
20
  "compensationFailures",
19
21
  ];
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/runtime/server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAOhD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAMzD,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AAExD,OAAO,EAAkB,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAItE,KAAK,SAAS,GAAG;IACf,SAAS,EAAE;QACT,IAAI,EAAE,YAAY,CAAC;QACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAMvC;AAeD;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,cAAc;;;;;sBAwVxB,MAAM,UAAU,MAAM;;;;GAgFtD"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/runtime/server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAQhD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAOzD,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AAExD,OAAO,EAAkB,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAItE,KAAK,SAAS,GAAG;IACf,SAAS,EAAE;QACT,IAAI,EAAE,YAAY,CAAC;QACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAMvC;AAeD;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,cAAc;;;;;sBAuXxB,MAAM,UAAU,MAAM;;;;GAgFtD"}
@@ -1,11 +1,13 @@
1
1
  import { OpenAPIHono } from "@hono/zod-openapi";
2
2
  import { cors } from "hono/cors";
3
3
  import { csrf } from "hono/csrf";
4
+ import { HTTPException } from "hono/http-exception";
4
5
  import { bodyLimit } from "hono/body-limit";
5
6
  import { rateLimiter } from "hono-rate-limiter";
6
7
  import { createHash } from "node:crypto";
7
8
  import { createClientIpResolver } from "./client-ip.js";
8
9
  import { authMiddleware } from "../auth/middleware.js";
10
+ import { CommerceCsrfError } from "../kernel/errors.js";
9
11
  import { createRestRoutes } from "../interfaces/rest/index.js";
10
12
  import { createCustomerPortalRoutes } from "../interfaces/rest/customer-portal.js";
11
13
  import { createCommerce } from "./commerce.js";
@@ -138,11 +140,41 @@ export async function createServer(config) {
138
140
  maxAge: 86400,
139
141
  }));
140
142
  // ─── CSRF Protection (F14) ──────────────────────────────────────────
141
- app.use("/api/*", csrf({
143
+ // CSRF defends cookie/session auth, where the browser attaches the credential
144
+ // ambiently. API-key (x-api-key) and bearer-token requests carry an explicit,
145
+ // non-ambient credential and are not CSRF-attackable, so the guard is skipped
146
+ // for them — otherwise a bodyless server-to-server POST (no Origin, no JSON
147
+ // content-type, e.g. /publish or /archive from the SDK) trips CSRF and 403s
148
+ // for no security benefit.
149
+ const csrfGuard = csrf({
142
150
  origin: trustedOrigins.length > 0
143
151
  ? trustedOrigins
144
152
  : (process.env.NODE_ENV === "production" ? [] : ["http://localhost:*"]),
145
- }));
153
+ });
154
+ app.use("/api/*", async (c, next) => {
155
+ const authenticatedByKey = !!c.req.header("x-api-key") ||
156
+ /^Bearer\s+/i.test(c.req.header("authorization") ?? "");
157
+ if (authenticatedByKey)
158
+ return next();
159
+ // Run only the CSRF origin check here; invoke the real downstream afterwards
160
+ // so a genuine 403 from a route handler can't be misattributed to CSRF.
161
+ let passedCsrf = false;
162
+ try {
163
+ await csrfGuard(c, async () => {
164
+ passedCsrf = true;
165
+ });
166
+ }
167
+ catch (err) {
168
+ if (err instanceof HTTPException && err.status === 403) {
169
+ throw new CommerceCsrfError("Origin check failed: the request Origin is not in the trusted origins allowlist. " +
170
+ "Browser clients must send a trusted Origin; server-to-server callers should authenticate with an API key (x-api-key).");
171
+ }
172
+ throw err;
173
+ }
174
+ if (!passedCsrf)
175
+ return;
176
+ return next();
177
+ });
146
178
  // ─── Body Size Limit (F6) ──────────────────────────────────────────
147
179
  // Media uploads (phone photos are 3–8MB) get their own larger limit and are
148
180
  // exempt from the global 1MB limit. Everything else stays at 1MB.
@@ -249,7 +281,7 @@ export async function createServer(config) {
249
281
  // body-limit stack apply (all are scoped to /api/*).
250
282
  app.route("/api/me", createCustomerPortalRoutes(kernel));
251
283
  if (config.routes) {
252
- config.routes(app, kernel);
284
+ config.routes(app, kernel, auth);
253
285
  }
254
286
  // OpenAPI spec — disabled in production unless explicitly enabled
255
287
  const exposeSpec = config.exposeOpenApiSpec ?? !isProd;
@@ -21,6 +21,7 @@ import type { PgDatabase, PgQueryResultHKT } from "drizzle-orm/pg-core";
21
21
  import type { CommerceConfig, CommercePlugin } from "../config/types.js";
22
22
  import type { Actor } from "../auth/types.js";
23
23
  import type { Kernel } from "../runtime/kernel.js";
24
+ import { type AuthInstance } from "../auth/setup.js";
24
25
  /**
25
26
  * Hono environment type for the test app. Declares the `actor` context
26
27
  * variable so c.set("actor", ...) / c.get("actor") are properly typed
@@ -38,6 +39,8 @@ export interface PluginTestApp {
38
39
  kernel: Kernel;
39
40
  /** Drizzle database instance for direct queries in test assertions. */
40
41
  db: PgDatabase<PgQueryResultHKT, Record<string, unknown>>;
42
+ /** The Better Auth instance passed to plugin routes (mint/verify API keys in tests). */
43
+ auth: AuthInstance;
41
44
  }
42
45
  /**
43
46
  * Creates a fully-wired test application for plugin E2E testing.
@@ -1 +1 @@
1
- {"version":3,"file":"create-plugin-test-app.d.ts","sourceRoot":"","sources":["../../src/test-utils/create-plugin-test-app.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAG9C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAgBnD;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,SAAS,EAAE;QACT,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;KACrB,CAAC;CACH,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,wFAAwF;IACxF,GAAG,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC;IAC7B,6DAA6D;IAC7D,MAAM,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,EAAE,EAAE,UAAU,CAAC,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAC3D;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,cAAc,EACtB,eAAe,GAAE,OAAO,CAAC,cAAc,CAAM,GAC5C,OAAO,CAAC,aAAa,CAAC,CAgFxB"}
1
+ {"version":3,"file":"create-plugin-test-app.d.ts","sourceRoot":"","sources":["../../src/test-utils/create-plugin-test-app.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAG9C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAKnD,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAajE;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,SAAS,EAAE;QACT,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;KACrB,CAAC;CACH,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,wFAAwF;IACxF,GAAG,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC;IAC7B,6DAA6D;IAC7D,MAAM,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,EAAE,EAAE,UAAU,CAAC,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC1D,wFAAwF;IACxF,IAAI,EAAE,YAAY,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,cAAc,EACtB,eAAe,GAAE,OAAO,CAAC,cAAc,CAAM,GAC5C,OAAO,CAAC,aAAa,CAAC,CAyFxB"}
@@ -24,6 +24,8 @@ import { buildSchema } from "../kernel/database/migrate.js";
24
24
  import { unwrapDb } from "../kernel/database/adapter.js";
25
25
  import { ensureDefaultOrg } from "../auth/org.js";
26
26
  import { mapErrorToStatus } from "../kernel/error-mapper.js";
27
+ import { createAuth } from "../auth/setup.js";
28
+ import { authMiddleware } from "../auth/middleware.js";
27
29
  // drizzle-kit/api uses CJS internally; createRequire provides ESM compat.
28
30
  const require = createRequire(import.meta.url);
29
31
  /**
@@ -56,7 +58,10 @@ export async function createPluginTestApp(plugin, configOverrides = {}) {
56
58
  // 5. Create OpenAPIHono --- matching production server.ts
57
59
  // Plugin routes register via manifest.ts which calls app.openapi().
58
60
  const app = new OpenAPIHono();
59
- // 6. Test actor middleware: parse x-test-actor header -> set on context
61
+ const auth = createAuth(kernel.database, config);
62
+ // 6. Test actor middleware: parse x-test-actor header -> set on context.
63
+ // Requests without the header fall through to the real auth middleware,
64
+ // so API keys minted by plugins (issue #51) authenticate like production.
60
65
  app.use("*", async (c, next) => {
61
66
  const header = c.req.header("x-test-actor");
62
67
  if (header) {
@@ -67,9 +72,15 @@ export async function createPluginTestApp(plugin, configOverrides = {}) {
67
72
  }
68
73
  await next();
69
74
  });
70
- // 7. Register plugin routes (deferred via config.routes)
75
+ app.use("*", async (c, next) => {
76
+ if (c.get("actor"))
77
+ return next();
78
+ return authMiddleware(auth, config)(c, next);
79
+ });
80
+ // 7. Register plugin routes (deferred via config.routes) — with the auth
81
+ // instance, matching server.ts's config.routes(app, kernel, auth).
71
82
  const routes = config.routes;
72
- routes?.(app, kernel);
83
+ routes?.(app, kernel, auth);
73
84
  // 8. Error handler matching production server.ts — maps CommerceError
74
85
  // subclasses (CommerceForbiddenError, CommerceNotFoundError, etc.)
75
86
  // to their proper HTTP status codes (403, 404, 409, 422, 503).
@@ -89,5 +100,6 @@ export async function createPluginTestApp(plugin, configOverrides = {}) {
89
100
  app,
90
101
  kernel,
91
102
  db: kernel.database.db,
103
+ auth,
92
104
  };
93
105
  }
@@ -1 +1 @@
1
- {"version":3,"file":"rest-api-test-utils.d.ts","sourceRoot":"","sources":["../../src/test-utils/rest-api-test-utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAOpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,KAAK,SAAS,GAAG;IACf,SAAS,EAAE;QACT,IAAI,EAAE,YAAY,CAAC;QACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;KACrB,CAAC;CACH,CAAC;AAiDF;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,SAAS,GAAE,OAAO,CAAC,cAAc,CAAM,GACtC,OAAO,CAAC;IACT,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IACxB,MAAM,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;IACxC,IAAI,EAAE,YAAY,CAAC;IACnB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAAC,CAqDD;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,CAAC,GAAG,OAAO,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,CAEnF;AAED;;GAEG;AACH,eAAO,MAAM,SAAS,EAAE,KA6BvB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,KAS3B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,WAAW,EAAE,KASzB,CAAC;AAEF;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE;IAClE,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,KAAK,CAAC;CACf,WAwBA;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,EACvB,OAAO,EAAE;IACP,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,KAAK,CAAC;CACf,qBAYF"}
1
+ {"version":3,"file":"rest-api-test-utils.d.ts","sourceRoot":"","sources":["../../src/test-utils/rest-api-test-utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAOpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,KAAK,SAAS,GAAG;IACf,SAAS,EAAE;QACT,IAAI,EAAE,YAAY,CAAC;QACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;KACrB,CAAC;CACH,CAAC;AAiDF;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,SAAS,GAAE,OAAO,CAAC,cAAc,CAAM,GACtC,OAAO,CAAC;IACT,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IACxB,MAAM,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;IACxC,IAAI,EAAE,YAAY,CAAC;IACnB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAAC,CAqDD;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,CAAC,GAAG,OAAO,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,CAEnF;AAED;;GAEG;AACH,eAAO,MAAM,SAAS,EAAE,KAmCvB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,KAS3B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,WAAW,EAAE,KASzB,CAAC;AAEF;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE;IAClE,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,KAAK,CAAC;CACf,WAwBA;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,EACvB,OAAO,EAAE;IACP,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,KAAK,CAAC;CACf,qBAYF"}
@@ -133,9 +133,15 @@ export const testActor = {
133
133
  "cart:create",
134
134
  "cart:update",
135
135
  "cart:read",
136
+ "cart:manage",
136
137
  "customers:update:self",
137
138
  "webhooks:manage",
138
139
  "pricing:manage",
140
+ "shipping:manage",
141
+ "tax:manage",
142
+ "settings:manage",
143
+ "analytics:read",
144
+ "staff:manage",
139
145
  "promotions:manage",
140
146
  "promotions:read",
141
147
  "audit:read",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@porulle/core",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -56,8 +56,8 @@
56
56
  "eslint": "^9.39.1",
57
57
  "typescript": "5.9.2",
58
58
  "vitest": "^3.2.4",
59
- "@porulle/eslint-config": "0.1.0",
60
- "@porulle/typescript-config": "0.1.0"
59
+ "@porulle/typescript-config": "0.1.0",
60
+ "@porulle/eslint-config": "0.1.0"
61
61
  },
62
62
  "publishConfig": {
63
63
  "access": "public"
package/src/auth/setup.ts CHANGED
@@ -90,6 +90,7 @@ export function createAuth(
90
90
  },
91
91
  }
92
92
  : {}),
93
+ ...(scope.keyExpiration ? { keyExpiration: scope.keyExpiration } : {}),
93
94
  }));
94
95
  plugins.push(apiKey(apiKeyConfigs));
95
96
  } else if (config.auth?.apiKeys?.enabled) {
@@ -30,6 +30,11 @@ export const defaultConfig: Partial<CommerceConfig> = {
30
30
  "orders:update",
31
31
  "cart:create",
32
32
  "cart:update",
33
+ "cart:manage",
34
+ "shipping:manage",
35
+ "tax:manage",
36
+ "settings:manage",
37
+ "analytics:read",
33
38
  "customers:read:self",
34
39
  "customers:update:self",
35
40
  ],
@@ -4,6 +4,7 @@ import type { BeforeHook, AfterHook } from "../kernel/hooks/types.js";
4
4
  import type { PaymentAdapter } from "../modules/payments/adapter.js";
5
5
  import type { StorageAdapter } from "../modules/media/adapter.js";
6
6
  import type { Kernel } from "../runtime/kernel.js";
7
+ import type { AuthInstance } from "../auth/setup.js";
7
8
  import type { DatabaseAdapter } from "../kernel/database/adapter.js";
8
9
  import type { TaxAdapter } from "../modules/tax/adapter.js";
9
10
  import type { SearchAdapter } from "../modules/search/adapter.js";
@@ -102,6 +103,16 @@ export interface ApiKeyScopeDefinition {
102
103
  /** Time window in milliseconds. */
103
104
  timeWindow: number;
104
105
  };
106
+ /**
107
+ * Expiry bounds for keys minted under this scope, in DAYS (Better Auth
108
+ * semantics; fractions allowed — 1/24 = one hour). Better Auth defaults
109
+ * minExpiresIn to 1 day, which blocks short-lived credentials like
110
+ * per-shift POS keys (issue #51).
111
+ */
112
+ keyExpiration?: {
113
+ minExpiresIn?: number;
114
+ maxExpiresIn?: number;
115
+ };
105
116
  }
106
117
 
107
118
  export interface AuthConfig {
@@ -374,7 +385,12 @@ export interface CommerceConfig {
374
385
  hooks?: Record<string, Array<(...args: unknown[]) => unknown>>;
375
386
  plugins?: CommercePlugin[];
376
387
  middleware?: MiddlewareHandler[];
377
- routes?: (app: Hono<any>, kernel: Kernel) => void;
388
+ /**
389
+ * Custom route registration. Receives the Better Auth instance as the
390
+ * third argument so integrations can mint/revoke API keys or call
391
+ * `auth.api.*` without module-global holder shims.
392
+ */
393
+ routes?: (app: Hono<any>, kernel: Kernel, auth?: AuthInstance) => void;
378
394
  /** Log level for structured logging. Default: "info". */
379
395
  logLevel?: "fatal" | "error" | "warn" | "info" | "debug" | "trace";
380
396
  /**
@@ -4,6 +4,7 @@
4
4
  export interface PluginCapabilityRegistryShape {
5
5
  "appointments": Record<string, unknown>;
6
6
  "gift-cards": Record<string, unknown>;
7
+ "layaway": Record<string, unknown>;
7
8
  "loyalty": Record<string, unknown>;
8
9
  "marketplace": Record<string, unknown>;
9
10
  "notifications": Record<string, unknown>;
@@ -5,6 +5,7 @@
5
5
  export const resolvedPluginManifest = [
6
6
  { id: "appointments", directory: "plugin-appointments" },
7
7
  { id: "gift-cards", directory: "plugin-gift-cards" },
8
+ { id: "layaway", directory: "plugin-layaway" },
8
9
  { id: "loyalty", directory: "plugin-loyalty" },
9
10
  { id: "marketplace", directory: "plugin-marketplace" },
10
11
  { id: "notifications", directory: "plugin-notifications" },