@endora-commerce/mod-catalog 0.100.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 (443) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +63 -0
  3. package/dist/admin/components/PackagingUnitsEditor.d.ts +12 -0
  4. package/dist/admin/components/PackagingUnitsEditor.d.ts.map +1 -0
  5. package/dist/admin/components/PackagingUnitsEditor.js +99 -0
  6. package/dist/admin/components/PackagingUnitsEditor.js.map +1 -0
  7. package/dist/admin/components/ProductAttributesTab.d.ts +13 -0
  8. package/dist/admin/components/ProductAttributesTab.d.ts.map +1 -0
  9. package/dist/admin/components/ProductAttributesTab.js +140 -0
  10. package/dist/admin/components/ProductAttributesTab.js.map +1 -0
  11. package/dist/admin/components/ProductInventoryTab.d.ts +14 -0
  12. package/dist/admin/components/ProductInventoryTab.d.ts.map +1 -0
  13. package/dist/admin/components/ProductInventoryTab.js +284 -0
  14. package/dist/admin/components/ProductInventoryTab.js.map +1 -0
  15. package/dist/admin/components/ProductScopeEditor.d.ts +94 -0
  16. package/dist/admin/components/ProductScopeEditor.d.ts.map +1 -0
  17. package/dist/admin/components/ProductScopeEditor.js +395 -0
  18. package/dist/admin/components/ProductScopeEditor.js.map +1 -0
  19. package/dist/admin/components/ProductsBulkEditDialog.d.ts +11 -0
  20. package/dist/admin/components/ProductsBulkEditDialog.d.ts.map +1 -0
  21. package/dist/admin/components/ProductsBulkEditDialog.js +304 -0
  22. package/dist/admin/components/ProductsBulkEditDialog.js.map +1 -0
  23. package/dist/admin/index.d.ts +45 -0
  24. package/dist/admin/index.d.ts.map +1 -0
  25. package/dist/admin/index.js +184 -0
  26. package/dist/admin/index.js.map +1 -0
  27. package/dist/admin/lib/resolve-product-selection.d.ts +21 -0
  28. package/dist/admin/lib/resolve-product-selection.d.ts.map +1 -0
  29. package/dist/admin/lib/resolve-product-selection.js +25 -0
  30. package/dist/admin/lib/resolve-product-selection.js.map +1 -0
  31. package/dist/admin/pages/AttachmentTypesPage.d.ts +18 -0
  32. package/dist/admin/pages/AttachmentTypesPage.d.ts.map +1 -0
  33. package/dist/admin/pages/AttachmentTypesPage.js +92 -0
  34. package/dist/admin/pages/AttachmentTypesPage.js.map +1 -0
  35. package/dist/admin/pages/AttributeSetsPage.d.ts +21 -0
  36. package/dist/admin/pages/AttributeSetsPage.d.ts.map +1 -0
  37. package/dist/admin/pages/AttributeSetsPage.js +158 -0
  38. package/dist/admin/pages/AttributeSetsPage.js.map +1 -0
  39. package/dist/admin/pages/AttributesManager.d.ts +9 -0
  40. package/dist/admin/pages/AttributesManager.d.ts.map +1 -0
  41. package/dist/admin/pages/AttributesManager.js +305 -0
  42. package/dist/admin/pages/AttributesManager.js.map +1 -0
  43. package/dist/admin/pages/BulkOperationDetailPage.d.ts +9 -0
  44. package/dist/admin/pages/BulkOperationDetailPage.d.ts.map +1 -0
  45. package/dist/admin/pages/BulkOperationDetailPage.js +187 -0
  46. package/dist/admin/pages/BulkOperationDetailPage.js.map +1 -0
  47. package/dist/admin/pages/BulkOperationsPage.d.ts +9 -0
  48. package/dist/admin/pages/BulkOperationsPage.d.ts.map +1 -0
  49. package/dist/admin/pages/BulkOperationsPage.js +98 -0
  50. package/dist/admin/pages/BulkOperationsPage.js.map +1 -0
  51. package/dist/admin/pages/CategoriesTree.d.ts +9 -0
  52. package/dist/admin/pages/CategoriesTree.d.ts.map +1 -0
  53. package/dist/admin/pages/CategoriesTree.js +176 -0
  54. package/dist/admin/pages/CategoriesTree.js.map +1 -0
  55. package/dist/admin/pages/ProductEditor.d.ts +13 -0
  56. package/dist/admin/pages/ProductEditor.d.ts.map +1 -0
  57. package/dist/admin/pages/ProductEditor.js +1050 -0
  58. package/dist/admin/pages/ProductEditor.js.map +1 -0
  59. package/dist/admin/pages/ProductsList.d.ts +9 -0
  60. package/dist/admin/pages/ProductsList.d.ts.map +1 -0
  61. package/dist/admin/pages/ProductsList.js +399 -0
  62. package/dist/admin/pages/ProductsList.js.map +1 -0
  63. package/dist/backend/commands/attribute-commands.d.ts +111 -0
  64. package/dist/backend/commands/attribute-commands.d.ts.map +1 -0
  65. package/dist/backend/commands/attribute-commands.js +482 -0
  66. package/dist/backend/commands/attribute-commands.js.map +1 -0
  67. package/dist/backend/demo/reset.d.ts +23 -0
  68. package/dist/backend/demo/reset.d.ts.map +1 -0
  69. package/dist/backend/demo/reset.js +41 -0
  70. package/dist/backend/demo/reset.js.map +1 -0
  71. package/dist/backend/demo/rows.d.ts +103 -0
  72. package/dist/backend/demo/rows.d.ts.map +1 -0
  73. package/dist/backend/demo/rows.js +162 -0
  74. package/dist/backend/demo/rows.js.map +1 -0
  75. package/dist/backend/demo/seed.d.ts +35 -0
  76. package/dist/backend/demo/seed.d.ts.map +1 -0
  77. package/dist/backend/demo/seed.js +177 -0
  78. package/dist/backend/demo/seed.js.map +1 -0
  79. package/dist/backend/entities/attachment-type.entity.d.ts +16 -0
  80. package/dist/backend/entities/attachment-type.entity.d.ts.map +1 -0
  81. package/dist/backend/entities/attachment-type.entity.js +57 -0
  82. package/dist/backend/entities/attachment-type.entity.js.map +1 -0
  83. package/dist/backend/entities/attribute-set-attribute.entity.d.ts +26 -0
  84. package/dist/backend/entities/attribute-set-attribute.entity.d.ts.map +1 -0
  85. package/dist/backend/entities/attribute-set-attribute.entity.js +55 -0
  86. package/dist/backend/entities/attribute-set-attribute.entity.js.map +1 -0
  87. package/dist/backend/entities/attribute-set.entity.d.ts +22 -0
  88. package/dist/backend/entities/attribute-set.entity.d.ts.map +1 -0
  89. package/dist/backend/entities/attribute-set.entity.js +67 -0
  90. package/dist/backend/entities/attribute-set.entity.js.map +1 -0
  91. package/dist/backend/entities/bulk-operation.entity.d.ts +78 -0
  92. package/dist/backend/entities/bulk-operation.entity.d.ts.map +1 -0
  93. package/dist/backend/entities/bulk-operation.entity.js +138 -0
  94. package/dist/backend/entities/bulk-operation.entity.js.map +1 -0
  95. package/dist/backend/entities/bundle-slot-option.entity.d.ts +23 -0
  96. package/dist/backend/entities/bundle-slot-option.entity.d.ts.map +1 -0
  97. package/dist/backend/entities/bundle-slot-option.entity.js +68 -0
  98. package/dist/backend/entities/bundle-slot-option.entity.js.map +1 -0
  99. package/dist/backend/entities/bundle-slot.entity.d.ts +25 -0
  100. package/dist/backend/entities/bundle-slot.entity.d.ts.map +1 -0
  101. package/dist/backend/entities/bundle-slot.entity.js +74 -0
  102. package/dist/backend/entities/bundle-slot.entity.js.map +1 -0
  103. package/dist/backend/entities/category.entity.d.ts +26 -0
  104. package/dist/backend/entities/category.entity.d.ts.map +1 -0
  105. package/dist/backend/entities/category.entity.js +119 -0
  106. package/dist/backend/entities/category.entity.js.map +1 -0
  107. package/dist/backend/entities/gallery-item-label.entity.d.ts +22 -0
  108. package/dist/backend/entities/gallery-item-label.entity.d.ts.map +1 -0
  109. package/dist/backend/entities/gallery-item-label.entity.js +50 -0
  110. package/dist/backend/entities/gallery-item-label.entity.js.map +1 -0
  111. package/dist/backend/entities/gallery-item.entity.d.ts +17 -0
  112. package/dist/backend/entities/gallery-item.entity.d.ts.map +1 -0
  113. package/dist/backend/entities/gallery-item.entity.js +58 -0
  114. package/dist/backend/entities/gallery-item.entity.js.map +1 -0
  115. package/dist/backend/entities/grouped-item.entity.d.ts +23 -0
  116. package/dist/backend/entities/grouped-item.entity.d.ts.map +1 -0
  117. package/dist/backend/entities/grouped-item.entity.js +68 -0
  118. package/dist/backend/entities/grouped-item.entity.js.map +1 -0
  119. package/dist/backend/entities/product-attachment.entity.d.ts +23 -0
  120. package/dist/backend/entities/product-attachment.entity.d.ts.map +1 -0
  121. package/dist/backend/entities/product-attachment.entity.js +77 -0
  122. package/dist/backend/entities/product-attachment.entity.js.map +1 -0
  123. package/dist/backend/entities/product-attribute.entity.d.ts +72 -0
  124. package/dist/backend/entities/product-attribute.entity.d.ts.map +1 -0
  125. package/dist/backend/entities/product-attribute.entity.js +161 -0
  126. package/dist/backend/entities/product-attribute.entity.js.map +1 -0
  127. package/dist/backend/entities/product-editor-preference.entity.d.ts +28 -0
  128. package/dist/backend/entities/product-editor-preference.entity.d.ts.map +1 -0
  129. package/dist/backend/entities/product-editor-preference.entity.js +63 -0
  130. package/dist/backend/entities/product-editor-preference.entity.js.map +1 -0
  131. package/dist/backend/entities/product-link.entity.d.ts +25 -0
  132. package/dist/backend/entities/product-link.entity.d.ts.map +1 -0
  133. package/dist/backend/entities/product-link.entity.js +70 -0
  134. package/dist/backend/entities/product-link.entity.js.map +1 -0
  135. package/dist/backend/entities/product-packaging-unit.entity.d.ts +26 -0
  136. package/dist/backend/entities/product-packaging-unit.entity.d.ts.map +1 -0
  137. package/dist/backend/entities/product-packaging-unit.entity.js +76 -0
  138. package/dist/backend/entities/product-packaging-unit.entity.js.map +1 -0
  139. package/dist/backend/entities/product-value-override.entity.d.ts +43 -0
  140. package/dist/backend/entities/product-value-override.entity.d.ts.map +1 -0
  141. package/dist/backend/entities/product-value-override.entity.js +90 -0
  142. package/dist/backend/entities/product-value-override.entity.js.map +1 -0
  143. package/dist/backend/entities/product-variant.entity.d.ts +18 -0
  144. package/dist/backend/entities/product-variant.entity.d.ts.map +1 -0
  145. package/dist/backend/entities/product-variant.entity.js +68 -0
  146. package/dist/backend/entities/product-variant.entity.js.map +1 -0
  147. package/dist/backend/entities/product.entity.d.ts +108 -0
  148. package/dist/backend/entities/product.entity.d.ts.map +1 -0
  149. package/dist/backend/entities/product.entity.js +230 -0
  150. package/dist/backend/entities/product.entity.js.map +1 -0
  151. package/dist/backend/index.d.ts +262 -0
  152. package/dist/backend/index.d.ts.map +1 -0
  153. package/dist/backend/index.js +642 -0
  154. package/dist/backend/index.js.map +1 -0
  155. package/dist/backend/plugin.d.ts +216 -0
  156. package/dist/backend/plugin.d.ts.map +1 -0
  157. package/dist/backend/plugin.js +223 -0
  158. package/dist/backend/plugin.js.map +1 -0
  159. package/dist/backend/prompt-tools.d.ts +47 -0
  160. package/dist/backend/prompt-tools.d.ts.map +1 -0
  161. package/dist/backend/prompt-tools.js +284 -0
  162. package/dist/backend/prompt-tools.js.map +1 -0
  163. package/dist/backend/routes.admin.d.ts +105 -0
  164. package/dist/backend/routes.admin.d.ts.map +1 -0
  165. package/dist/backend/routes.admin.js +1162 -0
  166. package/dist/backend/routes.admin.js.map +1 -0
  167. package/dist/backend/routes.api-key.d.ts +33 -0
  168. package/dist/backend/routes.api-key.d.ts.map +1 -0
  169. package/dist/backend/routes.api-key.js +43 -0
  170. package/dist/backend/routes.api-key.js.map +1 -0
  171. package/dist/backend/routes.external.d.ts +32 -0
  172. package/dist/backend/routes.external.d.ts.map +1 -0
  173. package/dist/backend/routes.external.js +115 -0
  174. package/dist/backend/routes.external.js.map +1 -0
  175. package/dist/backend/routes.public.d.ts +46 -0
  176. package/dist/backend/routes.public.d.ts.map +1 -0
  177. package/dist/backend/routes.public.js +318 -0
  178. package/dist/backend/routes.public.js.map +1 -0
  179. package/dist/backend/services/asset-references.d.ts +4 -0
  180. package/dist/backend/services/asset-references.d.ts.map +1 -0
  181. package/dist/backend/services/asset-references.js +104 -0
  182. package/dist/backend/services/asset-references.js.map +1 -0
  183. package/dist/backend/services/attachment.service.d.ts +45 -0
  184. package/dist/backend/services/attachment.service.d.ts.map +1 -0
  185. package/dist/backend/services/attachment.service.js +268 -0
  186. package/dist/backend/services/attachment.service.js.map +1 -0
  187. package/dist/backend/services/attribute-option-validator.d.ts +47 -0
  188. package/dist/backend/services/attribute-option-validator.d.ts.map +1 -0
  189. package/dist/backend/services/attribute-option-validator.js +69 -0
  190. package/dist/backend/services/attribute-option-validator.js.map +1 -0
  191. package/dist/backend/services/attribute-set-validations.d.ts +65 -0
  192. package/dist/backend/services/attribute-set-validations.d.ts.map +1 -0
  193. package/dist/backend/services/attribute-set-validations.js +89 -0
  194. package/dist/backend/services/attribute-set-validations.js.map +1 -0
  195. package/dist/backend/services/attribute-set.service.d.ts +50 -0
  196. package/dist/backend/services/attribute-set.service.d.ts.map +1 -0
  197. package/dist/backend/services/attribute-set.service.js +401 -0
  198. package/dist/backend/services/attribute-set.service.js.map +1 -0
  199. package/dist/backend/services/attribute-type-mapping.d.ts +66 -0
  200. package/dist/backend/services/attribute-type-mapping.d.ts.map +1 -0
  201. package/dist/backend/services/attribute-type-mapping.js +121 -0
  202. package/dist/backend/services/attribute-type-mapping.js.map +1 -0
  203. package/dist/backend/services/attribute-value-key.service.d.ts +48 -0
  204. package/dist/backend/services/attribute-value-key.service.d.ts.map +1 -0
  205. package/dist/backend/services/attribute-value-key.service.js +118 -0
  206. package/dist/backend/services/attribute-value-key.service.js.map +1 -0
  207. package/dist/backend/services/audit-references.d.ts +13 -0
  208. package/dist/backend/services/audit-references.d.ts.map +1 -0
  209. package/dist/backend/services/audit-references.js +51 -0
  210. package/dist/backend/services/audit-references.js.map +1 -0
  211. package/dist/backend/services/bulk-operation-queue.d.ts +26 -0
  212. package/dist/backend/services/bulk-operation-queue.d.ts.map +1 -0
  213. package/dist/backend/services/bulk-operation-queue.js +34 -0
  214. package/dist/backend/services/bulk-operation-queue.js.map +1 -0
  215. package/dist/backend/services/bulk-operation.service.d.ts +195 -0
  216. package/dist/backend/services/bulk-operation.service.d.ts.map +1 -0
  217. package/dist/backend/services/bulk-operation.service.js +510 -0
  218. package/dist/backend/services/bulk-operation.service.js.map +1 -0
  219. package/dist/backend/services/bundle.service.d.ts +87 -0
  220. package/dist/backend/services/bundle.service.d.ts.map +1 -0
  221. package/dist/backend/services/bundle.service.js +286 -0
  222. package/dist/backend/services/bundle.service.js.map +1 -0
  223. package/dist/backend/services/catalog-admin.service.d.ts +398 -0
  224. package/dist/backend/services/catalog-admin.service.d.ts.map +1 -0
  225. package/dist/backend/services/catalog-admin.service.js +1516 -0
  226. package/dist/backend/services/catalog-admin.service.js.map +1 -0
  227. package/dist/backend/services/catalog-attribute-read.service.d.ts +78 -0
  228. package/dist/backend/services/catalog-attribute-read.service.d.ts.map +1 -0
  229. package/dist/backend/services/catalog-attribute-read.service.js +216 -0
  230. package/dist/backend/services/catalog-attribute-read.service.js.map +1 -0
  231. package/dist/backend/services/catalog-bulk-import.service.d.ts +30 -0
  232. package/dist/backend/services/catalog-bulk-import.service.d.ts.map +1 -0
  233. package/dist/backend/services/catalog-bulk-import.service.js +136 -0
  234. package/dist/backend/services/catalog-bulk-import.service.js.map +1 -0
  235. package/dist/backend/services/catalog-bulk-update.service.d.ts +91 -0
  236. package/dist/backend/services/catalog-bulk-update.service.d.ts.map +1 -0
  237. package/dist/backend/services/catalog-bulk-update.service.js +330 -0
  238. package/dist/backend/services/catalog-bulk-update.service.js.map +1 -0
  239. package/dist/backend/services/catalog-category-read.service.d.ts +66 -0
  240. package/dist/backend/services/catalog-category-read.service.d.ts.map +1 -0
  241. package/dist/backend/services/catalog-category-read.service.js +248 -0
  242. package/dist/backend/services/catalog-category-read.service.js.map +1 -0
  243. package/dist/backend/services/catalog-org-price-decorator.d.ts +90 -0
  244. package/dist/backend/services/catalog-org-price-decorator.d.ts.map +1 -0
  245. package/dist/backend/services/catalog-org-price-decorator.js +184 -0
  246. package/dist/backend/services/catalog-org-price-decorator.js.map +1 -0
  247. package/dist/backend/services/catalog-product-filter.service.d.ts +82 -0
  248. package/dist/backend/services/catalog-product-filter.service.d.ts.map +1 -0
  249. package/dist/backend/services/catalog-product-filter.service.js +275 -0
  250. package/dist/backend/services/catalog-product-filter.service.js.map +1 -0
  251. package/dist/backend/services/catalog-product-read.service.d.ts +46 -0
  252. package/dist/backend/services/catalog-product-read.service.d.ts.map +1 -0
  253. package/dist/backend/services/catalog-product-read.service.js +197 -0
  254. package/dist/backend/services/catalog-product-read.service.js.map +1 -0
  255. package/dist/backend/services/catalog-query.service.d.ts +444 -0
  256. package/dist/backend/services/catalog-query.service.d.ts.map +1 -0
  257. package/dist/backend/services/catalog-query.service.js +1633 -0
  258. package/dist/backend/services/catalog-query.service.js.map +1 -0
  259. package/dist/backend/services/catalog-quick-search.service.d.ts +78 -0
  260. package/dist/backend/services/catalog-quick-search.service.d.ts.map +1 -0
  261. package/dist/backend/services/catalog-quick-search.service.js +159 -0
  262. package/dist/backend/services/catalog-quick-search.service.js.map +1 -0
  263. package/dist/backend/services/catalog-write-ports.d.ts +25 -0
  264. package/dist/backend/services/catalog-write-ports.d.ts.map +1 -0
  265. package/dist/backend/services/catalog-write-ports.js +63 -0
  266. package/dist/backend/services/catalog-write-ports.js.map +1 -0
  267. package/dist/backend/services/category-admin.service.d.ts +102 -0
  268. package/dist/backend/services/category-admin.service.d.ts.map +1 -0
  269. package/dist/backend/services/category-admin.service.js +271 -0
  270. package/dist/backend/services/category-admin.service.js.map +1 -0
  271. package/dist/backend/services/gallery.service.d.ts +85 -0
  272. package/dist/backend/services/gallery.service.d.ts.map +1 -0
  273. package/dist/backend/services/gallery.service.js +326 -0
  274. package/dist/backend/services/gallery.service.js.map +1 -0
  275. package/dist/backend/services/grouped.service.d.ts +39 -0
  276. package/dist/backend/services/grouped.service.d.ts.map +1 -0
  277. package/dist/backend/services/grouped.service.js +136 -0
  278. package/dist/backend/services/grouped.service.js.map +1 -0
  279. package/dist/backend/services/label-resolver.d.ts +16 -0
  280. package/dist/backend/services/label-resolver.d.ts.map +1 -0
  281. package/dist/backend/services/label-resolver.js +43 -0
  282. package/dist/backend/services/label-resolver.js.map +1 -0
  283. package/dist/backend/services/packaging-unit.service.d.ts +28 -0
  284. package/dist/backend/services/packaging-unit.service.d.ts.map +1 -0
  285. package/dist/backend/services/packaging-unit.service.js +197 -0
  286. package/dist/backend/services/packaging-unit.service.js.map +1 -0
  287. package/dist/backend/services/primary-asset-url.d.ts +4 -0
  288. package/dist/backend/services/primary-asset-url.d.ts.map +1 -0
  289. package/dist/backend/services/primary-asset-url.js +55 -0
  290. package/dist/backend/services/primary-asset-url.js.map +1 -0
  291. package/dist/backend/services/product-editor-preferences.service.d.ts +20 -0
  292. package/dist/backend/services/product-editor-preferences.service.d.ts.map +1 -0
  293. package/dist/backend/services/product-editor-preferences.service.js +44 -0
  294. package/dist/backend/services/product-editor-preferences.service.js.map +1 -0
  295. package/dist/backend/services/product-link.service.d.ts +169 -0
  296. package/dist/backend/services/product-link.service.d.ts.map +1 -0
  297. package/dist/backend/services/product-link.service.js +297 -0
  298. package/dist/backend/services/product-link.service.js.map +1 -0
  299. package/dist/backend/services/product-overrides.service.d.ts +77 -0
  300. package/dist/backend/services/product-overrides.service.d.ts.map +1 -0
  301. package/dist/backend/services/product-overrides.service.js +204 -0
  302. package/dist/backend/services/product-overrides.service.js.map +1 -0
  303. package/dist/backend/services/product-scope-context.service.d.ts +36 -0
  304. package/dist/backend/services/product-scope-context.service.d.ts.map +1 -0
  305. package/dist/backend/services/product-scope-context.service.js +106 -0
  306. package/dist/backend/services/product-scope-context.service.js.map +1 -0
  307. package/dist/backend/services/product-type-validations.d.ts +42 -0
  308. package/dist/backend/services/product-type-validations.d.ts.map +1 -0
  309. package/dist/backend/services/product-type-validations.js +55 -0
  310. package/dist/backend/services/product-type-validations.js.map +1 -0
  311. package/dist/backend/services/product-value-resolver.service.d.ts +59 -0
  312. package/dist/backend/services/product-value-resolver.service.d.ts.map +1 -0
  313. package/dist/backend/services/product-value-resolver.service.js +111 -0
  314. package/dist/backend/services/product-value-resolver.service.js.map +1 -0
  315. package/dist/backend/services/system-attribute-scopes.d.ts +18 -0
  316. package/dist/backend/services/system-attribute-scopes.d.ts.map +1 -0
  317. package/dist/backend/services/system-attribute-scopes.js +18 -0
  318. package/dist/backend/services/system-attribute-scopes.js.map +1 -0
  319. package/dist/backend/services/viewer-organization.d.ts +25 -0
  320. package/dist/backend/services/viewer-organization.d.ts.map +1 -0
  321. package/dist/backend/services/viewer-organization.js +31 -0
  322. package/dist/backend/services/viewer-organization.js.map +1 -0
  323. package/dist/manifest.d.ts +204 -0
  324. package/dist/manifest.d.ts.map +1 -0
  325. package/dist/manifest.js +686 -0
  326. package/dist/manifest.js.map +1 -0
  327. package/dist/migrations/20260429T064146_catalog_attribute_sets_init.d.ts +33 -0
  328. package/dist/migrations/20260429T064146_catalog_attribute_sets_init.d.ts.map +1 -0
  329. package/dist/migrations/20260429T064146_catalog_attribute_sets_init.js +118 -0
  330. package/dist/migrations/20260429T064146_catalog_attribute_sets_init.js.map +1 -0
  331. package/dist/migrations/20260429T070004_catalog_product_attribute_extensions.d.ts +24 -0
  332. package/dist/migrations/20260429T070004_catalog_product_attribute_extensions.d.ts.map +1 -0
  333. package/dist/migrations/20260429T070004_catalog_product_attribute_extensions.js +31 -0
  334. package/dist/migrations/20260429T070004_catalog_product_attribute_extensions.js.map +1 -0
  335. package/dist/migrations/20260429T102322_catalog_product_type_and_virtual_fields.d.ts +32 -0
  336. package/dist/migrations/20260429T102322_catalog_product_type_and_virtual_fields.d.ts.map +1 -0
  337. package/dist/migrations/20260429T102322_catalog_product_type_and_virtual_fields.js +58 -0
  338. package/dist/migrations/20260429T102322_catalog_product_type_and_virtual_fields.js.map +1 -0
  339. package/dist/migrations/20260429T111839_catalog_gallery_items_and_labels.d.ts +22 -0
  340. package/dist/migrations/20260429T111839_catalog_gallery_items_and_labels.d.ts.map +1 -0
  341. package/dist/migrations/20260429T111839_catalog_gallery_items_and_labels.js +66 -0
  342. package/dist/migrations/20260429T111839_catalog_gallery_items_and_labels.js.map +1 -0
  343. package/dist/migrations/20260429T112543_catalog_product_attachments.d.ts +18 -0
  344. package/dist/migrations/20260429T112543_catalog_product_attachments.d.ts.map +1 -0
  345. package/dist/migrations/20260429T112543_catalog_product_attachments.js +73 -0
  346. package/dist/migrations/20260429T112543_catalog_product_attachments.js.map +1 -0
  347. package/dist/migrations/20260429T123726_catalog_product_links.d.ts +21 -0
  348. package/dist/migrations/20260429T123726_catalog_product_links.d.ts.map +1 -0
  349. package/dist/migrations/20260429T123726_catalog_product_links.js +48 -0
  350. package/dist/migrations/20260429T123726_catalog_product_links.js.map +1 -0
  351. package/dist/migrations/20260429T130803_catalog_grouped_and_bundle.d.ts +23 -0
  352. package/dist/migrations/20260429T130803_catalog_grouped_and_bundle.d.ts.map +1 -0
  353. package/dist/migrations/20260429T130803_catalog_grouped_and_bundle.js +94 -0
  354. package/dist/migrations/20260429T130803_catalog_grouped_and_bundle.js.map +1 -0
  355. package/dist/migrations/20260501T185835_catalog_product_attribute_is_comparable.d.ts +24 -0
  356. package/dist/migrations/20260501T185835_catalog_product_attribute_is_comparable.d.ts.map +1 -0
  357. package/dist/migrations/20260501T185835_catalog_product_attribute_is_comparable.js +31 -0
  358. package/dist/migrations/20260501T185835_catalog_product_attribute_is_comparable.js.map +1 -0
  359. package/dist/migrations/20260505T060113_catalog_attribute_options_and_flags.d.ts +31 -0
  360. package/dist/migrations/20260505T060113_catalog_attribute_options_and_flags.d.ts.map +1 -0
  361. package/dist/migrations/20260505T060113_catalog_attribute_options_and_flags.js +133 -0
  362. package/dist/migrations/20260505T060113_catalog_attribute_options_and_flags.js.map +1 -0
  363. package/dist/migrations/20260515T082629_catalog_attribute_mass_editable.d.ts +16 -0
  364. package/dist/migrations/20260515T082629_catalog_attribute_mass_editable.d.ts.map +1 -0
  365. package/dist/migrations/20260515T082629_catalog_attribute_mass_editable.js +23 -0
  366. package/dist/migrations/20260515T082629_catalog_attribute_mass_editable.js.map +1 -0
  367. package/dist/migrations/20260526T124736_catalog_product_status_inactive.d.ts +10 -0
  368. package/dist/migrations/20260526T124736_catalog_product_status_inactive.d.ts.map +1 -0
  369. package/dist/migrations/20260526T124736_catalog_product_status_inactive.js +22 -0
  370. package/dist/migrations/20260526T124736_catalog_product_status_inactive.js.map +1 -0
  371. package/dist/migrations/20260611T140346_catalog_product_value_overrides_init.d.ts +38 -0
  372. package/dist/migrations/20260611T140346_catalog_product_value_overrides_init.d.ts.map +1 -0
  373. package/dist/migrations/20260611T140346_catalog_product_value_overrides_init.js +98 -0
  374. package/dist/migrations/20260611T140346_catalog_product_value_overrides_init.js.map +1 -0
  375. package/dist/migrations/20260611T140400_catalog_attribute_quick_searchable.d.ts +13 -0
  376. package/dist/migrations/20260611T140400_catalog_attribute_quick_searchable.d.ts.map +1 -0
  377. package/dist/migrations/20260611T140400_catalog_attribute_quick_searchable.js +17 -0
  378. package/dist/migrations/20260611T140400_catalog_attribute_quick_searchable.js.map +1 -0
  379. package/dist/migrations/20260611T140407_catalog_bulk_operations.d.ts +16 -0
  380. package/dist/migrations/20260611T140407_catalog_bulk_operations.d.ts.map +1 -0
  381. package/dist/migrations/20260611T140407_catalog_bulk_operations.js +42 -0
  382. package/dist/migrations/20260611T140407_catalog_bulk_operations.js.map +1 -0
  383. package/dist/migrations/20260611T140408_catalog_bulk_operation_logs.d.ts +14 -0
  384. package/dist/migrations/20260611T140408_catalog_bulk_operation_logs.d.ts.map +1 -0
  385. package/dist/migrations/20260611T140408_catalog_bulk_operation_logs.js +18 -0
  386. package/dist/migrations/20260611T140408_catalog_bulk_operation_logs.js.map +1 -0
  387. package/dist/migrations/20260611T140412_catalog_product_packaging_units.d.ts +11 -0
  388. package/dist/migrations/20260611T140412_catalog_product_packaging_units.d.ts.map +1 -0
  389. package/dist/migrations/20260611T140412_catalog_product_packaging_units.js +29 -0
  390. package/dist/migrations/20260611T140412_catalog_product_packaging_units.js.map +1 -0
  391. package/dist/migrations/20260718T060659_catalog_bulk_operation_revert_state.d.ts +23 -0
  392. package/dist/migrations/20260718T060659_catalog_bulk_operation_revert_state.d.ts.map +1 -0
  393. package/dist/migrations/20260718T060659_catalog_bulk_operation_revert_state.js +35 -0
  394. package/dist/migrations/20260718T060659_catalog_bulk_operation_revert_state.js.map +1 -0
  395. package/dist/migrations/20260718T200343_catalog_category_custom_field_values.d.ts +12 -0
  396. package/dist/migrations/20260718T200343_catalog_category_custom_field_values.d.ts.map +1 -0
  397. package/dist/migrations/20260718T200343_catalog_category_custom_field_values.js +16 -0
  398. package/dist/migrations/20260718T200343_catalog_category_custom_field_values.js.map +1 -0
  399. package/dist/migrations/20260723T230401_catalog_attributes_on_custom_fields.d.ts +32 -0
  400. package/dist/migrations/20260723T230401_catalog_attributes_on_custom_fields.d.ts.map +1 -0
  401. package/dist/migrations/20260723T230401_catalog_attributes_on_custom_fields.js +286 -0
  402. package/dist/migrations/20260723T230401_catalog_attributes_on_custom_fields.js.map +1 -0
  403. package/dist/migrations/20260804T152604_catalog_widen_product_sku.d.ts +36 -0
  404. package/dist/migrations/20260804T152604_catalog_widen_product_sku.d.ts.map +1 -0
  405. package/dist/migrations/20260804T152604_catalog_widen_product_sku.js +63 -0
  406. package/dist/migrations/20260804T152604_catalog_widen_product_sku.js.map +1 -0
  407. package/dist/migrations/20260804T160244_catalog_category_activation.d.ts +18 -0
  408. package/dist/migrations/20260804T160244_catalog_category_activation.d.ts.map +1 -0
  409. package/dist/migrations/20260804T160244_catalog_category_activation.js +22 -0
  410. package/dist/migrations/20260804T160244_catalog_category_activation.js.map +1 -0
  411. package/dist/migrations/20260912T094557_catalog_sales_channel_products.d.ts +27 -0
  412. package/dist/migrations/20260912T094557_catalog_sales_channel_products.d.ts.map +1 -0
  413. package/dist/migrations/20260912T094557_catalog_sales_channel_products.js +44 -0
  414. package/dist/migrations/20260912T094557_catalog_sales_channel_products.js.map +1 -0
  415. package/dist/migrations/20260912T094623_catalog_sales_channel_categories.d.ts +27 -0
  416. package/dist/migrations/20260912T094623_catalog_sales_channel_categories.d.ts.map +1 -0
  417. package/dist/migrations/20260912T094623_catalog_sales_channel_categories.js +44 -0
  418. package/dist/migrations/20260912T094623_catalog_sales_channel_categories.js.map +1 -0
  419. package/dist/migrations/20260925T125527_catalog_inventory_columns.d.ts +36 -0
  420. package/dist/migrations/20260925T125527_catalog_inventory_columns.d.ts.map +1 -0
  421. package/dist/migrations/20260925T125527_catalog_inventory_columns.js +87 -0
  422. package/dist/migrations/20260925T125527_catalog_inventory_columns.js.map +1 -0
  423. package/dist/migrations/index.d.ts +55 -0
  424. package/dist/migrations/index.d.ts.map +1 -0
  425. package/dist/migrations/index.js +80 -0
  426. package/dist/migrations/index.js.map +1 -0
  427. package/dist/ports/index.d.ts +96 -0
  428. package/dist/ports/index.d.ts.map +1 -0
  429. package/dist/ports/index.js +2 -0
  430. package/dist/ports/index.js.map +1 -0
  431. package/docs/catalog/attachments.md +69 -0
  432. package/docs/catalog/attribute-sets.md +63 -0
  433. package/docs/catalog/attributes.md +244 -0
  434. package/docs/catalog/composite-products.md +115 -0
  435. package/docs/catalog/gallery-and-labels.md +73 -0
  436. package/docs/catalog/packaging-units.md +67 -0
  437. package/docs/catalog/per-channel-per-language-overrides.md +234 -0
  438. package/docs/catalog/product-links.md +79 -0
  439. package/docs/catalog.md +207 -0
  440. package/i18n/en.json +649 -0
  441. package/i18n/pl.json +649 -0
  442. package/package.json +109 -0
  443. package/tailwind.css +14 -0
@@ -0,0 +1,248 @@
1
+ import { Category } from '../entities/category.entity.js';
2
+ /**
3
+ * The category read model `catalog` publishes (feature 075, Phase P).
4
+ *
5
+ * Fourteen inbound sites read the `Category` entity: the inventory threshold
6
+ * resolver, `price_lists` walking the ancestor chain, `product_feeds` mapping
7
+ * the tree onto an external taxonomy, `seo` rendering a category page.
8
+ *
9
+ * `ancestorsOf` is the one method that is not a transcription of an existing
10
+ * call. `price_lists` walks the parent pointer with a `findOne` per level —
11
+ * a loop whose depth is data, in a module that does not own the table. Moving
12
+ * it here bounds it: the write path forbids a cycle, but a corrupt
13
+ * `parent_id` should degrade into a short answer rather than hang a request.
14
+ */
15
+ const MAX_ANCESTOR_DEPTH = 64;
16
+ export class CatalogCategoryReadService {
17
+ emFactory;
18
+ constructor(emFactory) {
19
+ this.emFactory = emFactory;
20
+ }
21
+ async findById(id, options) {
22
+ const category = await this.emFactory().findOne(Category, {
23
+ id,
24
+ ...liveFilter(options),
25
+ });
26
+ return category ? toCatalogCategoryRecord(category) : null;
27
+ }
28
+ async findByIds(ids, options) {
29
+ if (ids.length === 0)
30
+ return [];
31
+ const categories = await this.emFactory().find(Category, {
32
+ id: { $in: [...ids] },
33
+ ...liveFilter(options),
34
+ });
35
+ return categories.map(toCatalogCategoryRecord);
36
+ }
37
+ async findBySlug(slug) {
38
+ const category = await this.emFactory().findOne(Category, { slug });
39
+ return category ? toCatalogCategoryRecord(category) : null;
40
+ }
41
+ async countByIds(ids) {
42
+ if (ids.length === 0)
43
+ return 0;
44
+ return this.emFactory().count(Category, { id: { $in: [...ids] } });
45
+ }
46
+ async listAll(options) {
47
+ const categories = await this.emFactory().find(Category, liveFilter(options), { orderBy: { sortOrder: 'asc', slug: 'asc' } });
48
+ return categories.map(toCatalogCategoryRecord);
49
+ }
50
+ async listWithInventoryThresholds() {
51
+ const categories = await this.emFactory().find(Category, {
52
+ $or: [
53
+ { inventoryThresholdHigh: { $ne: null } },
54
+ { inventoryThresholdMedium: { $ne: null } },
55
+ { inventoryThresholdLow: { $ne: null } },
56
+ ],
57
+ });
58
+ return categories.map(toCatalogCategoryRecord);
59
+ }
60
+ async ancestorsOf(categoryId) {
61
+ const em = this.emFactory();
62
+ const chain = [];
63
+ const seen = new Set();
64
+ let cursorId = categoryId;
65
+ while (cursorId !== null && chain.length < MAX_ANCESTOR_DEPTH && !seen.has(cursorId)) {
66
+ seen.add(cursorId);
67
+ const cursor = await em.findOne(Category, { id: cursorId });
68
+ if (!cursor)
69
+ break;
70
+ chain.push(toCatalogCategoryRecord(cursor));
71
+ cursorId = cursor.parentCategoryId ?? null;
72
+ }
73
+ return chain;
74
+ }
75
+ /**
76
+ * Feature 075 / D-87 — the `product_categories` bridge, which has no entity
77
+ * class and was therefore joined in raw SQL by whoever needed it. `search`
78
+ * ran this statement three times from inside its indexer.
79
+ *
80
+ * `em.execute` rather than `getConnection().execute`: the two are the same
81
+ * outside a transaction and only the first joins one when the caller has it
82
+ * open (issue #200).
83
+ */
84
+ async listAssignmentsForProducts(productIds, options) {
85
+ if (productIds.length === 0)
86
+ return [];
87
+ const placeholders = productIds.map(() => '?').join(',');
88
+ const activeFilter = options?.activeOnly === true ? ' and c.is_active = true' : '';
89
+ const rows = await this.emFactory().execute(`select pc.product_id, pc.category_id, c.slug
90
+ from product_categories pc
91
+ join categories c on c.id = pc.category_id
92
+ where pc.product_id in (${placeholders})${activeFilter}`, [...productIds]);
93
+ return rows.map((row) => ({
94
+ productId: row.product_id,
95
+ categoryId: row.category_id,
96
+ slug: row.slug,
97
+ }));
98
+ }
99
+ /**
100
+ * One recursive query rather than a query per level: a subtree re-projection
101
+ * runs on every category write, and the depth of the tree is data.
102
+ *
103
+ * No `is_active` / `deleted_at` filter anywhere in the walk — see the port's
104
+ * contract. The caller re-projects *because* a category changed, and a
105
+ * deactivation is the change that matters most.
106
+ */
107
+ async listProductIdsInSubtree(categoryId) {
108
+ const rows = await this.emFactory().execute(`with recursive subtree as (
109
+ select id from categories where id = ?
110
+ union all
111
+ select c.id from categories c join subtree s on c.parent_category_id = s.id
112
+ )
113
+ select distinct pc.product_id
114
+ from product_categories pc
115
+ join subtree s on s.id = pc.category_id`, [categoryId]);
116
+ return rows.map((row) => row.product_id);
117
+ }
118
+ /**
119
+ * The selection walk — see the port's contract for why it is not a batched
120
+ * {@link listProductIdsInSubtree}. It skips soft-deleted categories and it
121
+ * tolerates a cycle; the structural read above does neither, deliberately.
122
+ *
123
+ * It was `CatalogQueryService.expandCategoryProductIds` until
124
+ * `specs/110-instance-repository/` T118c, reached by both composition roots
125
+ * over the unpublished `catalogQueryPort` name and by nothing else. It is a
126
+ * category read, so it belongs to the category read model; there is one copy.
127
+ */
128
+ async expandCategoryProductIds(categoryIds) {
129
+ const result = new Map();
130
+ if (categoryIds.length === 0)
131
+ return result;
132
+ const em = this.emFactory();
133
+ const requested = [...new Set(categoryIds)];
134
+ // Descendant ids per requested root (BFS, one query per level for the
135
+ // whole batch — the tree is shallow and this keeps the round trips flat).
136
+ const descendants = new Map();
137
+ for (const id of requested)
138
+ descendants.set(id, [id]);
139
+ // Visited set per root. Re-parenting is guarded against cycles
140
+ // (`category-admin.service.ts`), but that guard itself is written to
141
+ // tolerate *pre-existing* cycles in the data, so a corrupted
142
+ // `parent_category_id` chain is reachable here. Without this the loop
143
+ // never terminates: it would re-query the same level forever and grow
144
+ // `descendants` without bound.
145
+ const seen = new Map(requested.map((id) => [id, new Set([id])]));
146
+ let frontier = new Map(requested.map((id) => [id, [id]]));
147
+ while (frontier.size > 0) {
148
+ const parentIds = [...new Set([...frontier.values()].flat())];
149
+ const children = await em.find(Category, {
150
+ parentCategoryId: { $in: parentIds },
151
+ deletedAt: null,
152
+ });
153
+ if (children.length === 0)
154
+ break;
155
+ const childrenByParent = new Map();
156
+ for (const child of children) {
157
+ const parent = String(child.parentCategoryId);
158
+ childrenByParent.set(parent, [...(childrenByParent.get(parent) ?? []), child.id]);
159
+ }
160
+ const next = new Map();
161
+ for (const [root, level] of frontier) {
162
+ const visited = seen.get(root) ?? new Set();
163
+ const nextLevel = level
164
+ .flatMap((id) => childrenByParent.get(id) ?? [])
165
+ .filter((id) => !visited.has(id));
166
+ if (nextLevel.length === 0)
167
+ continue;
168
+ for (const id of nextLevel)
169
+ visited.add(id);
170
+ seen.set(root, visited);
171
+ descendants.set(root, [...(descendants.get(root) ?? []), ...nextLevel]);
172
+ next.set(root, nextLevel);
173
+ }
174
+ frontier = next;
175
+ }
176
+ const allIds = [...new Set([...descendants.values()].flat())];
177
+ const rows = await em.execute(`select category_id, product_id from product_categories
178
+ where category_id in (${allIds.map(() => '?').join(',')})`, allIds);
179
+ const productsByCategory = new Map();
180
+ for (const row of rows) {
181
+ productsByCategory.set(row.category_id, [
182
+ ...(productsByCategory.get(row.category_id) ?? []),
183
+ row.product_id,
184
+ ]);
185
+ }
186
+ for (const id of requested) {
187
+ const set = new Set();
188
+ for (const categoryId of descendants.get(id) ?? []) {
189
+ for (const productId of productsByCategory.get(categoryId) ?? [])
190
+ set.add(productId);
191
+ }
192
+ result.set(id, set);
193
+ }
194
+ return result;
195
+ }
196
+ /**
197
+ * The one category, not its subtree — see the port's contract for why the two
198
+ * are separate methods. `pim_ergonode` wrote this statement itself before the
199
+ * port had it, to detach the products of a category the source tree dropped.
200
+ */
201
+ async listProductIdsInCategory(categoryId) {
202
+ const rows = await this.emFactory().execute('select distinct pc.product_id from product_categories pc where pc.category_id = ?', [categoryId]);
203
+ return rows.map((row) => row.product_id);
204
+ }
205
+ /**
206
+ * One grouped query rather than a count per id: the caller renders a source
207
+ * hierarchy of a few hundred bound categories on one screen, so a count per
208
+ * row would be a few hundred round trips every time it loads.
209
+ */
210
+ async countLiveProductsByCategory(categoryIds) {
211
+ if (categoryIds.length === 0)
212
+ return [];
213
+ const placeholders = categoryIds.map(() => '?').join(',');
214
+ const rows = await this.emFactory().execute(`select pc.category_id, count(*)::int as product_count
215
+ from product_categories pc
216
+ join products p on p.id = pc.product_id and p.deleted_at is null
217
+ where pc.category_id in (${placeholders})
218
+ group by pc.category_id`, [...categoryIds]);
219
+ return rows.map((row) => ({
220
+ categoryId: row.category_id,
221
+ productCount: Number(row.product_count),
222
+ }));
223
+ }
224
+ }
225
+ function liveFilter(options) {
226
+ return options?.liveOnly ? { deletedAt: null } : {};
227
+ }
228
+ export function toCatalogCategoryRecord(category) {
229
+ return {
230
+ id: category.id,
231
+ parentCategoryId: category.parentCategoryId ?? null,
232
+ name: category.name,
233
+ slug: category.slug,
234
+ sortOrder: category.sortOrder,
235
+ metaTitleOverride: category.metaTitleOverride ?? null,
236
+ metaDescriptionOverride: category.metaDescriptionOverride ?? null,
237
+ customFieldValues: category.customFieldValues ?? {},
238
+ isActive: category.isActive,
239
+ inventoryThresholdHigh: category.inventoryThresholdHigh ?? null,
240
+ inventoryThresholdMedium: category.inventoryThresholdMedium ?? null,
241
+ inventoryThresholdLow: category.inventoryThresholdLow ?? null,
242
+ mainImageAssetId: category.mainImageAssetId ?? null,
243
+ createdAt: category.createdAt,
244
+ updatedAt: category.updatedAt,
245
+ deletedAt: category.deletedAt ?? null,
246
+ };
247
+ }
248
+ //# sourceMappingURL=catalog-category-read.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog-category-read.service.js","sourceRoot":"","sources":["../../../src/backend/services/catalog-category-read.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,90 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql';
2
+ import type { CatalogBulkPriceLine, OrgLinePricePort, OrganizationDetailsPort, ProductAvailability, ProductDetail, ProductSummary } from '@endora-commerce/contracts';
3
+ import type { SalesChannelMembershipPort } from '@endora-commerce/platform/kernel';
4
+ /**
5
+ * Feature 062 (research §R7) — pricing/availability decoration for the
6
+ * external catalog namespace (`/api/v1/external/catalog/*`).
7
+ *
8
+ * Thin decoration ONLY: query logic stays in `CatalogQueryService`, price
9
+ * resolution stays in `PricingService` (the exact resolver cart pricing
10
+ * uses — SC-001 parity by construction). This service:
11
+ *
12
+ * - for BOUND keys, replaces the summary/detail `price` with the bound
13
+ * Organization's effective unit price at quantity 1, sets the
14
+ * `priceUnavailable` marker, and (detail only) derives the org's
15
+ * quantity-bracket `priceTiers` ladder by probing the resolver at each
16
+ * bracket start quantity;
17
+ * - for ANY key, attaches the channel-public `availability` indication via
18
+ * the injected inventory port;
19
+ * - answers the bound-only bulk `(sku, quantity)` pricing request with
20
+ * per-line results (misses are data, not errors).
21
+ */
22
+ /** The request's resolved sales channel — only `id` + `defaultCurrency` are read. */
23
+ export interface ExternalPricingChannel {
24
+ id: string;
25
+ defaultCurrency: string;
26
+ }
27
+ export type ResolveAvailabilityPort = (productIds: string[], salesChannelId: string) => Promise<Map<string, ProductAvailability>>;
28
+ export interface CatalogOrgPriceDecoratorDeps {
29
+ emFactory: () => EntityManager;
30
+ /**
31
+ * Feature 075 — the published line-pricing port, where
32
+ * `price_lists`' `PricingServiceContract` used to be. This decorator calls
33
+ * two of its methods. `OrgLinePricePort` — `LinePricePort` plus the bracket
34
+ * ladder — was published in this cut, because `PricingServiceContract` is
35
+ * deliberately not in `@endora-commerce/contracts` (it is the overlay decoration's
36
+ * contract gate) and was `listBracketMinQuantities`' only declaration.
37
+ */
38
+ pricingService: OrgLinePricePort;
39
+ /**
40
+ * Feature 075 — the buying organisation, read through `organizations`' port
41
+ * instead of `em.findOne(Organization, …)` against its table. The engine
42
+ * reads two fields off it (the id, and the customer group a rule keys on),
43
+ * and `OrganizationRecord` carries both.
44
+ */
45
+ organizations: OrganizationDetailsPort;
46
+ /** Optional inventory port — when absent, `availability` is omitted. */
47
+ resolveAvailability?: ResolveAvailabilityPort;
48
+ /**
49
+ * Issue #185 — the kernel's channel-membership accessor. The published
50
+ * assortment check below asked `sales_channel_products` directly, which is
51
+ * the bridge Constitution XII reserves to this service and a cross-module
52
+ * read no import specifier could reveal.
53
+ */
54
+ channelMembership: SalesChannelMembershipPort;
55
+ }
56
+ export declare class CatalogOrgPriceDecorator {
57
+ #private;
58
+ private readonly deps;
59
+ constructor(deps: CatalogOrgPriceDecoratorDeps);
60
+ /**
61
+ * Decorates list summaries in place-order: availability for every caller,
62
+ * org-effective qty-1 price + `priceUnavailable` for bound callers.
63
+ */
64
+ decorateSummaries(summaries: ProductSummary[], context: {
65
+ organizationId: string | null;
66
+ salesChannel: ExternalPricingChannel;
67
+ }): Promise<ProductSummary[]>;
68
+ /**
69
+ * Detail decoration = summary decoration + (bound only) the org's resolved
70
+ * bracket ladder.
71
+ */
72
+ decorateDetail(detail: ProductDetail, context: {
73
+ organizationId: string | null;
74
+ salesChannel: ExternalPricingChannel;
75
+ }): Promise<ProductDetail>;
76
+ /**
77
+ * Bound-only bulk pricing: per-line, order-preserving. A SKU outside the
78
+ * bound channel's published assortment (or unknown / inactive / deleted)
79
+ * is indistinguishable from non-existence (`sku_not_in_assortment`); an
80
+ * in-assortment SKU with no resolvable bracket is `price_unavailable`.
81
+ */
82
+ resolveBulkPrices(lines: Array<{
83
+ sku: string;
84
+ quantity: number;
85
+ }>, context: {
86
+ organizationId: string;
87
+ salesChannel: ExternalPricingChannel;
88
+ }): Promise<CatalogBulkPriceLine[]>;
89
+ }
90
+ //# sourceMappingURL=catalog-org-price-decorator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog-org-price-decorator.d.ts","sourceRoot":"","sources":["../../../src/backend/services/catalog-org-price-decorator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EACV,oBAAoB,EACpB,gBAAgB,EAChB,uBAAuB,EAEvB,mBAAmB,EACnB,aAAa,EAEb,cAAc,EACf,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,kCAAkC,CAAC;AAInF;;;;;;;;;;;;;;;;;GAiBG;AAEH,qFAAqF;AACrF,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,uBAAuB,GAAG,CACpC,UAAU,EAAE,MAAM,EAAE,EACpB,cAAc,EAAE,MAAM,KACnB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC,CAAC;AAE/C,MAAM,WAAW,4BAA4B;IAC3C,SAAS,EAAE,MAAM,aAAa,CAAC;IAC/B;;;;;;;OAOG;IACH,cAAc,EAAE,gBAAgB,CAAC;IACjC;;;;;OAKG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC,wEAAwE;IACxE,mBAAmB,CAAC,EAAE,uBAAuB,CAAC;IAC9C;;;;;OAKG;IACH,iBAAiB,EAAE,0BAA0B,CAAC;CAC/C;AAED,qBAAa,wBAAwB;;IACvB,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,4BAA4B;IAE/D;;;OAGG;IACG,iBAAiB,CACrB,SAAS,EAAE,cAAc,EAAE,EAC3B,OAAO,EAAE;QAAE,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,YAAY,EAAE,sBAAsB,CAAA;KAAE,GAC/E,OAAO,CAAC,cAAc,EAAE,CAAC;IA4C5B;;;OAGG;IACG,cAAc,CAClB,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE;QAAE,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,YAAY,EAAE,sBAAsB,CAAA;KAAE,GAC/E,OAAO,CAAC,aAAa,CAAC;IAazB;;;;;OAKG;IACG,iBAAiB,CACrB,KAAK,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,EAC/C,OAAO,EAAE;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,sBAAsB,CAAA;KAAE,GACxE,OAAO,CAAC,oBAAoB,EAAE,CAAC;CA8InC"}
@@ -0,0 +1,184 @@
1
+ import { Product } from '../entities/product.entity.js';
2
+ export class CatalogOrgPriceDecorator {
3
+ deps;
4
+ constructor(deps) {
5
+ this.deps = deps;
6
+ }
7
+ /**
8
+ * Decorates list summaries in place-order: availability for every caller,
9
+ * org-effective qty-1 price + `priceUnavailable` for bound callers.
10
+ */
11
+ async decorateSummaries(summaries, context) {
12
+ if (summaries.length === 0)
13
+ return summaries;
14
+ const availabilityByProduct = await this.#resolveAvailability(summaries.map((s) => s.id), context.salesChannel.id);
15
+ if (context.organizationId === null) {
16
+ return summaries.map((summary) => this.#withAvailability(summary, availabilityByProduct));
17
+ }
18
+ const em = this.deps.emFactory();
19
+ const organization = await this.deps.organizations.findById(context.organizationId);
20
+ const products = await em.find(Product, { id: { $in: summaries.map((s) => s.id) } });
21
+ const productById = new Map(products.map((p) => [p.id, p]));
22
+ const out = [];
23
+ for (const summary of summaries) {
24
+ const decorated = this.#withAvailability(summary, availabilityByProduct);
25
+ const product = productById.get(summary.id);
26
+ if (!product) {
27
+ out.push({ ...decorated, price: null, priceUnavailable: true });
28
+ continue;
29
+ }
30
+ const resolved = await this.deps.pricingService.resolveLinePrice({
31
+ product,
32
+ context: {
33
+ quantity: 1,
34
+ organization,
35
+ salesChannel: context.salesChannel,
36
+ },
37
+ });
38
+ out.push(resolved
39
+ ? {
40
+ ...decorated,
41
+ price: { amount: Number(resolved.amount), currency: resolved.currency },
42
+ }
43
+ : { ...decorated, price: null, priceUnavailable: true });
44
+ }
45
+ return out;
46
+ }
47
+ /**
48
+ * Detail decoration = summary decoration + (bound only) the org's resolved
49
+ * bracket ladder.
50
+ */
51
+ async decorateDetail(detail, context) {
52
+ const [decorated] = await this.decorateSummaries([detail], context);
53
+ const merged = { ...detail, ...decorated };
54
+ if (context.organizationId === null)
55
+ return merged;
56
+ const em = this.deps.emFactory();
57
+ const organization = await this.deps.organizations.findById(context.organizationId);
58
+ const product = await em.findOne(Product, { id: detail.id });
59
+ if (!product)
60
+ return merged;
61
+ const priceTiers = await this.#resolvePriceTiers(product, organization, context.salesChannel);
62
+ return { ...merged, priceTiers };
63
+ }
64
+ /**
65
+ * Bound-only bulk pricing: per-line, order-preserving. A SKU outside the
66
+ * bound channel's published assortment (or unknown / inactive / deleted)
67
+ * is indistinguishable from non-existence (`sku_not_in_assortment`); an
68
+ * in-assortment SKU with no resolvable bracket is `price_unavailable`.
69
+ */
70
+ async resolveBulkPrices(lines, context) {
71
+ const em = this.deps.emFactory();
72
+ const organization = await this.deps.organizations.findById(context.organizationId);
73
+ const skus = Array.from(new Set(lines.map((l) => l.sku)));
74
+ const products = await em.find(Product, {
75
+ sku: { $in: skus },
76
+ deletedAt: null,
77
+ status: 'active',
78
+ });
79
+ const productBySku = new Map(products.map((p) => [p.sku, p]));
80
+ const assortment = await this.#filterByChannel(products.map((p) => p.id), context.salesChannel.id);
81
+ const out = [];
82
+ for (const line of lines) {
83
+ const product = productBySku.get(line.sku);
84
+ if (!product || !assortment.has(product.id)) {
85
+ out.push({
86
+ sku: line.sku,
87
+ quantity: line.quantity,
88
+ price: null,
89
+ reason: 'sku_not_in_assortment',
90
+ });
91
+ continue;
92
+ }
93
+ const resolved = await this.deps.pricingService.resolveLinePrice({
94
+ product,
95
+ context: {
96
+ quantity: line.quantity,
97
+ organization,
98
+ salesChannel: context.salesChannel,
99
+ },
100
+ });
101
+ if (!resolved) {
102
+ out.push({
103
+ sku: line.sku,
104
+ quantity: line.quantity,
105
+ price: null,
106
+ reason: 'price_unavailable',
107
+ });
108
+ continue;
109
+ }
110
+ out.push({
111
+ sku: line.sku,
112
+ quantity: line.quantity,
113
+ price: {
114
+ amount: resolved.amount,
115
+ currency: resolved.currency,
116
+ isSale: resolved.isSale,
117
+ bracketStartQuantity: resolved.bracketStartQuantity,
118
+ priceListId: resolved.priceListId,
119
+ },
120
+ });
121
+ }
122
+ return out;
123
+ }
124
+ /**
125
+ * The org's effective ladder: probe the resolver at every bracket start
126
+ * quantity known for the (product, currency) across active lists, then
127
+ * drop consecutive rungs that resolve identically (a non-matching list's
128
+ * boundary changes nothing for this org — no cross-org leak).
129
+ */
130
+ async #resolvePriceTiers(product, organization, salesChannel) {
131
+ const currencyCode = salesChannel.defaultCurrency.toUpperCase();
132
+ const minQuantities = await this.deps.pricingService.listBracketMinQuantities(product.id, currencyCode);
133
+ const probeQuantities = Array.from(new Set([1, ...minQuantities])).sort((a, b) => a - b);
134
+ const tiers = [];
135
+ for (const quantity of probeQuantities) {
136
+ const resolved = await this.deps.pricingService.resolveLinePrice({
137
+ product,
138
+ context: { quantity, organization, salesChannel },
139
+ });
140
+ if (!resolved)
141
+ continue;
142
+ const tier = {
143
+ minQuantity: quantity,
144
+ amount: Number(resolved.amount),
145
+ currency: resolved.currency,
146
+ isSale: resolved.isSale,
147
+ };
148
+ const previous = tiers[tiers.length - 1];
149
+ if (previous && previous.amount === tier.amount && previous.isSale === tier.isSale) {
150
+ continue;
151
+ }
152
+ tiers.push(tier);
153
+ }
154
+ return tiers;
155
+ }
156
+ async #resolveAvailability(productIds, salesChannelId) {
157
+ // D-61 — no `catch` here. `inventory` being absent is *decided* by the
158
+ // contribution, in front of the gate, and arrives as the empty map this
159
+ // method already returns when nothing is wired; the manifest declares that
160
+ // degrade as `degrades-without`. What a `catch` would add is the ability to
161
+ // turn a genuine inventory failure into a silently unpriced band.
162
+ if (!this.deps.resolveAvailability)
163
+ return new Map();
164
+ return this.deps.resolveAvailability(productIds, salesChannelId);
165
+ }
166
+ #withAvailability(summary, availabilityByProduct) {
167
+ const availability = availabilityByProduct.get(summary.id);
168
+ return availability ? { ...summary, availability } : summary;
169
+ }
170
+ /**
171
+ * Published-assortment check — the same question
172
+ * `CatalogQueryService.filterByChannel` asks, through the same accessor since
173
+ * issue #185. This was a hand-written `select product_id from
174
+ * sales_channel_products`, which is precisely the read Principle XII reserves
175
+ * to the channel-membership service; it fails closed to the empty set either
176
+ * way.
177
+ */
178
+ async #filterByChannel(productIds, salesChannelId) {
179
+ if (productIds.length === 0)
180
+ return new Set();
181
+ return new Set(await this.deps.channelMembership.filterEntityIdsInChannel(salesChannelId, 'product', productIds));
182
+ }
183
+ }
184
+ //# sourceMappingURL=catalog-org-price-decorator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog-org-price-decorator.js","sourceRoot":"","sources":["../../../src/backend/services/catalog-org-price-decorator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,82 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql';
2
+ import type { CatalogProductFilter, CatalogProductFilterPort, CatalogProductRecord, CatalogSellableProductQuery } from '@endora-commerce/contracts';
3
+ /**
4
+ * The sellable-product filter `catalog` publishes (feature 075).
5
+ *
6
+ * `product_feeds` compiles an operator's selection rule into a predicate and
7
+ * walks the catalogue with it. Until this port existed it did that by holding
8
+ * `Product` and handing MikroORM a `where` object of its own construction —
9
+ * the one demand Phase P declined to guess at, because a query object is the
10
+ * ORM rather than a contract.
11
+ *
12
+ * Three things move across the boundary with the translation, and each of them
13
+ * is why the port is worth more than the import it replaces:
14
+ *
15
+ * 1. **The floor is applied here.** Feature 067's FR-026 says an operator's
16
+ * rule may only ever narrow "active, visible to the feed's reader, not
17
+ * archived, not soft-deleted, in the feed's channel". On the caller's side
18
+ * that was a conjunction a future edit could spread into one object and
19
+ * lose; here it is one `$and` clause the caller cannot reach. Who "the
20
+ * feed's reader" is has one answer and it is not this file's to invent —
21
+ * see {@link ANONYMOUS_AUDIENCE_CLAUSE}.
22
+ * 2. **The cursor is applied here.** A keyset walk and the floor both
23
+ * constrain `id`, and so does a category criterion. Composing all three
24
+ * was the caller's problem and is now nobody's.
25
+ * 3. **Rows leave as records.** The caller reads six fields off each row and
26
+ * yields ids; it never needed a managed entity, and holding one made the
27
+ * identity map grow with the catalogue rather than with the page.
28
+ *
29
+ * ## What the translation may and may not do
30
+ *
31
+ * The filter grammar is deliberately narrower than MikroORM: six columns, the
32
+ * JSONB attribute bag, twelve operators, `and` / `or`, and the two constants.
33
+ * There is no node this function could translate into a join, a relation walk
34
+ * or a raw fragment, which is what stops the port becoming the general query
35
+ * surface publishing the DSL would have made it.
36
+ *
37
+ * `contains` and `startsWith` build their own patterns, so the caller never
38
+ * writes `LIKE` syntax and the escaping rule has exactly one home — this file.
39
+ * See {@link escapeLikeLiteral} for what that rule is and why the value has to
40
+ * go through it.
41
+ */
42
+ /**
43
+ * The one place a `contains` / `startsWith` value becomes part of a pattern.
44
+ *
45
+ * Both operators name a **literal**: `contains "50%"` means the three
46
+ * characters `5`, `0`, `%`. `LIKE` reads `%` as "anything", `_` as "any one
47
+ * character" and — in PostgreSQL, absent an explicit `ESCAPE` clause — `\` as
48
+ * the escape character, so a value carrying any of the three used to mean
49
+ * something other than what the operator promises. Issue #181: an unescaped
50
+ * `contains "50%"` selected every product with "50" followed by anything.
51
+ *
52
+ * `\` is the escape character used here rather than an explicit `ESCAPE`
53
+ * clause, because there is nowhere to put one: MikroORM renders `$ilike` as
54
+ * `column ilike ?` with the pattern bound as a parameter, and reaching for a
55
+ * raw fragment to append `ESCAPE` would put SQL text back into a translation
56
+ * whose whole point is that it has no node for one. PostgreSQL's default escape
57
+ * character already *is* `\`, so escaping with it needs no clause — and it has
58
+ * to be escaped first in its own right, or a value ending in a backslash would
59
+ * escape the wildcard this function's callers append.
60
+ *
61
+ * Note the failure was not only over-matching: PostgreSQL degrades an escape
62
+ * sequence it does not recognise to the plain character, so the unescaped
63
+ * pattern for `C\D` also *missed* the row that actually holds the backslash.
64
+ */
65
+ export declare function escapeLikeLiteral(value: string): string;
66
+ export declare class CatalogProductFilterService implements CatalogProductFilterPort {
67
+ private readonly emFactory;
68
+ constructor(emFactory: () => EntityManager);
69
+ listSellable(query: CatalogSellableProductQuery): Promise<CatalogProductRecord[]>;
70
+ countSellable(query: Omit<CatalogSellableProductQuery, 'afterId' | 'limit'>): Promise<number>;
71
+ /**
72
+ * Floor AND filter AND cursor, as an explicit `$and`.
73
+ *
74
+ * Never an object spread: all three constrain `id`, and spreading them keeps
75
+ * only the last one — which for a category criterion would drop the channel
76
+ * scoping altogether.
77
+ */
78
+ private where;
79
+ }
80
+ /** `null` when the node constrains nothing, so the caller can omit the clause. */
81
+ export declare function translateFilter(filter: CatalogProductFilter): Record<string, unknown> | null;
82
+ //# sourceMappingURL=catalog-product-filter.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog-product-filter.service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/catalog-product-filter.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAe,MAAM,uBAAuB,CAAC;AACxE,OAAO,KAAK,EACV,oBAAoB,EAEpB,wBAAwB,EACxB,oBAAoB,EACpB,2BAA2B,EAC5B,MAAM,4BAA4B,CAAC;AAIpC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEvD;AAkBD,qBAAa,2BAA4B,YAAW,wBAAwB;IAC9D,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAAT,SAAS,EAAE,MAAM,aAAa;IAErD,YAAY,CAAC,KAAK,EAAE,2BAA2B,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAejF,aAAa,CACjB,KAAK,EAAE,IAAI,CAAC,2BAA2B,EAAE,SAAS,GAAG,OAAO,CAAC,GAC5D,OAAO,CAAC,MAAM,CAAC;IAQlB;;;;;;OAMG;IACH,OAAO,CAAC,KAAK;CAOd;AA2FD,kFAAkF;AAClF,wBAAgB,eAAe,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAS5F"}