@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.
- package/LICENSE +21 -0
- package/README.md +63 -0
- package/dist/admin/components/PackagingUnitsEditor.d.ts +12 -0
- package/dist/admin/components/PackagingUnitsEditor.d.ts.map +1 -0
- package/dist/admin/components/PackagingUnitsEditor.js +99 -0
- package/dist/admin/components/PackagingUnitsEditor.js.map +1 -0
- package/dist/admin/components/ProductAttributesTab.d.ts +13 -0
- package/dist/admin/components/ProductAttributesTab.d.ts.map +1 -0
- package/dist/admin/components/ProductAttributesTab.js +140 -0
- package/dist/admin/components/ProductAttributesTab.js.map +1 -0
- package/dist/admin/components/ProductInventoryTab.d.ts +14 -0
- package/dist/admin/components/ProductInventoryTab.d.ts.map +1 -0
- package/dist/admin/components/ProductInventoryTab.js +284 -0
- package/dist/admin/components/ProductInventoryTab.js.map +1 -0
- package/dist/admin/components/ProductScopeEditor.d.ts +94 -0
- package/dist/admin/components/ProductScopeEditor.d.ts.map +1 -0
- package/dist/admin/components/ProductScopeEditor.js +395 -0
- package/dist/admin/components/ProductScopeEditor.js.map +1 -0
- package/dist/admin/components/ProductsBulkEditDialog.d.ts +11 -0
- package/dist/admin/components/ProductsBulkEditDialog.d.ts.map +1 -0
- package/dist/admin/components/ProductsBulkEditDialog.js +304 -0
- package/dist/admin/components/ProductsBulkEditDialog.js.map +1 -0
- package/dist/admin/index.d.ts +45 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +184 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/lib/resolve-product-selection.d.ts +21 -0
- package/dist/admin/lib/resolve-product-selection.d.ts.map +1 -0
- package/dist/admin/lib/resolve-product-selection.js +25 -0
- package/dist/admin/lib/resolve-product-selection.js.map +1 -0
- package/dist/admin/pages/AttachmentTypesPage.d.ts +18 -0
- package/dist/admin/pages/AttachmentTypesPage.d.ts.map +1 -0
- package/dist/admin/pages/AttachmentTypesPage.js +92 -0
- package/dist/admin/pages/AttachmentTypesPage.js.map +1 -0
- package/dist/admin/pages/AttributeSetsPage.d.ts +21 -0
- package/dist/admin/pages/AttributeSetsPage.d.ts.map +1 -0
- package/dist/admin/pages/AttributeSetsPage.js +158 -0
- package/dist/admin/pages/AttributeSetsPage.js.map +1 -0
- package/dist/admin/pages/AttributesManager.d.ts +9 -0
- package/dist/admin/pages/AttributesManager.d.ts.map +1 -0
- package/dist/admin/pages/AttributesManager.js +305 -0
- package/dist/admin/pages/AttributesManager.js.map +1 -0
- package/dist/admin/pages/BulkOperationDetailPage.d.ts +9 -0
- package/dist/admin/pages/BulkOperationDetailPage.d.ts.map +1 -0
- package/dist/admin/pages/BulkOperationDetailPage.js +187 -0
- package/dist/admin/pages/BulkOperationDetailPage.js.map +1 -0
- package/dist/admin/pages/BulkOperationsPage.d.ts +9 -0
- package/dist/admin/pages/BulkOperationsPage.d.ts.map +1 -0
- package/dist/admin/pages/BulkOperationsPage.js +98 -0
- package/dist/admin/pages/BulkOperationsPage.js.map +1 -0
- package/dist/admin/pages/CategoriesTree.d.ts +9 -0
- package/dist/admin/pages/CategoriesTree.d.ts.map +1 -0
- package/dist/admin/pages/CategoriesTree.js +176 -0
- package/dist/admin/pages/CategoriesTree.js.map +1 -0
- package/dist/admin/pages/ProductEditor.d.ts +13 -0
- package/dist/admin/pages/ProductEditor.d.ts.map +1 -0
- package/dist/admin/pages/ProductEditor.js +1050 -0
- package/dist/admin/pages/ProductEditor.js.map +1 -0
- package/dist/admin/pages/ProductsList.d.ts +9 -0
- package/dist/admin/pages/ProductsList.d.ts.map +1 -0
- package/dist/admin/pages/ProductsList.js +399 -0
- package/dist/admin/pages/ProductsList.js.map +1 -0
- package/dist/backend/commands/attribute-commands.d.ts +111 -0
- package/dist/backend/commands/attribute-commands.d.ts.map +1 -0
- package/dist/backend/commands/attribute-commands.js +482 -0
- package/dist/backend/commands/attribute-commands.js.map +1 -0
- package/dist/backend/demo/reset.d.ts +23 -0
- package/dist/backend/demo/reset.d.ts.map +1 -0
- package/dist/backend/demo/reset.js +41 -0
- package/dist/backend/demo/reset.js.map +1 -0
- package/dist/backend/demo/rows.d.ts +103 -0
- package/dist/backend/demo/rows.d.ts.map +1 -0
- package/dist/backend/demo/rows.js +162 -0
- package/dist/backend/demo/rows.js.map +1 -0
- package/dist/backend/demo/seed.d.ts +35 -0
- package/dist/backend/demo/seed.d.ts.map +1 -0
- package/dist/backend/demo/seed.js +177 -0
- package/dist/backend/demo/seed.js.map +1 -0
- package/dist/backend/entities/attachment-type.entity.d.ts +16 -0
- package/dist/backend/entities/attachment-type.entity.d.ts.map +1 -0
- package/dist/backend/entities/attachment-type.entity.js +57 -0
- package/dist/backend/entities/attachment-type.entity.js.map +1 -0
- package/dist/backend/entities/attribute-set-attribute.entity.d.ts +26 -0
- package/dist/backend/entities/attribute-set-attribute.entity.d.ts.map +1 -0
- package/dist/backend/entities/attribute-set-attribute.entity.js +55 -0
- package/dist/backend/entities/attribute-set-attribute.entity.js.map +1 -0
- package/dist/backend/entities/attribute-set.entity.d.ts +22 -0
- package/dist/backend/entities/attribute-set.entity.d.ts.map +1 -0
- package/dist/backend/entities/attribute-set.entity.js +67 -0
- package/dist/backend/entities/attribute-set.entity.js.map +1 -0
- package/dist/backend/entities/bulk-operation.entity.d.ts +78 -0
- package/dist/backend/entities/bulk-operation.entity.d.ts.map +1 -0
- package/dist/backend/entities/bulk-operation.entity.js +138 -0
- package/dist/backend/entities/bulk-operation.entity.js.map +1 -0
- package/dist/backend/entities/bundle-slot-option.entity.d.ts +23 -0
- package/dist/backend/entities/bundle-slot-option.entity.d.ts.map +1 -0
- package/dist/backend/entities/bundle-slot-option.entity.js +68 -0
- package/dist/backend/entities/bundle-slot-option.entity.js.map +1 -0
- package/dist/backend/entities/bundle-slot.entity.d.ts +25 -0
- package/dist/backend/entities/bundle-slot.entity.d.ts.map +1 -0
- package/dist/backend/entities/bundle-slot.entity.js +74 -0
- package/dist/backend/entities/bundle-slot.entity.js.map +1 -0
- package/dist/backend/entities/category.entity.d.ts +26 -0
- package/dist/backend/entities/category.entity.d.ts.map +1 -0
- package/dist/backend/entities/category.entity.js +119 -0
- package/dist/backend/entities/category.entity.js.map +1 -0
- package/dist/backend/entities/gallery-item-label.entity.d.ts +22 -0
- package/dist/backend/entities/gallery-item-label.entity.d.ts.map +1 -0
- package/dist/backend/entities/gallery-item-label.entity.js +50 -0
- package/dist/backend/entities/gallery-item-label.entity.js.map +1 -0
- package/dist/backend/entities/gallery-item.entity.d.ts +17 -0
- package/dist/backend/entities/gallery-item.entity.d.ts.map +1 -0
- package/dist/backend/entities/gallery-item.entity.js +58 -0
- package/dist/backend/entities/gallery-item.entity.js.map +1 -0
- package/dist/backend/entities/grouped-item.entity.d.ts +23 -0
- package/dist/backend/entities/grouped-item.entity.d.ts.map +1 -0
- package/dist/backend/entities/grouped-item.entity.js +68 -0
- package/dist/backend/entities/grouped-item.entity.js.map +1 -0
- package/dist/backend/entities/product-attachment.entity.d.ts +23 -0
- package/dist/backend/entities/product-attachment.entity.d.ts.map +1 -0
- package/dist/backend/entities/product-attachment.entity.js +77 -0
- package/dist/backend/entities/product-attachment.entity.js.map +1 -0
- package/dist/backend/entities/product-attribute.entity.d.ts +72 -0
- package/dist/backend/entities/product-attribute.entity.d.ts.map +1 -0
- package/dist/backend/entities/product-attribute.entity.js +161 -0
- package/dist/backend/entities/product-attribute.entity.js.map +1 -0
- package/dist/backend/entities/product-editor-preference.entity.d.ts +28 -0
- package/dist/backend/entities/product-editor-preference.entity.d.ts.map +1 -0
- package/dist/backend/entities/product-editor-preference.entity.js +63 -0
- package/dist/backend/entities/product-editor-preference.entity.js.map +1 -0
- package/dist/backend/entities/product-link.entity.d.ts +25 -0
- package/dist/backend/entities/product-link.entity.d.ts.map +1 -0
- package/dist/backend/entities/product-link.entity.js +70 -0
- package/dist/backend/entities/product-link.entity.js.map +1 -0
- package/dist/backend/entities/product-packaging-unit.entity.d.ts +26 -0
- package/dist/backend/entities/product-packaging-unit.entity.d.ts.map +1 -0
- package/dist/backend/entities/product-packaging-unit.entity.js +76 -0
- package/dist/backend/entities/product-packaging-unit.entity.js.map +1 -0
- package/dist/backend/entities/product-value-override.entity.d.ts +43 -0
- package/dist/backend/entities/product-value-override.entity.d.ts.map +1 -0
- package/dist/backend/entities/product-value-override.entity.js +90 -0
- package/dist/backend/entities/product-value-override.entity.js.map +1 -0
- package/dist/backend/entities/product-variant.entity.d.ts +18 -0
- package/dist/backend/entities/product-variant.entity.d.ts.map +1 -0
- package/dist/backend/entities/product-variant.entity.js +68 -0
- package/dist/backend/entities/product-variant.entity.js.map +1 -0
- package/dist/backend/entities/product.entity.d.ts +108 -0
- package/dist/backend/entities/product.entity.d.ts.map +1 -0
- package/dist/backend/entities/product.entity.js +230 -0
- package/dist/backend/entities/product.entity.js.map +1 -0
- package/dist/backend/index.d.ts +262 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +642 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/plugin.d.ts +216 -0
- package/dist/backend/plugin.d.ts.map +1 -0
- package/dist/backend/plugin.js +223 -0
- package/dist/backend/plugin.js.map +1 -0
- package/dist/backend/prompt-tools.d.ts +47 -0
- package/dist/backend/prompt-tools.d.ts.map +1 -0
- package/dist/backend/prompt-tools.js +284 -0
- package/dist/backend/prompt-tools.js.map +1 -0
- package/dist/backend/routes.admin.d.ts +105 -0
- package/dist/backend/routes.admin.d.ts.map +1 -0
- package/dist/backend/routes.admin.js +1162 -0
- package/dist/backend/routes.admin.js.map +1 -0
- package/dist/backend/routes.api-key.d.ts +33 -0
- package/dist/backend/routes.api-key.d.ts.map +1 -0
- package/dist/backend/routes.api-key.js +43 -0
- package/dist/backend/routes.api-key.js.map +1 -0
- package/dist/backend/routes.external.d.ts +32 -0
- package/dist/backend/routes.external.d.ts.map +1 -0
- package/dist/backend/routes.external.js +115 -0
- package/dist/backend/routes.external.js.map +1 -0
- package/dist/backend/routes.public.d.ts +46 -0
- package/dist/backend/routes.public.d.ts.map +1 -0
- package/dist/backend/routes.public.js +318 -0
- package/dist/backend/routes.public.js.map +1 -0
- package/dist/backend/services/asset-references.d.ts +4 -0
- package/dist/backend/services/asset-references.d.ts.map +1 -0
- package/dist/backend/services/asset-references.js +104 -0
- package/dist/backend/services/asset-references.js.map +1 -0
- package/dist/backend/services/attachment.service.d.ts +45 -0
- package/dist/backend/services/attachment.service.d.ts.map +1 -0
- package/dist/backend/services/attachment.service.js +268 -0
- package/dist/backend/services/attachment.service.js.map +1 -0
- package/dist/backend/services/attribute-option-validator.d.ts +47 -0
- package/dist/backend/services/attribute-option-validator.d.ts.map +1 -0
- package/dist/backend/services/attribute-option-validator.js +69 -0
- package/dist/backend/services/attribute-option-validator.js.map +1 -0
- package/dist/backend/services/attribute-set-validations.d.ts +65 -0
- package/dist/backend/services/attribute-set-validations.d.ts.map +1 -0
- package/dist/backend/services/attribute-set-validations.js +89 -0
- package/dist/backend/services/attribute-set-validations.js.map +1 -0
- package/dist/backend/services/attribute-set.service.d.ts +50 -0
- package/dist/backend/services/attribute-set.service.d.ts.map +1 -0
- package/dist/backend/services/attribute-set.service.js +401 -0
- package/dist/backend/services/attribute-set.service.js.map +1 -0
- package/dist/backend/services/attribute-type-mapping.d.ts +66 -0
- package/dist/backend/services/attribute-type-mapping.d.ts.map +1 -0
- package/dist/backend/services/attribute-type-mapping.js +121 -0
- package/dist/backend/services/attribute-type-mapping.js.map +1 -0
- package/dist/backend/services/attribute-value-key.service.d.ts +48 -0
- package/dist/backend/services/attribute-value-key.service.d.ts.map +1 -0
- package/dist/backend/services/attribute-value-key.service.js +118 -0
- package/dist/backend/services/attribute-value-key.service.js.map +1 -0
- package/dist/backend/services/audit-references.d.ts +13 -0
- package/dist/backend/services/audit-references.d.ts.map +1 -0
- package/dist/backend/services/audit-references.js +51 -0
- package/dist/backend/services/audit-references.js.map +1 -0
- package/dist/backend/services/bulk-operation-queue.d.ts +26 -0
- package/dist/backend/services/bulk-operation-queue.d.ts.map +1 -0
- package/dist/backend/services/bulk-operation-queue.js +34 -0
- package/dist/backend/services/bulk-operation-queue.js.map +1 -0
- package/dist/backend/services/bulk-operation.service.d.ts +195 -0
- package/dist/backend/services/bulk-operation.service.d.ts.map +1 -0
- package/dist/backend/services/bulk-operation.service.js +510 -0
- package/dist/backend/services/bulk-operation.service.js.map +1 -0
- package/dist/backend/services/bundle.service.d.ts +87 -0
- package/dist/backend/services/bundle.service.d.ts.map +1 -0
- package/dist/backend/services/bundle.service.js +286 -0
- package/dist/backend/services/bundle.service.js.map +1 -0
- package/dist/backend/services/catalog-admin.service.d.ts +398 -0
- package/dist/backend/services/catalog-admin.service.d.ts.map +1 -0
- package/dist/backend/services/catalog-admin.service.js +1516 -0
- package/dist/backend/services/catalog-admin.service.js.map +1 -0
- package/dist/backend/services/catalog-attribute-read.service.d.ts +78 -0
- package/dist/backend/services/catalog-attribute-read.service.d.ts.map +1 -0
- package/dist/backend/services/catalog-attribute-read.service.js +216 -0
- package/dist/backend/services/catalog-attribute-read.service.js.map +1 -0
- package/dist/backend/services/catalog-bulk-import.service.d.ts +30 -0
- package/dist/backend/services/catalog-bulk-import.service.d.ts.map +1 -0
- package/dist/backend/services/catalog-bulk-import.service.js +136 -0
- package/dist/backend/services/catalog-bulk-import.service.js.map +1 -0
- package/dist/backend/services/catalog-bulk-update.service.d.ts +91 -0
- package/dist/backend/services/catalog-bulk-update.service.d.ts.map +1 -0
- package/dist/backend/services/catalog-bulk-update.service.js +330 -0
- package/dist/backend/services/catalog-bulk-update.service.js.map +1 -0
- package/dist/backend/services/catalog-category-read.service.d.ts +66 -0
- package/dist/backend/services/catalog-category-read.service.d.ts.map +1 -0
- package/dist/backend/services/catalog-category-read.service.js +248 -0
- package/dist/backend/services/catalog-category-read.service.js.map +1 -0
- package/dist/backend/services/catalog-org-price-decorator.d.ts +90 -0
- package/dist/backend/services/catalog-org-price-decorator.d.ts.map +1 -0
- package/dist/backend/services/catalog-org-price-decorator.js +184 -0
- package/dist/backend/services/catalog-org-price-decorator.js.map +1 -0
- package/dist/backend/services/catalog-product-filter.service.d.ts +82 -0
- package/dist/backend/services/catalog-product-filter.service.d.ts.map +1 -0
- package/dist/backend/services/catalog-product-filter.service.js +275 -0
- package/dist/backend/services/catalog-product-filter.service.js.map +1 -0
- package/dist/backend/services/catalog-product-read.service.d.ts +46 -0
- package/dist/backend/services/catalog-product-read.service.d.ts.map +1 -0
- package/dist/backend/services/catalog-product-read.service.js +197 -0
- package/dist/backend/services/catalog-product-read.service.js.map +1 -0
- package/dist/backend/services/catalog-query.service.d.ts +444 -0
- package/dist/backend/services/catalog-query.service.d.ts.map +1 -0
- package/dist/backend/services/catalog-query.service.js +1633 -0
- package/dist/backend/services/catalog-query.service.js.map +1 -0
- package/dist/backend/services/catalog-quick-search.service.d.ts +78 -0
- package/dist/backend/services/catalog-quick-search.service.d.ts.map +1 -0
- package/dist/backend/services/catalog-quick-search.service.js +159 -0
- package/dist/backend/services/catalog-quick-search.service.js.map +1 -0
- package/dist/backend/services/catalog-write-ports.d.ts +25 -0
- package/dist/backend/services/catalog-write-ports.d.ts.map +1 -0
- package/dist/backend/services/catalog-write-ports.js +63 -0
- package/dist/backend/services/catalog-write-ports.js.map +1 -0
- package/dist/backend/services/category-admin.service.d.ts +102 -0
- package/dist/backend/services/category-admin.service.d.ts.map +1 -0
- package/dist/backend/services/category-admin.service.js +271 -0
- package/dist/backend/services/category-admin.service.js.map +1 -0
- package/dist/backend/services/gallery.service.d.ts +85 -0
- package/dist/backend/services/gallery.service.d.ts.map +1 -0
- package/dist/backend/services/gallery.service.js +326 -0
- package/dist/backend/services/gallery.service.js.map +1 -0
- package/dist/backend/services/grouped.service.d.ts +39 -0
- package/dist/backend/services/grouped.service.d.ts.map +1 -0
- package/dist/backend/services/grouped.service.js +136 -0
- package/dist/backend/services/grouped.service.js.map +1 -0
- package/dist/backend/services/label-resolver.d.ts +16 -0
- package/dist/backend/services/label-resolver.d.ts.map +1 -0
- package/dist/backend/services/label-resolver.js +43 -0
- package/dist/backend/services/label-resolver.js.map +1 -0
- package/dist/backend/services/packaging-unit.service.d.ts +28 -0
- package/dist/backend/services/packaging-unit.service.d.ts.map +1 -0
- package/dist/backend/services/packaging-unit.service.js +197 -0
- package/dist/backend/services/packaging-unit.service.js.map +1 -0
- package/dist/backend/services/primary-asset-url.d.ts +4 -0
- package/dist/backend/services/primary-asset-url.d.ts.map +1 -0
- package/dist/backend/services/primary-asset-url.js +55 -0
- package/dist/backend/services/primary-asset-url.js.map +1 -0
- package/dist/backend/services/product-editor-preferences.service.d.ts +20 -0
- package/dist/backend/services/product-editor-preferences.service.d.ts.map +1 -0
- package/dist/backend/services/product-editor-preferences.service.js +44 -0
- package/dist/backend/services/product-editor-preferences.service.js.map +1 -0
- package/dist/backend/services/product-link.service.d.ts +169 -0
- package/dist/backend/services/product-link.service.d.ts.map +1 -0
- package/dist/backend/services/product-link.service.js +297 -0
- package/dist/backend/services/product-link.service.js.map +1 -0
- package/dist/backend/services/product-overrides.service.d.ts +77 -0
- package/dist/backend/services/product-overrides.service.d.ts.map +1 -0
- package/dist/backend/services/product-overrides.service.js +204 -0
- package/dist/backend/services/product-overrides.service.js.map +1 -0
- package/dist/backend/services/product-scope-context.service.d.ts +36 -0
- package/dist/backend/services/product-scope-context.service.d.ts.map +1 -0
- package/dist/backend/services/product-scope-context.service.js +106 -0
- package/dist/backend/services/product-scope-context.service.js.map +1 -0
- package/dist/backend/services/product-type-validations.d.ts +42 -0
- package/dist/backend/services/product-type-validations.d.ts.map +1 -0
- package/dist/backend/services/product-type-validations.js +55 -0
- package/dist/backend/services/product-type-validations.js.map +1 -0
- package/dist/backend/services/product-value-resolver.service.d.ts +59 -0
- package/dist/backend/services/product-value-resolver.service.d.ts.map +1 -0
- package/dist/backend/services/product-value-resolver.service.js +111 -0
- package/dist/backend/services/product-value-resolver.service.js.map +1 -0
- package/dist/backend/services/system-attribute-scopes.d.ts +18 -0
- package/dist/backend/services/system-attribute-scopes.d.ts.map +1 -0
- package/dist/backend/services/system-attribute-scopes.js +18 -0
- package/dist/backend/services/system-attribute-scopes.js.map +1 -0
- package/dist/backend/services/viewer-organization.d.ts +25 -0
- package/dist/backend/services/viewer-organization.d.ts.map +1 -0
- package/dist/backend/services/viewer-organization.js +31 -0
- package/dist/backend/services/viewer-organization.js.map +1 -0
- package/dist/manifest.d.ts +204 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +686 -0
- package/dist/manifest.js.map +1 -0
- package/dist/migrations/20260429T064146_catalog_attribute_sets_init.d.ts +33 -0
- package/dist/migrations/20260429T064146_catalog_attribute_sets_init.d.ts.map +1 -0
- package/dist/migrations/20260429T064146_catalog_attribute_sets_init.js +118 -0
- package/dist/migrations/20260429T064146_catalog_attribute_sets_init.js.map +1 -0
- package/dist/migrations/20260429T070004_catalog_product_attribute_extensions.d.ts +24 -0
- package/dist/migrations/20260429T070004_catalog_product_attribute_extensions.d.ts.map +1 -0
- package/dist/migrations/20260429T070004_catalog_product_attribute_extensions.js +31 -0
- package/dist/migrations/20260429T070004_catalog_product_attribute_extensions.js.map +1 -0
- package/dist/migrations/20260429T102322_catalog_product_type_and_virtual_fields.d.ts +32 -0
- package/dist/migrations/20260429T102322_catalog_product_type_and_virtual_fields.d.ts.map +1 -0
- package/dist/migrations/20260429T102322_catalog_product_type_and_virtual_fields.js +58 -0
- package/dist/migrations/20260429T102322_catalog_product_type_and_virtual_fields.js.map +1 -0
- package/dist/migrations/20260429T111839_catalog_gallery_items_and_labels.d.ts +22 -0
- package/dist/migrations/20260429T111839_catalog_gallery_items_and_labels.d.ts.map +1 -0
- package/dist/migrations/20260429T111839_catalog_gallery_items_and_labels.js +66 -0
- package/dist/migrations/20260429T111839_catalog_gallery_items_and_labels.js.map +1 -0
- package/dist/migrations/20260429T112543_catalog_product_attachments.d.ts +18 -0
- package/dist/migrations/20260429T112543_catalog_product_attachments.d.ts.map +1 -0
- package/dist/migrations/20260429T112543_catalog_product_attachments.js +73 -0
- package/dist/migrations/20260429T112543_catalog_product_attachments.js.map +1 -0
- package/dist/migrations/20260429T123726_catalog_product_links.d.ts +21 -0
- package/dist/migrations/20260429T123726_catalog_product_links.d.ts.map +1 -0
- package/dist/migrations/20260429T123726_catalog_product_links.js +48 -0
- package/dist/migrations/20260429T123726_catalog_product_links.js.map +1 -0
- package/dist/migrations/20260429T130803_catalog_grouped_and_bundle.d.ts +23 -0
- package/dist/migrations/20260429T130803_catalog_grouped_and_bundle.d.ts.map +1 -0
- package/dist/migrations/20260429T130803_catalog_grouped_and_bundle.js +94 -0
- package/dist/migrations/20260429T130803_catalog_grouped_and_bundle.js.map +1 -0
- package/dist/migrations/20260501T185835_catalog_product_attribute_is_comparable.d.ts +24 -0
- package/dist/migrations/20260501T185835_catalog_product_attribute_is_comparable.d.ts.map +1 -0
- package/dist/migrations/20260501T185835_catalog_product_attribute_is_comparable.js +31 -0
- package/dist/migrations/20260501T185835_catalog_product_attribute_is_comparable.js.map +1 -0
- package/dist/migrations/20260505T060113_catalog_attribute_options_and_flags.d.ts +31 -0
- package/dist/migrations/20260505T060113_catalog_attribute_options_and_flags.d.ts.map +1 -0
- package/dist/migrations/20260505T060113_catalog_attribute_options_and_flags.js +133 -0
- package/dist/migrations/20260505T060113_catalog_attribute_options_and_flags.js.map +1 -0
- package/dist/migrations/20260515T082629_catalog_attribute_mass_editable.d.ts +16 -0
- package/dist/migrations/20260515T082629_catalog_attribute_mass_editable.d.ts.map +1 -0
- package/dist/migrations/20260515T082629_catalog_attribute_mass_editable.js +23 -0
- package/dist/migrations/20260515T082629_catalog_attribute_mass_editable.js.map +1 -0
- package/dist/migrations/20260526T124736_catalog_product_status_inactive.d.ts +10 -0
- package/dist/migrations/20260526T124736_catalog_product_status_inactive.d.ts.map +1 -0
- package/dist/migrations/20260526T124736_catalog_product_status_inactive.js +22 -0
- package/dist/migrations/20260526T124736_catalog_product_status_inactive.js.map +1 -0
- package/dist/migrations/20260611T140346_catalog_product_value_overrides_init.d.ts +38 -0
- package/dist/migrations/20260611T140346_catalog_product_value_overrides_init.d.ts.map +1 -0
- package/dist/migrations/20260611T140346_catalog_product_value_overrides_init.js +98 -0
- package/dist/migrations/20260611T140346_catalog_product_value_overrides_init.js.map +1 -0
- package/dist/migrations/20260611T140400_catalog_attribute_quick_searchable.d.ts +13 -0
- package/dist/migrations/20260611T140400_catalog_attribute_quick_searchable.d.ts.map +1 -0
- package/dist/migrations/20260611T140400_catalog_attribute_quick_searchable.js +17 -0
- package/dist/migrations/20260611T140400_catalog_attribute_quick_searchable.js.map +1 -0
- package/dist/migrations/20260611T140407_catalog_bulk_operations.d.ts +16 -0
- package/dist/migrations/20260611T140407_catalog_bulk_operations.d.ts.map +1 -0
- package/dist/migrations/20260611T140407_catalog_bulk_operations.js +42 -0
- package/dist/migrations/20260611T140407_catalog_bulk_operations.js.map +1 -0
- package/dist/migrations/20260611T140408_catalog_bulk_operation_logs.d.ts +14 -0
- package/dist/migrations/20260611T140408_catalog_bulk_operation_logs.d.ts.map +1 -0
- package/dist/migrations/20260611T140408_catalog_bulk_operation_logs.js +18 -0
- package/dist/migrations/20260611T140408_catalog_bulk_operation_logs.js.map +1 -0
- package/dist/migrations/20260611T140412_catalog_product_packaging_units.d.ts +11 -0
- package/dist/migrations/20260611T140412_catalog_product_packaging_units.d.ts.map +1 -0
- package/dist/migrations/20260611T140412_catalog_product_packaging_units.js +29 -0
- package/dist/migrations/20260611T140412_catalog_product_packaging_units.js.map +1 -0
- package/dist/migrations/20260718T060659_catalog_bulk_operation_revert_state.d.ts +23 -0
- package/dist/migrations/20260718T060659_catalog_bulk_operation_revert_state.d.ts.map +1 -0
- package/dist/migrations/20260718T060659_catalog_bulk_operation_revert_state.js +35 -0
- package/dist/migrations/20260718T060659_catalog_bulk_operation_revert_state.js.map +1 -0
- package/dist/migrations/20260718T200343_catalog_category_custom_field_values.d.ts +12 -0
- package/dist/migrations/20260718T200343_catalog_category_custom_field_values.d.ts.map +1 -0
- package/dist/migrations/20260718T200343_catalog_category_custom_field_values.js +16 -0
- package/dist/migrations/20260718T200343_catalog_category_custom_field_values.js.map +1 -0
- package/dist/migrations/20260723T230401_catalog_attributes_on_custom_fields.d.ts +32 -0
- package/dist/migrations/20260723T230401_catalog_attributes_on_custom_fields.d.ts.map +1 -0
- package/dist/migrations/20260723T230401_catalog_attributes_on_custom_fields.js +286 -0
- package/dist/migrations/20260723T230401_catalog_attributes_on_custom_fields.js.map +1 -0
- package/dist/migrations/20260804T152604_catalog_widen_product_sku.d.ts +36 -0
- package/dist/migrations/20260804T152604_catalog_widen_product_sku.d.ts.map +1 -0
- package/dist/migrations/20260804T152604_catalog_widen_product_sku.js +63 -0
- package/dist/migrations/20260804T152604_catalog_widen_product_sku.js.map +1 -0
- package/dist/migrations/20260804T160244_catalog_category_activation.d.ts +18 -0
- package/dist/migrations/20260804T160244_catalog_category_activation.d.ts.map +1 -0
- package/dist/migrations/20260804T160244_catalog_category_activation.js +22 -0
- package/dist/migrations/20260804T160244_catalog_category_activation.js.map +1 -0
- package/dist/migrations/20260912T094557_catalog_sales_channel_products.d.ts +27 -0
- package/dist/migrations/20260912T094557_catalog_sales_channel_products.d.ts.map +1 -0
- package/dist/migrations/20260912T094557_catalog_sales_channel_products.js +44 -0
- package/dist/migrations/20260912T094557_catalog_sales_channel_products.js.map +1 -0
- package/dist/migrations/20260912T094623_catalog_sales_channel_categories.d.ts +27 -0
- package/dist/migrations/20260912T094623_catalog_sales_channel_categories.d.ts.map +1 -0
- package/dist/migrations/20260912T094623_catalog_sales_channel_categories.js +44 -0
- package/dist/migrations/20260912T094623_catalog_sales_channel_categories.js.map +1 -0
- package/dist/migrations/20260925T125527_catalog_inventory_columns.d.ts +36 -0
- package/dist/migrations/20260925T125527_catalog_inventory_columns.d.ts.map +1 -0
- package/dist/migrations/20260925T125527_catalog_inventory_columns.js +87 -0
- package/dist/migrations/20260925T125527_catalog_inventory_columns.js.map +1 -0
- package/dist/migrations/index.d.ts +55 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +80 -0
- package/dist/migrations/index.js.map +1 -0
- package/dist/ports/index.d.ts +96 -0
- package/dist/ports/index.d.ts.map +1 -0
- package/dist/ports/index.js +2 -0
- package/dist/ports/index.js.map +1 -0
- package/docs/catalog/attachments.md +69 -0
- package/docs/catalog/attribute-sets.md +63 -0
- package/docs/catalog/attributes.md +244 -0
- package/docs/catalog/composite-products.md +115 -0
- package/docs/catalog/gallery-and-labels.md +73 -0
- package/docs/catalog/packaging-units.md +67 -0
- package/docs/catalog/per-channel-per-language-overrides.md +234 -0
- package/docs/catalog/product-links.md +79 -0
- package/docs/catalog.md +207 -0
- package/i18n/en.json +649 -0
- package/i18n/pl.json +649 -0
- package/package.json +109 -0
- package/tailwind.css +14 -0
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ERROR_CODES, isPriceSort, productLinkKindSchema, productListSortSchema, validateBundleConfigurationRequestSchema, } from '@endora-commerce/contracts';
|
|
3
|
+
import { HttpError } from '@endora-commerce/platform/http';
|
|
4
|
+
import { effectiveState } from '@endora-commerce/platform/kernel';
|
|
5
|
+
import { getResolvedChannel } from '@endora-commerce/platform/kernel';
|
|
6
|
+
import { markPersonalisedPricing, productAudienceOf } from '@endora-commerce/platform/http';
|
|
7
|
+
/**
|
|
8
|
+
* Public catalog routes (US1 read surface).
|
|
9
|
+
* Storefront SSR + crawlers + anonymous API consumers. No auth.
|
|
10
|
+
*
|
|
11
|
+
* Read-backend selection (T067/T068):
|
|
12
|
+
* - When `CATALOG_SEARCH_BACKEND=meilisearch` AND a read backend was wired
|
|
13
|
+
* AND `search` is effectively present, list-products is served from
|
|
14
|
+
* Meilisearch.
|
|
15
|
+
* - When the index is unreachable, `searchQueryPort` answers
|
|
16
|
+
* `{ status: 'index-unavailable' }` and the route degrades to the Postgres
|
|
17
|
+
* path (R-08 reserved-fallback, so search is never fully broken).
|
|
18
|
+
* - The `changedSince` query param stays on Postgres because it has no
|
|
19
|
+
* equivalent in the Meilisearch index today.
|
|
20
|
+
* - Default backend remains Postgres so existing tests + deployments
|
|
21
|
+
* keep their behaviour without an opt-in.
|
|
22
|
+
*/
|
|
23
|
+
const acceptLanguageHeaderSchema = z.string().optional();
|
|
24
|
+
export async function registerCatalogPublicRoutes(app, deps) {
|
|
25
|
+
const { queryService, searchQueryService } = deps;
|
|
26
|
+
const resolvePlaceholder = deps.resolveProductImagePlaceholderUrl;
|
|
27
|
+
// Fills `primaryAssetUrl` on imageless summaries with the configured
|
|
28
|
+
// placeholder (resolved once per request, only when at least one item needs
|
|
29
|
+
// it). Leaves products that already have an image untouched.
|
|
30
|
+
async function withListPlaceholder(result, salesChannelCode) {
|
|
31
|
+
if (!resolvePlaceholder)
|
|
32
|
+
return result;
|
|
33
|
+
if (!result.data.some((p) => !p.primaryAssetUrl))
|
|
34
|
+
return result;
|
|
35
|
+
const url = await resolvePlaceholder(salesChannelCode);
|
|
36
|
+
if (!url)
|
|
37
|
+
return result;
|
|
38
|
+
return {
|
|
39
|
+
...result,
|
|
40
|
+
data: result.data.map((p) => p.primaryAssetUrl ? p : { ...p, primaryAssetUrl: url }),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Feature 086 / FR-016 — may this page order or filter by price at all?
|
|
45
|
+
*
|
|
46
|
+
* Two conditions, and both are refusals rather than degradations: an ordering
|
|
47
|
+
* by a number the page may not show discloses that number's rank, which is
|
|
48
|
+
* most of what a competitor wants, and it is cheap to get right and expensive
|
|
49
|
+
* to retrofit.
|
|
50
|
+
*
|
|
51
|
+
* - a **non-public sales channel** withholds prices wholesale — `CatalogQueryService`
|
|
52
|
+
* resolves none at all on one — so there is nothing to order by;
|
|
53
|
+
* - a **page-level display mode of `none`** is the supported "hide prices
|
|
54
|
+
* until login" configuration. Page-level, deliberately: a single product
|
|
55
|
+
* overridden to `none` keeps its position (spec clarification 2), because
|
|
56
|
+
* a per-product override reads as "ask us for a quote" rather than "this
|
|
57
|
+
* price is secret", and withdrawing the control for it would make the sort
|
|
58
|
+
* appear and disappear as a buyer walks the catalogue.
|
|
59
|
+
*
|
|
60
|
+
* The mode is resolved for **this viewer**, so the same shop refuses an
|
|
61
|
+
* anonymous visitor and serves a signed-in buyer where the two `pricing.*`
|
|
62
|
+
* settings keys differ, which is exactly what the configuration means.
|
|
63
|
+
*/
|
|
64
|
+
async function priceControlsAvailable(ctx) {
|
|
65
|
+
if (!ctx.resolvedChannel.isPublic) {
|
|
66
|
+
return {
|
|
67
|
+
available: false,
|
|
68
|
+
reason: 'This sales channel does not publish prices, so the catalogue cannot be ordered or filtered by price.',
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
if (!deps.listingPriceOrder)
|
|
72
|
+
return { available: true };
|
|
73
|
+
const organization = ctx.audience.organizationId === null ? null : { id: ctx.audience.organizationId };
|
|
74
|
+
const mode = await deps.listingPriceOrder.pageDisplayMode({
|
|
75
|
+
context: {
|
|
76
|
+
salesChannel: {
|
|
77
|
+
id: ctx.resolvedChannel.id,
|
|
78
|
+
defaultCurrency: ctx.resolvedChannel.defaultCurrency,
|
|
79
|
+
},
|
|
80
|
+
organization,
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
if (mode === 'none') {
|
|
84
|
+
return {
|
|
85
|
+
available: false,
|
|
86
|
+
reason: 'Prices are not displayed on this page, so the catalogue cannot be ordered or filtered by price.',
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
return { available: true };
|
|
90
|
+
}
|
|
91
|
+
// GET /api/v1/catalog/products
|
|
92
|
+
app.get('/api/v1/catalog/products', async (request, reply) => {
|
|
93
|
+
const { q, limit, cursor, sort, categorySlug, attributeFilters, changedSince, minPrice, maxPrice } = parseListQuery(request);
|
|
94
|
+
const ctx = readContext(request);
|
|
95
|
+
// The page's prices are resolved for this caller, so a page resolved for a
|
|
96
|
+
// buying organisation must not enter a shared cache.
|
|
97
|
+
markPersonalisedPricing(reply, ctx.audience);
|
|
98
|
+
// FR-008 — a minimum above a maximum is a 400 naming both parameters, not
|
|
99
|
+
// an empty page. An empty page for a contradictory range is
|
|
100
|
+
// indistinguishable from an empty page for a genuine one.
|
|
101
|
+
if (minPrice !== undefined && maxPrice !== undefined && minPrice > maxPrice) {
|
|
102
|
+
throw new HttpError(400, ERROR_CODES.PRICE_RANGE_INVALID, 'The minimum price must not exceed the maximum price.', [
|
|
103
|
+
{ path: 'minPrice', issue: 'must not exceed maxPrice' },
|
|
104
|
+
{ path: 'maxPrice', issue: 'must not be below minPrice' },
|
|
105
|
+
]);
|
|
106
|
+
}
|
|
107
|
+
// Resolved once and used twice: to refuse the request, and to tell the
|
|
108
|
+
// storefront whether to render the controls at all (FR-023). One answer, so
|
|
109
|
+
// a page cannot advertise a control the next request refuses.
|
|
110
|
+
const priceControls = await priceControlsAvailable(ctx);
|
|
111
|
+
const wantsPrice = isPriceSort(sort) || minPrice !== undefined || maxPrice !== undefined;
|
|
112
|
+
if (wantsPrice && !priceControls.available) {
|
|
113
|
+
request.log.info({ channel: ctx.resolvedChannel.code }, 'catalog: price ordering refused — this page does not display prices');
|
|
114
|
+
throw new HttpError(400, ERROR_CODES.PRICE_ORDERING_UNAVAILABLE, priceControls.reason);
|
|
115
|
+
}
|
|
116
|
+
const capabilities = { priceOrdering: priceControls.available };
|
|
117
|
+
// Issue #144 — the presence answer is *decided* here, before the query, and
|
|
118
|
+
// never caught after it. Constitution XVII: a module that is off behaves as
|
|
119
|
+
// if never installed, and with `search` never installed this listing is the
|
|
120
|
+
// Postgres query below.
|
|
121
|
+
//
|
|
122
|
+
// A degrade rather than a refusal, deliberately: `catalog` is
|
|
123
|
+
// non-deactivatable, Postgres is this route's default backend, and 503-ing a
|
|
124
|
+
// public catalogue because an optional search module is off would take the
|
|
125
|
+
// storefront down for a capability it never required. The manifest declares
|
|
126
|
+
// it, in `nonBindingDependencies`, so the operator's confirmation dialog
|
|
127
|
+
// says so before they flip `search` off.
|
|
128
|
+
//
|
|
129
|
+
// Issue #153 — `searchQueryService` is `search`'s **port** now, not a
|
|
130
|
+
// `SearchQueryService` this module constructed out of `search`'s class. The
|
|
131
|
+
// two facts that follow are why the code below looks different:
|
|
132
|
+
//
|
|
133
|
+
// - the presence probe is load-bearing rather than belt-and-braces. A
|
|
134
|
+
// port resolution is gated, so calling it with `search` off throws
|
|
135
|
+
// `ModuleDisabledError` — which is exactly what must not reach a public
|
|
136
|
+
// catalogue listing;
|
|
137
|
+
// - the unreachable-index fallback is read off the return type. It used to
|
|
138
|
+
// be `catch (err) { if (err instanceof SearchBackendUnavailable) … else
|
|
139
|
+
// throw err }`, which is the conditional re-throw `check:port-catches`
|
|
140
|
+
// refuses around a port call, and refuses because a status test lets a
|
|
141
|
+
// switched-off module through by accident. `SearchListOutcome` states
|
|
142
|
+
// the two answers, and the conversion happens inside `search`.
|
|
143
|
+
//
|
|
144
|
+
// Feature 086 adds one conjunct: **and no price ordering or price range was
|
|
145
|
+
// requested.** Not because Meilisearch cannot sort — issue #287 repaired
|
|
146
|
+
// that, and `name` and `-createdAt` are answered by the index now — but
|
|
147
|
+
// because the index has never carried a price, deliberately: one document
|
|
148
|
+
// per product per channel, and per-buyer pricing would multiply the corpus
|
|
149
|
+
// by the customer base. Nor could it express the resolution even if the
|
|
150
|
+
// fields were there: its `sort` is a tie-break *sequence*, not a coalesce,
|
|
151
|
+
// so `[priceOnListA, priceOnListB]` orders every product A prices above
|
|
152
|
+
// every product it does not and only then consults B — a different relation
|
|
153
|
+
// from "A's price if A prices it, else B's".
|
|
154
|
+
//
|
|
155
|
+
// `IndexedDocument.price` is not the exception it looks like. It carries
|
|
156
|
+
// the legacy `defaultPrice` attribute, is read by nothing on the query
|
|
157
|
+
// path, and is not any price list's figure.
|
|
158
|
+
const useMeili = process.env['CATALOG_SEARCH_BACKEND'] === 'meilisearch' &&
|
|
159
|
+
searchQueryService !== undefined &&
|
|
160
|
+
effectiveState.isPresent('search') &&
|
|
161
|
+
changedSince === undefined &&
|
|
162
|
+
!wantsPrice;
|
|
163
|
+
if (useMeili) {
|
|
164
|
+
const outcome = await searchQueryService.listProducts({
|
|
165
|
+
...(q !== undefined ? { q } : {}),
|
|
166
|
+
limit,
|
|
167
|
+
...(cursor !== undefined ? { cursor } : {}),
|
|
168
|
+
...(sort !== undefined ? { sort } : {}),
|
|
169
|
+
...(categorySlug !== undefined ? { categorySlug } : {}),
|
|
170
|
+
...(attributeFilters !== undefined ? { attributeFilters } : {}),
|
|
171
|
+
}, ctx);
|
|
172
|
+
if (outcome.status === 'ok') {
|
|
173
|
+
reply.header('x-search-backend', 'meilisearch');
|
|
174
|
+
return {
|
|
175
|
+
...(await withListPlaceholder(outcome.result, ctx.resolvedChannel.code)),
|
|
176
|
+
capabilities,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
request.log.warn({ err: outcome.reason }, 'meilisearch unavailable; falling back to postgres');
|
|
180
|
+
}
|
|
181
|
+
const result = await queryService.listProducts({ q, limit, cursor, sort, categorySlug, attributeFilters, changedSince, minPrice, maxPrice }, ctx);
|
|
182
|
+
reply.header('x-search-backend', 'postgres');
|
|
183
|
+
return { ...(await withListPlaceholder(result, ctx.resolvedChannel.code)), capabilities };
|
|
184
|
+
});
|
|
185
|
+
// GET /api/v1/catalog/products/:idOrSlug
|
|
186
|
+
app.get('/api/v1/catalog/products/:idOrSlug', async (request, reply) => {
|
|
187
|
+
const ctx = readContext(request);
|
|
188
|
+
markPersonalisedPricing(reply, ctx.audience);
|
|
189
|
+
const product = await queryService.getProductByIdOrSlug(request.params.idOrSlug, ctx);
|
|
190
|
+
// Imageless product → fall back to the configured placeholder so the PDP
|
|
191
|
+
// hero/card renders something instead of an empty box.
|
|
192
|
+
if (resolvePlaceholder &&
|
|
193
|
+
!product.primaryAssetUrl &&
|
|
194
|
+
product.assets.length === 0) {
|
|
195
|
+
const url = await resolvePlaceholder(ctx.resolvedChannel.code);
|
|
196
|
+
if (url)
|
|
197
|
+
product.primaryAssetUrl = url;
|
|
198
|
+
}
|
|
199
|
+
return { data: product };
|
|
200
|
+
});
|
|
201
|
+
// Feature 002 US4 — public Product Links read.
|
|
202
|
+
if (deps.productLinkService) {
|
|
203
|
+
const links = deps.productLinkService;
|
|
204
|
+
app.get('/api/v1/catalog/products/:idOrSlug/links', async (request, reply) => {
|
|
205
|
+
const ctx = readContext(request);
|
|
206
|
+
markPersonalisedPricing(reply, ctx.audience);
|
|
207
|
+
const product = await queryService.getProductByIdOrSlug(request.params.idOrSlug, ctx);
|
|
208
|
+
const kind = request.query.kind
|
|
209
|
+
? productLinkKindSchema.parse(request.query.kind)
|
|
210
|
+
: undefined;
|
|
211
|
+
const rows = await links.listForStorefront(product.id, ctx, kind);
|
|
212
|
+
return { data: rows };
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
// Feature 002 US5 — public bundle-configuration validation. Pure
|
|
216
|
+
// compute, no state mutation; only POST on the public surface. Errors
|
|
217
|
+
// are returned inside the data envelope so the storefront can highlight
|
|
218
|
+
// each offending slot — except PRODUCT_TYPE_MISMATCH (400) and
|
|
219
|
+
// PRODUCT_NOT_FOUND (404), which are HTTP-level rejections.
|
|
220
|
+
if (deps.bundleService) {
|
|
221
|
+
const bundle = deps.bundleService;
|
|
222
|
+
app.post('/api/v1/catalog/products/:idOrSlug/bundle-configuration/validate', {
|
|
223
|
+
schema: { body: validateBundleConfigurationRequestSchema },
|
|
224
|
+
}, async (request) => {
|
|
225
|
+
const ctx = readContext(request);
|
|
226
|
+
const product = await queryService.getProductByIdOrSlug(request.params.idOrSlug, ctx);
|
|
227
|
+
const body = validateBundleConfigurationRequestSchema.parse(request.body);
|
|
228
|
+
const result = await bundle.validateConfiguration(product.id, body.selections);
|
|
229
|
+
return { data: result };
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
// GET /api/v1/catalog/categories
|
|
233
|
+
app.get('/api/v1/catalog/categories', async (request) => {
|
|
234
|
+
const ctx = readContext(request);
|
|
235
|
+
const tree = await queryService.getCategoryTree(ctx);
|
|
236
|
+
return { data: tree };
|
|
237
|
+
});
|
|
238
|
+
// GET /api/v1/catalog/filters
|
|
239
|
+
app.get('/api/v1/catalog/filters', async (request) => {
|
|
240
|
+
const ctx = readContext(request);
|
|
241
|
+
const filters = await queryService.getFilterDefinitions(ctx);
|
|
242
|
+
return { data: filters };
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
function readContext(request) {
|
|
246
|
+
// Feature 053 / FR-002: the sales channel is resolved once by the canonical
|
|
247
|
+
// middleware. Read it here instead of re-parsing the `x-sales-channel` header.
|
|
248
|
+
const ch = getResolvedChannel(request);
|
|
249
|
+
const acceptLanguage = acceptLanguageHeaderSchema.parse(request.headers['accept-language']);
|
|
250
|
+
const preferredLanguage = acceptLanguage ? acceptLanguage.split(',')[0]?.trim() : undefined;
|
|
251
|
+
return {
|
|
252
|
+
resolvedChannel: {
|
|
253
|
+
id: ch.id,
|
|
254
|
+
code: ch.code,
|
|
255
|
+
isPublic: ch.isPublic,
|
|
256
|
+
defaultCurrency: ch.defaultCurrency,
|
|
257
|
+
defaultLanguage: ch.defaultLanguage,
|
|
258
|
+
},
|
|
259
|
+
audience: productAudienceOf(request),
|
|
260
|
+
preferredLanguage,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Parses list-query parameters out of the request URL, including the nested
|
|
265
|
+
* `filter[attr.<key>]=value` syntax. We hand-parse this because Fastify's
|
|
266
|
+
* default query parser flattens bracketed keys into strings.
|
|
267
|
+
*/
|
|
268
|
+
function parseListQuery(request) {
|
|
269
|
+
const raw = (request.query ?? {});
|
|
270
|
+
const q = typeof raw['q'] === 'string' ? raw['q'] : undefined;
|
|
271
|
+
const limitRaw = typeof raw['limit'] === 'string' ? Number(raw['limit']) : undefined;
|
|
272
|
+
const limit = Number.isFinite(limitRaw) && limitRaw > 0 ? Math.min(200, limitRaw) : 50;
|
|
273
|
+
const cursor = typeof raw['cursor'] === 'string' ? raw['cursor'] : undefined;
|
|
274
|
+
const changedSince = typeof raw['changedSince'] === 'string' ? raw['changedSince'] : undefined;
|
|
275
|
+
// The enum is the contract's, read once, so a member added there reaches this
|
|
276
|
+
// route without a second list to keep in step (feature 086 added two).
|
|
277
|
+
const sortRaw = typeof raw['sort'] === 'string' ? raw['sort'] : undefined;
|
|
278
|
+
const sortParsed = productListSortSchema.safeParse(sortRaw);
|
|
279
|
+
const sort = sortParsed.success ? sortParsed.data : undefined;
|
|
280
|
+
// Feature 086 — the range bounds. A non-numeric or negative bound is dropped
|
|
281
|
+
// rather than refused, which is what every other malformed parameter on this
|
|
282
|
+
// hand-parsed surface does; `minPrice > maxPrice` is the one the buyer is
|
|
283
|
+
// told about, because it is the one they typed on purpose.
|
|
284
|
+
const parseBound = (value) => {
|
|
285
|
+
if (typeof value !== 'string' || value.trim() === '')
|
|
286
|
+
return undefined;
|
|
287
|
+
const n = Number(value);
|
|
288
|
+
return Number.isFinite(n) && n >= 0 ? n : undefined;
|
|
289
|
+
};
|
|
290
|
+
const minPrice = parseBound(raw['minPrice']);
|
|
291
|
+
const maxPrice = parseBound(raw['maxPrice']);
|
|
292
|
+
const attributeFilters = {};
|
|
293
|
+
let categorySlug;
|
|
294
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
295
|
+
const attrMatch = /^filter\[attr\.([^\]]+)\]$/.exec(key);
|
|
296
|
+
if (attrMatch && attrMatch[1]) {
|
|
297
|
+
const attrKey = attrMatch[1];
|
|
298
|
+
const values = Array.isArray(value) ? value.map(String) : [String(value)];
|
|
299
|
+
attributeFilters[attrKey] = values;
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
if (key === 'filter[category]' && typeof value === 'string') {
|
|
303
|
+
categorySlug = value;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return {
|
|
307
|
+
q,
|
|
308
|
+
limit,
|
|
309
|
+
cursor,
|
|
310
|
+
sort,
|
|
311
|
+
categorySlug,
|
|
312
|
+
...(Object.keys(attributeFilters).length > 0 ? { attributeFilters } : {}),
|
|
313
|
+
changedSince,
|
|
314
|
+
minPrice,
|
|
315
|
+
maxPrice,
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
//# sourceMappingURL=routes.public.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.public.js","sourceRoot":"","sources":["../../src/backend/routes.public.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
2
|
+
import type { AssetReferenceRegistryPort } from '@endora-commerce/contracts';
|
|
3
|
+
export declare function registerCatalogAssetReferences(registry: AssetReferenceRegistryPort, emFactory: () => EntityManager): void;
|
|
4
|
+
//# sourceMappingURL=asset-references.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"asset-references.d.ts","sourceRoot":"","sources":["../../../src/backend/services/asset-references.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAGV,0BAA0B,EAC3B,MAAM,4BAA4B,CAAC;AAEpC,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,0BAA0B,EACpC,SAAS,EAAE,MAAM,aAAa,GAC7B,IAAI,CAQN"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// Catalog → Assets Library reference descriptors — feature 013 / US2 / T064.
|
|
2
|
+
//
|
|
3
|
+
// Registers four FK descriptors that the AssetReferenceRegistry consults
|
|
4
|
+
// when an admin tries to delete an asset (FR-030). Each descriptor runs a
|
|
5
|
+
// single batched query (`asset_id = ANY($1)`) and joins back to the parent
|
|
6
|
+
// row's display info for the human-readable label rendered in the admin
|
|
7
|
+
// "in use by" dialog.
|
|
8
|
+
export function registerCatalogAssetReferences(registry, emFactory) {
|
|
9
|
+
const descriptors = [
|
|
10
|
+
productGalleryDescriptor(emFactory),
|
|
11
|
+
productAttachmentDescriptor(emFactory),
|
|
12
|
+
productVirtualDownloadDescriptor(emFactory),
|
|
13
|
+
categoryMainImageDescriptor(emFactory),
|
|
14
|
+
];
|
|
15
|
+
for (const d of descriptors)
|
|
16
|
+
registry.register(d);
|
|
17
|
+
}
|
|
18
|
+
function productGalleryDescriptor(emFactory) {
|
|
19
|
+
return {
|
|
20
|
+
ownerModuleId: 'catalog',
|
|
21
|
+
async findReferences(assetIds) {
|
|
22
|
+
if (assetIds.length === 0)
|
|
23
|
+
return [];
|
|
24
|
+
const em = emFactory();
|
|
25
|
+
const rows = (await em.execute(`select gi.asset_id::text as asset_id,
|
|
26
|
+
gi.id::text as item_id,
|
|
27
|
+
gi.position,
|
|
28
|
+
p.id::text as product_id,
|
|
29
|
+
p.name as product_name
|
|
30
|
+
from gallery_items gi
|
|
31
|
+
join products p on p.id = gi.product_id
|
|
32
|
+
where gi.asset_id in (${assetIds.map(() => '?').join(',')})`, assetIds));
|
|
33
|
+
return rows.map((r) => ({
|
|
34
|
+
kind: 'product_gallery',
|
|
35
|
+
entityId: r.product_id,
|
|
36
|
+
label: `Product "${pickName(r.product_name)}" — gallery position ${r.position}`,
|
|
37
|
+
}));
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function productAttachmentDescriptor(emFactory) {
|
|
42
|
+
return {
|
|
43
|
+
ownerModuleId: 'catalog',
|
|
44
|
+
async findReferences(assetIds) {
|
|
45
|
+
if (assetIds.length === 0)
|
|
46
|
+
return [];
|
|
47
|
+
const em = emFactory();
|
|
48
|
+
const rows = (await em.execute(`select pa.asset_id::text as asset_id,
|
|
49
|
+
pa.name as attachment_name,
|
|
50
|
+
p.id::text as product_id,
|
|
51
|
+
p.name as product_name
|
|
52
|
+
from product_attachments pa
|
|
53
|
+
join products p on p.id = pa.product_id
|
|
54
|
+
where pa.asset_id in (${assetIds.map(() => '?').join(',')})`, assetIds));
|
|
55
|
+
return rows.map((r) => ({
|
|
56
|
+
kind: 'product_attachment',
|
|
57
|
+
entityId: r.product_id,
|
|
58
|
+
label: `Product "${pickName(r.product_name)}" — attachment "${r.attachment_name}"`,
|
|
59
|
+
}));
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function productVirtualDownloadDescriptor(emFactory) {
|
|
64
|
+
return {
|
|
65
|
+
ownerModuleId: 'catalog',
|
|
66
|
+
async findReferences(assetIds) {
|
|
67
|
+
if (assetIds.length === 0)
|
|
68
|
+
return [];
|
|
69
|
+
const em = emFactory();
|
|
70
|
+
const rows = (await em.execute(`select p.id::text as product_id, p.name as product_name
|
|
71
|
+
from products p
|
|
72
|
+
where p.download_asset_id in (${assetIds.map(() => '?').join(',')})`, assetIds));
|
|
73
|
+
return rows.map((r) => ({
|
|
74
|
+
kind: 'product_virtual_download',
|
|
75
|
+
entityId: r.product_id,
|
|
76
|
+
label: `Product "${pickName(r.product_name)}" — virtual download`,
|
|
77
|
+
}));
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function categoryMainImageDescriptor(emFactory) {
|
|
82
|
+
return {
|
|
83
|
+
ownerModuleId: 'catalog',
|
|
84
|
+
async findReferences(assetIds) {
|
|
85
|
+
if (assetIds.length === 0)
|
|
86
|
+
return [];
|
|
87
|
+
const em = emFactory();
|
|
88
|
+
const rows = (await em.execute(`select c.id::text as category_id, c.name as category_name
|
|
89
|
+
from categories c
|
|
90
|
+
where c.main_image_asset_id in (${assetIds.map(() => '?').join(',')})`, assetIds));
|
|
91
|
+
return rows.map((r) => ({
|
|
92
|
+
kind: 'category_main_image',
|
|
93
|
+
entityId: r.category_id,
|
|
94
|
+
label: `Category "${pickName(r.category_name)}" — main image`,
|
|
95
|
+
}));
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function pickName(blob) {
|
|
100
|
+
if (typeof blob === 'string')
|
|
101
|
+
return blob;
|
|
102
|
+
return blob['en-US'] ?? blob['en'] ?? blob['pl'] ?? Object.values(blob)[0] ?? '(unnamed)';
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=asset-references.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"asset-references.js","sourceRoot":"","sources":["../../../src/backend/services/asset-references.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AttachmentService — admin-only CRUD for AttachmentTypes (global
|
|
3
|
+
* dictionary) and ProductAttachments (per-product wrappers around an
|
|
4
|
+
* Asset). Feature 002 US3, data-model.md §2.5, §2.6.
|
|
5
|
+
*
|
|
6
|
+
* Foundation has no public AssetsService — Asset is imported as an
|
|
7
|
+
* entity directly (research.md R-9 + foundation pattern), and the
|
|
8
|
+
* service layer guards `kind ∈ {pdf, certificate, other}` for
|
|
9
|
+
* attachments (image / video are reserved for the gallery).
|
|
10
|
+
*/
|
|
11
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
12
|
+
import { type AssetReadPort, type AttachmentType as AttachmentTypeDto, type CreateAttachmentTypeRequest, type UpdateAttachmentTypeRequest, type ProductAttachment as ProductAttachmentDto, type CreateAttachmentRequest, type UpdateAttachmentRequest } from '@endora-commerce/contracts';
|
|
13
|
+
import type { CommandBus } from '@endora-commerce/platform/commands';
|
|
14
|
+
export declare class AttachmentService {
|
|
15
|
+
#private;
|
|
16
|
+
private readonly emFactory;
|
|
17
|
+
/** Feature 054 — audits attachment writes co-transactionally when provided. */
|
|
18
|
+
private readonly commandBus?;
|
|
19
|
+
/**
|
|
20
|
+
* Feature 075 — `assets_library`'s read port, where `em.findOne(Asset, …)`
|
|
21
|
+
* used to be. The check below is the reason this read exists: an attach
|
|
22
|
+
* that cannot verify the asset would store a dangling id, so with
|
|
23
|
+
* `assets_library` off the port fails closed and the attach is refused.
|
|
24
|
+
*/
|
|
25
|
+
private readonly assets?;
|
|
26
|
+
constructor(emFactory: () => EntityManager,
|
|
27
|
+
/** Feature 054 — audits attachment writes co-transactionally when provided. */
|
|
28
|
+
commandBus?: CommandBus | undefined,
|
|
29
|
+
/**
|
|
30
|
+
* Feature 075 — `assets_library`'s read port, where `em.findOne(Asset, …)`
|
|
31
|
+
* used to be. The check below is the reason this read exists: an attach
|
|
32
|
+
* that cannot verify the asset would store a dangling id, so with
|
|
33
|
+
* `assets_library` off the port fails closed and the attach is refused.
|
|
34
|
+
*/
|
|
35
|
+
assets?: AssetReadPort | undefined);
|
|
36
|
+
listTypes(): Promise<AttachmentTypeDto[]>;
|
|
37
|
+
createType(req: CreateAttachmentTypeRequest): Promise<AttachmentTypeDto>;
|
|
38
|
+
updateType(id: string, req: UpdateAttachmentTypeRequest): Promise<AttachmentTypeDto>;
|
|
39
|
+
deleteType(id: string): Promise<void>;
|
|
40
|
+
listAttachments(productId: string): Promise<ProductAttachmentDto[]>;
|
|
41
|
+
createAttachment(productId: string, req: CreateAttachmentRequest): Promise<ProductAttachmentDto>;
|
|
42
|
+
updateAttachment(productId: string, attachmentId: string, req: UpdateAttachmentRequest): Promise<ProductAttachmentDto>;
|
|
43
|
+
deleteAttachment(productId: string, attachmentId: string): Promise<void>;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=attachment.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attachment.service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/attachment.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAG3D,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,cAAc,IAAI,iBAAiB,EACxC,KAAK,2BAA2B,EAChC,KAAK,2BAA2B,EAChC,KAAK,iBAAiB,IAAI,oBAAoB,EAC9C,KAAK,uBAAuB,EAC5B,KAAK,uBAAuB,EAC7B,MAAM,4BAA4B,CAAC;AAGpC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAC;AAOrE,qBAAa,iBAAiB;;IAE1B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,+EAA+E;IAC/E,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC;IAC5B;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;gBATP,SAAS,EAAE,MAAM,aAAa;IAC/C,+EAA+E;IAC9D,UAAU,CAAC,EAAE,UAAU,YAAA;IACxC;;;;;OAKG;IACc,MAAM,CAAC,EAAE,aAAa,YAAA;IAkCnC,SAAS,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;IA2BzC,UAAU,CAAC,GAAG,EAAE,2BAA2B,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAkCxE,UAAU,CACd,EAAE,EAAE,MAAM,EACV,GAAG,EAAE,2BAA2B,GAC/B,OAAO,CAAC,iBAAiB,CAAC;IAwCvB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA2BrC,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAWnE,gBAAgB,CACpB,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,uBAAuB,GAC3B,OAAO,CAAC,oBAAoB,CAAC;IA+C1B,gBAAgB,CACpB,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,GAAG,EAAE,uBAAuB,GAC3B,OAAO,CAAC,oBAAoB,CAAC;IAkC1B,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAkE/E"}
|