@porulle/core 0.11.0 → 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 (289) 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 +30 -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 +6 -5
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +4 -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/route-coverage.d.ts +14 -0
  42. package/dist/interfaces/rest/route-coverage.d.ts.map +1 -0
  43. package/dist/interfaces/rest/route-coverage.js +81 -0
  44. package/dist/interfaces/rest/router.d.ts +7 -2
  45. package/dist/interfaces/rest/router.d.ts.map +1 -1
  46. package/dist/interfaces/rest/router.js +8 -4
  47. package/dist/interfaces/rest/routes/admin/compensation-failures.d.ts.map +1 -1
  48. package/dist/interfaces/rest/routes/admin/compensation-failures.js +7 -5
  49. package/dist/interfaces/rest/routes/admin/custom-field-proposals.js +2 -2
  50. package/dist/interfaces/rest/routes/admin/permissions.d.ts +1 -1
  51. package/dist/interfaces/rest/routes/admin/permissions.d.ts.map +1 -1
  52. package/dist/interfaces/rest/routes/admin/permissions.js +2 -0
  53. package/dist/interfaces/rest/routes/admin/staff.d.ts.map +1 -1
  54. package/dist/interfaces/rest/routes/admin/staff.js +126 -66
  55. package/dist/interfaces/rest/routes/admin-jobs.js +3 -3
  56. package/dist/interfaces/rest/routes/audit.js +3 -3
  57. package/dist/interfaces/rest/routes/carts.d.ts.map +1 -1
  58. package/dist/interfaces/rest/routes/carts.js +13 -10
  59. package/dist/interfaces/rest/routes/catalog.d.ts.map +1 -1
  60. package/dist/interfaces/rest/routes/catalog.js +10 -2
  61. package/dist/interfaces/rest/routes/checkout.d.ts.map +1 -1
  62. package/dist/interfaces/rest/routes/checkout.js +30 -35
  63. package/dist/interfaces/rest/routes/documents.d.ts.map +1 -1
  64. package/dist/interfaces/rest/routes/documents.js +10 -5
  65. package/dist/interfaces/rest/routes/inventory.d.ts.map +1 -1
  66. package/dist/interfaces/rest/routes/inventory.js +2 -0
  67. package/dist/interfaces/rest/routes/media.d.ts.map +1 -1
  68. package/dist/interfaces/rest/routes/media.js +7 -3
  69. package/dist/interfaces/rest/routes/orders.d.ts.map +1 -1
  70. package/dist/interfaces/rest/routes/orders.js +26 -8
  71. package/dist/interfaces/rest/routes/pricing.js +2 -2
  72. package/dist/interfaces/rest/routes/promotions.d.ts.map +1 -1
  73. package/dist/interfaces/rest/routes/promotions.js +13 -10
  74. package/dist/interfaces/rest/routes/search.d.ts.map +1 -1
  75. package/dist/interfaces/rest/routes/search.js +2 -1
  76. package/dist/interfaces/rest/schemas/carts.d.ts +7 -0
  77. package/dist/interfaces/rest/schemas/carts.d.ts.map +1 -1
  78. package/dist/interfaces/rest/schemas/customer-portal.d.ts +35 -0
  79. package/dist/interfaces/rest/schemas/customer-portal.d.ts.map +1 -1
  80. package/dist/interfaces/rest/schemas/orders.d.ts +204 -0
  81. package/dist/interfaces/rest/schemas/orders.d.ts.map +1 -1
  82. package/dist/interfaces/rest/schemas/responses.d.ts +71 -0
  83. package/dist/interfaces/rest/schemas/responses.d.ts.map +1 -1
  84. package/dist/interfaces/rest/utils.d.ts +15 -0
  85. package/dist/interfaces/rest/utils.d.ts.map +1 -1
  86. package/dist/interfaces/rest/utils.js +45 -2
  87. package/dist/kernel/compensation/executor.js +2 -2
  88. package/dist/kernel/database/tx-context.d.ts +8 -0
  89. package/dist/kernel/database/tx-context.d.ts.map +1 -1
  90. package/dist/kernel/database/tx-context.js +13 -0
  91. package/dist/kernel/hooks/create-context.d.ts +2 -0
  92. package/dist/kernel/hooks/create-context.d.ts.map +1 -1
  93. package/dist/kernel/hooks/create-context.js +1 -0
  94. package/dist/kernel/hooks/types.d.ts +2 -0
  95. package/dist/kernel/hooks/types.d.ts.map +1 -1
  96. package/dist/kernel/plugin/manifest.d.ts.map +1 -1
  97. package/dist/kernel/plugin/manifest.js +9 -5
  98. package/dist/modules/analytics/service.js +2 -2
  99. package/dist/modules/audit/service.js +3 -3
  100. package/dist/modules/cart/repository/index.d.ts +13 -7
  101. package/dist/modules/cart/repository/index.d.ts.map +1 -1
  102. package/dist/modules/cart/repository/index.js +16 -10
  103. package/dist/modules/cart/schema.d.ts +17 -0
  104. package/dist/modules/cart/schema.d.ts.map +1 -1
  105. package/dist/modules/cart/schema.js +4 -0
  106. package/dist/modules/cart/service.d.ts +22 -9
  107. package/dist/modules/cart/service.d.ts.map +1 -1
  108. package/dist/modules/cart/service.js +57 -33
  109. package/dist/modules/catalog/brand-service.js +7 -7
  110. package/dist/modules/catalog/category-service.js +7 -7
  111. package/dist/modules/catalog/entity-service.d.ts +14 -7
  112. package/dist/modules/catalog/entity-service.d.ts.map +1 -1
  113. package/dist/modules/catalog/entity-service.js +109 -31
  114. package/dist/modules/catalog/read-policy.d.ts +9 -0
  115. package/dist/modules/catalog/read-policy.d.ts.map +1 -0
  116. package/dist/modules/catalog/read-policy.js +8 -0
  117. package/dist/modules/catalog/repository/index.d.ts +3 -2
  118. package/dist/modules/catalog/repository/index.d.ts.map +1 -1
  119. package/dist/modules/catalog/repository/index.js +14 -4
  120. package/dist/modules/catalog/service.d.ts +15 -13
  121. package/dist/modules/catalog/service.d.ts.map +1 -1
  122. package/dist/modules/catalog/service.js +25 -18
  123. package/dist/modules/customers/module.d.ts.map +1 -1
  124. package/dist/modules/customers/module.js +1 -0
  125. package/dist/modules/customers/service.d.ts +2 -0
  126. package/dist/modules/customers/service.d.ts.map +1 -1
  127. package/dist/modules/customers/service.js +21 -20
  128. package/dist/modules/documents/module.d.ts.map +1 -1
  129. package/dist/modules/documents/module.js +1 -0
  130. package/dist/modules/documents/service.d.ts +18 -4
  131. package/dist/modules/documents/service.d.ts.map +1 -1
  132. package/dist/modules/documents/service.js +54 -16
  133. package/dist/modules/fulfillment/module.d.ts.map +1 -1
  134. package/dist/modules/fulfillment/module.js +1 -0
  135. package/dist/modules/fulfillment/service.d.ts +2 -0
  136. package/dist/modules/fulfillment/service.d.ts.map +1 -1
  137. package/dist/modules/fulfillment/service.js +15 -6
  138. package/dist/modules/inventory/service.d.ts.map +1 -1
  139. package/dist/modules/inventory/service.js +15 -14
  140. package/dist/modules/media/service.js +6 -6
  141. package/dist/modules/orders/guest-access.d.ts +27 -0
  142. package/dist/modules/orders/guest-access.d.ts.map +1 -0
  143. package/dist/modules/orders/guest-access.js +46 -0
  144. package/dist/modules/orders/module.d.ts.map +1 -1
  145. package/dist/modules/orders/module.js +1 -0
  146. package/dist/modules/orders/quote.d.ts.map +1 -1
  147. package/dist/modules/orders/quote.js +1 -0
  148. package/dist/modules/orders/repository/index.d.ts +1 -1
  149. package/dist/modules/orders/repository/index.d.ts.map +1 -1
  150. package/dist/modules/orders/repository/index.js +2 -2
  151. package/dist/modules/orders/schema.d.ts +17 -0
  152. package/dist/modules/orders/schema.d.ts.map +1 -1
  153. package/dist/modules/orders/schema.js +5 -3
  154. package/dist/modules/orders/service.d.ts +16 -7
  155. package/dist/modules/orders/service.d.ts.map +1 -1
  156. package/dist/modules/orders/service.js +184 -55
  157. package/dist/modules/pricing/module.d.ts.map +1 -1
  158. package/dist/modules/pricing/module.js +1 -0
  159. package/dist/modules/pricing/service.d.ts +2 -0
  160. package/dist/modules/pricing/service.d.ts.map +1 -1
  161. package/dist/modules/pricing/service.js +12 -11
  162. package/dist/modules/promotions/module.d.ts.map +1 -1
  163. package/dist/modules/promotions/module.js +1 -0
  164. package/dist/modules/promotions/service.d.ts +3 -0
  165. package/dist/modules/promotions/service.d.ts.map +1 -1
  166. package/dist/modules/promotions/service.js +19 -11
  167. package/dist/modules/search/adapter.d.ts +6 -0
  168. package/dist/modules/search/adapter.d.ts.map +1 -1
  169. package/dist/modules/search/module.d.ts.map +1 -1
  170. package/dist/modules/search/module.js +1 -0
  171. package/dist/modules/search/service.d.ts +1 -0
  172. package/dist/modules/search/service.d.ts.map +1 -1
  173. package/dist/modules/search/service.js +57 -6
  174. package/dist/modules/settings/module.d.ts.map +1 -1
  175. package/dist/modules/settings/module.js +1 -0
  176. package/dist/modules/settings/service.d.ts +3 -0
  177. package/dist/modules/settings/service.d.ts.map +1 -1
  178. package/dist/modules/settings/service.js +6 -4
  179. package/dist/modules/shipping/service.js +9 -9
  180. package/dist/modules/tax/module.d.ts.map +1 -1
  181. package/dist/modules/tax/module.js +1 -0
  182. package/dist/modules/tax/service.d.ts +3 -0
  183. package/dist/modules/tax/service.d.ts.map +1 -1
  184. package/dist/modules/tax/service.js +11 -9
  185. package/dist/modules/webhooks/hook.js +2 -2
  186. package/dist/modules/webhooks/module.d.ts.map +1 -1
  187. package/dist/modules/webhooks/module.js +1 -0
  188. package/dist/modules/webhooks/service.d.ts +2 -0
  189. package/dist/modules/webhooks/service.d.ts.map +1 -1
  190. package/dist/modules/webhooks/service.js +4 -4
  191. package/dist/runtime/server.d.ts.map +1 -1
  192. package/dist/runtime/server.js +10 -2
  193. package/dist/test-utils/create-pglite-adapter.d.ts.map +1 -1
  194. package/dist/test-utils/create-pglite-adapter.js +35 -13
  195. package/dist/test-utils/create-test-config.d.ts.map +1 -1
  196. package/dist/test-utils/create-test-config.js +4 -0
  197. package/dist/test-utils/rest-api-test-utils.d.ts.map +1 -1
  198. package/dist/test-utils/rest-api-test-utils.js +2 -0
  199. package/dist/test-utils/test-actors.js +2 -2
  200. package/dist/utils/id.d.ts +2 -0
  201. package/dist/utils/id.d.ts.map +1 -1
  202. package/dist/utils/id.js +11 -0
  203. package/package.json +6 -5
  204. package/src/auth/auth-schema-guard.ts +143 -0
  205. package/src/auth/auth-schema.ts +50 -19
  206. package/src/auth/middleware.ts +17 -5
  207. package/src/auth/org.ts +15 -3
  208. package/src/auth/organization-guard.ts +158 -0
  209. package/src/auth/permissions.ts +31 -8
  210. package/src/auth/role-authority.ts +107 -0
  211. package/src/auth/setup.ts +17 -0
  212. package/src/auth/strict-org-resolution.ts +7 -1
  213. package/src/auth/types.ts +1 -1
  214. package/src/config/defaults.ts +2 -0
  215. package/src/config/types.ts +29 -4
  216. package/src/hooks/checkout.ts +19 -12
  217. package/src/index.ts +16 -6
  218. package/src/interfaces/rest/audit-middleware.ts +2 -0
  219. package/src/interfaces/rest/customer-portal.ts +31 -24
  220. package/src/interfaces/rest/route-coverage.ts +103 -0
  221. package/src/interfaces/rest/router.ts +16 -10
  222. package/src/interfaces/rest/routes/admin/compensation-failures.ts +8 -5
  223. package/src/interfaces/rest/routes/admin/custom-field-proposals.ts +2 -2
  224. package/src/interfaces/rest/routes/admin/permissions.ts +3 -1
  225. package/src/interfaces/rest/routes/admin/staff.ts +161 -72
  226. package/src/interfaces/rest/routes/admin-jobs.ts +3 -3
  227. package/src/interfaces/rest/routes/audit.ts +3 -3
  228. package/src/interfaces/rest/routes/carts.ts +17 -7
  229. package/src/interfaces/rest/routes/catalog.ts +11 -0
  230. package/src/interfaces/rest/routes/checkout.ts +41 -38
  231. package/src/interfaces/rest/routes/documents.ts +15 -1
  232. package/src/interfaces/rest/routes/inventory.ts +2 -0
  233. package/src/interfaces/rest/routes/media.ts +8 -3
  234. package/src/interfaces/rest/routes/orders.ts +27 -8
  235. package/src/interfaces/rest/routes/pricing.ts +2 -2
  236. package/src/interfaces/rest/routes/promotions.ts +12 -9
  237. package/src/interfaces/rest/routes/search.ts +3 -1
  238. package/src/interfaces/rest/utils.ts +61 -2
  239. package/src/kernel/compensation/executor.ts +2 -2
  240. package/src/kernel/database/tx-context.ts +25 -0
  241. package/src/kernel/hooks/create-context.ts +3 -0
  242. package/src/kernel/hooks/types.ts +2 -0
  243. package/src/kernel/plugin/manifest.ts +9 -5
  244. package/src/modules/analytics/service.ts +2 -2
  245. package/src/modules/audit/service.ts +3 -3
  246. package/src/modules/cart/repository/index.ts +22 -8
  247. package/src/modules/cart/schema.ts +4 -0
  248. package/src/modules/cart/service.ts +62 -27
  249. package/src/modules/catalog/brand-service.ts +7 -7
  250. package/src/modules/catalog/category-service.ts +7 -7
  251. package/src/modules/catalog/entity-service.ts +117 -35
  252. package/src/modules/catalog/read-policy.ts +16 -0
  253. package/src/modules/catalog/repository/index.ts +19 -3
  254. package/src/modules/catalog/service.ts +46 -31
  255. package/src/modules/customers/module.ts +2 -0
  256. package/src/modules/customers/service.ts +23 -22
  257. package/src/modules/documents/module.ts +2 -0
  258. package/src/modules/documents/service.ts +74 -11
  259. package/src/modules/fulfillment/module.ts +2 -0
  260. package/src/modules/fulfillment/service.ts +17 -6
  261. package/src/modules/inventory/service.ts +15 -24
  262. package/src/modules/media/service.ts +6 -6
  263. package/src/modules/orders/guest-access.ts +55 -0
  264. package/src/modules/orders/module.ts +1 -0
  265. package/src/modules/orders/quote.ts +1 -0
  266. package/src/modules/orders/repository/index.ts +2 -0
  267. package/src/modules/orders/schema.ts +5 -3
  268. package/src/modules/orders/service.ts +302 -52
  269. package/src/modules/pricing/module.ts +2 -0
  270. package/src/modules/pricing/service.ts +14 -16
  271. package/src/modules/promotions/module.ts +2 -0
  272. package/src/modules/promotions/service.ts +25 -16
  273. package/src/modules/search/adapter.ts +6 -0
  274. package/src/modules/search/module.ts +1 -0
  275. package/src/modules/search/service.ts +55 -6
  276. package/src/modules/settings/module.ts +2 -0
  277. package/src/modules/settings/service.ts +8 -4
  278. package/src/modules/shipping/service.ts +9 -9
  279. package/src/modules/tax/module.ts +1 -0
  280. package/src/modules/tax/service.ts +13 -9
  281. package/src/modules/webhooks/hook.ts +2 -2
  282. package/src/modules/webhooks/module.ts +2 -0
  283. package/src/modules/webhooks/service.ts +6 -4
  284. package/src/runtime/server.ts +16 -2
  285. package/src/test-utils/create-pglite-adapter.ts +34 -12
  286. package/src/test-utils/create-test-config.ts +10 -6
  287. package/src/test-utils/rest-api-test-utils.ts +2 -0
  288. package/src/test-utils/test-actors.ts +2 -2
  289. package/src/utils/id.ts +20 -0
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/modules/webhooks/service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAEjD,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qCAAqC,CAAC;AACrE,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AA4CjF,UAAU,kBAAkB;IAC1B,UAAU,EAAE,kBAAkB,CAAC;CAChC;AAED,qBAAa,cAAc;IAGb,OAAO,CAAC,IAAI;IAFxB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAqB;gBAEtB,IAAI,EAAE,kBAAkB;IAItC,cAAc,CAClB,KAAK,EAAE;QACL,GAAG,EAAE,MAAM,CAAC;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,EAAE,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACpC,EACD,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EACpB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IAyB7B,aAAa,CACjB,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EACpB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC,CAAC;IAM/B,cAAc,CAClB,EAAE,EAAE,MAAM,EACV,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EACpB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAWlB,oBAAoB,CACxB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC,CAAC;CAItC"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/modules/webhooks/service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qCAAqC,CAAC;AACrE,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AA4CjF,UAAU,kBAAkB;IAC1B,UAAU,EAAE,kBAAkB,CAAC;IAC/B,MAAM,EAAE,cAAc,CAAC;CACxB;AAED,qBAAa,cAAc;IAGb,OAAO,CAAC,IAAI;IAFxB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAqB;gBAEtB,IAAI,EAAE,kBAAkB;IAItC,cAAc,CAClB,KAAK,EAAE;QACL,GAAG,EAAE,MAAM,CAAC;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,EAAE,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACpC,EACD,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EACpB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IAyB7B,aAAa,CACjB,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EACpB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC,CAAC;IAM/B,cAAc,CAClB,EAAE,EAAE,MAAM,EACV,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EACpB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAWlB,oBAAoB,CACxB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC,CAAC;CAItC"}
@@ -1,4 +1,4 @@
1
- import { resolveOrgId } from "../../auth/org.js";
1
+ import { resolveOrgIdForCommerce } from "../../auth/org.js";
2
2
  import { CommerceNotFoundError, CommerceValidationError } from "../../kernel/errors.js";
3
3
  import { Err, Ok } from "../../kernel/result.js";
4
4
  /**
@@ -62,7 +62,7 @@ export class WebhookService {
62
62
  if (isPrivateUrl(input.url)) {
63
63
  return Err(new CommerceValidationError("Webhook URL must not point to a private or internal address."));
64
64
  }
65
- const orgId = resolveOrgId(actor ?? ctx?.actor ?? null);
65
+ const orgId = resolveOrgIdForCommerce(actor ?? ctx?.actor ?? null, this.deps.config);
66
66
  const endpoint = await this.repo.createEndpoint({
67
67
  organizationId: orgId,
68
68
  url: input.url,
@@ -74,12 +74,12 @@ export class WebhookService {
74
74
  return Ok(endpoint);
75
75
  }
76
76
  async listEndpoints(actor, ctx) {
77
- const orgId = resolveOrgId(actor ?? ctx?.actor ?? null);
77
+ const orgId = resolveOrgIdForCommerce(actor ?? ctx?.actor ?? null, this.deps.config);
78
78
  const endpoints = await this.repo.findAllEndpoints(orgId, ctx);
79
79
  return Ok(endpoints);
80
80
  }
81
81
  async deleteEndpoint(id, actor, ctx) {
82
- const orgId = resolveOrgId(actor ?? ctx?.actor ?? null);
82
+ const orgId = resolveOrgIdForCommerce(actor ?? ctx?.actor ?? null, this.deps.config);
83
83
  const existing = await this.repo.findEndpointById(id, orgId, ctx);
84
84
  if (!existing) {
85
85
  return Err(new CommerceNotFoundError("Webhook endpoint not found."));
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/runtime/server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAQhD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAOzD,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAkB,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAItE,KAAK,SAAS,GAAG;IACf,SAAS,EAAE;QACT,IAAI,EAAE,YAAY,CAAC;QACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAcvC;AAeD;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,cAAc;;;;;sBAyVxB,MAAM,UAAU,MAAM;GA2FtD"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/runtime/server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAQhD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AASzD,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAkB,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAMtE,KAAK,SAAS,GAAG;IACf,SAAS,EAAE;QACT,IAAI,EAAE,YAAY,CAAC;QACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAcvC;AAeD;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,cAAc;;;;;sBAiWxB,MAAM,UAAU,MAAM;GA6FtD"}
@@ -7,12 +7,15 @@ import { rateLimiter } from "hono-rate-limiter";
7
7
  import { createHash } from "node:crypto";
8
8
  import { createClientIpResolver } from "./client-ip.js";
9
9
  import { authMiddleware } from "../auth/middleware.js";
10
+ import { organizationGuard } from "../auth/organization-guard.js";
10
11
  import { CommerceCsrfError } from "../kernel/errors.js";
11
12
  import { createRestRoutes } from "../interfaces/rest/index.js";
12
13
  import { createCustomerPortalRoutes } from "../interfaces/rest/customer-portal.js";
13
14
  import { createCommerce } from "./commerce.js";
14
15
  import { rewriteCommerceAliasRequest } from "./url-alias-rewrite.js";
15
16
  import { mapErrorToResponse } from "../kernel/error-mapper.js";
17
+ import { assertRouteCoverage } from "../interfaces/rest/route-coverage.js";
18
+ import { markRoutePermissionGuard } from "../interfaces/rest/utils.js";
16
19
  /**
17
20
  * Returns true if running in a Node-like runtime that supports
18
21
  * `process.on()` + `process.exit()`. Edge runtimes (Cloudflare Workers,
@@ -265,6 +268,10 @@ export async function createServer(config) {
265
268
  }
266
269
  }
267
270
  // ─── Auth ──────────────────────────────────────────────────────────
271
+ // Better Auth's organization endpoints are mounted below but governed here:
272
+ // `admin/staff.ts` owns membership, and this guard keeps the plugin's own
273
+ // paths from minting members outside those rules.
274
+ app.use("/api/auth/organization/*", organizationGuard(kernel.database.db, config));
268
275
  app.use("/api/auth/*", async (c, next) => {
269
276
  if (c.req.path.startsWith("/api/auth/pos/")) {
270
277
  await next();
@@ -367,7 +374,7 @@ export async function createServer(config) {
367
374
  // Strategy 1: Built-in cron endpoint for serverless deployments.
368
375
  // Point Vercel Cron or Cloudflare Cron Trigger at GET /api/jobs/run
369
376
  // Optional query params: ?queue=emails&limit=20
370
- app.get("/api/jobs/run", async (c) => {
377
+ app.get("/api/jobs/run", markRoutePermissionGuard(async (c) => {
371
378
  // Always require admin — cron triggers must authenticate
372
379
  const actor = c.get("actor");
373
380
  if (!actor?.permissions?.includes("*:*")) {
@@ -383,7 +390,7 @@ export async function createServer(config) {
383
390
  logger.error({ err }, "Job runner endpoint failed");
384
391
  return c.json({ error: { code: "INTERNAL_ERROR", message: "Job processing failed" } }, 500);
385
392
  }
386
- });
393
+ }));
387
394
  // Strategy 2: In-process polling for long-running servers (ECS, Cloud Run, Docker).
388
395
  // Enable via config.jobs.autorun.enabled = true
389
396
  if (config.jobs?.autorun?.enabled) {
@@ -413,6 +420,7 @@ export async function createServer(config) {
413
420
  "or set config.jobs.autorun.enabled = true for in-process polling.");
414
421
  }
415
422
  }
423
+ assertRouteCoverage(app);
416
424
  const dispatchFetch = app.fetch.bind(app);
417
425
  app.fetch = ((input, env, executionCtx) => {
418
426
  const req = input instanceof Request ? input : new Request(input);
@@ -1 +1 @@
1
- {"version":3,"file":"create-pglite-adapter.d.ts","sourceRoot":"","sources":["../../src/test-utils/create-pglite-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAMrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AA0BxE;;;;;;;;;;GAUG;AACH,wBAAsB,uBAAuB,IAAI,OAAO,CAAC;IACvD,OAAO,EAAE,eAAe,CAAC;IACzB,EAAE,EAAE,eAAe,CAAC;IACpB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAAC,CA+DD"}
1
+ {"version":3,"file":"create-pglite-adapter.d.ts","sourceRoot":"","sources":["../../src/test-utils/create-pglite-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAMrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AA0BxE;;;;;;;;;;GAUG;AACH,wBAAsB,uBAAuB,IAAI,OAAO,CAAC;IACvD,OAAO,EAAE,eAAe,CAAC;IACzB,EAAE,EAAE,eAAe,CAAC;IACpB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAAC,CAqFD"}
@@ -62,21 +62,43 @@ export async function createPGliteTestAdapter() {
62
62
  /**
63
63
  * PGlite-compatible transaction wrapper.
64
64
  *
65
- * Drizzle's transaction() method can hang with PGlite due to how
66
- * the adapter manages transaction state. This implementation uses
67
- * manual BEGIN/COMMIT control which works more reliably.
65
+ * Drizzle's transaction() method can hang with PGlite due to how the adapter
66
+ * manages transaction state, so this drives BEGIN/COMMIT manually.
67
+ *
68
+ * PGlite is a single connection, so two transaction bodies awaiting
69
+ * concurrently would otherwise interleave their statements between one
70
+ * BEGIN and one COMMIT — the second BEGIN is a no-op Postgres only warns
71
+ * about. Merged that way, two callers share a snapshot and hold each other's
72
+ * row locks, so an isolation bug reads as if the guard failed. Bodies are
73
+ * queued instead, which is what a connection pool would do to two
74
+ * transactions competing for the same rows.
75
+ *
76
+ * A nested call joins the transaction already open rather than deadlocking
77
+ * on the queue; without savepoints that is the only sound reading.
68
78
  */
79
+ let transactionQueue = Promise.resolve();
80
+ let inTransaction = false;
69
81
  async function transaction(fn) {
70
- await pg.exec("BEGIN");
71
- try {
72
- const result = await fn(db);
73
- await pg.exec("COMMIT");
74
- return result;
75
- }
76
- catch (error) {
77
- await pg.exec("ROLLBACK");
78
- throw error;
79
- }
82
+ if (inTransaction)
83
+ return fn(db);
84
+ const run = transactionQueue.then(async () => {
85
+ inTransaction = true;
86
+ await pg.exec("BEGIN");
87
+ try {
88
+ const result = await fn(db);
89
+ await pg.exec("COMMIT");
90
+ return result;
91
+ }
92
+ catch (error) {
93
+ await pg.exec("ROLLBACK");
94
+ throw error;
95
+ }
96
+ finally {
97
+ inTransaction = false;
98
+ }
99
+ });
100
+ transactionQueue = run.catch(() => undefined);
101
+ return run;
80
102
  }
81
103
  const adapter = {
82
104
  provider: "postgresql",
@@ -1 +1 @@
1
- {"version":3,"file":"create-test-config.d.ts","sourceRoot":"","sources":["../../src/test-utils/create-test-config.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAgDzD,wBAAsB,gBAAgB,CACpC,SAAS,GAAE,OAAO,CAAC,cAAc,CAAM,GACtC,OAAO,CAAC,cAAc,CAAC,CAiHzB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,sBAAsB,CAC1C,SAAS,GAAE,OAAO,CAAC,cAAc,CAAM,GACtC,OAAO,CAAC;IAAE,MAAM,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC,CAUnE"}
1
+ {"version":3,"file":"create-test-config.d.ts","sourceRoot":"","sources":["../../src/test-utils/create-test-config.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAgDzD,wBAAsB,gBAAgB,CACpC,SAAS,GAAE,OAAO,CAAC,cAAc,CAAM,GACtC,OAAO,CAAC,cAAc,CAAC,CAqHzB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,sBAAsB,CAC1C,SAAS,GAAE,OAAO,CAAC,cAAc,CAAM,GACtC,OAAO,CAAC;IAAE,MAAM,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC,CAUnE"}
@@ -67,11 +67,13 @@ export async function createTestConfig(overrides = {}) {
67
67
  "catalog:update",
68
68
  "catalog:delete",
69
69
  "catalog:read",
70
+ "catalog:read:unpublished",
70
71
  "inventory:adjust",
71
72
  "orders:create",
72
73
  "orders:read",
73
74
  "orders:update",
74
75
  "orders:manage",
76
+ "orders:create:on-behalf",
75
77
  "cart:create",
76
78
  "cart:update",
77
79
  "customers:update:self",
@@ -80,10 +82,12 @@ export async function createTestConfig(overrides = {}) {
80
82
  ai_agent: {
81
83
  permissions: [
82
84
  "catalog:read",
85
+ "catalog:read:unpublished",
83
86
  "catalog:create",
84
87
  "inventory:read",
85
88
  "inventory:adjust",
86
89
  "orders:read",
90
+ "orders:create:on-behalf",
87
91
  "cart:create",
88
92
  "cart:update",
89
93
  ],
@@ -1 +1 @@
1
- {"version":3,"file":"rest-api-test-utils.d.ts","sourceRoot":"","sources":["../../src/test-utils/rest-api-test-utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAOpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,KAAK,SAAS,GAAG;IACf,SAAS,EAAE;QACT,IAAI,EAAE,YAAY,CAAC;QACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;KACrB,CAAC;CACH,CAAC;AAiDF;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,SAAS,GAAE,OAAO,CAAC,cAAc,CAAM,GACtC,OAAO,CAAC;IACT,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IACxB,MAAM,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;IACxC,IAAI,EAAE,YAAY,CAAC;IACnB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAAC,CAqDD;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,CAAC,GAAG,OAAO,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,CAEnF;AAED;;GAEG;AACH,eAAO,MAAM,SAAS,EAAE,KAoCvB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,KAS3B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,WAAW,EAAE,KASzB,CAAC;AAEF;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE;IAClE,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,KAAK,CAAC;CACf,WAwBA;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,EACvB,OAAO,EAAE;IACP,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,KAAK,CAAC;CACf,qBAYF"}
1
+ {"version":3,"file":"rest-api-test-utils.d.ts","sourceRoot":"","sources":["../../src/test-utils/rest-api-test-utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAOpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,KAAK,SAAS,GAAG;IACf,SAAS,EAAE;QACT,IAAI,EAAE,YAAY,CAAC;QACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;KACrB,CAAC;CACH,CAAC;AAiDF;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,SAAS,GAAE,OAAO,CAAC,cAAc,CAAM,GACtC,OAAO,CAAC;IACT,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IACxB,MAAM,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;IACxC,IAAI,EAAE,YAAY,CAAC;IACnB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAAC,CAqDD;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,CAAC,GAAG,OAAO,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,CAEnF;AAED;;GAEG;AACH,eAAO,MAAM,SAAS,EAAE,KAsCvB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,KAS3B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,WAAW,EAAE,KASzB,CAAC;AAEF;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE;IAClE,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,KAAK,CAAC;CACf,WAwBA;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,EACvB,OAAO,EAAE;IACP,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,KAAK,CAAC;CACf,qBAYF"}
@@ -125,9 +125,11 @@ export const testActor = {
125
125
  "catalog:create",
126
126
  "catalog:update",
127
127
  "catalog:read",
128
+ "catalog:read:unpublished",
128
129
  "inventory:adjust",
129
130
  "inventory:read",
130
131
  "orders:create",
132
+ "orders:create:on-behalf",
131
133
  "orders:read",
132
134
  "orders:update",
133
135
  "orders:manage",
@@ -27,8 +27,8 @@ export const testStaffActor = {
27
27
  organizationId: TEST_ORG_ID,
28
28
  role: "staff",
29
29
  permissions: [
30
- "catalog:read", "catalog:create", "catalog:update",
31
- "inventory:adjust", "orders:read", "orders:create", "orders:update", "orders:manage",
30
+ "catalog:read", "catalog:read:unpublished", "catalog:create", "catalog:update",
31
+ "inventory:adjust", "orders:read", "orders:create", "orders:create:on-behalf", "orders:update", "orders:manage",
32
32
  ],
33
33
  };
34
34
  /** Customer with minimal read/write-own permissions. */
@@ -1,3 +1,5 @@
1
+ import type { Actor } from "../auth/types.js";
1
2
  export declare function makeId(): string;
2
3
  export declare function makeDeterministicId(value: string): Promise<string>;
4
+ export declare function makeIdempotencyScope(actor: Actor | null | undefined, guestCredential?: string, fallbackBinding?: string): Promise<string>;
3
5
  //# sourceMappingURL=id.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"id.d.ts","sourceRoot":"","sources":["../../src/utils/id.ts"],"names":[],"mappings":"AAAA,wBAAgB,MAAM,IAAI,MAAM,CAE/B;AAED,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CASxE"}
1
+ {"version":3,"file":"id.d.ts","sourceRoot":"","sources":["../../src/utils/id.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAE9C,wBAAgB,MAAM,IAAI,MAAM,CAE/B;AAED,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CASxE;AAED,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,KAAK,GAAG,IAAI,GAAG,SAAS,EAC/B,eAAe,CAAC,EAAE,MAAM,EACxB,eAAe,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,MAAM,CAAC,CAYjB"}
package/dist/utils/id.js CHANGED
@@ -9,3 +9,14 @@ export async function makeDeterministicId(value) {
9
9
  const hex = [...bytes].map((byte) => byte.toString(16).padStart(2, "0")).join("");
10
10
  return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
11
11
  }
12
+ export async function makeIdempotencyScope(actor, guestCredential, fallbackBinding) {
13
+ const binding = actor?.userId
14
+ ? `actor:${actor.type}:${actor.userId}`
15
+ : guestCredential
16
+ ? `guest:${guestCredential}`
17
+ : fallbackBinding
18
+ ? `resource:${fallbackBinding}`
19
+ : "anonymous";
20
+ const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(binding)));
21
+ return [...digest].map((byte) => byte.toString(16).padStart(2, "0")).join("");
22
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@porulle/core",
3
- "version": "0.11.0",
3
+ "version": "0.13.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -36,11 +36,11 @@
36
36
  }
37
37
  },
38
38
  "dependencies": {
39
- "@better-auth/api-key": "^1.3.8",
40
- "@better-auth/drizzle-adapter": "^1.3.8",
39
+ "@better-auth/api-key": "^1.7.1",
40
+ "@better-auth/drizzle-adapter": "^1.7.1",
41
41
  "@hono/zod-openapi": "^1.2.2",
42
42
  "@scalar/hono-api-reference": "^0.10.5",
43
- "better-auth": "^1.3.8",
43
+ "better-auth": "^1.7.1",
44
44
  "drizzle-orm": "^0.45.1",
45
45
  "drizzle-zod": "^0.8.3",
46
46
  "hono": "^4.9.8",
@@ -81,7 +81,8 @@
81
81
  "scripts": {
82
82
  "generate:plugins": "node ./scripts/generate-plugin-types.mjs",
83
83
  "check:store-guard": "node ./scripts/check-store-guard.mjs",
84
- "build": "rm -rf dist tsconfig.build.tsbuildinfo && npm run generate:plugins && npm run check:store-guard && tsc -p tsconfig.build.json",
84
+ "check:auth-schema": "vitest run test/auth-schema-better-auth-parity.test.ts",
85
+ "build": "rm -rf dist tsconfig.build.tsbuildinfo && npm run generate:plugins && npm run check:store-guard && npm run check:auth-schema && tsc -p tsconfig.build.json",
85
86
  "check-types": "npm run generate:plugins && npm run check:store-guard && tsc --noEmit",
86
87
  "lint": "eslint . --max-warnings 1000",
87
88
  "test": "vitest run"
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Compares Porulle's Drizzle auth tables against better-auth's canonical
3
+ * {@link getAuthTables} output so a dependency minor bump cannot add a column
4
+ * the declared schema omits.
5
+ */
6
+
7
+ import { getTableColumns, getTableName } from "drizzle-orm";
8
+ import type { PgColumn, PgTable } from "drizzle-orm/pg-core";
9
+ import { getAuthTables } from "better-auth/db";
10
+ import type { BetterAuthOptions } from "better-auth";
11
+ import { apiKey } from "@better-auth/api-key";
12
+ import { organization, twoFactor, phoneNumber, jwt, bearer } from "better-auth/plugins";
13
+ import * as authSchema from "./auth-schema.js";
14
+
15
+ /**
16
+ * Every plugin {@link createAuth} can enable, not only the ones it enables by
17
+ * default. The shipped schema has to satisfy any configuration a merchant
18
+ * chooses, so the guard compares against the maximal table set: a column that
19
+ * only `twoFactor` needs is still a column this package must declare.
20
+ */
21
+ export const AUTH_SCHEMA_GUARD_OPTIONS = {
22
+ plugins: [
23
+ organization({ roles: {} }),
24
+ bearer(),
25
+ jwt(),
26
+ twoFactor({ issuer: "porulle-auth-schema-guard" }),
27
+ apiKey(),
28
+ phoneNumber(),
29
+ ],
30
+ user: {
31
+ additionalFields: {
32
+ vendorId: { type: "string", required: false },
33
+ posOperatorPin: { type: "string", required: false },
34
+ },
35
+ },
36
+ } satisfies BetterAuthOptions;
37
+
38
+ /**
39
+ * Drizzle tables keyed by the SQL table name they declare, so the guard walks
40
+ * whatever models better-auth reports rather than a list someone has to
41
+ * remember to extend. A hand-maintained model list is how `jwks.alg` shipped
42
+ * missing while this guard passed.
43
+ */
44
+ function drizzleTablesByName(): Map<string, PgTable> {
45
+ const byName = new Map<string, PgTable>();
46
+ for (const value of Object.values(authSchema)) {
47
+ try {
48
+ byName.set(getTableName(value as PgTable), value as PgTable);
49
+ } catch {
50
+ // Not a Drizzle table (type export, index helper) — skip.
51
+ }
52
+ }
53
+ return byName;
54
+ }
55
+
56
+ function drizzleColumnForField(
57
+ table: PgTable,
58
+ fieldKey: string,
59
+ fieldName: string,
60
+ ): PgColumn | undefined {
61
+ const columns = getTableColumns(table);
62
+ if (fieldKey in columns) {
63
+ return columns[fieldKey] as PgColumn;
64
+ }
65
+ for (const column of Object.values(columns)) {
66
+ if (column.name === fieldName) {
67
+ return column as PgColumn;
68
+ }
69
+ }
70
+ return undefined;
71
+ }
72
+
73
+ export type AuthSchemaParityMismatch = {
74
+ model: string;
75
+ field: string;
76
+ issue: string;
77
+ };
78
+
79
+ /**
80
+ * Returns every mismatch between better-auth's expected auth tables and the
81
+ * Drizzle definitions Porulle ships. An empty array means parity holds.
82
+ */
83
+ export function findAuthSchemaParityMismatches(
84
+ options: BetterAuthOptions = AUTH_SCHEMA_GUARD_OPTIONS,
85
+ ): AuthSchemaParityMismatch[] {
86
+ const mismatches: AuthSchemaParityMismatch[] = [];
87
+ const authTables = getAuthTables(options);
88
+ const byName = drizzleTablesByName();
89
+
90
+ for (const [model, tableDef] of Object.entries(authTables)) {
91
+ if (!tableDef) continue;
92
+ const drizzleTable = byName.get(tableDef.modelName);
93
+ if (!drizzleTable) {
94
+ mismatches.push({
95
+ model,
96
+ field: "*",
97
+ issue: `no drizzle table declares better-auth model "${tableDef.modelName}"`,
98
+ });
99
+ continue;
100
+ }
101
+
102
+ for (const [fieldKey, fieldAttr] of Object.entries(tableDef.fields)) {
103
+ const column = drizzleColumnForField(
104
+ drizzleTable,
105
+ fieldKey,
106
+ fieldAttr.fieldName ?? fieldKey,
107
+ );
108
+ if (!column) {
109
+ mismatches.push({
110
+ model,
111
+ field: fieldKey,
112
+ issue: `drizzle column missing for better-auth field "${fieldKey}"`,
113
+ });
114
+ continue;
115
+ }
116
+
117
+ const required = fieldAttr.required !== false;
118
+ if (required && !column.notNull) {
119
+ mismatches.push({
120
+ model,
121
+ field: fieldKey,
122
+ issue: `column "${column.name}" must be NOT NULL (better-auth required)`,
123
+ });
124
+ }
125
+ }
126
+ }
127
+
128
+ return mismatches;
129
+ }
130
+
131
+ export function assertAuthSchemaParity(
132
+ options: BetterAuthOptions = AUTH_SCHEMA_GUARD_OPTIONS,
133
+ ): void {
134
+ const mismatches = findAuthSchemaParityMismatches(options);
135
+ if (mismatches.length === 0) return;
136
+
137
+ const detail = mismatches
138
+ .map((m) => ` ${m.model}.${m.field}: ${m.issue}`)
139
+ .join("\n");
140
+ throw new Error(
141
+ `Porulle auth-schema.ts is out of sync with installed better-auth:\n${detail}`,
142
+ );
143
+ }
@@ -4,6 +4,7 @@ import {
4
4
  timestamp,
5
5
  boolean,
6
6
  integer,
7
+ uniqueIndex,
7
8
  } from "drizzle-orm/pg-core";
8
9
 
9
10
  export const user = pgTable("user", {
@@ -19,6 +20,12 @@ export const user = pgTable("user", {
19
20
  .notNull(),
20
21
  vendorId: text("vendor_id"),
21
22
  posOperatorPin: text("pos_operator_pin"),
23
+ // Contributed by the twoFactor and phoneNumber plugins. Declared
24
+ // unconditionally: the plugins are config-gated, but one shipped schema has
25
+ // to satisfy every configuration a merchant can choose.
26
+ twoFactorEnabled: boolean("two_factor_enabled").default(false),
27
+ phoneNumber: text("phone_number").unique(),
28
+ phoneNumberVerified: boolean("phone_number_verified").default(false),
22
29
  });
23
30
 
24
31
  export const session = pgTable("session", {
@@ -37,25 +44,35 @@ export const session = pgTable("session", {
37
44
  activeOrganizationId: text("active_organization_id"),
38
45
  });
39
46
 
40
- export const account = pgTable("account", {
41
- id: text("id").primaryKey(),
42
- accountId: text("account_id").notNull(),
43
- providerId: text("provider_id").notNull(),
44
- userId: text("user_id")
45
- .notNull()
46
- .references(() => user.id, { onDelete: "cascade" }),
47
- accessToken: text("access_token"),
48
- refreshToken: text("refresh_token"),
49
- idToken: text("id_token"),
50
- accessTokenExpiresAt: timestamp("access_token_expires_at"),
51
- refreshTokenExpiresAt: timestamp("refresh_token_expires_at"),
52
- scope: text("scope"),
53
- password: text("password"),
54
- createdAt: timestamp("created_at").defaultNow().notNull(),
55
- updatedAt: timestamp("updated_at")
56
- .$onUpdate(() => /* @__PURE__ */ new Date())
57
- .notNull(),
58
- });
47
+ export const account = pgTable(
48
+ "account",
49
+ {
50
+ id: text("id").primaryKey(),
51
+ issuer: text("issuer").notNull(),
52
+ accountId: text("account_id").notNull(),
53
+ providerId: text("provider_id").notNull(),
54
+ userId: text("user_id")
55
+ .notNull()
56
+ .references(() => user.id, { onDelete: "cascade" }),
57
+ accessToken: text("access_token"),
58
+ refreshToken: text("refresh_token"),
59
+ idToken: text("id_token"),
60
+ accessTokenExpiresAt: timestamp("access_token_expires_at"),
61
+ refreshTokenExpiresAt: timestamp("refresh_token_expires_at"),
62
+ scope: text("scope"),
63
+ password: text("password"),
64
+ createdAt: timestamp("created_at").defaultNow().notNull(),
65
+ updatedAt: timestamp("updated_at")
66
+ .$onUpdate(() => /* @__PURE__ */ new Date())
67
+ .notNull(),
68
+ },
69
+ (table) => [
70
+ uniqueIndex("account_issuer_account_id_unique").on(
71
+ table.issuer,
72
+ table.accountId,
73
+ ),
74
+ ],
75
+ );
59
76
 
60
77
  export const verification = pgTable("verification", {
61
78
  id: text("id").primaryKey(),
@@ -136,4 +153,18 @@ export const jwks = pgTable("jwks", {
136
153
  privateKey: text("private_key").notNull(),
137
154
  createdAt: timestamp("created_at").notNull(),
138
155
  expiresAt: timestamp("expires_at"),
156
+ alg: text("alg"),
157
+ crv: text("crv"),
158
+ });
159
+
160
+ export const twoFactor = pgTable("twoFactor", {
161
+ id: text("id").primaryKey(),
162
+ secret: text("secret").notNull(),
163
+ backupCodes: text("backup_codes").notNull(),
164
+ userId: text("user_id")
165
+ .notNull()
166
+ .references(() => user.id, { onDelete: "cascade" }),
167
+ verified: boolean("verified").default(false),
168
+ failedVerificationCount: integer("failed_verification_count").default(0),
169
+ lockedUntil: timestamp("locked_until"),
139
170
  });
@@ -5,7 +5,15 @@ import type { AuthInstance } from "./setup.js";
5
5
  import { DEFAULT_ORG_ID } from "./org.js";
6
6
  import { isStrictOrgResolution } from "./strict-org-resolution.js";
7
7
 
8
- const DEFAULT_CUSTOMER_PERMISSIONS = [
8
+ function emptyToNull(value: string | null | undefined): string | null {
9
+ return value == null || value === "" ? null : value;
10
+ }
11
+
12
+ // Exported so the storefront contract can be pinned by a test: an anonymous
13
+ // visitor resolved by `storeResolver` gets these permissions, so dropping
14
+ // `catalog:read` here would 401 every public storefront read.
15
+
16
+ export const DEFAULT_CUSTOMER_PERMISSIONS = [
9
17
  "catalog:read",
10
18
  "cart:create",
11
19
  "cart:read",
@@ -192,9 +200,13 @@ export function authMiddleware(
192
200
  : null;
193
201
  const metaOrg = typeof meta?.organizationId === "string" ? meta.organizationId : undefined;
194
202
  const orgId = (metaOrg ?? apiKey.organizationId ?? defaultOrgId) as string;
195
- const userId = ((typeof meta?.operatorId === "string" ? meta.operatorId : undefined)
196
- ?? apiKey.referenceId
197
- ?? "") as string;
203
+ // No operator and no reference means the key carries no user
204
+ // identity. Leave it absent rather than substituting a shared
205
+ // placeholder, which would make every such key look like one person.
206
+ const userId = emptyToNull(
207
+ (typeof meta?.operatorId === "string" ? meta.operatorId : undefined) ??
208
+ (typeof apiKey.referenceId === "string" ? apiKey.referenceId : undefined),
209
+ );
198
210
 
199
211
  // Better Auth stores permissions as Record<string, string[]>
200
212
  // (e.g. {"catalog":["read","create"]}). Flatten to the
@@ -245,7 +257,7 @@ export function authMiddleware(
245
257
  // services can scope queries correctly.
246
258
  c.set("actor", {
247
259
  type: "user",
248
- userId: "anonymous",
260
+ userId: null,
249
261
  email: null,
250
262
  name: "Anonymous",
251
263
  vendorId: null,
package/src/auth/org.ts CHANGED
@@ -37,14 +37,26 @@ export function setBootDefaultOrgId(orgId: string): void {
37
37
  _bootDefaultOrgId = orgId;
38
38
  }
39
39
 
40
+ /**
41
+ * Resolves org using an explicit config default — not ambient boot state.
42
+ * Use when the caller holds CommerceConfig and may lack an actor organizationId.
43
+ */
44
+ export function resolveOrgIdForCommerce(
45
+ actor: unknown,
46
+ config?: CommerceConfig | null,
47
+ ): string {
48
+ return resolveOrgId(actor, config?.auth?.defaultOrganizationId, config ?? undefined);
49
+ }
50
+
40
51
  /**
41
52
  * Extracts the organization ID from an actor.
42
53
  *
43
54
  * Resolution order:
44
55
  * 1. Actor's organizationId (set by middleware from session/API key/storeResolver)
45
56
  * 2. Explicit defaultOrgId parameter (caller override)
46
- * 3. Boot-time default (from config.auth.defaultOrganizationId via setBootDefaultOrgId)
47
- * 4. Deprecated fallback to DEFAULT_ORG_ID (will be removed)
57
+ * 3. Strict check throw OrgResolutionError when enabled
58
+ * 4. Boot-time default (from config.auth.defaultOrganizationId via setBootDefaultOrgId)
59
+ * 5. Deprecated fallback to DEFAULT_ORG_ID (will be removed)
48
60
  */
49
61
  export function resolveOrgId(
50
62
  actor: unknown,
@@ -56,12 +68,12 @@ export function resolveOrgId(
56
68
  if (typeof orgId === "string") return orgId;
57
69
  }
58
70
  if (defaultOrgId) return defaultOrgId;
59
- if (_bootDefaultOrgId) return _bootDefaultOrgId;
60
71
  if (isStrictOrgResolution(commerceConfig)) {
61
72
  throw new OrgResolutionError(
62
73
  "Organization could not be resolved: no actor organizationId, no defaultOrgId, and no configured default organization.",
63
74
  );
64
75
  }
76
+ if (_bootDefaultOrgId) return _bootDefaultOrgId;
65
77
  warnDeprecatedOrgDefaultFallback();
66
78
  return DEFAULT_ORG_ID;
67
79
  }