@revenexx/cover 0.1.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 +39 -0
- package/app/api/account.ts +8 -0
- package/app/api/categories.ts +3 -0
- package/app/api/checkout.ts +6 -0
- package/app/api/images.ts +4 -0
- package/app/api/markets.ts +3 -0
- package/app/api/product.ts +3 -0
- package/app/api/products.ts +4 -0
- package/app/api/search.ts +4 -0
- package/app/app.config.ts +29 -0
- package/app/assets/css/cover.css +25 -0
- package/app/components/account/AccountMenu.vue +30 -0
- package/app/components/account/AccountShell.vue +18 -0
- package/app/components/account/AccountSidebar.vue +90 -0
- package/app/components/account/address/AccountAddressCard.vue +287 -0
- package/app/components/account/carts/AccountCartsTable.vue +284 -0
- package/app/components/account/dashboard/AccountDashboard.vue +351 -0
- package/app/components/account/directorder/AccountDirectOrder.vue +512 -0
- package/app/components/account/governance/AccountApprovalLimitsTable.vue +221 -0
- package/app/components/account/governance/AccountCostCenterDetail.vue +276 -0
- package/app/components/account/governance/AccountCostCentersTable.vue +252 -0
- package/app/components/account/governance/AccountRequisitionDetail.vue +295 -0
- package/app/components/account/governance/AccountRequisitionsTable.vue +255 -0
- package/app/components/account/governance/AccountWorkflowDetail.vue +215 -0
- package/app/components/account/governance/AccountWorkflowsTable.vue +168 -0
- package/app/components/account/governance/GovernanceApprovalLimitModal.vue +183 -0
- package/app/components/account/governance/GovernanceCostCenterModal.vue +188 -0
- package/app/components/account/governance/GovernanceWorkflowModal.vue +191 -0
- package/app/components/account/orderlists/AccountOrderListDetail.vue +349 -0
- package/app/components/account/orderlists/AccountOrderListsTable.vue +352 -0
- package/app/components/account/orders/AccountOrderDetail.vue +376 -0
- package/app/components/account/orders/AccountOrdersTable.vue +281 -0
- package/app/components/account/preferences/AccountPreferences.vue +50 -0
- package/app/components/auth/AuthForgotPasswordPanel.vue +88 -0
- package/app/components/auth/AuthLoginPanel.vue +103 -0
- package/app/components/auth/AuthLoginTeaser.vue +24 -0
- package/app/components/auth/AuthPageHeader.vue +21 -0
- package/app/components/auth/AuthRegisterPanel.vue +431 -0
- package/app/components/auth/AuthRegisterTeaser.vue +24 -0
- package/app/components/auth/AuthResetPasswordPanel.vue +115 -0
- package/app/components/cart/CartButton.vue +42 -0
- package/app/components/cart/actions/CartCostCenterModal.vue +110 -0
- package/app/components/cart/actions/CartExportModal.vue +82 -0
- package/app/components/cart/actions/CartPositionTextsModal.vue +92 -0
- package/app/components/cart/actions/CartSaveToListModal.vue +177 -0
- package/app/components/cart/actions/CartWorkflowPickerModal.vue +64 -0
- package/app/components/cart/drawer/CartDrawer.vue +49 -0
- package/app/components/cart/item/CartItem.vue +318 -0
- package/app/components/cart/manage/CartNameModal.vue +68 -0
- package/app/components/cart/position/CartPosition.vue +369 -0
- package/app/components/cart/quickadd/CartQuickAdd.vue +145 -0
- package/app/components/cart/recommend/CartCrossSell.vue +102 -0
- package/app/components/cart/recommend/CartRecommendCard.vue +59 -0
- package/app/components/cart/recommend/CartReorderStrip.vue +108 -0
- package/app/components/cart/requisition/CartRequisitionGroup.vue +74 -0
- package/app/components/cart/skeleton/CartSkeleton.vue +18 -0
- package/app/components/cart/summary/CartSummary.vue +25 -0
- package/app/components/cart/summary/CartSummaryBreakdown.vue +35 -0
- package/app/components/cart/switcher/CartSwitcher.vue +147 -0
- package/app/components/cart/target/CartTargetModal.vue +129 -0
- package/app/components/cart/view/CartApprovalsPanel.vue +89 -0
- package/app/components/cart/view/CartListHeader.vue +86 -0
- package/app/components/cart/view/CartPositionList.vue +94 -0
- package/app/components/cart/view/CartSummaryPanel.vue +254 -0
- package/app/components/cart/view/CartView.vue +310 -0
- package/app/components/category/info/CategoryPrice.vue +47 -0
- package/app/components/category/info/CategorySku.vue +15 -0
- package/app/components/category/info/CategoryStock.vue +25 -0
- package/app/components/category/list/CategoryItemCount.vue +27 -0
- package/app/components/category/list/CategoryListPagination.vue +81 -0
- package/app/components/category/skeleton/CategoryDetailSkeleton.vue +12 -0
- package/app/components/category/skeleton/CategoryListSkeleton.vue +65 -0
- package/app/components/category/view/CategoryViewToggle.vue +33 -0
- package/app/components/category/view/GridView.vue +78 -0
- package/app/components/category/view/ListView.vue +80 -0
- package/app/components/checkout/confirmation/CheckoutConfirmationView.vue +289 -0
- package/app/components/checkout/form/CheckoutSchemaForm.vue +72 -0
- package/app/components/checkout/onepage/CheckoutAddressPickerModal.vue +260 -0
- package/app/components/checkout/onepage/CheckoutAddressSection.vue +186 -0
- package/app/components/checkout/onepage/CheckoutDeliverySection.vue +158 -0
- package/app/components/checkout/onepage/CheckoutGate.vue +67 -0
- package/app/components/checkout/onepage/CheckoutGuestDetailsSection.vue +154 -0
- package/app/components/checkout/onepage/CheckoutNotesSection.vue +62 -0
- package/app/components/checkout/onepage/CheckoutOnePageView.vue +77 -0
- package/app/components/checkout/onepage/CheckoutPaymentSection.vue +181 -0
- package/app/components/checkout/onepage/CheckoutSectionTitle.vue +18 -0
- package/app/components/checkout/onepage/CheckoutSummarySection.vue +211 -0
- package/app/components/checkout/onepage/CheckoutWorkflowPanel.vue +38 -0
- package/app/components/layout/AppLogo.vue +21 -0
- package/app/components/layout/footer/Footer.vue +148 -0
- package/app/components/layout/header/AppBar.vue +75 -0
- package/app/components/layout/header/CategoryNav.vue +93 -0
- package/app/components/layout/header/HeaderActionItem.vue +81 -0
- package/app/components/layout/header/LocaleSwitcher.vue +123 -0
- package/app/components/layout/header/MainNav.vue +61 -0
- package/app/components/layout/header/MobileMenu.vue +81 -0
- package/app/components/layout/header/TopBar.vue +63 -0
- package/app/components/listing/ListingView.vue +227 -0
- package/app/components/product/detail/ProductDetailAddToCart.vue +188 -0
- package/app/components/product/detail/ProductDetailDescription.vue +20 -0
- package/app/components/product/detail/ProductDetailImageGallery.vue +58 -0
- package/app/components/product/detail/ProductDetailPricing.vue +108 -0
- package/app/components/product/detail/ProductDetailSaveToList.vue +291 -0
- package/app/components/product/detail/ProductDetailSpecifications.vue +33 -0
- package/app/components/product/detail/ProductDetailStock.vue +36 -0
- package/app/components/search/filter/SearchFacets.vue +256 -0
- package/app/components/search/filter/SearchFacetsDrawer.vue +58 -0
- package/app/components/ui/AppShell.vue +7 -0
- package/app/components/ui/debug/PiniaStateCard.vue +106 -0
- package/app/components/ui/feedback/EmptyState.vue +30 -0
- package/app/components/ui/feedback/ErrorBoundary.vue +44 -0
- package/app/components/ui/feedback/NotFound.vue +34 -0
- package/app/components/ui/forms/CountrySelection.vue +14 -0
- package/app/components/ui/forms/FormKitDatePicker.vue +124 -0
- package/app/components/ui/forms/PasswordInput.vue +136 -0
- package/app/components/ui/forms/SearchInput.vue +213 -0
- package/app/components/ui/table/TableSortButton.vue +51 -0
- package/app/components/ui/table/TableToolbar.vue +66 -0
- package/app/composables/useAccount.ts +27 -0
- package/app/composables/useAccountAddresses.ts +116 -0
- package/app/composables/useAccountOrders.ts +18 -0
- package/app/composables/useAppUrl.ts +38 -0
- package/app/composables/useAuthStore.ts +202 -0
- package/app/composables/useB2BContext.ts +23 -0
- package/app/composables/useCartActions.ts +25 -0
- package/app/composables/useCartCalculation.ts +90 -0
- package/app/composables/useCartSelection.ts +46 -0
- package/app/composables/useCartStore.ts +837 -0
- package/app/composables/useCartSummaryFormatting.ts +28 -0
- package/app/composables/useCategories.ts +29 -0
- package/app/composables/useCheckoutOnePage.ts +438 -0
- package/app/composables/useCheckoutProfileSource.ts +8 -0
- package/app/composables/useCheckoutSchema.ts +28 -0
- package/app/composables/useDataTable.ts +14 -0
- package/app/composables/useFormValidation.ts +29 -0
- package/app/composables/useIcons.ts +17 -0
- package/app/composables/useLocalePreferences.ts +45 -0
- package/app/composables/useMarkets.ts +15 -0
- package/app/composables/useProduct.ts +89 -0
- package/app/composables/useProductCard.ts +8 -0
- package/app/composables/useProductCardActions.ts +106 -0
- package/app/composables/useProductImage.ts +12 -0
- package/app/composables/useProductListing.ts +210 -0
- package/app/composables/useProductUrl.ts +17 -0
- package/app/composables/useProducts.ts +130 -0
- package/app/composables/useSearch.ts +12 -0
- package/app/composables/useThemeStore.ts +81 -0
- package/app/composables/useViewMode.ts +18 -0
- package/app/config/countries.ts +74 -0
- package/app/config/icons.ts +283 -0
- package/app/config/navigation.ts +191 -0
- package/app/config/palette.ts +13 -0
- package/app/config/themes.ts +20 -0
- package/app/formkit.config.ts +50 -0
- package/app/interfaces/account/address-list.ts +34 -0
- package/app/interfaces/account/order-list.ts +47 -0
- package/app/interfaces/account/order-lists.ts +35 -0
- package/app/interfaces/account/profile.ts +17 -0
- package/app/interfaces/account.ts +3 -0
- package/app/interfaces/address.ts +9 -0
- package/app/interfaces/auth.ts +104 -0
- package/app/interfaces/b2b.ts +234 -0
- package/app/interfaces/cart-calculation.ts +131 -0
- package/app/interfaces/cart-item.ts +45 -0
- package/app/interfaces/checkout-draft.ts +4 -0
- package/app/interfaces/checkout.ts +43 -0
- package/app/interfaces/delivery.ts +13 -0
- package/app/interfaces/market.ts +24 -0
- package/app/interfaces/payment.ts +19 -0
- package/app/interfaces/persisted-cart.ts +10 -0
- package/app/interfaces/product-detail.ts +54 -0
- package/app/interfaces/product-list.ts +33 -0
- package/app/interfaces/search-facets.ts +14 -0
- package/app/interfaces/validation.ts +14 -0
- package/app/layouts/default.vue +78 -0
- package/app/layouts/focus.vue +80 -0
- package/app/plugins/formkit-locale.client.ts +23 -0
- package/app/shared/constants.ts +1 -0
- package/app/validations/companyName.ts +18 -0
- package/app/validations/emailFormat.ts +10 -0
- package/app/validations/formValidationConfig.ts +50 -0
- package/app/validations/maxLength.ts +12 -0
- package/app/validations/minLength.ts +9 -0
- package/app/validations/optionalCompanyName.ts +23 -0
- package/app/validations/passwordsMatch.ts +5 -0
- package/app/validations/phoneNumber.ts +19 -0
- package/app/validations/required.ts +11 -0
- package/app/validations/termsRequired.ts +4 -0
- package/app/validations/types.ts +10 -0
- package/app/validations/zipCode.ts +15 -0
- package/i18n/locales/de/account.json +458 -0
- package/i18n/locales/de/auth.json +155 -0
- package/i18n/locales/de/cart.json +263 -0
- package/i18n/locales/de/checkout.json +217 -0
- package/i18n/locales/de/common.json +80 -0
- package/i18n/locales/de/cover.json +33 -0
- package/i18n/locales/de/order.json +1 -0
- package/i18n/locales/de/product.json +71 -0
- package/i18n/locales/de/search.json +54 -0
- package/i18n/locales/de/validation.json +12 -0
- package/i18n/locales/en/account.json +458 -0
- package/i18n/locales/en/auth.json +155 -0
- package/i18n/locales/en/cart.json +263 -0
- package/i18n/locales/en/checkout.json +217 -0
- package/i18n/locales/en/common.json +80 -0
- package/i18n/locales/en/cover.json +33 -0
- package/i18n/locales/en/order.json +1 -0
- package/i18n/locales/en/product.json +71 -0
- package/i18n/locales/en/search.json +54 -0
- package/i18n/locales/en/validation.json +12 -0
- package/nuxt.config.ts +109 -0
- package/package.json +65 -0
- package/public/img/product-placeholder.svg +8 -0
- package/public/templates/direct-order-template.csv +3 -0
- package/public/templates/direct-order-template.xlsx +0 -0
- package/server/api/account/address/[id].delete.ts +51 -0
- package/server/api/account/address/[id].put.ts +83 -0
- package/server/api/account/address/index.post.ts +60 -0
- package/server/api/account/addresses.get.ts +25 -0
- package/server/api/account/context.get.ts +16 -0
- package/server/api/account/governance/[domain]/[id].put.ts +57 -0
- package/server/api/account/governance/[domain].post.ts +111 -0
- package/server/api/account/order-lists/[id].delete.ts +22 -0
- package/server/api/account/order-lists/[id].get.ts +17 -0
- package/server/api/account/order-lists/[id].put.ts +46 -0
- package/server/api/account/order-lists/index.get.ts +14 -0
- package/server/api/account/order-lists/index.post.ts +38 -0
- package/server/api/account/orders/[id].get.ts +35 -0
- package/server/api/account/orders.get.ts +35 -0
- package/server/api/account/profile.get.ts +56 -0
- package/server/api/account/profile.put.ts +128 -0
- package/server/api/account/requisitions/[id].get.ts +15 -0
- package/server/api/account/requisitions.get.ts +23 -0
- package/server/api/auth/login.post.ts +37 -0
- package/server/api/auth/logout.post.ts +4 -0
- package/server/api/auth/me.get.ts +3 -0
- package/server/api/auth/personas.get.ts +7 -0
- package/server/api/auth/recovery.post.ts +32 -0
- package/server/api/auth/recovery.put.ts +37 -0
- package/server/api/auth/register.post.ts +126 -0
- package/server/api/cart/calculate.post.ts +25 -0
- package/server/api/cart/export.post.ts +81 -0
- package/server/api/cart/index.delete.ts +10 -0
- package/server/api/cart/index.get.ts +10 -0
- package/server/api/cart/sync.post.ts +14 -0
- package/server/api/carts/[id]/activate.post.ts +18 -0
- package/server/api/carts/[id]/index.delete.ts +18 -0
- package/server/api/carts/[id]/index.put.ts +19 -0
- package/server/api/carts/[id]/items.post.ts +21 -0
- package/server/api/carts/index.get.ts +14 -0
- package/server/api/carts/index.post.ts +14 -0
- package/server/api/categories/[slug].get.ts +22 -0
- package/server/api/categories/index.get.ts +11 -0
- package/server/api/checkout/profile.get.ts +21 -0
- package/server/api/checkout/schema/[key].get.ts +19 -0
- package/server/api/checkout/session.post.ts +8 -0
- package/server/api/images/detail/[filename].get.ts +18 -0
- package/server/api/images/list/[filename].get.ts +17 -0
- package/server/api/markets.get.ts +9 -0
- package/server/api/orders/index.post.ts +376 -0
- package/server/api/payment/methods.post.ts +65 -0
- package/server/api/product/[id].get.ts +29 -0
- package/server/api/products/[id].get.ts +30 -0
- package/server/api/products/index.get.ts +21 -0
- package/server/api/requisitions/index.post.ts +67 -0
- package/server/api/shipping/rates.post.ts +70 -0
- package/server/api/themes/index.get.ts +9 -0
- package/server/api/typesense/drop.get.ts +22 -0
- package/server/api/typesense/health.get.ts +9 -0
- package/server/api/typesense/search.post.ts +199 -0
- package/server/api/typesense/seed.get.ts +112 -0
- package/server/api/typesense/suggest.post.ts +69 -0
- package/server/config/account/organization.json +146 -0
- package/server/config/account/personas.json +169 -0
- package/server/config/account/user.json +8 -0
- package/server/config/account/workflows.json +19 -0
- package/server/data/account/address-list.json +103 -0
- package/server/data/account/order-list.json +491 -0
- package/server/data/account/order-lists.json +149 -0
- package/server/data/account/profile.json +9 -0
- package/server/data/account/registration-requests.json +3 -0
- package/server/data/account/requisitions.json +686 -0
- package/server/data/categories.json +186 -0
- package/server/data/forms/checkout/add-address.json +24 -0
- package/server/data/list/5137-1.png +0 -0
- package/server/data/list/5498-1.png +0 -0
- package/server/data/list/5498-2.png +0 -0
- package/server/data/list/5498-3.png +0 -0
- package/server/data/list/5498-4.png +0 -0
- package/server/data/list/5498-5.png +0 -0
- package/server/data/list/5498-6.png +0 -0
- package/server/data/list/5519-1.png +0 -0
- package/server/data/list/5713-1.png +0 -0
- package/server/data/list/5789-1.png +0 -0
- package/server/data/list/5930-1.png +0 -0
- package/server/data/list/6127-1.png +0 -0
- package/server/data/list/6234-1.png +0 -0
- package/server/data/list/6238-1.png +0 -0
- package/server/data/list/6246-1.png +0 -0
- package/server/data/list/6270-1.png +0 -0
- package/server/data/list/6330-1.png +0 -0
- package/server/data/list/6336-1.png +0 -0
- package/server/data/list/6360-1.png +0 -0
- package/server/data/list/6363-1.png +0 -0
- package/server/data/list/6375-1.png +0 -0
- package/server/data/list/6385-1.png +0 -0
- package/server/data/list/6413-1.png +0 -0
- package/server/data/list/6418-1.png +0 -0
- package/server/data/list/6465-1.png +0 -0
- package/server/data/list/6477-1.png +0 -0
- package/server/data/list/6509-1.png +0 -0
- package/server/data/list/6545-1.png +0 -0
- package/server/data/list/6548-1.png +0 -0
- package/server/data/list/6566-1.png +0 -0
- package/server/data/list/6581-1.png +0 -0
- package/server/data/list/6609-1.png +0 -0
- package/server/data/list/6611-1.png +0 -0
- package/server/data/list/6641-1.png +0 -0
- package/server/data/list/6659-1.png +0 -0
- package/server/data/list/6662-1.png +0 -0
- package/server/data/list/6689-1.png +0 -0
- package/server/data/list/6698-1.png +0 -0
- package/server/data/list/6701-1.png +0 -0
- package/server/data/list/6752-1.png +0 -0
- package/server/data/list/6755-1.png +0 -0
- package/server/data/list/6837-1.png +0 -0
- package/server/data/list/6841-1.png +0 -0
- package/server/data/list/6844-1.png +0 -0
- package/server/data/list/6846-1.png +0 -0
- package/server/data/list/6886-1.png +0 -0
- package/server/data/list/6895-1.png +0 -0
- package/server/data/list/6897-1.png +0 -0
- package/server/data/list/6919-1.png +0 -0
- package/server/data/list/6977-1.png +0 -0
- package/server/data/list/6983-1.png +0 -0
- package/server/data/list/6984-1.png +0 -0
- package/server/data/list/6985-1.png +0 -0
- package/server/data/list/6986-1.png +0 -0
- package/server/data/list/6989-1.png +0 -0
- package/server/data/list/6995-1.png +0 -0
- package/server/data/list/6998-1.png +0 -0
- package/server/data/markets.json +24 -0
- package/server/data/product-detail.json +2450 -0
- package/server/data/product-list.json +2450 -0
- package/server/data/themes.json +8 -0
- package/server/interfaces/account.ts +20 -0
- package/server/interfaces/auth.ts +32 -0
- package/server/interfaces/b2bContext.ts +23 -0
- package/server/interfaces/cart.ts +46 -0
- package/server/interfaces/cartCalculation.ts +17 -0
- package/server/interfaces/category.ts +12 -0
- package/server/interfaces/checkoutProfile.ts +5 -0
- package/server/interfaces/log.ts +3 -0
- package/server/interfaces/market.ts +10 -0
- package/server/interfaces/product.ts +21 -0
- package/server/interfaces/schema.ts +20 -0
- package/server/interfaces/theme.ts +10 -0
- package/server/services/ApiAuthService.ts +138 -0
- package/server/services/ApiCartService.ts +254 -0
- package/server/services/ApiCategoryService.ts +25 -0
- package/server/services/ApiMarketService.ts +71 -0
- package/server/services/ApiProductService.ts +53 -0
- package/server/services/LocalFileCategoryService.ts +23 -0
- package/server/services/LocalFileCheckoutProfileService.ts +117 -0
- package/server/services/LocalFileProductService.ts +128 -0
- package/server/services/LocalFileSchemaService.ts +70 -0
- package/server/services/LocalFileThemeService.ts +18 -0
- package/server/services/LogService.ts +28 -0
- package/server/services/MockAccountService.ts +58 -0
- package/server/services/MockAuthService.ts +105 -0
- package/server/services/MockB2BContextService.ts +149 -0
- package/server/services/MockCartCalculationService.ts +395 -0
- package/server/services/MockMarketService.ts +18 -0
- package/server/services/SdkAccountService.ts +56 -0
- package/server/services/SdkAuthService.ts +83 -0
- package/server/services/SessionCartService.ts +31 -0
- package/server/utils/accountService.ts +30 -0
- package/server/utils/authCookie.ts +13 -0
- package/server/utils/authService.ts +43 -0
- package/server/utils/b2bService.ts +49 -0
- package/server/utils/cartService.ts +59 -0
- package/server/utils/categoryService.ts +19 -0
- package/server/utils/checkoutProfileService.ts +9 -0
- package/server/utils/checkoutSession.ts +38 -0
- package/server/utils/coverData.ts +84 -0
- package/server/utils/governanceStore.ts +38 -0
- package/server/utils/i18n.ts +76 -0
- package/server/utils/inventoryService.ts +14 -0
- package/server/utils/liveCatalog.ts +234 -0
- package/server/utils/liveInventories.ts +76 -0
- package/server/utils/liveOrders.ts +139 -0
- package/server/utils/livePrices.ts +93 -0
- package/server/utils/locale.ts +39 -0
- package/server/utils/logService.ts +19 -0
- package/server/utils/marketService.ts +24 -0
- package/server/utils/orderService.ts +14 -0
- package/server/utils/paymentService.ts +14 -0
- package/server/utils/priceService.ts +14 -0
- package/server/utils/productService.ts +28 -0
- package/server/utils/productsSchema.ts +30 -0
- package/server/utils/revenexxApi.ts +136 -0
- package/server/utils/schemaService.ts +25 -0
- package/server/utils/serviceMode.ts +70 -0
- package/server/utils/shippingService.ts +14 -0
- package/server/utils/shopSdk.ts +88 -0
- package/server/utils/themeService.ts +16 -0
- package/server/utils/typesense.ts +27 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { H3Event } from "h3";
|
|
2
|
+
|
|
3
|
+
import type { IProductService } from "../interfaces/product";
|
|
4
|
+
import { ApiProductService } from "../services/ApiProductService";
|
|
5
|
+
import { LocalFileProductService } from "../services/LocalFileProductService";
|
|
6
|
+
|
|
7
|
+
const serviceRegistry: Record<string, IProductService> = {
|
|
8
|
+
mock: new LocalFileProductService("product-list.json", "product-detail.json"),
|
|
9
|
+
api: new ApiProductService(),
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function getProductService(event?: H3Event): IProductService {
|
|
13
|
+
const key = resolveServiceKey(event, {
|
|
14
|
+
domain: "productService",
|
|
15
|
+
mockKey: "mock",
|
|
16
|
+
liveKey: "api",
|
|
17
|
+
});
|
|
18
|
+
return serviceRegistry[key] ?? serviceRegistry["mock"]!;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Resolved registry key — search endpoints branch between local index and live API. */
|
|
22
|
+
export function resolveProductServiceKey(event?: H3Event): string {
|
|
23
|
+
return resolveServiceKey(event, {
|
|
24
|
+
domain: "productService",
|
|
25
|
+
mockKey: "mock",
|
|
26
|
+
liveKey: "api",
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { CollectionCreateSchema } from "typesense/lib/Typesense/Collections";
|
|
2
|
+
|
|
3
|
+
export const PRODUCTS_COLLECTION = "products";
|
|
4
|
+
|
|
5
|
+
export const productsSchema = {
|
|
6
|
+
name: PRODUCTS_COLLECTION,
|
|
7
|
+
enable_nested_fields: true,
|
|
8
|
+
fields: [
|
|
9
|
+
{ name: "name", type: "string" as const },
|
|
10
|
+
{ name: "sku", type: "string" as const },
|
|
11
|
+
{ name: "manufacturer", type: "string" as const, facet: true },
|
|
12
|
+
{ name: "available", type: "bool" as const, facet: true },
|
|
13
|
+
{ name: "stockQuantity", type: "int32" as const },
|
|
14
|
+
{ name: "price", type: "int32" as const },
|
|
15
|
+
{ name: "prices", type: "object" as const },
|
|
16
|
+
{ name: "description", type: "string" as const, optional: true },
|
|
17
|
+
{ name: "category", type: "string" as const, facet: true },
|
|
18
|
+
{ name: "categorySlug", type: "string" as const, facet: true },
|
|
19
|
+
{ name: "subcategory", type: "string" as const, facet: true, optional: true },
|
|
20
|
+
{ name: "subcategorySlug", type: "string" as const, facet: true, optional: true },
|
|
21
|
+
{ name: "slug", type: "string" as const },
|
|
22
|
+
{ name: "image", type: "string" as const, optional: true },
|
|
23
|
+
{ name: "taxRate", type: "float" as const },
|
|
24
|
+
],
|
|
25
|
+
default_sorting_field: "price",
|
|
26
|
+
} satisfies CollectionCreateSchema;
|
|
27
|
+
|
|
28
|
+
export const FACET_FIELDS: string[] = productsSchema.fields
|
|
29
|
+
.filter(f => "facet" in f && f.facet === true)
|
|
30
|
+
.map(f => f.name);
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mini client for the public revenexx gateway (api.revenexx.com) — the
|
|
3
|
+
* stand-in for the generated web SDKs that do not exist yet. Every live
|
|
4
|
+
* "api"-keyed BFF implementation goes through this client; once the real
|
|
5
|
+
* SDKs land, only the service implementations swap, the BFF contracts stay.
|
|
6
|
+
*
|
|
7
|
+
* Auth model: tenant API key (server-side only, never reaches the client).
|
|
8
|
+
* Configure via runtime config / env:
|
|
9
|
+
* NUXT_REVENEXX_API_URL (default https://api.revenexx.com)
|
|
10
|
+
* NUXT_REVENEXX_TENANT (tenant slug, e.g. "revenexx")
|
|
11
|
+
* NUXT_REVENEXX_API_KEY (rvxk_… gateway key)
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export class RevenexxApiError extends Error {
|
|
15
|
+
readonly statusCode: number;
|
|
16
|
+
readonly type: string;
|
|
17
|
+
|
|
18
|
+
constructor(statusCode: number, type: string, message: string) {
|
|
19
|
+
super(message);
|
|
20
|
+
this.name = "RevenexxApiError";
|
|
21
|
+
this.statusCode = statusCode;
|
|
22
|
+
this.type = type;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Expected user-facing failures (wrong credentials, duplicate email, …) are
|
|
28
|
+
* forwarded to the client as-is; everything else is an infrastructure error.
|
|
29
|
+
*/
|
|
30
|
+
const API_USER_ERROR_STATUS = new Set([400, 401, 404, 409, 429]);
|
|
31
|
+
|
|
32
|
+
export function isApiUserError(err: unknown): err is RevenexxApiError {
|
|
33
|
+
return err instanceof RevenexxApiError && API_USER_ERROR_STATUS.has(err.statusCode);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The customers app's auth passthrough answers with a generic error envelope
|
|
38
|
+
* (no machine type). Map the HTTP status onto the platform error types the
|
|
39
|
+
* client-side auth store already translates, so mock/sdk/api modes produce
|
|
40
|
+
* identical UX for the standard failures.
|
|
41
|
+
*/
|
|
42
|
+
export function apiAuthErrorType(err: RevenexxApiError): string {
|
|
43
|
+
switch (err.statusCode) {
|
|
44
|
+
case 401: return "user_invalid_credentials";
|
|
45
|
+
case 404: return "user_not_found";
|
|
46
|
+
case 409: return "user_email_already_exists";
|
|
47
|
+
case 429: return "general_rate_limit_exceeded";
|
|
48
|
+
default: return err.type;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Diagnostic fields for structured logs — never includes the API key. */
|
|
53
|
+
export function apiErrorContext(err: unknown): Record<string, unknown> {
|
|
54
|
+
if (err instanceof RevenexxApiError) {
|
|
55
|
+
return { apiStatus: err.statusCode, apiType: err.type, apiMessage: err.message };
|
|
56
|
+
}
|
|
57
|
+
return { errorMessage: err instanceof Error ? err.message : String(err) };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface RevenexxApiRequest {
|
|
61
|
+
readonly method?: "GET" | "POST" | "PUT" | "DELETE";
|
|
62
|
+
readonly query?: Record<string, string | number | boolean | undefined>;
|
|
63
|
+
readonly body?: unknown;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface RevenexxApiClient {
|
|
67
|
+
request<T>(path: string, options?: RevenexxApiRequest): Promise<T>;
|
|
68
|
+
get<T>(path: string, query?: RevenexxApiRequest["query"]): Promise<T>;
|
|
69
|
+
post<T>(path: string, body?: unknown): Promise<T>;
|
|
70
|
+
put<T>(path: string, body?: unknown): Promise<T>;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
let client: RevenexxApiClient | null = null;
|
|
74
|
+
|
|
75
|
+
export function useRevenexxApi(): RevenexxApiClient {
|
|
76
|
+
if (client) {
|
|
77
|
+
return client;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const config = useRuntimeConfig();
|
|
81
|
+
const baseUrl = String(config.revenexxApiUrl || "https://api.revenexx.com").replace(/\/+$/, "");
|
|
82
|
+
const tenant = String(config.revenexxTenant || "");
|
|
83
|
+
const apiKey = String(config.revenexxApiKey || "");
|
|
84
|
+
|
|
85
|
+
async function request<T>(path: string, options: RevenexxApiRequest = {}): Promise<T> {
|
|
86
|
+
if (!tenant || !apiKey) {
|
|
87
|
+
throw new RevenexxApiError(503, "api_not_configured", "revenexx API credentials are not configured (NUXT_REVENEXX_TENANT / NUXT_REVENEXX_API_KEY)");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const url = new URL(baseUrl + path);
|
|
91
|
+
for (const [key, value] of Object.entries(options.query ?? {})) {
|
|
92
|
+
if (value !== undefined) {
|
|
93
|
+
url.searchParams.set(key, String(value));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const response = await fetch(url, {
|
|
98
|
+
method: options.method ?? "GET",
|
|
99
|
+
headers: {
|
|
100
|
+
"X-Revenexx-Tenant": tenant,
|
|
101
|
+
"X-Revenexx-Api-Key": apiKey,
|
|
102
|
+
"Accept": "application/json",
|
|
103
|
+
...(options.body !== undefined ? { "Content-Type": "application/json" } : {}),
|
|
104
|
+
},
|
|
105
|
+
...(options.body !== undefined ? { body: JSON.stringify(options.body) } : {}),
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const text = await response.text();
|
|
109
|
+
let payload: unknown = null;
|
|
110
|
+
try {
|
|
111
|
+
payload = text ? JSON.parse(text) : null;
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
payload = null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (!response.ok) {
|
|
118
|
+
const envelope = (payload ?? {}) as { type?: string; message?: string; error?: string };
|
|
119
|
+
throw new RevenexxApiError(
|
|
120
|
+
response.status,
|
|
121
|
+
envelope.type ?? "api_error",
|
|
122
|
+
envelope.message ?? envelope.error ?? `revenexx API responded with ${response.status}`,
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return payload as T;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
client = {
|
|
130
|
+
request,
|
|
131
|
+
get: (path, query) => request(path, { query }),
|
|
132
|
+
post: (path, body) => request(path, { method: "POST", body }),
|
|
133
|
+
put: (path, body) => request(path, { method: "PUT", body }),
|
|
134
|
+
};
|
|
135
|
+
return client;
|
|
136
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { H3Event } from "h3";
|
|
2
|
+
|
|
3
|
+
import type { ISchemaService } from "../interfaces/schema";
|
|
4
|
+
import { LocalFileSchemaService } from "../services/LocalFileSchemaService";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Registry of available schema service implementations.
|
|
8
|
+
* The active implementation is selected by the `schemaService` key in app.config.ts.
|
|
9
|
+
* Add new entries here when additional providers (CMS, remote API, etc.) are implemented.
|
|
10
|
+
*/
|
|
11
|
+
const serviceRegistry: Record<string, ISchemaService> = {
|
|
12
|
+
"local-file": new LocalFileSchemaService("forms/checkout"),
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Returns the schema service registered for the current app configuration.
|
|
17
|
+
* Falls back to "local-file" if the configured key is not found.
|
|
18
|
+
*/
|
|
19
|
+
export function getSchemaService(event?: H3Event): ISchemaService {
|
|
20
|
+
const key = resolveServiceKey(event, {
|
|
21
|
+
domain: "schemaService",
|
|
22
|
+
mockKey: "local-file",
|
|
23
|
+
});
|
|
24
|
+
return serviceRegistry[key] ?? serviceRegistry["local-file"]!;
|
|
25
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { H3Event } from "h3";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Per-request service mode resolution — the demo/live switch.
|
|
5
|
+
*
|
|
6
|
+
* Selection order for every BFF domain:
|
|
7
|
+
* 1. per-domain override from the dev-mode cookie (set by the dev panel)
|
|
8
|
+
* 2. global mode from the dev-mode cookie ("live" picks the domain's live
|
|
9
|
+
* key when one exists, "mock" forces the mock key)
|
|
10
|
+
* 3. the app.config registry key (layer default: mock)
|
|
11
|
+
*
|
|
12
|
+
* The cookie is intentionally readable client-side so the demo shop's dev
|
|
13
|
+
* panel can render the current state without an extra endpoint.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export const DEV_MODE_COOKIE = "cover-dev-mode";
|
|
17
|
+
|
|
18
|
+
export type DevMode = "mock" | "live";
|
|
19
|
+
|
|
20
|
+
export interface DevModeState {
|
|
21
|
+
mode?: DevMode;
|
|
22
|
+
/** Per-domain registry-key overrides, e.g. { accountService: "sdk" } */
|
|
23
|
+
overrides?: Record<string, string>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ServiceDomain {
|
|
27
|
+
/** app.config registry key name, e.g. "accountService" */
|
|
28
|
+
domain: string;
|
|
29
|
+
/** Registry key serving bundled demo data */
|
|
30
|
+
mockKey: string;
|
|
31
|
+
/** Registry key of the live implementation, once one exists */
|
|
32
|
+
liveKey?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function readDevModeState(event?: H3Event): DevModeState {
|
|
36
|
+
if (!event) {
|
|
37
|
+
return {};
|
|
38
|
+
}
|
|
39
|
+
const raw = getCookie(event, DEV_MODE_COOKIE);
|
|
40
|
+
if (!raw) {
|
|
41
|
+
return {};
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const parsed = JSON.parse(raw) as DevModeState;
|
|
45
|
+
return typeof parsed === "object" && parsed !== null ? parsed : {};
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return {};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function resolveServiceKey(event: H3Event | undefined, def: ServiceDomain): string {
|
|
53
|
+
const state = readDevModeState(event);
|
|
54
|
+
|
|
55
|
+
const override = state.overrides?.[def.domain];
|
|
56
|
+
if (override) {
|
|
57
|
+
return override;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (state.mode === "live" && def.liveKey) {
|
|
61
|
+
return def.liveKey;
|
|
62
|
+
}
|
|
63
|
+
if (state.mode === "mock") {
|
|
64
|
+
return def.mockKey;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const config = useAppConfig() as Record<string, unknown>;
|
|
68
|
+
const configured = config[def.domain];
|
|
69
|
+
return typeof configured === "string" ? configured : def.mockKey;
|
|
70
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { H3Event } from "h3";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The shipping domain has no service class — the BFF endpoints branch on
|
|
5
|
+
* the resolved registry key ("mock" = demo profile methods, "api" = the
|
|
6
|
+
* shipping app via the public revenexx API).
|
|
7
|
+
*/
|
|
8
|
+
export function resolveShippingServiceKey(event?: H3Event): string {
|
|
9
|
+
return resolveServiceKey(event, {
|
|
10
|
+
domain: "shippingService",
|
|
11
|
+
mockKey: "mock",
|
|
12
|
+
liveKey: "api",
|
|
13
|
+
});
|
|
14
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Account, Client, RevenexxException } from "@revenexx/sdk";
|
|
2
|
+
|
|
3
|
+
let baseClient: Client | null = null;
|
|
4
|
+
|
|
5
|
+
function getBaseClient(): Client {
|
|
6
|
+
if (baseClient) {
|
|
7
|
+
return baseClient;
|
|
8
|
+
}
|
|
9
|
+
const config = useRuntimeConfig();
|
|
10
|
+
baseClient = new Client();
|
|
11
|
+
baseClient
|
|
12
|
+
.setEndpoint(config.webSdkApiUrl as string)
|
|
13
|
+
.setProject(config.webSdkProject as string)
|
|
14
|
+
.setDevKey(config.webSdkDevKey as string);
|
|
15
|
+
return baseClient;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function useShopSdkAccount(): Account {
|
|
19
|
+
return new Account(getBaseClient());
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function useAuthenticatedAccount(fallbackCookie: string): Account {
|
|
23
|
+
const config = useRuntimeConfig();
|
|
24
|
+
const client = new Client();
|
|
25
|
+
client
|
|
26
|
+
.setEndpoint(config.webSdkApiUrl as string)
|
|
27
|
+
.setProject(config.webSdkProject as string)
|
|
28
|
+
.setDevKey(config.webSdkDevKey as string);
|
|
29
|
+
client.headers["X-Fallback-Cookies"] = fallbackCookie;
|
|
30
|
+
return new Account(client);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const SDK_USER_ERROR_TYPES = new Set([
|
|
34
|
+
"user_invalid_credentials",
|
|
35
|
+
"user_not_found",
|
|
36
|
+
"user_blocked",
|
|
37
|
+
"general_rate_limit_exceeded",
|
|
38
|
+
"user_session_already_exists",
|
|
39
|
+
"user_email_already_exists",
|
|
40
|
+
"user_invalid_token",
|
|
41
|
+
"general_argument_invalid",
|
|
42
|
+
"password_recently_used",
|
|
43
|
+
]);
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Returns true for expected user-facing errors (wrong credentials, rate limit, etc.)
|
|
47
|
+
* that should be forwarded to the client as-is without server-side logging.
|
|
48
|
+
* Everything else is treated as an infrastructure error.
|
|
49
|
+
*/
|
|
50
|
+
export function isSdkUserError(err: unknown): err is RevenexxException {
|
|
51
|
+
return err instanceof RevenexxException && SDK_USER_ERROR_TYPES.has(err.type);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Extracts diagnostic fields from an SDK error for structured log entries.
|
|
56
|
+
* Does not include secrets (API key is never logged).
|
|
57
|
+
*/
|
|
58
|
+
export function sdkErrorContext(err: unknown): Record<string, unknown> {
|
|
59
|
+
if (err instanceof RevenexxException) {
|
|
60
|
+
return { sdkCode: err.code, sdkType: err.type, sdkMessage: err.message };
|
|
61
|
+
}
|
|
62
|
+
return { errorMessage: err instanceof Error ? err.message : String(err) };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export async function withCookieCapture<T>(
|
|
66
|
+
fn: () => Promise<T>,
|
|
67
|
+
): Promise<{ result: T; fallbackCookie: string }> {
|
|
68
|
+
let fallbackCookie = "";
|
|
69
|
+
const origFetch = globalThis.fetch;
|
|
70
|
+
globalThis.fetch = async (
|
|
71
|
+
input: RequestInfo | URL,
|
|
72
|
+
init?: RequestInit,
|
|
73
|
+
) => {
|
|
74
|
+
const res = await origFetch(input, init);
|
|
75
|
+
const header = res.headers.get("x-fallback-cookies");
|
|
76
|
+
if (header) {
|
|
77
|
+
fallbackCookie = header;
|
|
78
|
+
}
|
|
79
|
+
return res;
|
|
80
|
+
};
|
|
81
|
+
try {
|
|
82
|
+
const result = await fn();
|
|
83
|
+
return { result, fallbackCookie };
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
globalThis.fetch = origFetch;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { H3Event } from "h3";
|
|
2
|
+
|
|
3
|
+
import type { IThemeService } from "../interfaces/theme";
|
|
4
|
+
import { LocalFileThemeService } from "../services/LocalFileThemeService";
|
|
5
|
+
|
|
6
|
+
const serviceRegistry: Record<string, IThemeService> = {
|
|
7
|
+
mock: new LocalFileThemeService("themes.json"),
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export function getThemeService(event?: H3Event): IThemeService {
|
|
11
|
+
const key = resolveServiceKey(event, {
|
|
12
|
+
domain: "themeService",
|
|
13
|
+
mockKey: "mock",
|
|
14
|
+
});
|
|
15
|
+
return serviceRegistry[key] ?? serviceRegistry["mock"]!;
|
|
16
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Client } from "typesense";
|
|
2
|
+
import type { ConfigurationOptions } from "typesense";
|
|
3
|
+
|
|
4
|
+
let client: Client | null = null;
|
|
5
|
+
|
|
6
|
+
export function useTypesense(): Client {
|
|
7
|
+
if (client) {
|
|
8
|
+
return client;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const config = useRuntimeConfig();
|
|
12
|
+
|
|
13
|
+
const options: ConfigurationOptions = {
|
|
14
|
+
apiKey: config.typesenseApiKey as string,
|
|
15
|
+
nodes: [
|
|
16
|
+
{
|
|
17
|
+
host: config.typesenseHost as string,
|
|
18
|
+
port: config.typesensePort as number,
|
|
19
|
+
protocol: config.typesenseProtocol as string,
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
connectionTimeoutSeconds: 5,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
client = new Client(options);
|
|
26
|
+
return client;
|
|
27
|
+
}
|