@porulle/core 0.10.8 → 0.13.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 (360) hide show
  1. package/dist/auth/auth-schema-guard.d.ts +2444 -0
  2. package/dist/auth/auth-schema-guard.d.ts.map +1 -0
  3. package/dist/auth/auth-schema-guard.js +113 -0
  4. package/dist/auth/auth-schema.d.ts +228 -0
  5. package/dist/auth/auth-schema.d.ts.map +1 -1
  6. package/dist/auth/auth-schema.js +24 -2
  7. package/dist/auth/middleware.d.ts +1 -0
  8. package/dist/auth/middleware.d.ts.map +1 -1
  9. package/dist/auth/middleware.js +13 -5
  10. package/dist/auth/org.d.ts +8 -2
  11. package/dist/auth/org.d.ts.map +1 -1
  12. package/dist/auth/org.js +12 -4
  13. package/dist/auth/organization-guard.d.ts +5 -0
  14. package/dist/auth/organization-guard.d.ts.map +1 -0
  15. package/dist/auth/organization-guard.js +124 -0
  16. package/dist/auth/permissions.d.ts +10 -0
  17. package/dist/auth/permissions.d.ts.map +1 -1
  18. package/dist/auth/permissions.js +31 -7
  19. package/dist/auth/role-authority.d.ts +44 -0
  20. package/dist/auth/role-authority.d.ts.map +1 -0
  21. package/dist/auth/role-authority.js +85 -0
  22. package/dist/auth/setup.d.ts.map +1 -1
  23. package/dist/auth/setup.js +17 -0
  24. package/dist/auth/strict-org-resolution.d.ts.map +1 -1
  25. package/dist/auth/strict-org-resolution.js +9 -1
  26. package/dist/auth/types.d.ts +1 -1
  27. package/dist/auth/types.d.ts.map +1 -1
  28. package/dist/config/defaults.d.ts.map +1 -1
  29. package/dist/config/defaults.js +2 -0
  30. package/dist/config/types.d.ts +33 -4
  31. package/dist/config/types.d.ts.map +1 -1
  32. package/dist/hooks/checkout.d.ts.map +1 -1
  33. package/dist/hooks/checkout.js +10 -9
  34. package/dist/index.d.ts +9 -6
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +5 -3
  37. package/dist/interfaces/rest/audit-middleware.d.ts.map +1 -1
  38. package/dist/interfaces/rest/audit-middleware.js +2 -0
  39. package/dist/interfaces/rest/customer-portal.d.ts.map +1 -1
  40. package/dist/interfaces/rest/customer-portal.js +9 -8
  41. package/dist/interfaces/rest/index.d.ts.map +1 -1
  42. package/dist/interfaces/rest/index.js +4 -0
  43. package/dist/interfaces/rest/route-coverage.d.ts +14 -0
  44. package/dist/interfaces/rest/route-coverage.d.ts.map +1 -0
  45. package/dist/interfaces/rest/route-coverage.js +81 -0
  46. package/dist/interfaces/rest/router.d.ts +7 -2
  47. package/dist/interfaces/rest/router.d.ts.map +1 -1
  48. package/dist/interfaces/rest/router.js +8 -4
  49. package/dist/interfaces/rest/routes/admin/compensation-failures.d.ts.map +1 -1
  50. package/dist/interfaces/rest/routes/admin/compensation-failures.js +7 -5
  51. package/dist/interfaces/rest/routes/admin/custom-field-proposals.d.ts +5 -0
  52. package/dist/interfaces/rest/routes/admin/custom-field-proposals.d.ts.map +1 -0
  53. package/dist/interfaces/rest/routes/admin/custom-field-proposals.js +22 -0
  54. package/dist/interfaces/rest/routes/admin/entity-field-definitions.d.ts +5 -0
  55. package/dist/interfaces/rest/routes/admin/entity-field-definitions.d.ts.map +1 -0
  56. package/dist/interfaces/rest/routes/admin/entity-field-definitions.js +39 -0
  57. package/dist/interfaces/rest/routes/admin/permissions.d.ts +1 -1
  58. package/dist/interfaces/rest/routes/admin/permissions.d.ts.map +1 -1
  59. package/dist/interfaces/rest/routes/admin/permissions.js +2 -0
  60. package/dist/interfaces/rest/routes/admin/staff.d.ts.map +1 -1
  61. package/dist/interfaces/rest/routes/admin/staff.js +126 -66
  62. package/dist/interfaces/rest/routes/admin-jobs.js +3 -3
  63. package/dist/interfaces/rest/routes/audit.js +3 -3
  64. package/dist/interfaces/rest/routes/carts.d.ts.map +1 -1
  65. package/dist/interfaces/rest/routes/carts.js +13 -10
  66. package/dist/interfaces/rest/routes/catalog.d.ts.map +1 -1
  67. package/dist/interfaces/rest/routes/catalog.js +47 -4
  68. package/dist/interfaces/rest/routes/checkout.d.ts.map +1 -1
  69. package/dist/interfaces/rest/routes/checkout.js +30 -35
  70. package/dist/interfaces/rest/routes/documents.d.ts.map +1 -1
  71. package/dist/interfaces/rest/routes/documents.js +10 -5
  72. package/dist/interfaces/rest/routes/inventory.d.ts.map +1 -1
  73. package/dist/interfaces/rest/routes/inventory.js +2 -0
  74. package/dist/interfaces/rest/routes/media.d.ts.map +1 -1
  75. package/dist/interfaces/rest/routes/media.js +7 -3
  76. package/dist/interfaces/rest/routes/orders.d.ts.map +1 -1
  77. package/dist/interfaces/rest/routes/orders.js +26 -8
  78. package/dist/interfaces/rest/routes/pricing.js +2 -2
  79. package/dist/interfaces/rest/routes/promotions.d.ts.map +1 -1
  80. package/dist/interfaces/rest/routes/promotions.js +13 -10
  81. package/dist/interfaces/rest/routes/search.d.ts.map +1 -1
  82. package/dist/interfaces/rest/routes/search.js +14 -1
  83. package/dist/interfaces/rest/schemas/admin-custom-field-proposals.d.ts +134 -0
  84. package/dist/interfaces/rest/schemas/admin-custom-field-proposals.d.ts.map +1 -0
  85. package/dist/interfaces/rest/schemas/admin-custom-field-proposals.js +26 -0
  86. package/dist/interfaces/rest/schemas/admin-entity-field-definitions.d.ts +592 -0
  87. package/dist/interfaces/rest/schemas/admin-entity-field-definitions.d.ts.map +1 -0
  88. package/dist/interfaces/rest/schemas/admin-entity-field-definitions.js +109 -0
  89. package/dist/interfaces/rest/schemas/carts.d.ts +7 -0
  90. package/dist/interfaces/rest/schemas/carts.d.ts.map +1 -1
  91. package/dist/interfaces/rest/schemas/catalog.d.ts +1364 -401
  92. package/dist/interfaces/rest/schemas/catalog.d.ts.map +1 -1
  93. package/dist/interfaces/rest/schemas/catalog.js +86 -0
  94. package/dist/interfaces/rest/schemas/customer-portal.d.ts +35 -0
  95. package/dist/interfaces/rest/schemas/customer-portal.d.ts.map +1 -1
  96. package/dist/interfaces/rest/schemas/orders.d.ts +204 -0
  97. package/dist/interfaces/rest/schemas/orders.d.ts.map +1 -1
  98. package/dist/interfaces/rest/schemas/pricing.d.ts +1 -0
  99. package/dist/interfaces/rest/schemas/pricing.d.ts.map +1 -1
  100. package/dist/interfaces/rest/schemas/responses.d.ts +71 -0
  101. package/dist/interfaces/rest/schemas/responses.d.ts.map +1 -1
  102. package/dist/interfaces/rest/schemas/search.d.ts +1 -0
  103. package/dist/interfaces/rest/schemas/search.d.ts.map +1 -1
  104. package/dist/interfaces/rest/schemas/search.js +1 -0
  105. package/dist/interfaces/rest/utils.d.ts +15 -0
  106. package/dist/interfaces/rest/utils.d.ts.map +1 -1
  107. package/dist/interfaces/rest/utils.js +45 -2
  108. package/dist/kernel/compensation/executor.js +2 -2
  109. package/dist/kernel/database/tx-context.d.ts +8 -0
  110. package/dist/kernel/database/tx-context.d.ts.map +1 -1
  111. package/dist/kernel/database/tx-context.js +13 -0
  112. package/dist/kernel/hooks/create-context.d.ts +2 -0
  113. package/dist/kernel/hooks/create-context.d.ts.map +1 -1
  114. package/dist/kernel/hooks/create-context.js +1 -0
  115. package/dist/kernel/hooks/types.d.ts +2 -0
  116. package/dist/kernel/hooks/types.d.ts.map +1 -1
  117. package/dist/kernel/plugin/manifest.d.ts.map +1 -1
  118. package/dist/kernel/plugin/manifest.js +9 -5
  119. package/dist/modules/analytics/service.js +2 -2
  120. package/dist/modules/audit/service.js +3 -3
  121. package/dist/modules/cart/repository/index.d.ts +13 -7
  122. package/dist/modules/cart/repository/index.d.ts.map +1 -1
  123. package/dist/modules/cart/repository/index.js +16 -10
  124. package/dist/modules/cart/schema.d.ts +17 -0
  125. package/dist/modules/cart/schema.d.ts.map +1 -1
  126. package/dist/modules/cart/schema.js +4 -0
  127. package/dist/modules/cart/service.d.ts +22 -9
  128. package/dist/modules/cart/service.d.ts.map +1 -1
  129. package/dist/modules/cart/service.js +57 -33
  130. package/dist/modules/catalog/brand-service.js +7 -7
  131. package/dist/modules/catalog/category-service.js +7 -7
  132. package/dist/modules/catalog/entity-service.d.ts +19 -10
  133. package/dist/modules/catalog/entity-service.d.ts.map +1 -1
  134. package/dist/modules/catalog/entity-service.js +180 -51
  135. package/dist/modules/catalog/module.d.ts +6 -1
  136. package/dist/modules/catalog/module.d.ts.map +1 -1
  137. package/dist/modules/catalog/module.js +6 -1
  138. package/dist/modules/catalog/ownership.d.ts +5 -0
  139. package/dist/modules/catalog/ownership.d.ts.map +1 -0
  140. package/dist/modules/catalog/ownership.js +11 -0
  141. package/dist/modules/catalog/read-policy.d.ts +9 -0
  142. package/dist/modules/catalog/read-policy.d.ts.map +1 -0
  143. package/dist/modules/catalog/read-policy.js +8 -0
  144. package/dist/modules/catalog/repository/index.d.ts +55 -3
  145. package/dist/modules/catalog/repository/index.d.ts.map +1 -1
  146. package/dist/modules/catalog/repository/index.js +335 -6
  147. package/dist/modules/catalog/revision.d.ts +4 -0
  148. package/dist/modules/catalog/revision.d.ts.map +1 -0
  149. package/dist/modules/catalog/revision.js +33 -0
  150. package/dist/modules/catalog/schema.d.ts +979 -110
  151. package/dist/modules/catalog/schema.d.ts.map +1 -1
  152. package/dist/modules/catalog/schema.js +112 -2
  153. package/dist/modules/catalog/schemas.d.ts +2 -0
  154. package/dist/modules/catalog/schemas.d.ts.map +1 -1
  155. package/dist/modules/catalog/schemas.js +2 -0
  156. package/dist/modules/catalog/service.d.ts +80 -15
  157. package/dist/modules/catalog/service.d.ts.map +1 -1
  158. package/dist/modules/catalog/service.js +522 -14
  159. package/dist/modules/channels/adapter.d.ts +85 -0
  160. package/dist/modules/channels/adapter.d.ts.map +1 -1
  161. package/dist/modules/channels/adapter.js +9 -1
  162. package/dist/modules/customers/module.d.ts.map +1 -1
  163. package/dist/modules/customers/module.js +1 -0
  164. package/dist/modules/customers/service.d.ts +2 -0
  165. package/dist/modules/customers/service.d.ts.map +1 -1
  166. package/dist/modules/customers/service.js +21 -20
  167. package/dist/modules/documents/module.d.ts.map +1 -1
  168. package/dist/modules/documents/module.js +1 -0
  169. package/dist/modules/documents/service.d.ts +18 -4
  170. package/dist/modules/documents/service.d.ts.map +1 -1
  171. package/dist/modules/documents/service.js +54 -16
  172. package/dist/modules/fulfillment/module.d.ts.map +1 -1
  173. package/dist/modules/fulfillment/module.js +1 -0
  174. package/dist/modules/fulfillment/service.d.ts +2 -0
  175. package/dist/modules/fulfillment/service.d.ts.map +1 -1
  176. package/dist/modules/fulfillment/service.js +15 -6
  177. package/dist/modules/inventory/service.d.ts.map +1 -1
  178. package/dist/modules/inventory/service.js +15 -14
  179. package/dist/modules/media/module.d.ts +68 -0
  180. package/dist/modules/media/module.d.ts.map +1 -1
  181. package/dist/modules/media/module.js +2 -0
  182. package/dist/modules/media/schema.d.ts +70 -0
  183. package/dist/modules/media/schema.d.ts.map +1 -1
  184. package/dist/modules/media/schema.js +20 -2
  185. package/dist/modules/media/service.d.ts +8 -1
  186. package/dist/modules/media/service.d.ts.map +1 -1
  187. package/dist/modules/media/service.js +71 -23
  188. package/dist/modules/orders/guest-access.d.ts +27 -0
  189. package/dist/modules/orders/guest-access.d.ts.map +1 -0
  190. package/dist/modules/orders/guest-access.js +46 -0
  191. package/dist/modules/orders/module.d.ts.map +1 -1
  192. package/dist/modules/orders/module.js +1 -0
  193. package/dist/modules/orders/quote.d.ts.map +1 -1
  194. package/dist/modules/orders/quote.js +1 -0
  195. package/dist/modules/orders/repository/index.d.ts +1 -1
  196. package/dist/modules/orders/repository/index.d.ts.map +1 -1
  197. package/dist/modules/orders/repository/index.js +2 -2
  198. package/dist/modules/orders/schema.d.ts +17 -0
  199. package/dist/modules/orders/schema.d.ts.map +1 -1
  200. package/dist/modules/orders/schema.js +5 -3
  201. package/dist/modules/orders/service.d.ts +16 -7
  202. package/dist/modules/orders/service.d.ts.map +1 -1
  203. package/dist/modules/orders/service.js +184 -55
  204. package/dist/modules/pricing/module.d.ts.map +1 -1
  205. package/dist/modules/pricing/module.js +1 -0
  206. package/dist/modules/pricing/schema.d.ts +17 -0
  207. package/dist/modules/pricing/schema.d.ts.map +1 -1
  208. package/dist/modules/pricing/schema.js +1 -0
  209. package/dist/modules/pricing/schemas.d.ts +1 -0
  210. package/dist/modules/pricing/schemas.d.ts.map +1 -1
  211. package/dist/modules/pricing/schemas.js +1 -0
  212. package/dist/modules/pricing/service.d.ts +2 -0
  213. package/dist/modules/pricing/service.d.ts.map +1 -1
  214. package/dist/modules/pricing/service.js +18 -12
  215. package/dist/modules/promotions/module.d.ts.map +1 -1
  216. package/dist/modules/promotions/module.js +1 -0
  217. package/dist/modules/promotions/service.d.ts +3 -0
  218. package/dist/modules/promotions/service.d.ts.map +1 -1
  219. package/dist/modules/promotions/service.js +19 -11
  220. package/dist/modules/search/adapter.d.ts +8 -0
  221. package/dist/modules/search/adapter.d.ts.map +1 -1
  222. package/dist/modules/search/module.d.ts.map +1 -1
  223. package/dist/modules/search/module.js +3 -0
  224. package/dist/modules/search/service.d.ts +5 -0
  225. package/dist/modules/search/service.d.ts.map +1 -1
  226. package/dist/modules/search/service.js +131 -11
  227. package/dist/modules/settings/module.d.ts.map +1 -1
  228. package/dist/modules/settings/module.js +1 -0
  229. package/dist/modules/settings/service.d.ts +3 -0
  230. package/dist/modules/settings/service.d.ts.map +1 -1
  231. package/dist/modules/settings/service.js +6 -4
  232. package/dist/modules/shipping/service.js +9 -9
  233. package/dist/modules/tax/module.d.ts.map +1 -1
  234. package/dist/modules/tax/module.js +1 -0
  235. package/dist/modules/tax/service.d.ts +3 -0
  236. package/dist/modules/tax/service.d.ts.map +1 -1
  237. package/dist/modules/tax/service.js +11 -9
  238. package/dist/modules/webhooks/hook.js +2 -2
  239. package/dist/modules/webhooks/module.d.ts.map +1 -1
  240. package/dist/modules/webhooks/module.js +1 -0
  241. package/dist/modules/webhooks/service.d.ts +2 -0
  242. package/dist/modules/webhooks/service.d.ts.map +1 -1
  243. package/dist/modules/webhooks/service.js +4 -4
  244. package/dist/runtime/kernel-modules.d.ts +73 -0
  245. package/dist/runtime/kernel-modules.d.ts.map +1 -1
  246. package/dist/runtime/server.d.ts.map +1 -1
  247. package/dist/runtime/server.js +10 -2
  248. package/dist/test-utils/create-pglite-adapter.d.ts.map +1 -1
  249. package/dist/test-utils/create-pglite-adapter.js +35 -13
  250. package/dist/test-utils/create-test-config.d.ts.map +1 -1
  251. package/dist/test-utils/create-test-config.js +4 -0
  252. package/dist/test-utils/rest-api-test-utils.d.ts.map +1 -1
  253. package/dist/test-utils/rest-api-test-utils.js +2 -0
  254. package/dist/test-utils/test-actors.js +2 -2
  255. package/dist/utils/id.d.ts +2 -0
  256. package/dist/utils/id.d.ts.map +1 -1
  257. package/dist/utils/id.js +11 -0
  258. package/package.json +6 -5
  259. package/src/auth/auth-schema-guard.ts +143 -0
  260. package/src/auth/auth-schema.ts +50 -19
  261. package/src/auth/middleware.ts +17 -5
  262. package/src/auth/org.ts +15 -3
  263. package/src/auth/organization-guard.ts +158 -0
  264. package/src/auth/permissions.ts +31 -8
  265. package/src/auth/role-authority.ts +107 -0
  266. package/src/auth/setup.ts +17 -0
  267. package/src/auth/strict-org-resolution.ts +7 -1
  268. package/src/auth/types.ts +1 -1
  269. package/src/config/defaults.ts +2 -0
  270. package/src/config/types.ts +32 -4
  271. package/src/hooks/checkout.ts +19 -12
  272. package/src/index.ts +30 -6
  273. package/src/interfaces/rest/audit-middleware.ts +2 -0
  274. package/src/interfaces/rest/customer-portal.ts +31 -24
  275. package/src/interfaces/rest/index.ts +4 -0
  276. package/src/interfaces/rest/route-coverage.ts +103 -0
  277. package/src/interfaces/rest/router.ts +16 -10
  278. package/src/interfaces/rest/routes/admin/compensation-failures.ts +8 -5
  279. package/src/interfaces/rest/routes/admin/custom-field-proposals.ts +37 -0
  280. package/src/interfaces/rest/routes/admin/entity-field-definitions.ts +89 -0
  281. package/src/interfaces/rest/routes/admin/permissions.ts +3 -1
  282. package/src/interfaces/rest/routes/admin/staff.ts +161 -72
  283. package/src/interfaces/rest/routes/admin-jobs.ts +3 -3
  284. package/src/interfaces/rest/routes/audit.ts +3 -3
  285. package/src/interfaces/rest/routes/carts.ts +17 -7
  286. package/src/interfaces/rest/routes/catalog.ts +96 -0
  287. package/src/interfaces/rest/routes/checkout.ts +41 -38
  288. package/src/interfaces/rest/routes/documents.ts +15 -1
  289. package/src/interfaces/rest/routes/inventory.ts +2 -0
  290. package/src/interfaces/rest/routes/media.ts +8 -3
  291. package/src/interfaces/rest/routes/orders.ts +27 -8
  292. package/src/interfaces/rest/routes/pricing.ts +2 -2
  293. package/src/interfaces/rest/routes/promotions.ts +12 -9
  294. package/src/interfaces/rest/routes/search.ts +19 -1
  295. package/src/interfaces/rest/schemas/admin-custom-field-proposals.ts +29 -0
  296. package/src/interfaces/rest/schemas/admin-entity-field-definitions.ts +120 -0
  297. package/src/interfaces/rest/schemas/catalog.ts +92 -0
  298. package/src/interfaces/rest/schemas/search.ts +2 -0
  299. package/src/interfaces/rest/utils.ts +61 -2
  300. package/src/kernel/compensation/executor.ts +2 -2
  301. package/src/kernel/database/tx-context.ts +25 -0
  302. package/src/kernel/hooks/create-context.ts +3 -0
  303. package/src/kernel/hooks/types.ts +2 -0
  304. package/src/kernel/plugin/manifest.ts +9 -5
  305. package/src/modules/analytics/service.ts +2 -2
  306. package/src/modules/audit/service.ts +3 -3
  307. package/src/modules/cart/repository/index.ts +22 -8
  308. package/src/modules/cart/schema.ts +4 -0
  309. package/src/modules/cart/service.ts +62 -27
  310. package/src/modules/catalog/brand-service.ts +7 -7
  311. package/src/modules/catalog/category-service.ts +7 -7
  312. package/src/modules/catalog/entity-service.ts +188 -53
  313. package/src/modules/catalog/module.ts +15 -0
  314. package/src/modules/catalog/ownership.ts +17 -0
  315. package/src/modules/catalog/read-policy.ts +16 -0
  316. package/src/modules/catalog/repository/index.ts +566 -4
  317. package/src/modules/catalog/revision.ts +35 -0
  318. package/src/modules/catalog/schema.ts +169 -1
  319. package/src/modules/catalog/schemas.ts +2 -0
  320. package/src/modules/catalog/service.ts +766 -26
  321. package/src/modules/channels/adapter.ts +107 -1
  322. package/src/modules/customers/module.ts +2 -0
  323. package/src/modules/customers/service.ts +23 -22
  324. package/src/modules/documents/module.ts +2 -0
  325. package/src/modules/documents/service.ts +74 -11
  326. package/src/modules/fulfillment/module.ts +2 -0
  327. package/src/modules/fulfillment/service.ts +17 -6
  328. package/src/modules/inventory/service.ts +15 -24
  329. package/src/modules/media/module.ts +2 -0
  330. package/src/modules/media/schema.ts +23 -2
  331. package/src/modules/media/service.ts +89 -28
  332. package/src/modules/orders/guest-access.ts +55 -0
  333. package/src/modules/orders/module.ts +1 -0
  334. package/src/modules/orders/quote.ts +1 -0
  335. package/src/modules/orders/repository/index.ts +2 -0
  336. package/src/modules/orders/schema.ts +5 -3
  337. package/src/modules/orders/service.ts +302 -52
  338. package/src/modules/pricing/module.ts +2 -0
  339. package/src/modules/pricing/schema.ts +1 -0
  340. package/src/modules/pricing/schemas.ts +1 -0
  341. package/src/modules/pricing/service.ts +21 -18
  342. package/src/modules/promotions/module.ts +2 -0
  343. package/src/modules/promotions/service.ts +25 -16
  344. package/src/modules/search/adapter.ts +8 -0
  345. package/src/modules/search/module.ts +3 -0
  346. package/src/modules/search/service.ts +142 -12
  347. package/src/modules/settings/module.ts +2 -0
  348. package/src/modules/settings/service.ts +8 -4
  349. package/src/modules/shipping/service.ts +9 -9
  350. package/src/modules/tax/module.ts +1 -0
  351. package/src/modules/tax/service.ts +13 -9
  352. package/src/modules/webhooks/hook.ts +2 -2
  353. package/src/modules/webhooks/module.ts +2 -0
  354. package/src/modules/webhooks/service.ts +6 -4
  355. package/src/runtime/server.ts +16 -2
  356. package/src/test-utils/create-pglite-adapter.ts +34 -12
  357. package/src/test-utils/create-test-config.ts +10 -6
  358. package/src/test-utils/rest-api-test-utils.ts +2 -0
  359. package/src/test-utils/test-actors.ts +2 -2
  360. package/src/utils/id.ts +20 -0
@@ -0,0 +1,107 @@
1
+ import type { CommerceConfig } from "../config/types.js";
2
+
3
+ const BUILTIN_ROLE_RANK: Record<string, number> = {
4
+ owner: 3,
5
+ admin: 2,
6
+ };
7
+ const CUSTOM_ROLE_RANK = 1;
8
+
9
+ /**
10
+ * Who may grant which role.
11
+ *
12
+ * Every surface that mints or changes a membership answers this question, and
13
+ * they must all answer it the same way — the admin staff routes, and the guard
14
+ * in front of Better Auth's organization endpoints. Red-team round 6 found the
15
+ * takeover that follows from two surfaces disagreeing, so the arithmetic lives
16
+ * here and nowhere else.
17
+ */
18
+
19
+ /**
20
+ * Better Auth stores a member's role as a comma-separated list, so `"owner"`
21
+ * and `"owner,admin"` are both owners to it. Commerce role lookups are exact
22
+ * string matches, which is how a composite string used to read as an unknown
23
+ * custom role on one side and an owner on the other.
24
+ */
25
+ export function roleParts(role: string | null | undefined): string[] {
26
+ return (role ?? "")
27
+ .split(",")
28
+ .map((part) => part.trim())
29
+ .filter((part) => part.length > 0);
30
+ }
31
+
32
+ export function isCompositeRole(role: string | null | undefined): boolean {
33
+ return roleParts(role).length > 1;
34
+ }
35
+
36
+ /** True when any part of the role string is `owner`, matching Better Auth. */
37
+ export function isOwnerRole(role: string | null | undefined): boolean {
38
+ return roleParts(role).includes("owner");
39
+ }
40
+
41
+ export function permissionsForRole(config: CommerceConfig, role: string): string[] {
42
+ return config.auth?.roles?.[role]?.permissions ?? [];
43
+ }
44
+
45
+ export function hasPermissionFromList(
46
+ actorPermissions: readonly string[],
47
+ required: string,
48
+ ): boolean {
49
+ if (actorPermissions.includes("*:*")) return true;
50
+
51
+ const [resource] = required.split(":");
52
+ if (resource && actorPermissions.includes(`${resource}:*`)) return true;
53
+ return actorPermissions.includes(required);
54
+ }
55
+
56
+ /**
57
+ * The role's privilege rank. A composite role ranks as its strongest part, so
58
+ * `"owner,admin"` cannot slip past a rank floor by failing an exact lookup.
59
+ */
60
+ export function roleRank(config: CommerceConfig, role: string | null | undefined): number {
61
+ const ranks = roleParts(role).map((part) => {
62
+ const builtin = BUILTIN_ROLE_RANK[part];
63
+ if (builtin !== undefined) return builtin;
64
+ if (permissionsForRole(config, part).includes("*:*")) return BUILTIN_ROLE_RANK["admin"]!;
65
+ return CUSTOM_ROLE_RANK;
66
+ });
67
+ return ranks.length > 0 ? Math.max(...ranks) : CUSTOM_ROLE_RANK;
68
+ }
69
+
70
+ export interface Granter {
71
+ role: string;
72
+ permissions: readonly string[];
73
+ }
74
+
75
+ /**
76
+ * A granter may assign `targetRole` only when it already holds every permission
77
+ * that role carries (containment) and outranks or equals it (rank floor).
78
+ */
79
+ export function canGrantRole(
80
+ config: CommerceConfig,
81
+ granter: Granter | null,
82
+ targetRole: string,
83
+ ): boolean {
84
+ if (!granter) return false;
85
+ const contains = permissionsForRole(config, targetRole).every((permission) =>
86
+ hasPermissionFromList(granter.permissions, permission),
87
+ );
88
+ return contains && roleRank(config, granter.role) >= roleRank(config, targetRole);
89
+ }
90
+
91
+ /**
92
+ * The granter a stored membership row represents. Permissions come from the
93
+ * role's current definition, which is the whole point of re-checking authority
94
+ * later: the row is re-read, not the authority the actor had when they acted.
95
+ */
96
+ export function granterForRole(config: CommerceConfig, role: string): Granter {
97
+ return { role, permissions: permissionsForRole(config, role) };
98
+ }
99
+
100
+ export function validRoles(config: CommerceConfig): Set<string> {
101
+ return new Set([
102
+ ...Object.keys(config.auth?.roles ?? {}),
103
+ "owner",
104
+ "admin",
105
+ "member",
106
+ ]);
107
+ }
package/src/auth/setup.ts CHANGED
@@ -65,6 +65,23 @@ export function createAuth(
65
65
  | ReturnType<typeof jwt>
66
66
  | ReturnType<typeof bearer>
67
67
  > = [
68
+ // These are commerce permission arrays, not Better Auth access-control
69
+ // `Role` objects exposing `.authorize()`. Its organization plugin therefore
70
+ // cannot evaluate a permission with them, and every check reaching its
71
+ // authorize loop throws instead of deciding — fail-closed, but by type
72
+ // confusion rather than by intent.
73
+ //
74
+ // That is deliberate and stays. This repository does not use Better Auth's
75
+ // access-control model: `admin/staff.ts` is the governed membership
76
+ // surface, and `auth/role-authority.ts` holds the arithmetic. Handing the
77
+ // plugin real `Role` objects would stand up a second permission model to
78
+ // keep in agreement with the first, which is how the two layers disagreed
79
+ // about composite roles and owner counting in the first place.
80
+ //
81
+ // What must not be relied on is the throw. `auth/organization-guard.ts`
82
+ // refuses the plugin's membership writers outright, so repairing this
83
+ // wiring cannot turn an accidental closure into an open door. Pin that
84
+ // guard's test before changing anything here.
68
85
  organization({
69
86
  roles: (config.auth?.roles ?? {}) as unknown as Record<string, Role | undefined>,
70
87
  }),
@@ -7,8 +7,14 @@ export function setBootStrictOrgResolution(strict: boolean): void {
7
7
  }
8
8
 
9
9
  export function isStrictOrgResolution(config?: CommerceConfig | null): boolean {
10
+ // Fail closed by default. Without an actor there is no tenant, and silently
11
+ // resolving to a default organization served one merchant's data to
12
+ // unauthenticated callers on every unguarded or allowlisted read path.
13
+ // Single-tenant deployments that relied on that fallback opt out explicitly.
14
+ if (config?.auth?.strictOrgResolution === false) return false;
15
+ if (process.env.STRICT_ORG_RESOLUTION === "false") return false;
10
16
  if (config?.auth?.strictOrgResolution === true) return true;
11
17
  if (process.env.STRICT_ORG_RESOLUTION === "true") return true;
12
18
  if ((config === undefined || config === null) && _bootStrictOrgResolution) return true;
13
- return false;
19
+ return true;
14
20
  }
package/src/auth/types.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export interface Actor {
2
2
  type: "user" | "api_key";
3
- userId: string;
3
+ userId: string | null;
4
4
  email: string | null;
5
5
  name: string;
6
6
  vendorId: string | null;
@@ -24,10 +24,12 @@ export const defaultConfig: Partial<CommerceConfig> = {
24
24
  "catalog:update",
25
25
  "catalog:delete",
26
26
  "catalog:read",
27
+ "catalog:read:unpublished",
27
28
  "inventory:read",
28
29
  "inventory:adjust",
29
30
  "orders:read",
30
31
  "orders:create",
32
+ "orders:create:on-behalf",
31
33
  "orders:update",
32
34
  "orders:manage",
33
35
  "cart:create",
@@ -34,10 +34,13 @@ export type FieldType = "text" | "number" | "boolean" | "date" | "json" | "relat
34
34
  export interface EntityFieldDefinition {
35
35
  name: string;
36
36
  type: FieldType;
37
+ filterable?: boolean;
38
+ localized?: boolean;
37
39
  unit?: string;
38
40
  schema?: unknown;
39
41
  target?: string;
40
42
  options?: string[];
43
+ sortOrder?: number;
41
44
  }
42
45
 
43
46
  export interface EntityVariantConfig {
@@ -197,10 +200,17 @@ export interface AuthConfig {
197
200
  */
198
201
  storeResolver?: (request: Request) => string | null | Promise<string | null>;
199
202
  /**
200
- * When true, a failing `storeResolver` for anonymous requests returns HTTP 503
201
- * instead of continuing with `actor = null` (legacy). Same behavior when env
202
- * `STRICT_ORG_RESOLUTION` is `"true"`. Default false keeps backward-compatible
203
- * fallback for single-store and lenient multi-store setups.
203
+ * Governs two layers, and its default differs between them.
204
+ *
205
+ * In the store-resolver middleware, `true` makes a failing `storeResolver`
206
+ * return HTTP 503 rather than continuing with `actor = null`; that layer
207
+ * still defaults to the lenient behaviour.
208
+ *
209
+ * In organization resolution it **fails closed by default**: an actor-less
210
+ * request with no explicit organization and no `defaultOrganizationId` is
211
+ * refused rather than served a fallback tenant. Set this to `false`, or the
212
+ * env var `STRICT_ORG_RESOLUTION` to `"false"`, to restore the old fallback
213
+ * during a migration.
204
214
  */
205
215
  strictOrgResolution?: boolean;
206
216
  /**
@@ -255,7 +265,25 @@ export interface CheckoutConfig {
255
265
  };
256
266
  }
257
267
 
268
+ /**
269
+ * Decides whether a guest bearer credential (a cart secret) may still read a
270
+ * placed order. The authenticated customer who owns an order is never subject
271
+ * to this — ownership grants permanent access.
272
+ *
273
+ * Modelled on Vendure's `OrderByCodeAccessStrategy`: the value of the seam is
274
+ * that the policy has one home, rather than being an emergent property of the
275
+ * read path.
276
+ */
277
+ export interface GuestOrderAccessStrategy {
278
+ canAccessOrder(order: { placedAt: Date }, now: Date): boolean;
279
+ }
280
+
258
281
  export interface OrdersConfig {
282
+ /**
283
+ * How long a guest cart secret keeps reading an order after placement.
284
+ * Defaults to `windowedGuestOrderAccess("7d")`.
285
+ */
286
+ guestAccessStrategy?: GuestOrderAccessStrategy;
259
287
  hooks?: {
260
288
  beforeCreate?: BeforeHook<unknown>[];
261
289
  afterCreate?: AfterHook<unknown>[];
@@ -1,5 +1,5 @@
1
1
  import { CommerceValidationError } from "../kernel/errors.js";
2
- import { resolveOrgId } from "../auth/org.js";
2
+ import { resolveOrgIdForCommerce } from "../auth/org.js";
3
3
  import type { CompensationFailuresRepository } from "../kernel/compensation/repository.js";
4
4
  import type { AfterHook, BeforeHook } from "../kernel/hooks/types.js";
5
5
  import type { ShippingAddress } from "../modules/shipping/calculator.js";
@@ -97,7 +97,7 @@ export const validateCartNotEmpty: BeforeHook<CheckoutData> = async ({
97
97
  context,
98
98
  }) => {
99
99
  const cartService = context.services.cart as {
100
- getById(id: string, actor?: unknown): Promise<
100
+ getByIdForInternalUse(id: string, organizationId: string, ctx?: TxContext): Promise<
101
101
  | {
102
102
  ok: true;
103
103
  value: {
@@ -114,7 +114,7 @@ export const validateCartNotEmpty: BeforeHook<CheckoutData> = async ({
114
114
  }
115
115
  | { ok: false }
116
116
  >;
117
- claimForCheckout(cartId: string, ctx?: unknown): Promise<
117
+ claimForCheckout(cartId: string, claimToken: string, ctx?: unknown): Promise<
118
118
  | { ok: true; value: { id: string } }
119
119
  | { ok: false; error: { message: string } }
120
120
  >;
@@ -122,7 +122,9 @@ export const validateCartNotEmpty: BeforeHook<CheckoutData> = async ({
122
122
  const catalogService = context.services.catalog as {
123
123
  getById(
124
124
  id: string,
125
- options?: { includeAttributes?: boolean },
125
+ options: { includeAttributes?: boolean } | undefined,
126
+ actor: unknown,
127
+ ctx?: TxContext,
126
128
  ): Promise<
127
129
  | {
128
130
  ok: true;
@@ -137,20 +139,25 @@ export const validateCartNotEmpty: BeforeHook<CheckoutData> = async ({
137
139
 
138
140
  // M1 fix: Atomically claim the cart for checkout (active → checking_out).
139
141
  // If a concurrent request already claimed it, this returns Err and we fail fast.
140
- const claimed = await cartService.claimForCheckout(data.cartId, context.tx);
142
+ // The checkout id is the claim token, so only this attempt can release it.
143
+ const claimed = await cartService.claimForCheckout(data.cartId, data.checkoutId, context.tx);
141
144
  if (!claimed.ok) {
142
145
  throw new CommerceValidationError(
143
146
  "Cart is not available for checkout. It may have already been checked out by a concurrent request.",
144
147
  );
145
148
  }
146
149
 
147
- const cart = await cartService.getById(data.cartId, context.actor);
150
+ const cart = await cartService.getByIdForInternalUse(
151
+ data.cartId,
152
+ resolveOrgIdForCommerce(context.actor, context.commerceConfig),
153
+ context.tx as TxContext | undefined,
154
+ );
148
155
  if (!cart.ok || cart.value.lineItems.length === 0) {
149
156
  throw new CommerceValidationError("Cannot checkout an empty cart.");
150
157
  }
151
158
 
152
159
  // Cross-org guard: prevent org B from checking out org A's cart
153
- const actorOrgId = resolveOrgId(context.actor);
160
+ const actorOrgId = resolveOrgIdForCommerce(context.actor, context.commerceConfig);
154
161
  if (cart.value.organizationId && cart.value.organizationId !== actorOrgId) {
155
162
  throw new CommerceValidationError("Cart does not belong to this organization.");
156
163
  }
@@ -174,7 +181,7 @@ export const validateCartNotEmpty: BeforeHook<CheckoutData> = async ({
174
181
  cart.value.lineItems.map(async (item) => {
175
182
  const entity = await catalogService.getById(item.entityId, {
176
183
  includeAttributes: true,
177
- });
184
+ }, context.actor, context.tx as TxContext | undefined);
178
185
  const title = entity.ok
179
186
  ? (entity.value.attributes?.[0]?.title ?? item.entityId)
180
187
  : item.entityId;
@@ -323,7 +330,7 @@ export const applyPromotionCodes: BeforeHook<CheckoutData> = async ({
323
330
  };
324
331
 
325
332
  const result = await promotions.applyPromotions({
326
- orgId: resolveOrgId(context.actor),
333
+ orgId: resolveOrgIdForCommerce(context.actor, context.commerceConfig),
327
334
  cartId: data.cartId,
328
335
  currency: data.currency,
329
336
  subtotal: data.subtotal,
@@ -411,7 +418,7 @@ export const calculateTax: BeforeHook<CheckoutData> = async ({
411
418
  ...(data.shippingAddress !== undefined
412
419
  ? { toAddress: data.shippingAddress }
413
420
  : {}),
414
- }, resolveOrgId(context.actor ?? null));
421
+ }, resolveOrgIdForCommerce(context.actor ?? null, context.commerceConfig));
415
422
 
416
423
  if (!calculated.ok) {
417
424
  throw new CommerceValidationError(
@@ -469,7 +476,7 @@ export const calculateShipping: BeforeHook<CheckoutData> = async ({
469
476
  })),
470
477
  subtotalAfterDiscount: Math.max(0, data.subtotal - data.discountTotal),
471
478
  currency: data.currency,
472
- orgId: resolveOrgId(context.actor ?? null),
479
+ orgId: resolveOrgIdForCommerce(context.actor ?? null, context.commerceConfig),
473
480
  ...(data.shippingAddress !== undefined
474
481
  ? { address: data.shippingAddress }
475
482
  : {}),
@@ -546,7 +553,7 @@ export const authorizePayment: BeforeHook<CheckoutData> = async ({
546
553
  : {}),
547
554
  metadata: {
548
555
  ...(data.orderId !== undefined ? { orderId: data.orderId } : {}),
549
- organizationId: resolveOrgId(context.actor),
556
+ organizationId: resolveOrgIdForCommerce(context.actor, context.commerceConfig),
550
557
  checkoutId: data.checkoutId,
551
558
  cartId: data.cartId,
552
559
  },
package/src/index.ts CHANGED
@@ -3,6 +3,7 @@ export type {
3
3
  CommerceConfig,
4
4
  CommercePlugin,
5
5
  ApiKeyScopeDefinition,
6
+ GuestOrderAccessStrategy,
6
7
  } from "./config/types.js";
7
8
  export { defineCommercePlugin } from "./kernel/plugin/manifest.js";
8
9
  export type {
@@ -31,12 +32,12 @@ export type { Kernel } from "./runtime/kernel.js";
31
32
  // This avoids pulling drizzle-kit/tsx/esbuild into Turbopack's bundle graph.
32
33
 
33
34
  export type { Actor } from "./auth/types.js";
34
- export { resolveOrgId, ensureDefaultOrg, DEFAULT_ORG_ID } from "./auth/org.js";
35
+ export { resolveOrgId, resolveOrgIdForCommerce, ensureDefaultOrg, DEFAULT_ORG_ID } from "./auth/org.js";
35
36
  export { createSystemActor } from "./auth/system-actor.js";
36
37
  export { OrganizationService } from "./modules/organization/service.js";
37
38
  export { createScopedDb } from "./kernel/database/scoped-db.js";
38
39
  export type { ScopedOrganizationId } from "./kernel/database/scoped-db.js";
39
- export { assertOwnership, assertPermission } from "./auth/permissions.js";
40
+ export { assertOwnership, assertPermission, requireUserId } from "./auth/permissions.js";
40
41
  export type { AccessResult, AccessContext, AccessFn, WhereClause } from "./auth/access.js";
41
42
  export {
42
43
  accessOR,
@@ -141,8 +142,12 @@ export type {
141
142
  } from "./modules/payments/adapter.js";
142
143
  export { defineChannelConnector } from "./modules/channels/adapter.js";
143
144
  export type {
145
+ ChannelCatalogImage,
144
146
  ChannelCatalogItem,
147
+ ChannelCatalogLocalizedAttributes,
148
+ ChannelCatalogOptionType,
145
149
  ChannelCatalogPage,
150
+ ChannelCatalogPrice,
146
151
  ChannelCatalogVariant,
147
152
  ChannelConnector,
148
153
  ChannelConnectorCapabilities,
@@ -150,6 +155,14 @@ export type {
150
155
  ChannelOrderLine,
151
156
  ChannelOrderSlice,
152
157
  ChannelConnectorError,
158
+ ChannelPushCatalogField,
159
+ ChannelPushCatalogImage,
160
+ ChannelPushCatalogIntent,
161
+ ChannelPushCatalogItem,
162
+ ChannelPushCatalogItemOutcome,
163
+ ChannelPushCatalogPreviousField,
164
+ ChannelPushCatalogResult,
165
+ ChannelPushCatalogVariant,
153
166
  ChannelOrderStatus,
154
167
  ChannelPushOrderResult,
155
168
  ChannelRefundResult,
@@ -169,14 +182,20 @@ export type {
169
182
  SearchSuggestParams,
170
183
  } from "./modules/search/adapter.js";
171
184
  export type { DatabaseAdapter } from "./kernel/database/adapter.js";
185
+ export { isValidFieldPath, validateFieldPath } from "./modules/catalog/ownership.js";
186
+ export type { FieldPath, FieldOwner } from "./modules/catalog/ownership.js";
187
+ export type {
188
+ TxContext,
189
+ WithTransactionOptions,
190
+ CatalogWriteContext,
191
+ HookContextCarrier,
192
+ } from "./kernel/database/tx-context.js";
172
193
  export {
173
194
  createTxContext,
174
195
  reuseOrCreateTxContext,
175
196
  withTransaction,
176
- } from "./kernel/database/tx-context.js";
177
- export type {
178
- TxContext,
179
- WithTransactionOptions,
197
+ isWriteContextTransactional,
198
+ resolveWriteContextHookContext,
180
199
  } from "./kernel/database/tx-context.js";
181
200
  export type {
182
201
  TaxAdapter,
@@ -235,6 +254,11 @@ export type { QueryInput, QueryResult } from "./kernel/query/executor.js";
235
254
  export type { CommerceModuleTypes } from "./types/commerce-types.js";
236
255
 
237
256
  export { staleOrderCleanupTask } from "./modules/orders/stale-order-cleanup.js";
257
+ export {
258
+ windowedGuestOrderAccess,
259
+ defaultGuestOrderAccess,
260
+ parseAccessWindow,
261
+ } from "./modules/orders/guest-access.js";
238
262
  export { COMMERCE_AGENT_SYSTEM_PROMPT } from "./utils/agent-prompt.js";
239
263
 
240
264
  export { DrizzleAnalyticsAdapter } from "./modules/analytics/drizzle-adapter.js";
@@ -95,12 +95,14 @@ export function auditMiddleware(kernel: Kernel): MiddlewareHandler {
95
95
  try {
96
96
  const requestId = c.get("requestId") as string | undefined;
97
97
  const ctx = createHookContext({
98
+ // Anonymous audit records retain actor: null; organization comes from the deployment config.
98
99
  actor: (c.get("actor") as Actor | null) ?? null,
99
100
  ...(requestId ? { requestId } : {}),
100
101
  logger: kernel.logger,
101
102
  services: kernel.services as unknown as ServiceContainer,
102
103
  database: { db: kernel.database.db as unknown as PluginDb },
103
104
  origin: "rest",
105
+ commerceConfig: kernel.config,
104
106
  });
105
107
  await kernel.services.audit.record({ entityType, entityId, event, payload, ctx });
106
108
  } catch {
@@ -1,6 +1,6 @@
1
1
  import { OpenAPIHono } from "@hono/zod-openapi";
2
2
  import type { Kernel } from "../../runtime/kernel.js";
3
- import { assertPermission } from "../../auth/permissions.js";
3
+ import { assertPermission, requireUserId } from "../../auth/permissions.js";
4
4
  import type { Actor } from "../../auth/types.js";
5
5
  import type { AppEnv } from "./utils.js";
6
6
  import {
@@ -16,27 +16,30 @@ import {
16
16
  updateProfileRoute,
17
17
  createAddressRoute,
18
18
  } from "./schemas/customer-portal.js";
19
- import { isUUID, mapErrorToStatus } from "./utils.js";
19
+ import { isUUID, mapErrorToStatus, markRoutePermissionGuard } from "./utils.js";
20
+
21
+ type AuthenticatedActor = Actor & { userId: string };
20
22
 
21
23
  export function createCustomerPortalRoutes(kernel: Kernel) {
22
24
  const router = new OpenAPIHono<AppEnv>();
23
25
 
24
- router.use("*", async (c, next) => {
25
- if (!c.get("actor")) {
26
+ router.use("*", markRoutePermissionGuard(async (c, next) => {
27
+ const actor = c.get("actor") as Actor | null;
28
+ if (!actor?.userId) {
26
29
  return c.json(
27
30
  { error: { code: "FORBIDDEN", message: "Authentication required." } },
28
31
  401,
29
32
  );
30
33
  }
31
34
  await next();
32
- });
35
+ }));
33
36
 
34
37
  /**
35
38
  * Resolves an actor whose userId is the customer profile UUID (not the Better Auth user ID).
36
39
  * Required so that `assertOwnership(actor, order.customerId)` compares UUIDs correctly,
37
40
  * since orders.customer_id stores the customer profile UUID, not the Better Auth string ID.
38
41
  */
39
- async function resolveCustomerActor(actor: Actor): Promise<Actor | null> {
42
+ async function resolveCustomerActor(actor: AuthenticatedActor): Promise<AuthenticatedActor | null> {
40
43
  const customer = await kernel.services.customers.getByUserId(actor.userId, actor);
41
44
  if (!customer.ok) return null;
42
45
  return { ...actor, userId: customer.value.id };
@@ -44,7 +47,7 @@ export function createCustomerPortalRoutes(kernel: Kernel) {
44
47
 
45
48
  // @ts-expect-error -- openapi handler union return type
46
49
  router.openapi(getProfileRoute, async (c) => {
47
- const actor = c.get("actor") as Actor;
50
+ const actor = c.get("actor") as AuthenticatedActor;
48
51
  const customer = await kernel.services.customers.getByUserId(actor.userId, actor);
49
52
  if (!customer.ok) return c.json({ error: customer.error }, 404);
50
53
  return c.json({ data: customer.value });
@@ -52,7 +55,7 @@ export function createCustomerPortalRoutes(kernel: Kernel) {
52
55
 
53
56
  // @ts-expect-error -- openapi handler union return type
54
57
  router.openapi(updateProfileRoute, async (c) => {
55
- const actor = c.get("actor") as Actor;
58
+ const actor = c.get("actor") as AuthenticatedActor;
56
59
  assertPermission(actor, "customers:update:self");
57
60
  const result = await kernel.services.customers.updateByUserId(
58
61
  actor.userId,
@@ -64,7 +67,7 @@ export function createCustomerPortalRoutes(kernel: Kernel) {
64
67
  });
65
68
 
66
69
  router.openapi(listAddressesRoute, async (c) => {
67
- const actor = c.get("actor") as Actor;
70
+ const actor = c.get("actor") as AuthenticatedActor;
68
71
  const addresses = await kernel.services.customers.getAddresses(
69
72
  actor.userId,
70
73
  actor,
@@ -74,7 +77,7 @@ export function createCustomerPortalRoutes(kernel: Kernel) {
74
77
 
75
78
  // @ts-expect-error -- openapi handler union return type
76
79
  router.openapi(createAddressRoute, async (c) => {
77
- const actor = c.get("actor") as Actor;
80
+ const actor = c.get("actor") as AuthenticatedActor;
78
81
  const result = await kernel.services.customers.addAddress(
79
82
  actor.userId,
80
83
  c.req.valid("json") as Parameters<typeof kernel.services.customers.addAddress>[1],
@@ -86,7 +89,7 @@ export function createCustomerPortalRoutes(kernel: Kernel) {
86
89
 
87
90
  // @ts-expect-error -- openapi handler union return type
88
91
  router.openapi(deleteAddressRoute, async (c) => {
89
- const actor = c.get("actor") as Actor;
92
+ const actor = c.get("actor") as AuthenticatedActor;
90
93
  const result = await kernel.services.customers.deleteAddress(
91
94
  actor.userId,
92
95
  c.req.param("id"),
@@ -98,23 +101,27 @@ export function createCustomerPortalRoutes(kernel: Kernel) {
98
101
 
99
102
  // @ts-expect-error -- openapi handler union return type
100
103
  router.openapi(listCustomerOrdersRoute, async (c) => {
101
- const actor = c.get("actor") as Actor;
104
+ const actor = c.get("actor") as AuthenticatedActor;
102
105
  const status = c.req.query("status");
103
106
  // Resolve customer profile UUID from Better Auth userId
104
107
  const customerResult = await kernel.services.customers.getByUserId(actor.userId, actor);
105
108
  if (!customerResult.ok) return c.json({ data: [], meta: { total: 0, page: 1, limit: 20, totalPages: 0 } });
106
- const result = await kernel.services.orders.listByCustomer(customerResult.value.id, {
107
- page: Number.parseInt(c.req.query("page") ?? "1", 10),
108
- limit: Number.parseInt(c.req.query("limit") ?? "20", 10),
109
- ...(status !== undefined ? { status } : {}),
110
- });
111
- if (!result.ok) return c.json({ error: result.error }, 500);
109
+ const result = await kernel.services.orders.listByCustomer(
110
+ customerResult.value.id,
111
+ {
112
+ page: Number.parseInt(c.req.query("page") ?? "1", 10),
113
+ limit: Number.parseInt(c.req.query("limit") ?? "20", 10),
114
+ ...(status !== undefined ? { status } : {}),
115
+ },
116
+ actor,
117
+ );
118
+ if (!result.ok) return c.json({ error: result.error }, mapErrorToStatus(result.error));
112
119
  return c.json({ data: result.value.items, meta: result.value.pagination });
113
120
  });
114
121
 
115
122
  // @ts-expect-error -- openapi handler union return type
116
123
  router.openapi(getCustomerOrderRoute, async (c) => {
117
- const actor = c.get("actor") as Actor;
124
+ const actor = c.get("actor") as AuthenticatedActor;
118
125
  const id = c.req.param("idOrNumber");
119
126
  const customerActor = await resolveCustomerActor(actor);
120
127
  if (!customerActor) return c.json({ error: { code: "NOT_FOUND", message: "Customer profile not found." } }, 404);
@@ -129,7 +136,7 @@ export function createCustomerPortalRoutes(kernel: Kernel) {
129
136
 
130
137
  // @ts-expect-error -- openapi handler union return type
131
138
  router.openapi(getOrderTrackingRoute, async (c) => {
132
- const actor = c.get("actor") as Actor;
139
+ const actor = c.get("actor") as AuthenticatedActor;
133
140
  const id = c.req.param("idOrNumber");
134
141
  const customerActor = await resolveCustomerActor(actor);
135
142
  if (!customerActor) return c.json({ error: { code: "NOT_FOUND", message: "Customer profile not found." } }, 404);
@@ -167,7 +174,7 @@ export function createCustomerPortalRoutes(kernel: Kernel) {
167
174
 
168
175
  // @ts-expect-error -- openapi handler union return type
169
176
  router.openapi(getOrderDownloadsRoute, async (c) => {
170
- const actor = c.get("actor") as Actor;
177
+ const actor = c.get("actor") as AuthenticatedActor;
171
178
  const customerActor = await resolveCustomerActor(actor);
172
179
  if (!customerActor) return c.json({ error: { code: "NOT_FOUND", message: "Customer profile not found." } }, 404);
173
180
  const orderResult = await kernel.services.orders.getById(
@@ -209,7 +216,7 @@ export function createCustomerPortalRoutes(kernel: Kernel) {
209
216
 
210
217
  // @ts-expect-error -- openapi handler union return type
211
218
  router.openapi(listCoursesRoute, async (c) => {
212
- const actor = c.get("actor") as Actor;
219
+ const actor = c.get("actor") as AuthenticatedActor;
213
220
  const result = await kernel.services.fulfillment.getDigitalAccess(
214
221
  actor.userId,
215
222
  "course",
@@ -230,7 +237,7 @@ export function createCustomerPortalRoutes(kernel: Kernel) {
230
237
 
231
238
  // @ts-expect-error -- openapi handler union return type
232
239
  router.openapi(reorderRoute, async (c) => {
233
- const actor = c.get("actor") as Actor;
240
+ const actor = c.get("actor") as AuthenticatedActor;
234
241
  const customerActor = await resolveCustomerActor(actor);
235
242
  if (!customerActor) return c.json({ error: { code: "NOT_FOUND", message: "Customer profile not found." } }, 404);
236
243
  const orderResult = await kernel.services.orders.getById(
@@ -246,7 +253,7 @@ export function createCustomerPortalRoutes(kernel: Kernel) {
246
253
 
247
254
  const cartResult = await kernel.services.cart.create(
248
255
  {
249
- customerId: actor.userId,
256
+ customerId: requireUserId(actor),
250
257
  currency: orderResult.value.currency,
251
258
  },
252
259
  actor,
@@ -21,6 +21,8 @@ import { adminJobRoutes } from "./routes/admin-jobs.js";
21
21
  import { compensationFailureAdminRoutes } from "./routes/admin/compensation-failures.js";
22
22
  import { adminPermissionsRoutes } from "./routes/admin/permissions.js";
23
23
  import { adminStaffRoutes } from "./routes/admin/staff.js";
24
+ import { adminEntityFieldDefinitionRoutes } from "./routes/admin/entity-field-definitions.js";
25
+ import { adminCustomFieldProposalRoutes } from "./routes/admin/custom-field-proposals.js";
24
26
  import { customerRoutes } from "./routes/customers.js";
25
27
  import { documentRoutes } from "./routes/documents.js";
26
28
  import { settingsRoutes } from "./routes/settings.js";
@@ -94,6 +96,8 @@ export function createRestRoutes(kernel: Kernel) {
94
96
  router.route("/admin", compensationFailureAdminRoutes(kernel));
95
97
  router.route("/admin", adminPermissionsRoutes(kernel));
96
98
  router.route("/admin", adminStaffRoutes(kernel));
99
+ router.route("/admin", adminEntityFieldDefinitionRoutes(kernel));
100
+ router.route("/admin", adminCustomFieldProposalRoutes(kernel));
97
101
 
98
102
  // API Reference (Scalar) — disabled in production unless config.exposeOpenApiSpec is true
99
103
  const exposeSpec = kernel.config.exposeOpenApiSpec ?? (process.env.NODE_ENV !== "production");