@vendure-platform/create 0.5.0 → 1.0.0-major.202609091053.0.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/CHANGELOG.md +4 -0
- package/README.md +53 -41
- package/dist/add-to-existing.d.ts +19 -13
- package/dist/add-to-existing.js +1 -1
- package/dist/assets/demo-data/assets/branding/logo.svg +4 -0
- package/dist/assets/demo-data/assets/images/absorbent-pad.webp +0 -0
- package/dist/assets/demo-data/assets/images/adjustable-wrench.webp +0 -0
- package/dist/assets/demo-data/assets/images/anchor-bolt.webp +0 -0
- package/dist/assets/demo-data/assets/images/angle-grinder.webp +0 -0
- package/dist/assets/demo-data/assets/images/barrier-tape.webp +0 -0
- package/dist/assets/demo-data/assets/images/bench-grinder.webp +0 -0
- package/dist/assets/demo-data/assets/images/bracket.webp +0 -0
- package/dist/assets/demo-data/assets/images/broom.webp +0 -0
- package/dist/assets/demo-data/assets/images/bucket.webp +0 -0
- package/dist/assets/demo-data/assets/images/cable-gland.webp +0 -0
- package/dist/assets/demo-data/assets/images/cable-tie.webp +0 -0
- package/dist/assets/demo-data/assets/images/cable-tray.webp +0 -0
- package/dist/assets/demo-data/assets/images/cable.webp +0 -0
- package/dist/assets/demo-data/assets/images/chimney-base-kit.webp +0 -0
- package/dist/assets/demo-data/assets/images/chimney-cowl-bird-guard.webp +0 -0
- package/dist/assets/demo-data/assets/images/chimney-cowl-open.webp +0 -0
- package/dist/assets/demo-data/assets/images/chimney-storm-collar.webp +0 -0
- package/dist/assets/demo-data/assets/images/chimney-wall-bracket.webp +0 -0
- package/dist/assets/demo-data/assets/images/circular-saw.webp +0 -0
- package/dist/assets/demo-data/assets/images/cleaning-spray.webp +0 -0
- package/dist/assets/demo-data/assets/images/conduit.webp +0 -0
- package/dist/assets/demo-data/assets/images/cordless-drill.webp +0 -0
- package/dist/assets/demo-data/assets/images/drill-bits.webp +0 -0
- package/dist/assets/demo-data/assets/images/dust-mask.webp +0 -0
- package/dist/assets/demo-data/assets/images/ear-defenders.webp +0 -0
- package/dist/assets/demo-data/assets/images/ear-plugs.webp +0 -0
- package/dist/assets/demo-data/assets/images/extension-lead.webp +0 -0
- package/dist/assets/demo-data/assets/images/eye-wash-station.webp +0 -0
- package/dist/assets/demo-data/assets/images/face-shield.webp +0 -0
- package/dist/assets/demo-data/assets/images/file-set.webp +0 -0
- package/dist/assets/demo-data/assets/images/first-aid-kit.webp +0 -0
- package/dist/assets/demo-data/assets/images/floor-sign.webp +0 -0
- package/dist/assets/demo-data/assets/images/flue-bend-45.webp +0 -0
- package/dist/assets/demo-data/assets/images/flue-bend-90.webp +0 -0
- package/dist/assets/demo-data/assets/images/flue-section-1000mm.webp +0 -0
- package/dist/assets/demo-data/assets/images/flue-section-250mm.webp +0 -0
- package/dist/assets/demo-data/assets/images/flue-section-500mm.webp +0 -0
- package/dist/assets/demo-data/assets/images/fuse.webp +0 -0
- package/dist/assets/demo-data/assets/images/glove-dispenser.webp +0 -0
- package/dist/assets/demo-data/assets/images/grinding-disc.webp +0 -0
- package/dist/assets/demo-data/assets/images/hacksaw.webp +0 -0
- package/dist/assets/demo-data/assets/images/hammer.webp +0 -0
- package/dist/assets/demo-data/assets/images/hand-sanitiser.webp +0 -0
- package/dist/assets/demo-data/assets/images/head-torch.webp +0 -0
- package/dist/assets/demo-data/assets/images/heat-gun.webp +0 -0
- package/dist/assets/demo-data/assets/images/hex-bolt.webp +0 -0
- package/dist/assets/demo-data/assets/images/hex-key-set.webp +0 -0
- package/dist/assets/demo-data/assets/images/hex-nut.webp +0 -0
- package/dist/assets/demo-data/assets/images/hi-vis-jacket.webp +0 -0
- package/dist/assets/demo-data/assets/images/hi-vis-vest.webp +0 -0
- package/dist/assets/demo-data/assets/images/hose-clamp.webp +0 -0
- package/dist/assets/demo-data/assets/images/impact-driver.webp +0 -0
- package/dist/assets/demo-data/assets/images/jigsaw.webp +0 -0
- package/dist/assets/demo-data/assets/images/junction-box.webp +0 -0
- package/dist/assets/demo-data/assets/images/knee-pads.webp +0 -0
- package/dist/assets/demo-data/assets/images/machine-screw.webp +0 -0
- package/dist/assets/demo-data/assets/images/made-to-order-configurable-chimney.webp +0 -0
- package/dist/assets/demo-data/assets/images/mallet.webp +0 -0
- package/dist/assets/demo-data/assets/images/mcb.webp +0 -0
- package/dist/assets/demo-data/assets/images/mop.webp +0 -0
- package/dist/assets/demo-data/assets/images/paper-towels.webp +0 -0
- package/dist/assets/demo-data/assets/images/pipe-wrench.webp +0 -0
- package/dist/assets/demo-data/assets/images/pliers-set.webp +0 -0
- package/dist/assets/demo-data/assets/images/pry-bar.webp +0 -0
- package/dist/assets/demo-data/assets/images/rcd.webp +0 -0
- package/dist/assets/demo-data/assets/images/reciprocating-saw.webp +0 -0
- package/dist/assets/demo-data/assets/images/rivet.webp +0 -0
- package/dist/assets/demo-data/assets/images/roof-flashing-high-pitch.webp +0 -0
- package/dist/assets/demo-data/assets/images/roof-flashing-low-pitch.webp +0 -0
- package/dist/assets/demo-data/assets/images/safety-boots.webp +0 -0
- package/dist/assets/demo-data/assets/images/safety-glasses.webp +0 -0
- package/dist/assets/demo-data/assets/images/safety-harness.webp +0 -0
- package/dist/assets/demo-data/assets/images/safety-helmet.webp +0 -0
- package/dist/assets/demo-data/assets/images/safety-sign.webp +0 -0
- package/dist/assets/demo-data/assets/images/sanding-disc.webp +0 -0
- package/dist/assets/demo-data/assets/images/saw-blade.webp +0 -0
- package/dist/assets/demo-data/assets/images/screwdriver-set.webp +0 -0
- package/dist/assets/demo-data/assets/images/screwdriver.webp +0 -0
- package/dist/assets/demo-data/assets/images/self-tapping-screw.webp +0 -0
- package/dist/assets/demo-data/assets/images/socket-set.webp +0 -0
- package/dist/assets/demo-data/assets/images/socket.webp +0 -0
- package/dist/assets/demo-data/assets/images/spanner-set.webp +0 -0
- package/dist/assets/demo-data/assets/images/spanner.webp +0 -0
- package/dist/assets/demo-data/assets/images/spill-kit.webp +0 -0
- package/dist/assets/demo-data/assets/images/spirit-level.webp +0 -0
- package/dist/assets/demo-data/assets/images/switch.webp +0 -0
- package/dist/assets/demo-data/assets/images/tape-measure.webp +0 -0
- package/dist/assets/demo-data/assets/images/terminal-block.webp +0 -0
- package/dist/assets/demo-data/assets/images/threaded-rod.webp +0 -0
- package/dist/assets/demo-data/assets/images/torque-wrench.webp +0 -0
- package/dist/assets/demo-data/assets/images/utility-knife.webp +0 -0
- package/dist/assets/demo-data/assets/images/wall-pass-sleeve.webp +0 -0
- package/dist/assets/demo-data/assets/images/wall-plug.webp +0 -0
- package/dist/assets/demo-data/assets/images/washer.webp +0 -0
- package/dist/assets/demo-data/assets/images/waste-bin.webp +0 -0
- package/dist/assets/demo-data/assets/images/wire-connector.webp +0 -0
- package/dist/assets/demo-data/assets/images/wire-cutters.webp +0 -0
- package/dist/assets/demo-data/assets/images/wood-screw.webp +0 -0
- package/dist/assets/demo-data/assets/images/work-gloves.webp +0 -0
- package/dist/assets/demo-data/assets/images/work-light.webp +0 -0
- package/dist/assets/demo-data/assets/initial-data.json +667 -0
- package/dist/assets/demo-data/assets/products.csv +779 -0
- package/dist/assets/demo-data/initial-data.json +663 -663
- package/dist/assets/storefront/components.json +20 -20
- package/dist/assets/storefront/eslint.config.mjs +10 -10
- package/dist/assets/storefront/graphql.config.yml +1 -1
- package/dist/assets/storefront/next-env.d.ts +2 -1
- package/dist/assets/storefront/next.config.ts +19 -17
- package/dist/assets/storefront/package.json +3 -2
- package/dist/assets/storefront/postcss.config.mjs +1 -1
- package/dist/assets/storefront/src/app/[locale]/(catalog)/catalog/page.tsx +17 -21
- package/dist/assets/storefront/src/app/[locale]/(catalog)/collection/[slug]/page.tsx +64 -42
- package/dist/assets/storefront/src/app/[locale]/(catalog)/layout.tsx +1 -5
- package/dist/assets/storefront/src/app/[locale]/(catalog)/product/[slug]/actions.ts +69 -65
- package/dist/assets/storefront/src/app/[locale]/(catalog)/product/[slug]/page.tsx +64 -38
- package/dist/assets/storefront/src/app/[locale]/(catalog)/search/page.tsx +24 -27
- package/dist/assets/storefront/src/app/[locale]/(catalog)/search/search-results.tsx +11 -9
- package/dist/assets/storefront/src/app/[locale]/(catalog)/search/search-term.tsx +4 -4
- package/dist/assets/storefront/src/app/[locale]/(scenario)/[...path]/page.tsx +1 -1
- package/dist/assets/storefront/src/app/[locale]/accept-invitation/[token]/accept-invitation-client.tsx +8 -13
- package/dist/assets/storefront/src/app/[locale]/accept-invitation/[token]/actions.ts +11 -14
- package/dist/assets/storefront/src/app/[locale]/accept-invitation/[token]/page.tsx +30 -33
- package/dist/assets/storefront/src/app/[locale]/accept-invitation/[token]/sign-out-and-retry.tsx +6 -6
- package/dist/assets/storefront/src/app/[locale]/account/addresses/actions.ts +30 -45
- package/dist/assets/storefront/src/app/[locale]/account/addresses/address-form.tsx +166 -159
- package/dist/assets/storefront/src/app/[locale]/account/addresses/addresses-client.tsx +35 -26
- package/dist/assets/storefront/src/app/[locale]/account/addresses/loading.tsx +1 -3
- package/dist/assets/storefront/src/app/[locale]/account/addresses/page.tsx +18 -18
- package/dist/assets/storefront/src/app/[locale]/account/approvals/actions.ts +5 -8
- package/dist/assets/storefront/src/app/[locale]/account/approvals/page.tsx +79 -69
- package/dist/assets/storefront/src/app/[locale]/account/company/actions.ts +10 -11
- package/dist/assets/storefront/src/app/[locale]/account/company/addresses/actions.ts +6 -15
- package/dist/assets/storefront/src/app/[locale]/account/company/addresses/address-form.tsx +57 -49
- package/dist/assets/storefront/src/app/[locale]/account/company/addresses/addresses-client.tsx +23 -25
- package/dist/assets/storefront/src/app/[locale]/account/company/addresses/page.tsx +30 -30
- package/dist/assets/storefront/src/app/[locale]/account/company/business-units/actions.ts +20 -29
- package/dist/assets/storefront/src/app/[locale]/account/company/business-units/business-unit-form.tsx +32 -37
- package/dist/assets/storefront/src/app/[locale]/account/company/business-units/business-units-client.tsx +32 -25
- package/dist/assets/storefront/src/app/[locale]/account/company/business-units/page.tsx +26 -24
- package/dist/assets/storefront/src/app/[locale]/account/company/business-units/types.ts +7 -4
- package/dist/assets/storefront/src/app/[locale]/account/company/edit-company-form.tsx +11 -21
- package/dist/assets/storefront/src/app/[locale]/account/company/page.tsx +25 -28
- package/dist/assets/storefront/src/app/[locale]/account/company/payment-methods/page.tsx +26 -24
- package/dist/assets/storefront/src/app/[locale]/account/company/payment-methods/payment-methods-client.tsx +5 -7
- package/dist/assets/storefront/src/app/[locale]/account/company/roles/page.tsx +24 -25
- package/dist/assets/storefront/src/app/[locale]/account/company/roles/roles-client.tsx +11 -26
- package/dist/assets/storefront/src/app/[locale]/account/company/tax-ids/actions.ts +6 -10
- package/dist/assets/storefront/src/app/[locale]/account/company/tax-ids/page.tsx +18 -16
- package/dist/assets/storefront/src/app/[locale]/account/company/tax-ids/tax-ids-client.tsx +74 -67
- package/dist/assets/storefront/src/app/[locale]/account/company/users/actions.ts +16 -25
- package/dist/assets/storefront/src/app/[locale]/account/company/users/edit-user-form.tsx +27 -25
- package/dist/assets/storefront/src/app/[locale]/account/company/users/invite-user-form.tsx +27 -19
- package/dist/assets/storefront/src/app/[locale]/account/company/users/page.tsx +34 -29
- package/dist/assets/storefront/src/app/[locale]/account/company/users/users-client.tsx +35 -38
- package/dist/assets/storefront/src/app/[locale]/account/error.tsx +1 -3
- package/dist/assets/storefront/src/app/[locale]/account/layout.tsx +16 -22
- package/dist/assets/storefront/src/app/[locale]/account/orders/[code]/page.tsx +151 -103
- package/dist/assets/storefront/src/app/[locale]/account/orders/[code]/reorder-actions.ts +6 -7
- package/dist/assets/storefront/src/app/[locale]/account/orders/[code]/reorder-dialog.tsx +22 -48
- package/dist/assets/storefront/src/app/[locale]/account/orders/loading.tsx +8 -8
- package/dist/assets/storefront/src/app/[locale]/account/orders/page.tsx +101 -61
- package/dist/assets/storefront/src/app/[locale]/account/profile/actions.ts +32 -23
- package/dist/assets/storefront/src/app/[locale]/account/profile/change-password-form.tsx +6 -16
- package/dist/assets/storefront/src/app/[locale]/account/profile/edit-email-form.tsx +7 -17
- package/dist/assets/storefront/src/app/[locale]/account/profile/edit-profile-form.tsx +6 -16
- package/dist/assets/storefront/src/app/[locale]/account/profile/loading.tsx +12 -18
- package/dist/assets/storefront/src/app/[locale]/account/profile/page.tsx +17 -15
- package/dist/assets/storefront/src/app/[locale]/account/quick-order/file-upload-tab.tsx +2 -6
- package/dist/assets/storefront/src/app/[locale]/account/quick-order/manual-entry-tab.tsx +18 -15
- package/dist/assets/storefront/src/app/[locale]/account/quick-order/page.tsx +5 -6
- package/dist/assets/storefront/src/app/[locale]/account/quick-order/preview-table.tsx +10 -2
- package/dist/assets/storefront/src/app/[locale]/account/quick-order/save-to-list-dialog.tsx +24 -26
- package/dist/assets/storefront/src/app/[locale]/account/quick-order/sku-autocomplete.tsx +19 -10
- package/dist/assets/storefront/src/app/[locale]/account/quick-order/text-input-tab.tsx +3 -8
- package/dist/assets/storefront/src/app/[locale]/account/quotes/[id]/page.tsx +93 -62
- package/dist/assets/storefront/src/app/[locale]/account/quotes/actions.ts +38 -45
- package/dist/assets/storefront/src/app/[locale]/account/quotes/new/new-quote-form.tsx +3 -5
- package/dist/assets/storefront/src/app/[locale]/account/quotes/new/page.tsx +6 -6
- package/dist/assets/storefront/src/app/[locale]/account/quotes/page.tsx +92 -59
- package/dist/assets/storefront/src/app/[locale]/account/shared-carts/[id]/add-item-search.tsx +11 -9
- package/dist/assets/storefront/src/app/[locale]/account/shared-carts/[id]/add-member-form.tsx +31 -15
- package/dist/assets/storefront/src/app/[locale]/account/shared-carts/[id]/members-panel.tsx +52 -33
- package/dist/assets/storefront/src/app/[locale]/account/shared-carts/[id]/page.tsx +22 -14
- package/dist/assets/storefront/src/app/[locale]/account/shared-carts/[id]/rename-dialog.tsx +34 -13
- package/dist/assets/storefront/src/app/[locale]/account/shared-carts/actions.ts +117 -72
- package/dist/assets/storefront/src/app/[locale]/account/shared-carts/new/new-shared-cart-form.tsx +21 -14
- package/dist/assets/storefront/src/app/[locale]/account/shared-carts/new/page.tsx +10 -10
- package/dist/assets/storefront/src/app/[locale]/account/shared-carts/page.tsx +54 -41
- package/dist/assets/storefront/src/app/[locale]/account/shopping-lists/[id]/page.tsx +22 -8
- package/dist/assets/storefront/src/app/[locale]/account/shopping-lists/[id]/shopping-list-detail-client.tsx +62 -49
- package/dist/assets/storefront/src/app/[locale]/account/shopping-lists/actions.ts +9 -4
- package/dist/assets/storefront/src/app/[locale]/account/shopping-lists/create-shopping-list-form.tsx +5 -11
- package/dist/assets/storefront/src/app/[locale]/account/shopping-lists/loading.tsx +1 -1
- package/dist/assets/storefront/src/app/[locale]/account/shopping-lists/page.tsx +32 -42
- package/dist/assets/storefront/src/app/[locale]/account/store-credits/actions.ts +8 -8
- package/dist/assets/storefront/src/app/[locale]/account/store-credits/claim-form.tsx +6 -10
- package/dist/assets/storefront/src/app/[locale]/account/store-credits/page.tsx +76 -55
- package/dist/assets/storefront/src/app/[locale]/account/store-credits/reveal-code.tsx +10 -6
- package/dist/assets/storefront/src/app/[locale]/account/verify-email/page.tsx +32 -39
- package/dist/assets/storefront/src/app/[locale]/cart/actions.ts +29 -23
- package/dist/assets/storefront/src/app/[locale]/cart/cart-items.tsx +111 -40
- package/dist/assets/storefront/src/app/[locale]/cart/cart-line-actions.tsx +9 -9
- package/dist/assets/storefront/src/app/[locale]/cart/cart-quantity-control.tsx +61 -48
- package/dist/assets/storefront/src/app/[locale]/cart/cart-remove-button.tsx +15 -9
- package/dist/assets/storefront/src/app/[locale]/cart/cart.tsx +38 -41
- package/dist/assets/storefront/src/app/[locale]/cart/code-input.tsx +8 -8
- package/dist/assets/storefront/src/app/[locale]/cart/order-summary.tsx +56 -35
- package/dist/assets/storefront/src/app/[locale]/cart/page.tsx +11 -11
- package/dist/assets/storefront/src/app/[locale]/cart/promotion-code.tsx +56 -47
- package/dist/assets/storefront/src/app/[locale]/cart/reapproval-confirm.tsx +22 -20
- package/dist/assets/storefront/src/app/[locale]/cart/request-quote-button.tsx +7 -16
- package/dist/assets/storefront/src/app/[locale]/checkout/actions.ts +39 -65
- package/dist/assets/storefront/src/app/[locale]/checkout/cancel-quote-checkout-button.tsx +5 -5
- package/dist/assets/storefront/src/app/[locale]/checkout/checkout-flow.tsx +202 -202
- package/dist/assets/storefront/src/app/[locale]/checkout/checkout-provider.tsx +142 -142
- package/dist/assets/storefront/src/app/[locale]/checkout/order-summary.tsx +158 -139
- package/dist/assets/storefront/src/app/[locale]/checkout/page.tsx +58 -47
- package/dist/assets/storefront/src/app/[locale]/checkout/steps/delivery-step.tsx +86 -88
- package/dist/assets/storefront/src/app/[locale]/checkout/steps/payment-step.tsx +90 -91
- package/dist/assets/storefront/src/app/[locale]/checkout/steps/review-step.tsx +166 -161
- package/dist/assets/storefront/src/app/[locale]/checkout/steps/shipping-address-step.tsx +573 -494
- package/dist/assets/storefront/src/app/[locale]/error.tsx +3 -5
- package/dist/assets/storefront/src/app/[locale]/forgot-password/actions.ts +10 -7
- package/dist/assets/storefront/src/app/[locale]/forgot-password/forgot-password-form.tsx +10 -25
- package/dist/assets/storefront/src/app/[locale]/forgot-password/page.tsx +5 -5
- package/dist/assets/storefront/src/app/[locale]/globals.css +48 -48
- package/dist/assets/storefront/src/app/[locale]/layout.tsx +56 -50
- package/dist/assets/storefront/src/app/[locale]/not-found.tsx +3 -5
- package/dist/assets/storefront/src/app/[locale]/order-confirmation/[code]/order-confirmation.tsx +71 -102
- package/dist/assets/storefront/src/app/[locale]/order-confirmation/[code]/page.tsx +7 -5
- package/dist/assets/storefront/src/app/[locale]/page.tsx +33 -35
- package/dist/assets/storefront/src/app/[locale]/register/actions.ts +7 -7
- package/dist/assets/storefront/src/app/[locale]/register/page.tsx +22 -16
- package/dist/assets/storefront/src/app/[locale]/register/registration-form.tsx +22 -34
- package/dist/assets/storefront/src/app/[locale]/register-company/actions.ts +15 -19
- package/dist/assets/storefront/src/app/[locale]/register-company/page.tsx +17 -15
- package/dist/assets/storefront/src/app/[locale]/register-company/registration-form.tsx +9 -15
- package/dist/assets/storefront/src/app/[locale]/reset-password/actions.ts +7 -8
- package/dist/assets/storefront/src/app/[locale]/reset-password/page.tsx +18 -12
- package/dist/assets/storefront/src/app/[locale]/reset-password/reset-password-form.tsx +8 -16
- package/dist/assets/storefront/src/app/[locale]/shopping-list/[code]/add-to-cart-button.tsx +10 -4
- package/dist/assets/storefront/src/app/[locale]/shopping-list/[code]/page.tsx +62 -30
- package/dist/assets/storefront/src/app/[locale]/sign-in/actions.ts +14 -16
- package/dist/assets/storefront/src/app/[locale]/sign-in/login-form.tsx +17 -30
- package/dist/assets/storefront/src/app/[locale]/sign-in/page.tsx +35 -30
- package/dist/assets/storefront/src/app/[locale]/verify/actions.ts +8 -8
- package/dist/assets/storefront/src/app/[locale]/verify/page.tsx +18 -14
- package/dist/assets/storefront/src/app/[locale]/verify/verify-content.tsx +25 -27
- package/dist/assets/storefront/src/app/[locale]/verify-pending/page.tsx +23 -23
- package/dist/assets/storefront/src/app/api/clear-company-context/route.ts +6 -3
- package/dist/assets/storefront/src/app/api/revalidate/route.ts +19 -28
- package/dist/assets/storefront/src/app/api/search-analytics/route.ts +1 -1
- package/dist/assets/storefront/src/components/commerce/account-nav-items.ts +43 -18
- package/dist/assets/storefront/src/components/commerce/account-sidebar.tsx +30 -22
- package/dist/assets/storefront/src/components/commerce/active-filters.tsx +1 -1
- package/dist/assets/storefront/src/components/commerce/add-to-shared-cart.tsx +49 -27
- package/dist/assets/storefront/src/components/commerce/approval-decision-form.tsx +9 -13
- package/dist/assets/storefront/src/components/commerce/approval-status-badge.tsx +23 -15
- package/dist/assets/storefront/src/components/commerce/business-unit-switcher.tsx +11 -11
- package/dist/assets/storefront/src/components/commerce/catalog-search-bar.tsx +2 -2
- package/dist/assets/storefront/src/components/commerce/category-product-section.tsx +28 -16
- package/dist/assets/storefront/src/components/commerce/category-sidebar.tsx +31 -18
- package/dist/assets/storefront/src/components/commerce/collection-hero-banner.tsx +2 -4
- package/dist/assets/storefront/src/components/commerce/company-state-badge.tsx +10 -16
- package/dist/assets/storefront/src/components/commerce/company-switcher.tsx +12 -12
- package/dist/assets/storefront/src/components/commerce/configurator-form.tsx +188 -79
- package/dist/assets/storefront/src/components/commerce/facet-filters.tsx +29 -49
- package/dist/assets/storefront/src/components/commerce/gift-card-pdp.tsx +4 -4
- package/dist/assets/storefront/src/components/commerce/gift-card-product-info.tsx +20 -30
- package/dist/assets/storefront/src/components/commerce/instant-search-dropdown.tsx +258 -227
- package/dist/assets/storefront/src/components/commerce/order-status-badge.tsx +27 -19
- package/dist/assets/storefront/src/components/commerce/packaging-unit-selector.tsx +27 -26
- package/dist/assets/storefront/src/components/commerce/permission-gate.tsx +4 -4
- package/dist/assets/storefront/src/components/commerce/price-range-filter.tsx +5 -5
- package/dist/assets/storefront/src/components/commerce/price-tiers.tsx +38 -21
- package/dist/assets/storefront/src/components/commerce/price.tsx +3 -3
- package/dist/assets/storefront/src/components/commerce/product-card-list.tsx +39 -16
- package/dist/assets/storefront/src/components/commerce/product-card.tsx +35 -18
- package/dist/assets/storefront/src/components/commerce/product-carousel.tsx +20 -12
- package/dist/assets/storefront/src/components/commerce/product-grid.tsx +14 -13
- package/dist/assets/storefront/src/components/commerce/product-image-carousel.tsx +10 -8
- package/dist/assets/storefront/src/components/commerce/product-info.tsx +118 -74
- package/dist/assets/storefront/src/components/commerce/product-list-expansion.tsx +102 -74
- package/dist/assets/storefront/src/components/commerce/product-list-view.tsx +1 -1
- package/dist/assets/storefront/src/components/commerce/product-sidebar.tsx +36 -11
- package/dist/assets/storefront/src/components/commerce/product-view-switcher.tsx +33 -21
- package/dist/assets/storefront/src/components/commerce/quote-action-buttons.tsx +80 -45
- package/dist/assets/storefront/src/components/commerce/quote-activity-timeline.tsx +126 -50
- package/dist/assets/storefront/src/components/commerce/quote-comment-form.tsx +9 -13
- package/dist/assets/storefront/src/components/commerce/quote-csv-export-button.tsx +6 -6
- package/dist/assets/storefront/src/components/commerce/quote-product-search.tsx +22 -18
- package/dist/assets/storefront/src/components/commerce/quote-request-form.tsx +28 -27
- package/dist/assets/storefront/src/components/commerce/quote-status-badge.tsx +26 -18
- package/dist/assets/storefront/src/components/commerce/quote-validity-indicator.tsx +7 -7
- package/dist/assets/storefront/src/components/commerce/quote-version-comparison.tsx +50 -30
- package/dist/assets/storefront/src/components/commerce/related-products.tsx +37 -28
- package/dist/assets/storefront/src/components/commerce/shared-cart-status-badge.tsx +12 -8
- package/dist/assets/storefront/src/components/commerce/shop-by-category.tsx +8 -7
- package/dist/assets/storefront/src/components/commerce/shopping-list-toggle.tsx +14 -20
- package/dist/assets/storefront/src/components/commerce/sort-dropdown.tsx +9 -16
- package/dist/assets/storefront/src/components/commerce/store-credit-summary.tsx +30 -32
- package/dist/assets/storefront/src/components/commerce/subcollection-tiles.tsx +11 -13
- package/dist/assets/storefront/src/components/layout/auth-page-shell.tsx +2 -2
- package/dist/assets/storefront/src/components/layout/draft-mode-indicator.tsx +2 -7
- package/dist/assets/storefront/src/components/layout/draft-mode-link.tsx +4 -2
- package/dist/assets/storefront/src/components/layout/footer.tsx +70 -28
- package/dist/assets/storefront/src/components/layout/navbar/actions.ts +9 -12
- package/dist/assets/storefront/src/components/layout/navbar/cart-icon.tsx +9 -9
- package/dist/assets/storefront/src/components/layout/navbar/channel-picker-wrapper.tsx +4 -9
- package/dist/assets/storefront/src/components/layout/navbar/channel-picker.tsx +13 -8
- package/dist/assets/storefront/src/components/layout/navbar/currency-picker-wrapper.tsx +4 -4
- package/dist/assets/storefront/src/components/layout/navbar/currency-picker.tsx +11 -6
- package/dist/assets/storefront/src/components/layout/navbar/language-picker.tsx +7 -7
- package/dist/assets/storefront/src/components/layout/navbar/login-button.tsx +24 -21
- package/dist/assets/storefront/src/components/layout/navbar/mobile-nav.tsx +8 -8
- package/dist/assets/storefront/src/components/layout/navbar/nav-menu-items.tsx +4 -6
- package/dist/assets/storefront/src/components/layout/navbar/navbar-cart.tsx +1 -1
- package/dist/assets/storefront/src/components/layout/navbar/navbar-collections.tsx +41 -42
- package/dist/assets/storefront/src/components/layout/navbar/navbar-link.tsx +7 -10
- package/dist/assets/storefront/src/components/layout/navbar/navbar-user.tsx +18 -23
- package/dist/assets/storefront/src/components/layout/navbar/theme-switcher.tsx +9 -9
- package/dist/assets/storefront/src/components/layout/navbar.tsx +36 -31
- package/dist/assets/storefront/src/components/layout/search-input.tsx +35 -32
- package/dist/assets/storefront/src/components/providers/awaiting-approval-banner.tsx +12 -10
- package/dist/assets/storefront/src/components/providers/cart-count-server.tsx +14 -12
- package/dist/assets/storefront/src/components/providers/company-context-guard.tsx +4 -4
- package/dist/assets/storefront/src/components/providers/company-permissions-server.tsx +2 -6
- package/dist/assets/storefront/src/components/providers/theme-provider.tsx +3 -3
- package/dist/assets/storefront/src/components/shared/breadcrumbs.tsx +4 -1
- package/dist/assets/storefront/src/components/shared/country-select.tsx +63 -67
- package/dist/assets/storefront/src/components/shared/pagination.tsx +15 -28
- package/dist/assets/storefront/src/components/shared/prefetch-link.tsx +1 -1
- package/dist/assets/storefront/src/components/shared/skeletons/search-input-skeleton.tsx +1 -3
- package/dist/assets/storefront/src/components/ui/accordion.tsx +46 -51
- package/dist/assets/storefront/src/components/ui/alert-dialog.tsx +94 -121
- package/dist/assets/storefront/src/components/ui/alert.tsx +48 -54
- package/dist/assets/storefront/src/components/ui/aspect-ratio.tsx +5 -7
- package/dist/assets/storefront/src/components/ui/avatar.tsx +29 -44
- package/dist/assets/storefront/src/components/ui/badge.tsx +28 -37
- package/dist/assets/storefront/src/components/ui/breadcrumb.tsx +82 -89
- package/dist/assets/storefront/src/components/ui/button.tsx +43 -49
- package/dist/assets/storefront/src/components/ui/calendar.tsx +157 -198
- package/dist/assets/storefront/src/components/ui/card.tsx +53 -76
- package/dist/assets/storefront/src/components/ui/carousel.tsx +194 -212
- package/dist/assets/storefront/src/components/ui/chart.tsx +274 -317
- package/dist/assets/storefront/src/components/ui/checkbox.tsx +24 -27
- package/dist/assets/storefront/src/components/ui/collapsible.tsx +9 -21
- package/dist/assets/storefront/src/components/ui/command.tsx +114 -159
- package/dist/assets/storefront/src/components/ui/context-menu.tsx +167 -199
- package/dist/assets/storefront/src/components/ui/dialog.tsx +97 -114
- package/dist/assets/storefront/src/components/ui/drawer.tsx +93 -107
- package/dist/assets/storefront/src/components/ui/dropdown-menu.tsx +171 -206
- package/dist/assets/storefront/src/components/ui/empty-state.tsx +5 -5
- package/dist/assets/storefront/src/components/ui/field.tsx +196 -208
- package/dist/assets/storefront/src/components/ui/form.tsx +115 -139
- package/dist/assets/storefront/src/components/ui/hover-card.tsx +27 -33
- package/dist/assets/storefront/src/components/ui/input-otp.tsx +50 -59
- package/dist/assets/storefront/src/components/ui/input.tsx +17 -17
- package/dist/assets/storefront/src/components/ui/kbd-shortcut.tsx +3 -1
- package/dist/assets/storefront/src/components/ui/label.tsx +16 -19
- package/dist/assets/storefront/src/components/ui/menubar.tsx +193 -223
- package/dist/assets/storefront/src/components/ui/navigation-menu.tsx +123 -130
- package/dist/assets/storefront/src/components/ui/pagination.tsx +87 -108
- package/dist/assets/storefront/src/components/ui/popover.tsx +29 -35
- package/dist/assets/storefront/src/components/ui/progress.tsx +19 -26
- package/dist/assets/storefront/src/components/ui/radio-group.tsx +32 -38
- package/dist/assets/storefront/src/components/ui/resizable.tsx +37 -42
- package/dist/assets/storefront/src/components/ui/scroll-area.tsx +41 -47
- package/dist/assets/storefront/src/components/ui/select.tsx +130 -152
- package/dist/assets/storefront/src/components/ui/separator.tsx +21 -21
- package/dist/assets/storefront/src/components/ui/sheet.tsx +90 -111
- package/dist/assets/storefront/src/components/ui/sidebar.tsx +611 -649
- package/dist/assets/storefront/src/components/ui/skeleton.tsx +10 -10
- package/dist/assets/storefront/src/components/ui/slider.tsx +50 -55
- package/dist/assets/storefront/src/components/ui/sonner.tsx +20 -20
- package/dist/assets/storefront/src/components/ui/switch.tsx +23 -26
- package/dist/assets/storefront/src/components/ui/table.tsx +65 -97
- package/dist/assets/storefront/src/components/ui/tabs.tsx +39 -55
- package/dist/assets/storefront/src/components/ui/textarea.tsx +14 -14
- package/dist/assets/storefront/src/components/ui/toggle-group.tsx +56 -62
- package/dist/assets/storefront/src/components/ui/toggle.tsx +37 -38
- package/dist/assets/storefront/src/components/ui/tooltip.tsx +38 -46
- package/dist/assets/storefront/src/config/capabilities.ts +41 -0
- package/dist/assets/storefront/src/config/scenario-meta.ts +2 -2
- package/dist/assets/storefront/src/config/scenario.ts +24 -16
- package/dist/assets/storefront/src/contexts/auth-context.tsx +5 -6
- package/dist/assets/storefront/src/graphql-env.d.ts +47 -41
- package/dist/assets/storefront/src/graphql.ts +5 -5
- package/dist/assets/storefront/src/hooks/use-mobile.ts +13 -13
- package/dist/assets/storefront/src/i18n/locale-utils.ts +3 -3
- package/dist/assets/storefront/src/i18n/navigation.ts +4 -4
- package/dist/assets/storefront/src/i18n/request.ts +4 -4
- package/dist/assets/storefront/src/i18n/routing.ts +5 -5
- package/dist/assets/storefront/src/i18n/server.ts +35 -7
- package/dist/assets/storefront/src/lib/actions/switch-channel.ts +9 -9
- package/dist/assets/storefront/src/lib/actions/switch-currency.ts +7 -7
- package/dist/assets/storefront/src/lib/auth.ts +1 -1
- package/dist/assets/storefront/src/lib/backend-health.ts +14 -6
- package/dist/assets/storefront/src/lib/cart-count-context.tsx +10 -4
- package/dist/assets/storefront/src/lib/cart-guard-utils.ts +2 -1
- package/dist/assets/storefront/src/lib/cart-guard.ts +7 -6
- package/dist/assets/storefront/src/lib/channel-server.ts +4 -4
- package/dist/assets/storefront/src/lib/company-context.ts +1 -1
- package/dist/assets/storefront/src/lib/company-permissions-context.tsx +1 -5
- package/dist/assets/storefront/src/lib/currency-server.ts +4 -4
- package/dist/assets/storefront/src/lib/format.ts +16 -7
- package/dist/assets/storefront/src/lib/metadata.ts +39 -44
- package/dist/assets/storefront/src/lib/quantity.ts +2 -4
- package/dist/assets/storefront/src/lib/search-bar-context.tsx +7 -4
- package/dist/assets/storefront/src/lib/use-recent-searches.ts +8 -6
- package/dist/assets/storefront/src/lib/utils.ts +3 -3
- package/dist/assets/storefront/src/lib/vendure/actions.ts +18 -16
- package/dist/assets/storefront/src/lib/vendure/api.ts +36 -23
- package/dist/assets/storefront/src/lib/vendure/approval-utils.ts +9 -8
- package/dist/assets/storefront/src/lib/vendure/cached.ts +20 -15
- package/dist/assets/storefront/src/lib/vendure/capability-documents.ts +246 -0
- package/dist/assets/storefront/src/lib/vendure/capability-schema.ts +358 -0
- package/dist/assets/storefront/src/lib/vendure/capability-ui.ts +15 -0
- package/dist/assets/storefront/src/lib/vendure/mutations.ts +835 -532
- package/dist/assets/storefront/src/lib/vendure/queries.ts +877 -524
- package/dist/assets/storefront/src/proxy.ts +11 -0
- package/dist/assets/storefront/src/scenarios/customer/hero.tsx +2 -2
- package/dist/assets/storefront/src/scenarios/customer/meta.ts +1 -1
- package/dist/assets/storefront/src/schema.graphql +337 -243
- package/dist/assets/templates/README.md.hbs +25 -1
- package/dist/assets/templates/docker-compose.yml.hbs +4 -0
- package/dist/assets/templates/env.hbs +34 -9
- package/dist/assets/templates/environment.d.ts.hbs +1 -0
- package/dist/assets/templates/gitignore.hbs +7 -4
- package/dist/assets/templates/migrate.ts.hbs +55 -0
- package/dist/assets/templates/npmrc.hbs +7 -2
- package/dist/assets/templates/platform-edition.ts.hbs +21 -0
- package/dist/assets/templates/server-package.json.hbs +15 -8
- package/dist/assets/templates/vendure-config.ts.hbs +18 -0
- package/dist/bin/run.js +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +1 -1
- package/dist/console-onboarding.d.ts +44 -0
- package/dist/console-onboarding.js +1 -0
- package/dist/hub-projects.d.ts +48 -0
- package/dist/hub-projects.js +1 -0
- package/dist/index.d.ts +5 -5
- package/dist/index.js +1 -1
- package/dist/plugin-registry.d.ts +61 -2
- package/dist/plugin-registry.js +1 -1
- package/dist/registry.d.ts +16 -13
- package/dist/registry.js +1 -1
- package/dist/scaffold-shared.js +1 -1
- package/dist/scaffold-storefront-only.js +1 -1
- package/dist/scaffolder.d.ts +11 -0
- package/dist/scaffolder.js +1 -1
- package/dist/storefront-copier.d.ts +7 -2
- package/dist/storefront-copier.js +1 -1
- package/dist/template-renderer.d.ts +26 -5
- package/dist/template-renderer.js +1 -1
- package/dist/types.d.ts +54 -23
- package/package.json +14 -2
- package/dist/assets/storefront/src/middleware.ts +0 -11
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { useCallback, useState } from 'react';
|
|
4
4
|
|
|
5
5
|
const STORAGE_KEY = 'vendure-recent-searches';
|
|
6
6
|
const MAX_RECENT = 5;
|
|
@@ -11,7 +11,9 @@ function readFromStorage(): string[] {
|
|
|
11
11
|
const raw = localStorage.getItem(STORAGE_KEY);
|
|
12
12
|
if (!raw) return [];
|
|
13
13
|
const parsed = JSON.parse(raw);
|
|
14
|
-
return Array.isArray(parsed)
|
|
14
|
+
return Array.isArray(parsed)
|
|
15
|
+
? parsed.filter((s): s is string => typeof s === 'string').slice(0, MAX_RECENT)
|
|
16
|
+
: [];
|
|
15
17
|
} catch {
|
|
16
18
|
return [];
|
|
17
19
|
}
|
|
@@ -31,16 +33,16 @@ export function useRecentSearches() {
|
|
|
31
33
|
const addSearch = useCallback((term: string) => {
|
|
32
34
|
const trimmed = term.trim();
|
|
33
35
|
if (!trimmed) return;
|
|
34
|
-
setSearches(prev => {
|
|
35
|
-
const next = [trimmed, ...prev.filter(s => s !== trimmed)].slice(0, MAX_RECENT);
|
|
36
|
+
setSearches((prev) => {
|
|
37
|
+
const next = [trimmed, ...prev.filter((s) => s !== trimmed)].slice(0, MAX_RECENT);
|
|
36
38
|
writeToStorage(next);
|
|
37
39
|
return next;
|
|
38
40
|
});
|
|
39
41
|
}, []);
|
|
40
42
|
|
|
41
43
|
const removeSearch = useCallback((term: string) => {
|
|
42
|
-
setSearches(prev => {
|
|
43
|
-
const next = prev.filter(s => s !== term);
|
|
44
|
+
setSearches((prev) => {
|
|
45
|
+
const next = prev.filter((s) => s !== term);
|
|
44
46
|
writeToStorage(next);
|
|
45
47
|
return next;
|
|
46
48
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { clsx, type ClassValue } from
|
|
2
|
-
import { twMerge } from
|
|
1
|
+
import { clsx, type ClassValue } from 'clsx';
|
|
2
|
+
import { twMerge } from 'tailwind-merge';
|
|
3
3
|
|
|
4
4
|
export function cn(...inputs: ClassValue[]) {
|
|
5
|
-
|
|
5
|
+
return twMerge(clsx(inputs));
|
|
6
6
|
}
|
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {GetActiveCustomerQuery, GetCompanyAccountQuery, GetMyCompaniesQuery, type CompanyUserCompany} from './queries';
|
|
3
|
-
import {getActiveChannelCached} from './cached';
|
|
4
|
-
import {cache} from "react";
|
|
5
|
-
import {readFragment, ResultOf} from "@/graphql";
|
|
1
|
+
import { readFragment, ResultOf } from '@/graphql';
|
|
6
2
|
import {
|
|
7
3
|
ActiveCustomerFragment,
|
|
8
4
|
CompanyBusinessUnitFragment,
|
|
@@ -10,7 +6,16 @@ import {
|
|
|
10
6
|
CompanyPermissionFragment,
|
|
11
7
|
CompanyRoleFragment,
|
|
12
8
|
CompanyUserFragment,
|
|
13
|
-
} from
|
|
9
|
+
} from '@/lib/vendure/fragments';
|
|
10
|
+
import { cache } from 'react';
|
|
11
|
+
import { client } from './api';
|
|
12
|
+
import { getActiveChannelCached } from './cached';
|
|
13
|
+
import {
|
|
14
|
+
GetActiveCustomerQuery,
|
|
15
|
+
GetCompanyAccountQuery,
|
|
16
|
+
GetMyCompaniesQuery,
|
|
17
|
+
type CompanyUserCompany,
|
|
18
|
+
} from './queries';
|
|
14
19
|
|
|
15
20
|
type CompanyRoleData = Omit<ResultOf<typeof CompanyRoleFragment>, 'permissions'> & {
|
|
16
21
|
permissions: ResultOf<typeof CompanyPermissionFragment>[];
|
|
@@ -34,7 +39,7 @@ export const getActiveCustomer = cache(async () => {
|
|
|
34
39
|
return null;
|
|
35
40
|
}
|
|
36
41
|
return readFragment(ActiveCustomerFragment, result.data.activeCustomer);
|
|
37
|
-
})
|
|
42
|
+
});
|
|
38
43
|
|
|
39
44
|
export const getCompanyAccount = cache(async (): Promise<CompanyAccount | null> => {
|
|
40
45
|
const [result, error] = await client.query(GetCompanyAccountQuery);
|
|
@@ -52,14 +57,14 @@ export const getCompanyAccount = cache(async (): Promise<CompanyAccount | null>
|
|
|
52
57
|
|
|
53
58
|
const companyUser: CompanyUserData = {
|
|
54
59
|
...rawUser,
|
|
55
|
-
companyRoles: rawUser.companyRoles.map(r => {
|
|
60
|
+
companyRoles: rawUser.companyRoles.map((r) => {
|
|
56
61
|
const role = readFragment(CompanyRoleFragment, r);
|
|
57
62
|
return {
|
|
58
63
|
...role,
|
|
59
|
-
permissions: role.permissions.map(p => readFragment(CompanyPermissionFragment, p)),
|
|
64
|
+
permissions: role.permissions.map((p) => readFragment(CompanyPermissionFragment, p)),
|
|
60
65
|
};
|
|
61
66
|
}),
|
|
62
|
-
businessUnits: rawUser.businessUnits.map(bu => readFragment(CompanyBusinessUnitFragment, bu)),
|
|
67
|
+
businessUnits: rawUser.businessUnits.map((bu) => readFragment(CompanyBusinessUnitFragment, bu)),
|
|
63
68
|
};
|
|
64
69
|
|
|
65
70
|
return {
|
|
@@ -76,8 +81,8 @@ export const getMyCompanies = cache(async (): Promise<CompanyUserCompany[]> => {
|
|
|
76
81
|
const customer = result.data.activeCustomer;
|
|
77
82
|
if (!customer) return [];
|
|
78
83
|
return customer.companyUsers
|
|
79
|
-
.filter(cu => cu.state === 'Active')
|
|
80
|
-
.map(cu => ({ id: cu.company.id, name: cu.company.name, state: cu.company.state }));
|
|
84
|
+
.filter((cu) => cu.state === 'Active')
|
|
85
|
+
.map((cu) => ({ id: cu.company.id, name: cu.company.name, state: cu.company.state }));
|
|
81
86
|
});
|
|
82
87
|
|
|
83
88
|
/**
|
|
@@ -86,10 +91,7 @@ export const getMyCompanies = cache(async (): Promise<CompanyUserCompany[]> => {
|
|
|
86
91
|
* to getMyCompanies (which includes pending companies via CompanyUser state).
|
|
87
92
|
*/
|
|
88
93
|
export const getHasCompany = cache(async (): Promise<boolean> => {
|
|
89
|
-
const [companyAccount, companies] = await Promise.all([
|
|
90
|
-
getCompanyAccount(),
|
|
91
|
-
getMyCompanies(),
|
|
92
|
-
]);
|
|
94
|
+
const [companyAccount, companies] = await Promise.all([getCompanyAccount(), getMyCompanies()]);
|
|
93
95
|
return !!companyAccount || companies.length > 0;
|
|
94
96
|
});
|
|
95
97
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import type { RequestMiddleware } from '@vendure-platform/storefront-client';
|
|
2
|
+
import { createClient } from '@vendure-platform/storefront-client';
|
|
3
3
|
|
|
4
4
|
const DRAFT_MODE_SECRET = process.env.DRAFT_MODE_SECRET;
|
|
5
5
|
|
|
@@ -11,7 +11,7 @@ const DRAFT_MODE_SECRET = process.env.DRAFT_MODE_SECRET;
|
|
|
11
11
|
const draftModeMiddleware: RequestMiddleware = async (req, next) => {
|
|
12
12
|
if (DRAFT_MODE_SECRET) {
|
|
13
13
|
try {
|
|
14
|
-
const {draftMode} = await import('next/headers');
|
|
14
|
+
const { draftMode } = await import('next/headers');
|
|
15
15
|
const draft = await draftMode();
|
|
16
16
|
if (draft.isEnabled) {
|
|
17
17
|
const url = new URL(req.url);
|
|
@@ -26,35 +26,44 @@ const draftModeMiddleware: RequestMiddleware = async (req, next) => {
|
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
const VENDURE_API_URL = process.env.VENDURE_SHOP_API_URL || process.env.NEXT_PUBLIC_VENDURE_SHOP_API_URL;
|
|
29
|
-
const VENDURE_CHANNEL_TOKEN =
|
|
29
|
+
const VENDURE_CHANNEL_TOKEN =
|
|
30
|
+
process.env.VENDURE_CHANNEL_TOKEN || process.env.NEXT_PUBLIC_VENDURE_CHANNEL_TOKEN;
|
|
30
31
|
const VENDURE_AUTH_TOKEN_HEADER = process.env.VENDURE_AUTH_TOKEN_HEADER || 'vendure-auth-token';
|
|
31
32
|
const VENDURE_CHANNEL_TOKEN_HEADER = process.env.VENDURE_CHANNEL_TOKEN_HEADER || 'vendure-token';
|
|
32
33
|
|
|
33
34
|
if (!VENDURE_API_URL) {
|
|
34
|
-
throw new Error(
|
|
35
|
+
throw new Error(
|
|
36
|
+
'VENDURE_SHOP_API_URL or NEXT_PUBLIC_VENDURE_SHOP_API_URL environment variable is not set',
|
|
37
|
+
);
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
// Dynamic imports to avoid Next.js static analysis marking this module as dynamic.
|
|
38
41
|
// cookies() is only available in request context — providers gracefully return undefined otherwise.
|
|
39
42
|
async function safeGetAuthToken(): Promise<string | undefined> {
|
|
40
43
|
try {
|
|
41
|
-
const {getAuthToken} = await import('@/lib/auth');
|
|
44
|
+
const { getAuthToken } = await import('@/lib/auth');
|
|
42
45
|
return await getAuthToken();
|
|
43
|
-
} catch {
|
|
46
|
+
} catch {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
44
49
|
}
|
|
45
50
|
|
|
46
51
|
async function safeGetCompanyId(): Promise<string | undefined> {
|
|
47
52
|
try {
|
|
48
|
-
const {getCompanyId} = await import('@/lib/company-context');
|
|
53
|
+
const { getCompanyId } = await import('@/lib/company-context');
|
|
49
54
|
return await getCompanyId();
|
|
50
|
-
} catch {
|
|
55
|
+
} catch {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
51
58
|
}
|
|
52
59
|
|
|
53
60
|
async function safeGetBusinessUnitId(): Promise<string | undefined> {
|
|
54
61
|
try {
|
|
55
|
-
const {getBusinessUnitId} = await import('@/lib/company-context');
|
|
62
|
+
const { getBusinessUnitId } = await import('@/lib/company-context');
|
|
56
63
|
return await getBusinessUnitId();
|
|
57
|
-
} catch {
|
|
64
|
+
} catch {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
58
67
|
}
|
|
59
68
|
|
|
60
69
|
/**
|
|
@@ -65,16 +74,20 @@ async function safeGetBusinessUnitId(): Promise<string | undefined> {
|
|
|
65
74
|
*/
|
|
66
75
|
async function safeGetChannelToken(): Promise<string | undefined> {
|
|
67
76
|
try {
|
|
68
|
-
const {getActiveChannelToken} = await import('@/lib/channel-server');
|
|
77
|
+
const { getActiveChannelToken } = await import('@/lib/channel-server');
|
|
69
78
|
return await getActiveChannelToken();
|
|
70
|
-
} catch {
|
|
79
|
+
} catch {
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
71
82
|
}
|
|
72
83
|
|
|
73
84
|
async function safeGetLanguageCode(): Promise<string | undefined> {
|
|
74
85
|
try {
|
|
75
|
-
const {getLocale} = await import('next-intl/server');
|
|
86
|
+
const { getLocale } = await import('next-intl/server');
|
|
76
87
|
return await getLocale();
|
|
77
|
-
} catch {
|
|
88
|
+
} catch {
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
78
91
|
}
|
|
79
92
|
|
|
80
93
|
const sharedConfig = {
|
|
@@ -95,7 +108,7 @@ const sharedConfig = {
|
|
|
95
108
|
*/
|
|
96
109
|
const currencyMiddleware: RequestMiddleware = async (req, next) => {
|
|
97
110
|
try {
|
|
98
|
-
const {getActiveCurrencyCode} = await import('@/lib/currency-server');
|
|
111
|
+
const { getActiveCurrencyCode } = await import('@/lib/currency-server');
|
|
99
112
|
const currency = await getActiveCurrencyCode();
|
|
100
113
|
const url = new URL(req.url);
|
|
101
114
|
url.searchParams.set('currencyCode', currency);
|
|
@@ -130,7 +143,7 @@ export const client = createClient({
|
|
|
130
143
|
* access cookies, headers, or draftMode. Pass languageCode and currencyCode explicitly
|
|
131
144
|
* as query options from the calling function.
|
|
132
145
|
*/
|
|
133
|
-
export const publicClient = createClient({...sharedConfig});
|
|
146
|
+
export const publicClient = createClient({ ...sharedConfig });
|
|
134
147
|
|
|
135
148
|
/**
|
|
136
149
|
* Pre-resolved context for use at cache boundaries where cookies() is not available.
|
|
@@ -149,20 +162,20 @@ export interface VendureContext {
|
|
|
149
162
|
* Use this at cache boundaries ('use cache') where providers can't access cookies().
|
|
150
163
|
*/
|
|
151
164
|
export async function getVendureContext(): Promise<VendureContext> {
|
|
152
|
-
const {getAuthToken} = await import('@/lib/auth');
|
|
165
|
+
const { getAuthToken } = await import('@/lib/auth');
|
|
153
166
|
const authToken = await getAuthToken();
|
|
154
167
|
let companyId: string | undefined;
|
|
155
168
|
let businessUnitId: string | undefined;
|
|
156
169
|
try {
|
|
157
|
-
const {getCompanyId, getBusinessUnitId} = await import('@/lib/company-context');
|
|
170
|
+
const { getCompanyId, getBusinessUnitId } = await import('@/lib/company-context');
|
|
158
171
|
companyId = await getCompanyId();
|
|
159
172
|
businessUnitId = await getBusinessUnitId();
|
|
160
173
|
} catch {}
|
|
161
|
-
const {getActiveChannelToken} = await import('@/lib/channel-server');
|
|
174
|
+
const { getActiveChannelToken } = await import('@/lib/channel-server');
|
|
162
175
|
const channelToken = await getActiveChannelToken();
|
|
163
|
-
const {getRouteLocale} = await import('@/i18n/server');
|
|
176
|
+
const { getRouteLocale } = await import('@/i18n/server');
|
|
164
177
|
const locale = await getRouteLocale();
|
|
165
|
-
const {getActiveCurrencyCode} = await import('@/lib/currency-server');
|
|
178
|
+
const { getActiveCurrencyCode } = await import('@/lib/currency-server');
|
|
166
179
|
const currencyCode = await getActiveCurrencyCode();
|
|
167
|
-
return {authToken, channelToken, companyId, businessUnitId, locale, currencyCode};
|
|
180
|
+
return { authToken, channelToken, companyId, businessUnitId, locale, currencyCode };
|
|
168
181
|
}
|
|
@@ -1,21 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import {GetApprovalHistoryForEntityQuery} from '@/lib/vendure/queries';
|
|
1
|
+
import type { VendureContext } from '@/lib/vendure/api';
|
|
2
|
+
import { client } from '@/lib/vendure/api';
|
|
3
|
+
import { GetApprovalHistoryForEntityQuery } from '@/lib/vendure/queries';
|
|
4
4
|
|
|
5
5
|
export async function getLatestApprovalForOrder(orderId: string, ctx?: VendureContext) {
|
|
6
6
|
const queryArgs = { entityType: 'Order', entityId: orderId } as const;
|
|
7
7
|
const [result] = ctx
|
|
8
8
|
? await client.query(GetApprovalHistoryForEntityQuery, queryArgs, ctx)
|
|
9
9
|
: await client.query(GetApprovalHistoryForEntityQuery, queryArgs);
|
|
10
|
-
const history = [...(result?.data?.approvalHistoryForEntity ?? [])]
|
|
11
|
-
|
|
10
|
+
const history = [...(result?.data?.approvalHistoryForEntity ?? [])].sort(
|
|
11
|
+
(a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(),
|
|
12
|
+
);
|
|
12
13
|
const latestApproval = history[0] ?? null;
|
|
13
14
|
const changesRequested = latestApproval?.status === 'CHANGES_REQUESTED';
|
|
14
15
|
const changesComment = changesRequested
|
|
15
16
|
? latestApproval.decisions
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
?.filter((d: any) => d.decision === 'CHANGES_REQUESTED' && d.comment)
|
|
18
|
+
.map((d: any) => d.comment)
|
|
19
|
+
.at(-1) ?? null
|
|
19
20
|
: null;
|
|
20
21
|
return { latestApproval, changesRequested, changesComment };
|
|
21
22
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {cacheLife, cacheTag} from 'next/cache';
|
|
2
|
-
import {publicClient} from './api';
|
|
3
|
-
import {GetActiveChannelQuery, GetAvailableCountriesQuery, GetTopCollectionsQuery} from './queries';
|
|
1
|
+
import { cacheLife, cacheTag } from 'next/cache';
|
|
2
|
+
import { publicClient } from './api';
|
|
3
|
+
import { GetActiveChannelQuery, GetAvailableCountriesQuery, GetTopCollectionsQuery } from './queries';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Get the active channel with caching enabled.
|
|
@@ -19,11 +19,18 @@ export async function getActiveChannelCached(channelToken?: string) {
|
|
|
19
19
|
const [result, error] = await publicClient.query(
|
|
20
20
|
GetActiveChannelQuery,
|
|
21
21
|
undefined,
|
|
22
|
-
channelToken ? {channelToken} : undefined,
|
|
22
|
+
channelToken ? { channelToken } : undefined,
|
|
23
23
|
);
|
|
24
24
|
if (error) {
|
|
25
25
|
// Vendure API unavailable during build
|
|
26
|
-
return {
|
|
26
|
+
return {
|
|
27
|
+
id: '',
|
|
28
|
+
code: '',
|
|
29
|
+
defaultLanguageCode: 'en' as const,
|
|
30
|
+
availableLanguageCodes: [],
|
|
31
|
+
defaultCurrencyCode: 'USD' as const,
|
|
32
|
+
availableCurrencyCodes: [],
|
|
33
|
+
};
|
|
27
34
|
}
|
|
28
35
|
return result.data.activeChannel;
|
|
29
36
|
}
|
|
@@ -38,11 +45,10 @@ export async function getAvailableCountriesCached(locale: string, channelToken?:
|
|
|
38
45
|
cacheLife('max');
|
|
39
46
|
cacheTag(`countries-${locale}${channelToken ? `-${channelToken}` : ''}`);
|
|
40
47
|
|
|
41
|
-
const [result, error] = await publicClient.query(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
);
|
|
48
|
+
const [result, error] = await publicClient.query(GetAvailableCountriesQuery, undefined, {
|
|
49
|
+
languageCode: locale,
|
|
50
|
+
...(channelToken ? { channelToken } : {}),
|
|
51
|
+
});
|
|
46
52
|
if (error) {
|
|
47
53
|
return [];
|
|
48
54
|
}
|
|
@@ -59,11 +65,10 @@ export async function getTopCollections(locale: string, channelToken?: string) {
|
|
|
59
65
|
cacheLife('days');
|
|
60
66
|
cacheTag(`collections-${locale}${channelToken ? `-${channelToken}` : ''}`);
|
|
61
67
|
|
|
62
|
-
const [result, error] = await publicClient.query(
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
);
|
|
68
|
+
const [result, error] = await publicClient.query(GetTopCollectionsQuery, undefined, {
|
|
69
|
+
languageCode: locale,
|
|
70
|
+
...(channelToken ? { channelToken } : {}),
|
|
71
|
+
});
|
|
67
72
|
if (error) {
|
|
68
73
|
return [];
|
|
69
74
|
}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { hasCapability } from '@/config/capabilities';
|
|
2
|
+
import type {
|
|
3
|
+
DefinitionNode,
|
|
4
|
+
DocumentNode,
|
|
5
|
+
FieldNode,
|
|
6
|
+
FragmentDefinitionNode,
|
|
7
|
+
SelectionNode,
|
|
8
|
+
SelectionSetNode,
|
|
9
|
+
} from 'graphql';
|
|
10
|
+
import { visit } from 'graphql';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A selection to drop, written as the path of field names from the operation
|
|
14
|
+
* root, e.g. `['product', 'customFields', 'configuratorSystemCode']`.
|
|
15
|
+
*/
|
|
16
|
+
export type FieldPath = readonly string[];
|
|
17
|
+
|
|
18
|
+
export interface CapabilityDocumentRule {
|
|
19
|
+
capability: string;
|
|
20
|
+
paths: readonly FieldPath[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface CapabilityDocumentRegistration<T extends DocumentNode = DocumentNode> {
|
|
24
|
+
name: string;
|
|
25
|
+
fullDocument: T;
|
|
26
|
+
rules: readonly CapabilityDocumentRule[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Capability-owned storefront documents, used by the schema composition tests. */
|
|
30
|
+
export const STOREFRONT_CAPABILITY_DOCUMENTS: CapabilityDocumentRegistration[] = [];
|
|
31
|
+
|
|
32
|
+
function isField(selection: SelectionNode): selection is FieldNode {
|
|
33
|
+
return selection.kind === 'Field';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function fieldName(field: FieldNode): string {
|
|
37
|
+
return field.name.value;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function addOperationFallback<T extends DocumentNode>(document: T): T {
|
|
41
|
+
return {
|
|
42
|
+
...document,
|
|
43
|
+
definitions: document.definitions.map((definition) => {
|
|
44
|
+
if (definition.kind !== 'OperationDefinition') return definition;
|
|
45
|
+
if (
|
|
46
|
+
definition.selectionSet.selections.some(
|
|
47
|
+
(selection) => isField(selection) && fieldName(selection) === '__typename',
|
|
48
|
+
)
|
|
49
|
+
) {
|
|
50
|
+
return definition;
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
...definition,
|
|
54
|
+
selectionSet: {
|
|
55
|
+
...definition.selectionSet,
|
|
56
|
+
selections: [
|
|
57
|
+
...definition.selectionSet.selections,
|
|
58
|
+
{ kind: 'Field', name: { kind: 'Name', value: '__typename' } },
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}),
|
|
63
|
+
} as T;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function fragmentSpreads(selectionSet: SelectionSetNode): string[] {
|
|
67
|
+
const names: string[] = [];
|
|
68
|
+
for (const selection of selectionSet.selections) {
|
|
69
|
+
if (selection.kind === 'FragmentSpread') {
|
|
70
|
+
names.push(selection.name.value);
|
|
71
|
+
} else if (selection.kind === 'Field' && selection.selectionSet) {
|
|
72
|
+
names.push(...fragmentSpreads(selection.selectionSet));
|
|
73
|
+
} else if (selection.kind === 'InlineFragment') {
|
|
74
|
+
names.push(...fragmentSpreads(selection.selectionSet));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return names;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function removeUnusedFragments(definitions: readonly DefinitionNode[]): readonly DefinitionNode[] {
|
|
81
|
+
const fragments = new Map<string, FragmentDefinitionNode>();
|
|
82
|
+
for (const definition of definitions) {
|
|
83
|
+
if (definition.kind === 'FragmentDefinition') {
|
|
84
|
+
fragments.set(definition.name.value, definition);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const used = new Set<string>();
|
|
89
|
+
const pending = definitions
|
|
90
|
+
.filter((definition) => definition.kind === 'OperationDefinition')
|
|
91
|
+
.flatMap((definition) => fragmentSpreads(definition.selectionSet));
|
|
92
|
+
while (pending.length > 0) {
|
|
93
|
+
const name = pending.pop()!;
|
|
94
|
+
if (used.has(name)) continue;
|
|
95
|
+
used.add(name);
|
|
96
|
+
const fragment = fragments.get(name);
|
|
97
|
+
if (fragment) pending.push(...fragmentSpreads(fragment.selectionSet));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return definitions.filter(
|
|
101
|
+
(definition) => definition.kind !== 'FragmentDefinition' || used.has(definition.name.value),
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function removeUnusedVariables(definitions: readonly DefinitionNode[]): readonly DefinitionNode[] {
|
|
106
|
+
const used = new Set<string>();
|
|
107
|
+
visit({ kind: 'Document', definitions } as DocumentNode, {
|
|
108
|
+
VariableDefinition() {
|
|
109
|
+
return false;
|
|
110
|
+
},
|
|
111
|
+
Variable(node) {
|
|
112
|
+
used.add(node.name.value);
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
return definitions.map((definition) => {
|
|
117
|
+
if (definition.kind !== 'OperationDefinition' || !definition.variableDefinitions) {
|
|
118
|
+
return definition;
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
...definition,
|
|
122
|
+
variableDefinitions: definition.variableDefinitions.filter((variable) =>
|
|
123
|
+
used.has(variable.variable.name.value),
|
|
124
|
+
),
|
|
125
|
+
};
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Returns `selectionSet` with `path` removed, or `null` when removing it would
|
|
131
|
+
* leave an empty selection set — GraphQL has no syntax for one, so the caller
|
|
132
|
+
* drops the parent field instead.
|
|
133
|
+
*/
|
|
134
|
+
function stripPath(selectionSet: SelectionSetNode, path: FieldPath): SelectionSetNode | null {
|
|
135
|
+
const [head, ...rest] = path;
|
|
136
|
+
const selections: SelectionNode[] = [];
|
|
137
|
+
|
|
138
|
+
for (const selection of selectionSet.selections) {
|
|
139
|
+
if (!isField(selection) || fieldName(selection) !== head) {
|
|
140
|
+
selections.push(selection);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (rest.length === 0) {
|
|
144
|
+
// Leaf reached: drop it by not re-adding it.
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if (!selection.selectionSet) {
|
|
148
|
+
selections.push(selection);
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
const nested = stripPath(selection.selectionSet, rest);
|
|
152
|
+
if (nested) {
|
|
153
|
+
selections.push({ ...selection, selectionSet: nested });
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return selections.length > 0 ? { ...selectionSet, selections } : null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Removes capability-gated selections from a GraphQL document.
|
|
162
|
+
*
|
|
163
|
+
* The server composes its schema from the capability modules the deployment is
|
|
164
|
+
* entitled to, so selecting a field owned by an absent module fails validation
|
|
165
|
+
* on every request. Rather than maintaining a hand-written copy of each
|
|
166
|
+
* document per composition, the full document stays the single typed source of
|
|
167
|
+
* truth and the selections a composition cannot serve are pruned from it here.
|
|
168
|
+
*
|
|
169
|
+
* The returned document keeps the input's TypeScript type: a pruned field is
|
|
170
|
+
* `undefined` at runtime, so consumers must treat capability-gated data as
|
|
171
|
+
* optional. That is the same contract they already need for a field the server
|
|
172
|
+
* may legitimately return as null.
|
|
173
|
+
*/
|
|
174
|
+
export function stripCapabilityFields<T extends DocumentNode>(document: T, paths: FieldPath[]): T {
|
|
175
|
+
if (paths.length === 0) {
|
|
176
|
+
return document;
|
|
177
|
+
}
|
|
178
|
+
let definitions = document.definitions;
|
|
179
|
+
for (const path of paths) {
|
|
180
|
+
definitions = definitions.map((definition) => {
|
|
181
|
+
if (definition.kind !== 'OperationDefinition' && definition.kind !== 'FragmentDefinition') {
|
|
182
|
+
return definition;
|
|
183
|
+
}
|
|
184
|
+
const stripped = stripPath(definition.selectionSet, path);
|
|
185
|
+
if (!stripped) {
|
|
186
|
+
// Every selection collapsed, which GraphQL cannot express. A
|
|
187
|
+
// prune list that empties a whole operation is a mistake in the
|
|
188
|
+
// capability mapping, not something to paper over by keeping
|
|
189
|
+
// the field that was supposed to be removed.
|
|
190
|
+
throw new Error(
|
|
191
|
+
`Pruning "${path.join('.')}" would leave an empty selection set. ` +
|
|
192
|
+
'Split the document instead of pruning its only selection.',
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
return { ...definition, selectionSet: stripped };
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
const usedDefinitions = removeUnusedFragments(definitions);
|
|
199
|
+
return { ...document, definitions: removeUnusedVariables(usedDefinitions) } as T;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Convenience wrapper: prunes `paths` only when `granted` is false.
|
|
204
|
+
*/
|
|
205
|
+
export function whenGranted<T extends DocumentNode>(granted: boolean, document: T, paths: FieldPath[]): T {
|
|
206
|
+
return granted ? document : stripCapabilityFields(addOperationFallback(document), paths);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Registers and composes one typed storefront document from capability rules.
|
|
211
|
+
* The full typed document remains the source of truth. Missing grants prune
|
|
212
|
+
* only the selections that their modules own.
|
|
213
|
+
*/
|
|
214
|
+
export function composeCapabilityDocument<T extends DocumentNode>(
|
|
215
|
+
document: T,
|
|
216
|
+
rules: readonly CapabilityDocumentRule[],
|
|
217
|
+
): T {
|
|
218
|
+
const operation = document.definitions.find((definition) => definition.kind === 'OperationDefinition');
|
|
219
|
+
const name = operation?.name?.value;
|
|
220
|
+
if (!name) {
|
|
221
|
+
throw new Error('Capability-composed storefront documents must use a named operation.');
|
|
222
|
+
}
|
|
223
|
+
STOREFRONT_CAPABILITY_DOCUMENTS.push({ name, fullDocument: document, rules });
|
|
224
|
+
return composeCapabilityDocumentFor(document, rules, (capability) => hasCapability(capability));
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/** Compose a registered document for an explicit grant set in tests. */
|
|
228
|
+
export function composeCapabilityDocumentFor<T extends DocumentNode>(
|
|
229
|
+
document: T,
|
|
230
|
+
rules: readonly CapabilityDocumentRule[],
|
|
231
|
+
granted: (capability: string) => boolean,
|
|
232
|
+
): T {
|
|
233
|
+
return rules.reduce(
|
|
234
|
+
(composed, rule) => whenGranted(granted(rule.capability), composed, [...rule.paths]),
|
|
235
|
+
document,
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/** Registers a document owned by one capability. */
|
|
240
|
+
export function forCapability<T extends DocumentNode>(
|
|
241
|
+
capability: string,
|
|
242
|
+
document: T,
|
|
243
|
+
paths: readonly FieldPath[],
|
|
244
|
+
): T {
|
|
245
|
+
return composeCapabilityDocument(document, [{ capability, paths }]);
|
|
246
|
+
}
|