@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
@@ -22,8 +22,8 @@ import type { PluginDb } from "../../../kernel/database/plugin-types.js";
22
22
  import { type AppEnv, mapErrorToResponse, mapErrorToStatus } from "../utils.js";
23
23
  import { isCommerceError } from "../../../kernel/errors.js";
24
24
  import { assertPermission } from "../../../auth/permissions.js";
25
- import { resolveOrgId } from "../../../auth/org.js";
26
- import { makeDeterministicId, makeId } from "../../../utils/id.js";
25
+ import { resolveOrgIdForCommerce } from "../../../auth/org.js";
26
+ import { makeDeterministicId, makeId, makeIdempotencyScope } from "../../../utils/id.js";
27
27
  import type { ShippingAddress } from "../../../modules/shipping/calculator.js";
28
28
  import type { Actor } from "../../../auth/types.js";
29
29
 
@@ -85,50 +85,48 @@ export function checkoutRoutes(kernel: Kernel) {
85
85
  const body = c.req.valid("json");
86
86
 
87
87
  const actor = c.get("actor");
88
+ const cartSecret = c.req.header("x-cart-secret") ?? c.req.header("X-Cart-Secret") ?? undefined;
88
89
 
89
- // Idempotent replay: a re-submitted checkout (offline POS queue, network
90
- // retry) returns the already-created order BEFORE the pipeline runs — no
91
- // double payment authorization, no duplicate order.
90
+ // Idempotent replay is authorized by the order service before payment runs.
91
+ // This preflight only preserves the no-double-payment fast path; the create
92
+ // primitive repeats the same binding for direct callers and race losers.
93
+ let replay: Awaited<ReturnType<typeof kernel.services.orders.getByIdempotencyKey>> | undefined;
92
94
  if (body.idempotencyKey) {
93
- const replay = await kernel.services.orders.getByIdempotencyKey(
95
+ replay = await kernel.services.orders.getByIdempotencyKey(
94
96
  body.idempotencyKey,
95
97
  actor,
98
+ undefined,
99
+ cartSecret,
96
100
  );
97
- if (replay.ok && replay.value) {
98
- // IDOR guard: an idempotency key must only replay the requester's OWN
99
- // order. Without this, a same-org customer who supplies/guesses another
100
- // customer's key would receive that customer's order (PII, totals, lines).
101
- let canActForOthers = false;
102
- try {
103
- assertPermission(actor, "customers:read");
104
- canActForOthers = true;
105
- } catch {
106
- canActForOthers = false;
107
- }
108
- const ownCustomer = actor
109
- ? await resolveCheckoutCustomerUuid(kernel.services.customers, actor, undefined)
110
- : undefined;
111
- const replayCustomer = replay.value.customerId ?? null;
112
- const ownsOrder =
113
- (replayCustomer !== null && replayCustomer === ownCustomer) ||
114
- (replayCustomer === null && !ownCustomer);
115
- if (canActForOthers || ownsOrder) {
116
- return c.json({ data: replay.value }, 201);
117
- }
118
- return c.json(
119
- {
120
- error: {
121
- code: "IDEMPOTENCY_CONFLICT",
122
- message: "Idempotency key does not belong to this account.",
123
- },
124
- },
125
- 409,
126
- );
101
+ if (!replay.ok) {
102
+ return c.json(mapErrorToResponse(replay.error), mapErrorToStatus(replay.error));
127
103
  }
104
+ if (replay.value) return c.json({ data: replay.value }, 201);
128
105
  }
129
106
 
107
+ // Authenticate the cart once at the HTTP boundary. Guest checkout is
108
+ // authorized by the cart secret; customer checkout is authorized by the
109
+ // customer cart ownership check. The hook pipeline uses its explicit
110
+ // internal cart-read path instead of carrying this credential inward.
111
+ const cartAccess = await kernel.services.cart.getById(
112
+ body.cartId,
113
+ actor,
114
+ undefined,
115
+ cartSecret,
116
+ );
117
+ if (!cartAccess.ok) {
118
+ return c.json(
119
+ mapErrorToResponse(cartAccess.error),
120
+ mapErrorToStatus(cartAccess.error),
121
+ );
122
+ }
123
+ const idempotencyScope = body.idempotencyKey
124
+ ? await makeIdempotencyScope(actor, cartSecret, body.cartId)
125
+ : undefined;
130
126
  const orderId = body.idempotencyKey
131
- ? await makeDeterministicId(`checkout:${resolveOrgId(actor)}:${body.idempotencyKey}`)
127
+ ? await makeDeterministicId(
128
+ `checkout:${resolveOrgIdForCommerce(actor, kernel.config)}:${idempotencyScope}:${body.idempotencyKey}`,
129
+ )
132
130
  : makeId();
133
131
  const checkoutData: CheckoutData = {
134
132
  checkoutId: makeId(),
@@ -203,6 +201,7 @@ export function checkoutRoutes(kernel: Kernel) {
203
201
  context: { moduleName: "checkout" },
204
202
  origin: "rest",
205
203
  database: { db: kernel.database.db as PluginDb },
204
+ commerceConfig: kernel.config,
206
205
  });
207
206
 
208
207
  let pipelineStage = "validate-and-calculate";
@@ -293,6 +292,7 @@ export function checkoutRoutes(kernel: Kernel) {
293
292
  pipelineStage = "create-order";
294
293
  const order = await kernel.services.orders.create(orderPayload, actor, undefined, {
295
294
  trustedPricing: true,
295
+ ...(cartSecret !== undefined ? { guestCredential: cartSecret } : {}),
296
296
  });
297
297
 
298
298
  if (!order.ok) {
@@ -379,7 +379,10 @@ export function checkoutRoutes(kernel: Kernel) {
379
379
  // Validation, payment authorization, or compensated completion may fail
380
380
  // after the cart's atomic active -> checking_out claim. Release only
381
381
  // that transient state so the shopper can correct the problem and retry.
382
- await kernel.services.cart.releaseCheckoutClaim(body.cartId).catch(
382
+ // Releasing is scoped to this attempt's claim token: an attempt that lost
383
+ // the claim never held it, so its failure cannot reopen a cart another
384
+ // checkout is still working through.
385
+ await kernel.services.cart.releaseCheckoutClaim(body.cartId, checkoutData.checkoutId).catch(
383
386
  (releaseError: unknown) => {
384
387
  console.error("[checkout] Failed to release cart claim:", {
385
388
  cartId: body.cartId,
@@ -6,7 +6,7 @@ import {
6
6
  invoicePdfRoute,
7
7
  receiptHtmlRoute,
8
8
  } from "../schemas/documents.js";
9
- import { type AppEnv, mapErrorToResponse, mapErrorToStatus } from "../utils.js";
9
+ import { type AppEnv, mapErrorToResponse, mapErrorToStatus, requireAnyPerm } from "../utils.js";
10
10
 
11
11
  /**
12
12
  * Order document rendering (issue #47). Mounted under /orders. Access
@@ -16,10 +16,15 @@ import { type AppEnv, mapErrorToResponse, mapErrorToStatus } from "../utils.js";
16
16
  export function documentRoutes(kernel: Kernel) {
17
17
  const router = new OpenAPIHono<AppEnv>();
18
18
 
19
+ router.use("/*", requireAnyPerm(["orders:read", "orders:read:own"]));
20
+
19
21
  router.openapi(invoicePdfRoute, async (c) => {
22
+ const guestCredential = c.req.header("x-cart-secret") ?? c.req.header("X-Cart-Secret") ?? undefined;
20
23
  const result = await kernel.services.documents.renderInvoicePdf(
21
24
  c.req.param("id"),
22
25
  c.get("actor"),
26
+ undefined,
27
+ guestCredential,
23
28
  );
24
29
  if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
25
30
  return c.body(result.value.pdf.slice().buffer as ArrayBuffer, 200, {
@@ -29,18 +34,24 @@ export function documentRoutes(kernel: Kernel) {
29
34
  });
30
35
 
31
36
  router.openapi(invoiceHtmlRoute, async (c) => {
37
+ const guestCredential = c.req.header("x-cart-secret") ?? c.req.header("X-Cart-Secret") ?? undefined;
32
38
  const result = await kernel.services.documents.renderInvoiceHtml(
33
39
  c.req.param("id"),
34
40
  c.get("actor"),
41
+ undefined,
42
+ guestCredential,
35
43
  );
36
44
  if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
37
45
  return c.html(result.value.html);
38
46
  });
39
47
 
40
48
  router.openapi(receiptHtmlRoute, async (c) => {
49
+ const guestCredential = c.req.header("x-cart-secret") ?? c.req.header("X-Cart-Secret") ?? undefined;
41
50
  const result = await kernel.services.documents.renderReceiptHtml(
42
51
  c.req.param("id"),
43
52
  c.get("actor"),
53
+ undefined,
54
+ guestCredential,
44
55
  );
45
56
  if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
46
57
  return c.html(result.value.html);
@@ -48,10 +59,13 @@ export function documentRoutes(kernel: Kernel) {
48
59
 
49
60
  // @ts-expect-error -- openapi handler union return type
50
61
  router.openapi(emailInvoiceRoute, async (c) => {
62
+ const guestCredential = c.req.header("x-cart-secret") ?? c.req.header("X-Cart-Secret") ?? undefined;
51
63
  const result = await kernel.services.documents.emailInvoice(
52
64
  c.req.param("id"),
53
65
  c.req.valid("json").to,
54
66
  c.get("actor"),
67
+ undefined,
68
+ guestCredential,
55
69
  );
56
70
  if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
57
71
  return c.json({ data: result.value });
@@ -24,6 +24,8 @@ export function inventoryRoutes(kernel: Kernel) {
24
24
  router.use("/warehouses", requirePerm("inventory:read"));
25
25
  router.use("/reserve", requirePerm("inventory:adjust"));
26
26
  router.use("/release", requirePerm("inventory:adjust"));
27
+ router.use("/check", requirePerm("inventory:read"));
28
+ router.use("/adjust", requirePerm("inventory:adjust"));
27
29
 
28
30
  // @ts-expect-error -- openapi handler union return type
29
31
  router.openapi(listInventoryLevelsRoute, async (c) => {
@@ -2,13 +2,14 @@ import { OpenAPIHono } from "@hono/zod-openapi";
2
2
  import type { Kernel } from "../../../runtime/kernel.js";
3
3
  import type { AttachMediaInput } from "../../../modules/media/service.js";
4
4
  import { attachMediaRoute, getMediaRoute, deleteMediaRoute } from "../schemas/media.js";
5
- import { type AppEnv, mapErrorToResponse, mapErrorToStatus, requirePerm } from "../utils.js";
5
+ import { type AppEnv, mapErrorToResponse, mapErrorToStatus, requireMethodPerm, requirePerm } from "../utils.js";
6
6
 
7
7
  export function mediaRoutes(kernel: Kernel) {
8
8
  const router = new OpenAPIHono<AppEnv>();
9
9
 
10
10
  // Upload requires authentication + media:write permission
11
11
  router.use("/upload", requirePerm("media:write"));
12
+ router.use("/:id", requireMethodPerm(["DELETE"], "media:write"));
12
13
 
13
14
  router.post("/upload", async (c) => {
14
15
  // No storage configured (the default no-op adapter) → media is disabled.
@@ -43,16 +44,20 @@ export function mediaRoutes(kernel: Kernel) {
43
44
 
44
45
  router.openapi(getMediaRoute, async (c) => {
45
46
  const signed = c.req.query("signed") === "true";
47
+ const actor = c.get("actor");
48
+
49
+ if (!actor && !signed) {
50
+ return c.json({ error: { code: "NOT_FOUND", message: "Media not found." } }, 404);
51
+ }
46
52
 
47
53
  // Signed URLs require authentication
48
- if (signed && !c.get("actor")) {
54
+ if (signed && !actor) {
49
55
  return c.json(
50
56
  { error: { code: "UNAUTHORIZED", message: "Authentication required for signed URLs." } },
51
57
  401,
52
58
  );
53
59
  }
54
60
 
55
- const actor = c.get("actor");
56
61
  const result = signed
57
62
  ? await kernel.services.media.getSignedUrl(c.req.param("id"), undefined, actor)
58
63
  : await kernel.services.media.getUrl(c.req.param("id"), actor);
@@ -1,7 +1,7 @@
1
1
  import { OpenAPIHono } from "@hono/zod-openapi";
2
2
  import type { Kernel } from "../../../runtime/kernel.js";
3
3
  import { changeOrderStatusRoute, listOrdersRoute, orderLookupRoute, getOrderRoute, getOrderFulfillmentsRoute, createOrderRoute, quoteOrderRoute, refundOrderRoute, captureOrderRoute, createOrderFulfillmentRoute, addOrderLineItemRoute, updateOrderLineItemRoute, removeOrderLineItemRoute, refundOrderLinesRoute, undoOrderRefundRoute, listOrderRefundsRoute, refundCapStatusRoute, createOrderNoteRoute, listOrderNotesRoute, deleteOrderNoteRoute, orderTimelineRoute } from "../schemas/orders.js";
4
- import { type AppEnv, isUUID, mapErrorToResponse, mapErrorToStatus, parsePagination } from "../utils.js";
4
+ import { type AppEnv, isUUID, mapErrorToResponse, mapErrorToStatus, parsePagination, requireAnyPerm, requireMethodPerm, requirePerm } from "../utils.js";
5
5
  import type { CreateOrderInput } from "../../../modules/orders/service.js";
6
6
  import { computeOrderPricing } from "../../../modules/orders/quote.js";
7
7
  import { assertPermission } from "../../../auth/permissions.js";
@@ -9,6 +9,13 @@ import { assertPermission } from "../../../auth/permissions.js";
9
9
  export function orderRoutes(kernel: Kernel) {
10
10
  const router = new OpenAPIHono<AppEnv>();
11
11
 
12
+ router.use("/", requireMethodPerm(["GET"], "orders:read"));
13
+ router.use("/", requireMethodPerm(["POST"], "orders:create"));
14
+ router.use("/lookup", requirePerm("orders:read"));
15
+ router.use("/:idOrNumber", requireAnyPerm(["orders:read", "orders:read:own"]));
16
+ router.use("/:id/*", requireAnyPerm(["orders:read", "orders:read:own", "orders:update", "orders:manage"]));
17
+ router.use("/refunds/cap", requirePerm("orders:read"));
18
+
12
19
  // @ts-expect-error -- openapi handler union return type
13
20
  router.openapi(listOrdersRoute, async (c) => {
14
21
  const pagination = parsePagination(c.req.query());
@@ -54,9 +61,10 @@ export function orderRoutes(kernel: Kernel) {
54
61
  // @ts-expect-error -- openapi handler union return type
55
62
  router.openapi(getOrderRoute, async (c) => {
56
63
  const idOrNumber = c.req.param("idOrNumber");
64
+ const guestCredential = c.req.header("x-cart-secret") ?? c.req.header("X-Cart-Secret") ?? undefined;
57
65
  const result = isUUID(idOrNumber)
58
- ? await kernel.services.orders.getById(idOrNumber, c.get("actor"))
59
- : await kernel.services.orders.getByNumber(idOrNumber, c.get("actor"));
66
+ ? await kernel.services.orders.getById(idOrNumber, c.get("actor"), undefined, guestCredential)
67
+ : await kernel.services.orders.getByNumber(idOrNumber, c.get("actor"), undefined, guestCredential);
60
68
 
61
69
  if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
62
70
  return c.json({ data: result.value });
@@ -65,12 +73,15 @@ export function orderRoutes(kernel: Kernel) {
65
73
  // @ts-expect-error -- openapi handler union return type
66
74
  router.openapi(createOrderRoute, async (c) => {
67
75
  try {
68
- assertPermission(c.get("actor"), "orders:manage");
76
+ assertPermission(c.get("actor"), "orders:create");
69
77
  } catch (error) {
70
78
  return c.json(mapErrorToResponse(error), mapErrorToStatus(error));
71
79
  }
72
80
  const body = c.req.valid("json") as CreateOrderInput;
73
- const result = await kernel.services.orders.create(body, c.get("actor"));
81
+ const guestCredential = c.req.header("x-cart-secret") ?? c.req.header("X-Cart-Secret") ?? undefined;
82
+ const result = await kernel.services.orders.create(body, c.get("actor"), undefined, {
83
+ ...(guestCredential !== undefined ? { guestCredential } : {}),
84
+ });
74
85
  if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
75
86
  return c.json({ data: result.value }, 201);
76
87
  });
@@ -273,8 +284,15 @@ export function orderRoutes(kernel: Kernel) {
273
284
  const actor = c.get("actor");
274
285
  const orderId = c.req.param("id");
275
286
 
287
+ try {
288
+ assertPermission(actor, "orders:update");
289
+ } catch (error) {
290
+ return c.json(mapErrorToResponse(error), mapErrorToStatus(error));
291
+ }
292
+ const guestCredential = c.req.header("x-cart-secret") ?? c.req.header("X-Cart-Secret") ?? undefined;
293
+
276
294
  // Verify the order exists and the actor has access before recording
277
- const orderResult = await kernel.services.orders.getById(orderId, actor);
295
+ const orderResult = await kernel.services.orders.getById(orderId, actor, undefined, guestCredential);
278
296
  if (!orderResult.ok) return c.json(mapErrorToResponse(orderResult.error), mapErrorToStatus(orderResult.error));
279
297
 
280
298
  const body = c.req.valid("json") as {
@@ -298,11 +316,12 @@ export function orderRoutes(kernel: Kernel) {
298
316
  router.openapi(getOrderFulfillmentsRoute, async (c) => {
299
317
  const orderId = c.req.param("id");
300
318
  const actor = c.get("actor");
319
+ const guestCredential = c.req.header("x-cart-secret") ?? c.req.header("X-Cart-Secret") ?? undefined;
301
320
 
302
321
  // Verify the order exists and the actor has access before returning fulfillments
303
322
  const orderResult = isUUID(orderId)
304
- ? await kernel.services.orders.getById(orderId, actor)
305
- : await kernel.services.orders.getByNumber(orderId, actor);
323
+ ? await kernel.services.orders.getById(orderId, actor, undefined, guestCredential)
324
+ : await kernel.services.orders.getByNumber(orderId, actor, undefined, guestCredential);
306
325
 
307
326
  if (!orderResult.ok) return c.json(mapErrorToResponse(orderResult.error), mapErrorToStatus(orderResult.error));
308
327
 
@@ -1,5 +1,5 @@
1
1
  import { OpenAPIHono } from "@hono/zod-openapi";
2
- import { resolveOrgId } from "../../../auth/org.js";
2
+ import { resolveOrgIdForCommerce } from "../../../auth/org.js";
3
3
  import { CommerceValidationError } from "../../../kernel/errors.js";
4
4
  import type { DrizzleDatabase } from "../../../kernel/database/drizzle-db.js";
5
5
  import { CatalogRepository } from "../../../modules/catalog/repository/index.js";
@@ -98,7 +98,7 @@ export function pricingRoutes(kernel: Kernel) {
98
98
 
99
99
  if (input.entityId) {
100
100
  const catalogRepo = new CatalogRepository(kernel.database.db as DrizzleDatabase);
101
- const orgId = resolveOrgId(actor);
101
+ const orgId = resolveOrgIdForCommerce(actor, kernel.config);
102
102
  const entity = await catalogRepo.findEntityById(input.entityId);
103
103
  if (!entity || entity.organizationId !== orgId) {
104
104
  return c.json(
@@ -3,8 +3,9 @@ import { rateLimiter } from "hono-rate-limiter";
3
3
  import type { Kernel } from "../../../runtime/kernel.js";
4
4
  import { createPromotionRoute, updatePromotionRoute, validatePromotionRoute, deactivatePromotionRoute, listPromotionsRoute } from "../schemas/promotions.js";
5
5
  import type { PromotionStatusFilter } from "../../../modules/promotions/service.js";
6
- import { type AppEnv, mapErrorToResponse, mapErrorToStatus, requirePerm } from "../utils.js";
7
- import { resolveOrgId } from "../../../auth/org.js";
6
+ import { type AppEnv, mapErrorToResponse, mapErrorToStatus, requireMethodPerm, requirePerm } from "../utils.js";
7
+ import { resolveOrgIdForCommerce } from "../../../auth/org.js";
8
+ import { assertPermission } from "../../../auth/permissions.js";
8
9
 
9
10
  export function promotionRoutes(kernel: Kernel) {
10
11
  const router = new OpenAPIHono<AppEnv>();
@@ -59,7 +60,7 @@ export function promotionRoutes(kernel: Kernel) {
59
60
  router.openapi(validatePromotionRoute, async (c) => {
60
61
  const payload = c.req.valid("json");
61
62
  const actor = c.get("actor");
62
- const orgId = resolveOrgId(actor);
63
+ const orgId = resolveOrgIdForCommerce(actor, kernel.config);
63
64
 
64
65
  // Apply (not just validate) so the response carries the authoritative
65
66
  // discount the cart would receive — the same computation checkout runs.
@@ -84,10 +85,7 @@ export function promotionRoutes(kernel: Kernel) {
84
85
 
85
86
  // Guard inline (not via router.use("/:id")) because a "/:id" middleware
86
87
  // would also match single-segment routes like POST /validate.
87
- router.use("/:id", async (c, next) => {
88
- if (c.req.method !== "PATCH") return next();
89
- return requirePerm("promotions:manage")(c, next);
90
- });
88
+ router.use("/:id", requireMethodPerm(["PATCH"], "promotions:manage"));
91
89
 
92
90
  // @ts-expect-error -- openapi() enforces strict response typing but our handler
93
91
  // returns union responses (200 | 400 | 404 | 422). The route definition
@@ -95,7 +93,12 @@ export function promotionRoutes(kernel: Kernel) {
95
93
  router.openapi(updatePromotionRoute, async (c) => {
96
94
  const body = c.req.valid("json");
97
95
  const actor = c.get("actor");
98
- const orgId = resolveOrgId(actor);
96
+ try {
97
+ assertPermission(actor, "promotions:manage");
98
+ } catch (error) {
99
+ return c.json(mapErrorToResponse(error), mapErrorToStatus(error));
100
+ }
101
+ const orgId = resolveOrgIdForCommerce(actor, kernel.config);
99
102
  const result = await kernel.services.promotions.update(orgId, c.req.param("id"), body, actor);
100
103
  if (!result.ok) {
101
104
  return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
@@ -110,7 +113,7 @@ export function promotionRoutes(kernel: Kernel) {
110
113
  // contract; the handler returns dynamic status.
111
114
  router.openapi(deactivatePromotionRoute, async (c) => {
112
115
  const actor = c.get("actor");
113
- const orgId = resolveOrgId(actor);
116
+ const orgId = resolveOrgIdForCommerce(actor, kernel.config);
114
117
  const result = await kernel.services.promotions.deactivate(orgId, c.req.param("id"));
115
118
  if (!result.ok) {
116
119
  return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
@@ -1,11 +1,15 @@
1
1
  import { OpenAPIHono } from "@hono/zod-openapi";
2
2
  import type { Kernel } from "../../../runtime/kernel.js";
3
3
  import { searchRoute, suggestRoute } from "../schemas/search.js";
4
- import { type AppEnv, mapErrorToResponse, mapErrorToStatus } from "../utils.js";
4
+ import { type AppEnv, mapErrorToResponse, mapErrorToStatus, requirePerm } from "../utils.js";
5
+
6
+ const SAFE_ATTRIBUTE_NAME = /^[A-Za-z0-9_-]+$/;
5
7
 
6
8
  export function searchRoutes(kernel: Kernel) {
7
9
  const router = new OpenAPIHono<AppEnv>();
8
10
 
11
+ router.use("/*", requirePerm("catalog:read"));
12
+
9
13
  // @ts-expect-error -- openapi handler union return type
10
14
  router.openapi(searchRoute, async (c) => {
11
15
  try {
@@ -21,6 +25,19 @@ export function searchRoutes(kernel: Kernel) {
21
25
  .map((facet) => facet.trim())
22
26
  .filter(Boolean);
23
27
 
28
+ const attributes: Record<string, string | string[]> = {};
29
+ for (const [key, values] of Object.entries(c.req.queries())) {
30
+ if (!key.startsWith("attr.")) continue;
31
+ const name = key.slice("attr.".length);
32
+ if (!SAFE_ATTRIBUTE_NAME.test(name)) {
33
+ return c.json(
34
+ { error: "invalid_argument", field: key, message: `Invalid attribute filter name: ${name}` },
35
+ 422,
36
+ );
37
+ }
38
+ attributes[name] = values.length === 1 ? values[0]! : values;
39
+ }
40
+
24
41
  const result = await kernel.services.search.query({
25
42
  query: q,
26
43
  page,
@@ -30,6 +47,7 @@ export function searchRoutes(kernel: Kernel) {
30
47
  ...(category ? { category } : {}),
31
48
  ...(brand ? { brand } : {}),
32
49
  ...(status ? { status } : {}),
50
+ ...(Object.keys(attributes).length > 0 ? { attributes } : {}),
33
51
  },
34
52
  ...(facets.length > 0 ? { facets } : {}),
35
53
  }, { actor: c.get("actor"), tx: null, requestId: "" });
@@ -0,0 +1,29 @@
1
+ import { createRoute, z } from "@hono/zod-openapi";
2
+ import { errorResponses } from "./shared.js";
3
+
4
+ const ProposalResponse = z.object({
5
+ data: z.object({
6
+ items: z.array(z.any()),
7
+ total: z.number(),
8
+ page: z.number(),
9
+ limit: z.number(),
10
+ }),
11
+ });
12
+
13
+ const ListQuery = z.object({
14
+ entityType: z.string().min(1).optional(),
15
+ page: z.string().max(10).optional(),
16
+ limit: z.string().max(10).optional(),
17
+ });
18
+
19
+ export const listCustomFieldProposalsRoute = createRoute({
20
+ method: "get",
21
+ path: "/custom-field-proposals",
22
+ tags: ["Admin"],
23
+ summary: "List proposed custom-field values",
24
+ request: { query: ListQuery },
25
+ responses: {
26
+ 200: { content: { "application/json": { schema: ProposalResponse } }, description: "Proposed custom fields." },
27
+ ...errorResponses,
28
+ },
29
+ });
@@ -0,0 +1,120 @@
1
+ import { createRoute, z } from "@hono/zod-openapi";
2
+ import { errorResponses } from "./shared.js";
3
+
4
+ const FieldType = z.enum([
5
+ "text",
6
+ "number",
7
+ "boolean",
8
+ "date",
9
+ "json",
10
+ "relation",
11
+ "select",
12
+ ]);
13
+
14
+ const CreateBody = z
15
+ .object({
16
+ entityType: z.string().min(1),
17
+ name: z.string().min(1),
18
+ type: FieldType,
19
+ unit: z.string().nullable().optional(),
20
+ options: z.array(z.string()).nullable().optional(),
21
+ target: z.string().nullable().optional(),
22
+ filterable: z.boolean().optional(),
23
+ localized: z.boolean().optional(),
24
+ sortOrder: z.number().int().optional(),
25
+ })
26
+ .openapi("CreateEntityFieldDefinitionRequest");
27
+
28
+ const UpdateBody = z
29
+ .object({
30
+ entityType: z.string().min(1).optional(),
31
+ name: z.string().min(1).optional(),
32
+ type: FieldType.optional(),
33
+ unit: z.string().nullable().optional(),
34
+ options: z.array(z.string()).nullable().optional(),
35
+ target: z.string().nullable().optional(),
36
+ filterable: z.boolean().optional(),
37
+ localized: z.boolean().optional(),
38
+ sortOrder: z.number().int().optional(),
39
+ })
40
+ .openapi("UpdateEntityFieldDefinitionRequest");
41
+
42
+ const IdParam = z.object({ id: z.uuid() });
43
+ const ListQuery = z.object({ entityType: z.string().min(1).optional() });
44
+ const DataResponse = z
45
+ .object({ data: z.any() })
46
+ .openapi("EntityFieldDefinitionResponse");
47
+
48
+ export const createEntityFieldDefinitionRoute = createRoute({
49
+ method: "post",
50
+ path: "/entity-field-definitions",
51
+ tags: ["Admin"],
52
+ summary: "Create a runtime entity field definition",
53
+ request: {
54
+ body: {
55
+ content: { "application/json": { schema: CreateBody } },
56
+ required: true,
57
+ },
58
+ },
59
+ responses: {
60
+ 201: {
61
+ content: { "application/json": { schema: DataResponse } },
62
+ description: "Definition created.",
63
+ },
64
+ ...errorResponses,
65
+ },
66
+ });
67
+
68
+ export const listEntityFieldDefinitionsRoute = createRoute({
69
+ method: "get",
70
+ path: "/entity-field-definitions",
71
+ tags: ["Admin"],
72
+ summary: "List runtime entity field definitions",
73
+ request: { query: ListQuery },
74
+ responses: {
75
+ 200: {
76
+ content: { "application/json": { schema: DataResponse } },
77
+ description: "Definitions.",
78
+ },
79
+ ...errorResponses,
80
+ },
81
+ });
82
+
83
+ export const updateEntityFieldDefinitionRoute = createRoute({
84
+ method: "patch",
85
+ path: "/entity-field-definitions/{id}",
86
+ tags: ["Admin"],
87
+ summary: "Update a runtime entity field definition",
88
+ request: {
89
+ params: IdParam,
90
+ body: {
91
+ content: { "application/json": { schema: UpdateBody } },
92
+ required: true,
93
+ },
94
+ },
95
+ responses: {
96
+ 200: {
97
+ content: { "application/json": { schema: DataResponse } },
98
+ description: "Definition updated.",
99
+ },
100
+ ...errorResponses,
101
+ },
102
+ });
103
+
104
+ export const archiveEntityFieldDefinitionRoute = createRoute({
105
+ method: "post",
106
+ path: "/entity-field-definitions/{id}/archive",
107
+ tags: ["Admin"],
108
+ summary: "Archive a runtime entity field definition",
109
+ request: { params: IdParam },
110
+ responses: {
111
+ 200: {
112
+ content: { "application/json": { schema: DataResponse } },
113
+ description: "Definition archived.",
114
+ },
115
+ ...errorResponses,
116
+ },
117
+ });
118
+
119
+ export type CreateEntityFieldDefinitionBody = z.infer<typeof CreateBody>;
120
+ export type UpdateEntityFieldDefinitionBody = z.infer<typeof UpdateBody>;