@laioutr/app-shopware 0.14.4
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.md +21 -0
- package/README.md +146 -0
- package/dist/module.d.mts +88 -0
- package/dist/module.json +9 -0
- package/dist/module.mjs +60 -0
- package/dist/runtime/app/components/ShopwareEmbedFrame.d.vue.ts +14 -0
- package/dist/runtime/app/components/ShopwareEmbedFrame.vue +57 -0
- package/dist/runtime/app/components/ShopwareEmbedFrame.vue.d.ts +14 -0
- package/dist/runtime/app/composables/useShopwareEmbedBridge.d.ts +24 -0
- package/dist/runtime/app/composables/useShopwareEmbedBridge.js +22 -0
- package/dist/runtime/app/const/bridge.d.ts +63 -0
- package/dist/runtime/app/const/bridge.js +9 -0
- package/dist/runtime/app/image/providers/shopware.d.ts +7 -0
- package/dist/runtime/app/image/providers/shopware.js +44 -0
- package/dist/runtime/app/lib/createBridgeHandler.d.ts +44 -0
- package/dist/runtime/app/lib/createBridgeHandler.js +40 -0
- package/dist/runtime/app/lib/isTrustedOrigin.d.ts +9 -0
- package/dist/runtime/app/lib/isTrustedOrigin.js +8 -0
- package/dist/runtime/app/lib/parseBridgeMessage.d.ts +11 -0
- package/dist/runtime/app/lib/parseBridgeMessage.js +34 -0
- package/dist/runtime/app/plugins/pagetypes.d.ts +7 -0
- package/dist/runtime/app/plugins/pagetypes.js +6 -0
- package/dist/runtime/app/public/app-shopware/image-placeholder.svg +7 -0
- package/dist/runtime/app/public/app-shopware/shopware-logo.svg +10 -0
- package/dist/runtime/app/sections/SectionShopwareCheckout.d.vue.ts +0 -0
- package/dist/runtime/app/sections/SectionShopwareCheckout.vue +44 -0
- package/dist/runtime/app/sections/SectionShopwareCheckout.vue.d.ts +0 -0
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/index.js +1 -0
- package/dist/runtime/server/client/shopwareAdminClientFactory.d.ts +4 -0
- package/dist/runtime/server/client/shopwareAdminClientFactory.js +13 -0
- package/dist/runtime/server/client/shopwareClientFactory.d.ts +3 -0
- package/dist/runtime/server/client/shopwareClientFactory.js +18 -0
- package/dist/runtime/server/composable/useGetProductParentId.d.ts +4 -0
- package/dist/runtime/server/composable/useGetProductParentId.js +41 -0
- package/dist/runtime/server/const/checkout.d.ts +7 -0
- package/dist/runtime/server/const/checkout.js +2 -0
- package/dist/runtime/server/const/cookieKeys.d.ts +1 -0
- package/dist/runtime/server/const/cookieKeys.js +1 -0
- package/dist/runtime/server/const/fallbacks.d.ts +5 -0
- package/dist/runtime/server/const/fallbacks.js +14 -0
- package/dist/runtime/server/const/includes.d.ts +1 -0
- package/dist/runtime/server/const/includes.js +1 -0
- package/dist/runtime/server/const/passthroughTokens.d.ts +14 -0
- package/dist/runtime/server/const/passthroughTokens.js +10 -0
- package/dist/runtime/server/media-libraries/shopware.d.ts +20 -0
- package/dist/runtime/server/media-libraries/shopware.js +90 -0
- package/dist/runtime/server/middleware/defineShopware.d.ts +7 -0
- package/dist/runtime/server/middleware/defineShopware.js +33 -0
- package/dist/runtime/server/orchestr/auth/login.action.d.ts +2 -0
- package/dist/runtime/server/orchestr/auth/login.action.js +17 -0
- package/dist/runtime/server/orchestr/cart/add-item.action.d.ts +2 -0
- package/dist/runtime/server/orchestr/cart/add-item.action.js +46 -0
- package/dist/runtime/server/orchestr/cart/base.resolver.d.ts +2 -0
- package/dist/runtime/server/orchestr/cart/base.resolver.js +28 -0
- package/dist/runtime/server/orchestr/cart/cart-items.link.d.ts +2 -0
- package/dist/runtime/server/orchestr/cart/cart-items.link.js +10 -0
- package/dist/runtime/server/orchestr/cart/get-checkout-url.action.d.ts +10 -0
- package/dist/runtime/server/orchestr/cart/get-checkout-url.action.js +6 -0
- package/dist/runtime/server/orchestr/cart/get-current.query.d.ts +2 -0
- package/dist/runtime/server/orchestr/cart/get-current.query.js +9 -0
- package/dist/runtime/server/orchestr/cart/remove-items.action.d.ts +2 -0
- package/dist/runtime/server/orchestr/cart/remove-items.action.js +14 -0
- package/dist/runtime/server/orchestr/cart/update-items.action.d.ts +2 -0
- package/dist/runtime/server/orchestr/cart/update-items.action.js +16 -0
- package/dist/runtime/server/orchestr/cart-item/base.resolver.d.ts +2 -0
- package/dist/runtime/server/orchestr/cart-item/base.resolver.js +33 -0
- package/dist/runtime/server/orchestr/category/all.query.d.ts +2 -0
- package/dist/runtime/server/orchestr/category/all.query.js +15 -0
- package/dist/runtime/server/orchestr/category/base.resolver.d.ts +2 -0
- package/dist/runtime/server/orchestr/category/base.resolver.js +47 -0
- package/dist/runtime/server/orchestr/category/bySlug.query.d.ts +2 -0
- package/dist/runtime/server/orchestr/category/bySlug.query.js +12 -0
- package/dist/runtime/server/orchestr/category/child-categories.link.d.ts +2 -0
- package/dist/runtime/server/orchestr/category/child-categories.link.js +36 -0
- package/dist/runtime/server/orchestr/category/listing-page.page-index.d.ts +2 -0
- package/dist/runtime/server/orchestr/category/listing-page.page-index.js +79 -0
- package/dist/runtime/server/orchestr/category/menu-item.link.d.ts +2 -0
- package/dist/runtime/server/orchestr/category/menu-item.link.js +10 -0
- package/dist/runtime/server/orchestr/menu/base.resolver.d.ts +2 -0
- package/dist/runtime/server/orchestr/menu/base.resolver.js +63 -0
- package/dist/runtime/server/orchestr/menu/byAlias.query.d.ts +2 -0
- package/dist/runtime/server/orchestr/menu/byAlias.query.js +29 -0
- package/dist/runtime/server/orchestr/menu/byAlias.template.d.ts +2 -0
- package/dist/runtime/server/orchestr/menu/byAlias.template.js +25 -0
- package/dist/runtime/server/orchestr/newsletter/subscribe.action.d.ts +2 -0
- package/dist/runtime/server/orchestr/newsletter/subscribe.action.js +27 -0
- package/dist/runtime/server/orchestr/product/base.resolver.d.ts +2 -0
- package/dist/runtime/server/orchestr/product/base.resolver.js +119 -0
- package/dist/runtime/server/orchestr/product/breadcrumb.link.d.ts +2 -0
- package/dist/runtime/server/orchestr/product/breadcrumb.link.js +57 -0
- package/dist/runtime/server/orchestr/product/byCategoryId.query.d.ts +2 -0
- package/dist/runtime/server/orchestr/product/byCategoryId.query.js +47 -0
- package/dist/runtime/server/orchestr/product/byCategorySlug.query.d.ts +2 -0
- package/dist/runtime/server/orchestr/product/byCategorySlug.query.js +59 -0
- package/dist/runtime/server/orchestr/product/byCategorySlug.template.d.ts +2 -0
- package/dist/runtime/server/orchestr/product/byCategorySlug.template.js +40 -0
- package/dist/runtime/server/orchestr/product/bySlug.query.d.ts +2 -0
- package/dist/runtime/server/orchestr/product/bySlug.query.js +19 -0
- package/dist/runtime/server/orchestr/product/detail-page.page-index.d.ts +3 -0
- package/dist/runtime/server/orchestr/product/detail-page.page-index.js +58 -0
- package/dist/runtime/server/orchestr/product/reviews.link.d.ts +2 -0
- package/dist/runtime/server/orchestr/product/reviews.link.js +27 -0
- package/dist/runtime/server/orchestr/product/search.query.d.ts +2 -0
- package/dist/runtime/server/orchestr/product/search.query.js +43 -0
- package/dist/runtime/server/orchestr/product/variants.link.d.ts +2 -0
- package/dist/runtime/server/orchestr/product/variants.link.js +18 -0
- package/dist/runtime/server/orchestr/product-variant/base.resolver.d.ts +2 -0
- package/dist/runtime/server/orchestr/product-variant/base.resolver.js +129 -0
- package/dist/runtime/server/orchestr/review/base.resolver.d.ts +2 -0
- package/dist/runtime/server/orchestr/review/base.resolver.js +38 -0
- package/dist/runtime/server/orchestr/review/create-review.action.d.ts +2 -0
- package/dist/runtime/server/orchestr/review/create-review.action.js +14 -0
- package/dist/runtime/server/orchestr/suggested-search/suggested-search-entry.link.d.ts +2 -0
- package/dist/runtime/server/orchestr/suggested-search/suggested-search-entry.link.js +14 -0
- package/dist/runtime/server/orchestr/suggested-search/suggested-search.query.d.ts +2 -0
- package/dist/runtime/server/orchestr/suggested-search/suggested-search.query.js +54 -0
- package/dist/runtime/server/orchestr/suggested-search-entry/base.resolver.d.ts +2 -0
- package/dist/runtime/server/orchestr/suggested-search-entry/base.resolver.js +22 -0
- package/dist/runtime/server/orchestr-helper/isLinkSingle.d.ts +2 -0
- package/dist/runtime/server/orchestr-helper/isLinkSingle.js +1 -0
- package/dist/runtime/server/orchestr-helper/matchAndMap.d.ts +1 -0
- package/dist/runtime/server/orchestr-helper/matchAndMap.js +15 -0
- package/dist/runtime/server/orchestr-helper/requestedFields.d.ts +18 -0
- package/dist/runtime/server/orchestr-helper/requestedFields.js +145 -0
- package/dist/runtime/server/routes/adopt-session.post.d.ts +8 -0
- package/dist/runtime/server/routes/adopt-session.post.js +16 -0
- package/dist/runtime/server/routes/checkout.d.ts +16 -0
- package/dist/runtime/server/routes/checkout.js +31 -0
- package/dist/runtime/server/shopware-helper/adoptSession.d.ts +18 -0
- package/dist/runtime/server/shopware-helper/adoptSession.js +23 -0
- package/dist/runtime/server/shopware-helper/bootstrapContextToken.d.ts +18 -0
- package/dist/runtime/server/shopware-helper/bootstrapContextToken.js +22 -0
- package/dist/runtime/server/shopware-helper/cartErrors.d.ts +11 -0
- package/dist/runtime/server/shopware-helper/cartErrors.js +21 -0
- package/dist/runtime/server/shopware-helper/cartMapper.d.ts +14 -0
- package/dist/runtime/server/shopware-helper/cartMapper.js +83 -0
- package/dist/runtime/server/shopware-helper/categoryFlattener.d.ts +75 -0
- package/dist/runtime/server/shopware-helper/categoryFlattener.js +11 -0
- package/dist/runtime/server/shopware-helper/checkoutUrl.d.ts +12 -0
- package/dist/runtime/server/shopware-helper/checkoutUrl.js +5 -0
- package/dist/runtime/server/shopware-helper/contextTokenCookie.d.ts +11 -0
- package/dist/runtime/server/shopware-helper/contextTokenCookie.js +7 -0
- package/dist/runtime/server/shopware-helper/facetMapper.d.ts +38 -0
- package/dist/runtime/server/shopware-helper/facetMapper.js +79 -0
- package/dist/runtime/server/shopware-helper/fetchAllProductVariants.d.ts +137 -0
- package/dist/runtime/server/shopware-helper/fetchAllProductVariants.js +16 -0
- package/dist/runtime/server/shopware-helper/getCart.d.ts +10 -0
- package/dist/runtime/server/shopware-helper/getCart.js +4 -0
- package/dist/runtime/server/shopware-helper/isShopwareId.d.ts +4 -0
- package/dist/runtime/server/shopware-helper/isShopwareId.js +2 -0
- package/dist/runtime/server/shopware-helper/mappers/slugMapper.d.ts +10 -0
- package/dist/runtime/server/shopware-helper/mappers/slugMapper.js +9 -0
- package/dist/runtime/server/shopware-helper/mediaMapper.d.ts +8 -0
- package/dist/runtime/server/shopware-helper/mediaMapper.js +42 -0
- package/dist/runtime/server/shopware-helper/pageIndexEntries.d.ts +20 -0
- package/dist/runtime/server/shopware-helper/pageIndexEntries.js +22 -0
- package/dist/runtime/server/shopware-helper/persistContextToken.d.ts +13 -0
- package/dist/runtime/server/shopware-helper/persistContextToken.js +12 -0
- package/dist/runtime/server/shopware-helper/productLocate.d.ts +14 -0
- package/dist/runtime/server/shopware-helper/productLocate.js +9 -0
- package/dist/runtime/server/shopware-helper/readCategoryPageMeta.d.ts +10 -0
- package/dist/runtime/server/shopware-helper/readCategoryPageMeta.js +19 -0
- package/dist/runtime/server/shopware-helper/readProductPageMeta.d.ts +10 -0
- package/dist/runtime/server/shopware-helper/readProductPageMeta.js +20 -0
- package/dist/runtime/server/shopware-helper/resolveAdoptSession.d.ts +23 -0
- package/dist/runtime/server/shopware-helper/resolveAdoptSession.js +13 -0
- package/dist/runtime/server/shopware-helper/resolveCheckout.d.ts +39 -0
- package/dist/runtime/server/shopware-helper/resolveCheckout.js +24 -0
- package/dist/runtime/server/shopware-helper/sessionHandoff.d.ts +27 -0
- package/dist/runtime/server/shopware-helper/sessionHandoff.js +29 -0
- package/dist/runtime/server/shopware-helper/sortingMapper.d.ts +3 -0
- package/dist/runtime/server/shopware-helper/sortingMapper.js +7 -0
- package/dist/runtime/server/shopware-helper/storeApiPageFetcher.d.ts +20 -0
- package/dist/runtime/server/shopware-helper/storeApiPageFetcher.js +10 -0
- package/dist/runtime/server/shopware-helper/swTranslated.d.ts +7 -0
- package/dist/runtime/server/shopware-helper/swTranslated.js +4 -0
- package/dist/runtime/server/shopware-helper/system/getCurrentSystemEntities.d.ts +20 -0
- package/dist/runtime/server/shopware-helper/system/getCurrentSystemEntities.js +39 -0
- package/dist/runtime/server/shopware-helper/system/getSystemEntities.d.ts +31 -0
- package/dist/runtime/server/shopware-helper/system/getSystemEntities.js +47 -0
- package/dist/runtime/server/shopware-helper/useSeoResolver.d.ts +11 -0
- package/dist/runtime/server/shopware-helper/useSeoResolver.js +70 -0
- package/dist/runtime/server/tsconfig.json +3 -0
- package/dist/runtime/server/types/shopware.d.ts +29 -0
- package/dist/runtime/server/types/shopware.js +0 -0
- package/dist/runtime/server/types/storeApiTypes.d.ts +5677 -0
- package/dist/runtime/shared/const/checkout.d.ts +18 -0
- package/dist/runtime/shared/const/checkout.js +2 -0
- package/dist/runtime/shared/pageTypes/checkout.pagetype.d.ts +21 -0
- package/dist/runtime/shared/pageTypes/checkout.pagetype.js +12 -0
- package/dist/types.d.mts +3 -0
- package/package.json +96 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build the redeem URL against the `LaioutrConnector` plugin's
|
|
3
|
+
* `/laioutr/connect-session` endpoint.
|
|
4
|
+
*
|
|
5
|
+
* Only the opaque, single-use `code` (minted server-to-server via
|
|
6
|
+
* {@link mintSessionHandoffCode}) travels through the browser — the raw
|
|
7
|
+
* `sw-context-token` never appears in a URL, history, or `Referer`.
|
|
8
|
+
*/
|
|
9
|
+
export declare const buildConnectSessionUrl: (params: {
|
|
10
|
+
storefrontUrl: string;
|
|
11
|
+
code: string;
|
|
12
|
+
}) => string;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cookie options for the `sw-context-token` cookie on the laioutr domain. `httpOnly` so the
|
|
3
|
+
* token is never readable by client JS (an XSS on laioutr can no longer exfiltrate it).
|
|
4
|
+
* Pure — no `#imports` — so it is unit-testable.
|
|
5
|
+
*/
|
|
6
|
+
export declare const contextTokenCookieOptions: () => {
|
|
7
|
+
maxAge: number;
|
|
8
|
+
path: string;
|
|
9
|
+
sameSite: "lax";
|
|
10
|
+
httpOnly: true;
|
|
11
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { AvailableFilter, QueryWireRequestFilter } from '#orchestr/types';
|
|
2
|
+
export type ShopwareAggregationKey = 'price' | 'shipping-free' | 'manufacturer';
|
|
3
|
+
export interface ShopwareAggregation {
|
|
4
|
+
name: string;
|
|
5
|
+
min?: number;
|
|
6
|
+
max?: number;
|
|
7
|
+
entities?: Array<{
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
}>;
|
|
11
|
+
}
|
|
12
|
+
export type ShopwareAggregations = Record<ShopwareAggregationKey, ShopwareAggregation>;
|
|
13
|
+
export declare const mapShopwareAggregationToAvailableFilters: (facets: ShopwareAggregations) => AvailableFilter[];
|
|
14
|
+
export declare const mapSelectedFiltersToShopwareFilters: (filters: QueryWireRequestFilter, currency: string) => {
|
|
15
|
+
swFilters: ({
|
|
16
|
+
field: string;
|
|
17
|
+
type: "contains" | "equalsAny" | "prefix" | "suffix";
|
|
18
|
+
value: string;
|
|
19
|
+
} | {
|
|
20
|
+
field: string;
|
|
21
|
+
type: "equals";
|
|
22
|
+
value: string | number | boolean | null;
|
|
23
|
+
} | {
|
|
24
|
+
operator: "AND" | "and" | "OR" | "or";
|
|
25
|
+
queries: import("../types/storeApiTypes.js").components["schemas"]["Filters"];
|
|
26
|
+
type: "multi" | "not";
|
|
27
|
+
} | {
|
|
28
|
+
field: string;
|
|
29
|
+
parameters: {
|
|
30
|
+
gt?: number;
|
|
31
|
+
gte?: number;
|
|
32
|
+
lt?: number;
|
|
33
|
+
lte?: number;
|
|
34
|
+
};
|
|
35
|
+
type: "range";
|
|
36
|
+
})[];
|
|
37
|
+
swBuiltInFilters: Record<"manufacturer" | "shipping-free" | "min-price" | "max-price", string | number | boolean | undefined>;
|
|
38
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { isRangeFilter } from "#orchestr/types";
|
|
2
|
+
import { Money } from "@screeny05/ts-money";
|
|
3
|
+
export const mapShopwareAggregationToAvailableFilters = (facets) => {
|
|
4
|
+
const filters = [];
|
|
5
|
+
const listFacets = ["manufacturer"];
|
|
6
|
+
const rangeFacets = ["price"];
|
|
7
|
+
const booleanFacets = ["shipping-free"];
|
|
8
|
+
for (const facet in facets) {
|
|
9
|
+
const facetKey = facet;
|
|
10
|
+
const facetObj = facets[facet];
|
|
11
|
+
if (listFacets.includes(facetKey)) {
|
|
12
|
+
filters.push({
|
|
13
|
+
id: facetObj.name,
|
|
14
|
+
label: facetObj.name,
|
|
15
|
+
type: "list",
|
|
16
|
+
presentation: "text",
|
|
17
|
+
values: (facetObj.entities ?? []).map((entity) => ({ id: entity.id, label: entity.name }))
|
|
18
|
+
});
|
|
19
|
+
} else if (rangeFacets.includes(facetKey)) {
|
|
20
|
+
filters.push({
|
|
21
|
+
id: facetObj.name,
|
|
22
|
+
label: facetObj.name,
|
|
23
|
+
type: "range",
|
|
24
|
+
min: 0,
|
|
25
|
+
max: facetObj.max ?? 0
|
|
26
|
+
});
|
|
27
|
+
} else if (booleanFacets.includes(facetKey)) {
|
|
28
|
+
filters.push({
|
|
29
|
+
id: facetObj.name,
|
|
30
|
+
label: facetObj.name,
|
|
31
|
+
type: "boolean"
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return filters;
|
|
36
|
+
};
|
|
37
|
+
export const mapSelectedFiltersToShopwareFilters = (filters, currency) => {
|
|
38
|
+
const swFilters = [];
|
|
39
|
+
const swBuiltInFilters = {};
|
|
40
|
+
for (const filter in filters) {
|
|
41
|
+
const filterObj = filters[filter];
|
|
42
|
+
if (isRangeFilter(filterObj)) {
|
|
43
|
+
if (filter === "price") {
|
|
44
|
+
swBuiltInFilters["min-price"] = filterObj.min === void 0 ? void 0 : new Money(filterObj.min, currency).toDecimal();
|
|
45
|
+
swBuiltInFilters["max-price"] = filterObj.max === void 0 ? void 0 : new Money(filterObj.max, currency).toDecimal();
|
|
46
|
+
} else {
|
|
47
|
+
swFilters.push({
|
|
48
|
+
type: "range",
|
|
49
|
+
field: filter,
|
|
50
|
+
parameters: {
|
|
51
|
+
lte: filterObj.max,
|
|
52
|
+
gte: filterObj.min
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
} else if (typeof filterObj === "boolean") {
|
|
57
|
+
if (filter === "shipping-free") {
|
|
58
|
+
swBuiltInFilters["shipping-free"] = filterObj;
|
|
59
|
+
} else {
|
|
60
|
+
swFilters.push({
|
|
61
|
+
type: "equals",
|
|
62
|
+
field: filter,
|
|
63
|
+
value: filterObj
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
} else {
|
|
67
|
+
if (filter === "manufacturer") {
|
|
68
|
+
swBuiltInFilters.manufacturer = filterObj.join("|");
|
|
69
|
+
} else {
|
|
70
|
+
swFilters.push({
|
|
71
|
+
type: "equalsAny",
|
|
72
|
+
field: filter,
|
|
73
|
+
value: filterObj.join("|")
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return { swFilters, swBuiltInFilters };
|
|
79
|
+
};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { StorefrontClient } from '../types/shopware.js';
|
|
2
|
+
export declare const fetchAllProducts: (storefrontClient: StorefrontClient, { productIds, loadVariants }: {
|
|
3
|
+
productIds: string[];
|
|
4
|
+
loadVariants: boolean;
|
|
5
|
+
}) => Promise<{
|
|
6
|
+
active?: boolean;
|
|
7
|
+
apiAlias: "product";
|
|
8
|
+
available?: boolean;
|
|
9
|
+
availableStock?: number;
|
|
10
|
+
calculatedCheapestPrice?: {
|
|
11
|
+
apiAlias?: "calculated_cheapest_price";
|
|
12
|
+
hasRange?: boolean;
|
|
13
|
+
listPrice?: import("../types/storeApiTypes.js").components["schemas"]["ListPrice"] | null;
|
|
14
|
+
quantity?: number;
|
|
15
|
+
referencePrice?: import("../types/storeApiTypes.js").components["schemas"]["ReferencePrice"] | null;
|
|
16
|
+
regulationPrice?: {
|
|
17
|
+
price: number;
|
|
18
|
+
} | null;
|
|
19
|
+
totalPrice?: number;
|
|
20
|
+
unitPrice?: number;
|
|
21
|
+
variantId?: string | null;
|
|
22
|
+
};
|
|
23
|
+
calculatedMaxPurchase?: number;
|
|
24
|
+
calculatedPrice: import("../types/storeApiTypes.js").components["schemas"]["CalculatedPrice"];
|
|
25
|
+
calculatedPrices: import("../types/storeApiTypes.js").components["schemas"]["CalculatedPrice"][];
|
|
26
|
+
canonicalProduct?: import("../types/storeApiTypes.js").components["schemas"]["Product"];
|
|
27
|
+
canonicalProductId?: string;
|
|
28
|
+
canonicalProductVersionId?: string;
|
|
29
|
+
categories?: import("../types/storeApiTypes.js").components["schemas"]["Category"][];
|
|
30
|
+
categoriesRo?: import("../types/storeApiTypes.js").components["schemas"]["Category"][];
|
|
31
|
+
categoryIds?: readonly string[];
|
|
32
|
+
categoryTree?: readonly string[];
|
|
33
|
+
childCount?: number;
|
|
34
|
+
children?: import("../types/storeApiTypes.js").components["schemas"]["Product"][];
|
|
35
|
+
cmsPage?: import("../types/storeApiTypes.js").components["schemas"]["CmsPage"];
|
|
36
|
+
cmsPageId?: string;
|
|
37
|
+
cmsPageVersionId?: string;
|
|
38
|
+
configuratorSettings?: import("../types/storeApiTypes.js").components["schemas"]["ProductConfiguratorSetting"][];
|
|
39
|
+
cover?: import("../types/storeApiTypes.js").components["schemas"]["ProductMedia"];
|
|
40
|
+
coverId?: string;
|
|
41
|
+
createdAt: string;
|
|
42
|
+
crossSellings?: import("../types/storeApiTypes.js").components["schemas"]["ProductCrossSelling"][];
|
|
43
|
+
customFields?: import("../types/storeApiTypes.js").GenericRecord;
|
|
44
|
+
deliveryTime?: import("../types/storeApiTypes.js").components["schemas"]["DeliveryTime"];
|
|
45
|
+
deliveryTimeId?: string;
|
|
46
|
+
description?: string;
|
|
47
|
+
displayGroup?: string;
|
|
48
|
+
downloads?: import("../types/storeApiTypes.js").components["schemas"]["ProductDownload"][];
|
|
49
|
+
ean?: string;
|
|
50
|
+
height?: number;
|
|
51
|
+
id: string;
|
|
52
|
+
isCloseout?: boolean;
|
|
53
|
+
isNew?: boolean;
|
|
54
|
+
keywords?: string;
|
|
55
|
+
length?: number;
|
|
56
|
+
mainCategories?: import("../types/storeApiTypes.js").components["schemas"]["MainCategory"][];
|
|
57
|
+
manufacturer?: import("../types/storeApiTypes.js").components["schemas"]["ProductManufacturer"];
|
|
58
|
+
manufacturerId?: string;
|
|
59
|
+
manufacturerNumber?: string;
|
|
60
|
+
markAsTopseller?: boolean;
|
|
61
|
+
maxPurchase?: number;
|
|
62
|
+
media?: import("../types/storeApiTypes.js").components["schemas"]["ProductMedia"][];
|
|
63
|
+
metaDescription?: string;
|
|
64
|
+
metaTitle?: string;
|
|
65
|
+
minPurchase?: number;
|
|
66
|
+
name: string;
|
|
67
|
+
optionIds?: readonly string[];
|
|
68
|
+
options?: import("../types/storeApiTypes.js").components["schemas"]["PropertyGroupOption"][];
|
|
69
|
+
packUnit?: string;
|
|
70
|
+
packUnitPlural?: string;
|
|
71
|
+
parent?: import("../types/storeApiTypes.js").components["schemas"]["Product"];
|
|
72
|
+
parentId?: string;
|
|
73
|
+
parentVersionId?: string;
|
|
74
|
+
productManufacturerVersionId?: string;
|
|
75
|
+
productMediaVersionId?: string;
|
|
76
|
+
productNumber: string;
|
|
77
|
+
productReviews?: import("../types/storeApiTypes.js").components["schemas"]["ProductReview"][];
|
|
78
|
+
properties?: import("../types/storeApiTypes.js").components["schemas"]["PropertyGroupOption"][];
|
|
79
|
+
propertyIds?: readonly string[];
|
|
80
|
+
purchaseSteps?: number;
|
|
81
|
+
purchaseUnit?: number;
|
|
82
|
+
ratingAverage?: number;
|
|
83
|
+
referenceUnit?: number;
|
|
84
|
+
releaseDate?: string;
|
|
85
|
+
restockTime?: number;
|
|
86
|
+
sales?: number;
|
|
87
|
+
seoCategory: import("../types/storeApiTypes.js").components["schemas"]["Category"];
|
|
88
|
+
seoUrls?: import("../types/storeApiTypes.js").components["schemas"]["SeoUrl"][];
|
|
89
|
+
shippingFree?: boolean;
|
|
90
|
+
sortedProperties?: import("../types/storeApiTypes.js").GenericRecord;
|
|
91
|
+
states?: readonly string[];
|
|
92
|
+
stock: number;
|
|
93
|
+
streamIds?: readonly string[];
|
|
94
|
+
streams?: import("../types/storeApiTypes.js").components["schemas"]["ProductStream"][];
|
|
95
|
+
tagIds?: readonly string[];
|
|
96
|
+
tags?: import("../types/storeApiTypes.js").components["schemas"]["Tag"][];
|
|
97
|
+
tax?: import("../types/storeApiTypes.js").components["schemas"]["Tax"];
|
|
98
|
+
taxId: string;
|
|
99
|
+
translated: {
|
|
100
|
+
apiAlias?: string;
|
|
101
|
+
canonicalProductId?: string;
|
|
102
|
+
canonicalProductVersionId?: string;
|
|
103
|
+
cmsPageId?: string;
|
|
104
|
+
cmsPageVersionId?: string;
|
|
105
|
+
coverId?: string;
|
|
106
|
+
deliveryTimeId?: string;
|
|
107
|
+
description?: string;
|
|
108
|
+
displayGroup?: string;
|
|
109
|
+
ean?: string;
|
|
110
|
+
keywords?: string;
|
|
111
|
+
manufacturerId?: string;
|
|
112
|
+
manufacturerNumber?: string;
|
|
113
|
+
metaDescription?: string;
|
|
114
|
+
metaTitle?: string;
|
|
115
|
+
name?: string;
|
|
116
|
+
packUnit?: string;
|
|
117
|
+
packUnitPlural?: string;
|
|
118
|
+
parentId?: string;
|
|
119
|
+
parentVersionId?: string;
|
|
120
|
+
productManufacturerVersionId?: string;
|
|
121
|
+
productMediaVersionId?: string;
|
|
122
|
+
productNumber?: string;
|
|
123
|
+
releaseDate?: string;
|
|
124
|
+
taxId?: string;
|
|
125
|
+
unitId?: string;
|
|
126
|
+
versionId?: string;
|
|
127
|
+
};
|
|
128
|
+
unit?: import("../types/storeApiTypes.js").components["schemas"]["Unit"];
|
|
129
|
+
unitId?: string;
|
|
130
|
+
updatedAt?: string;
|
|
131
|
+
variantListingConfig?: {
|
|
132
|
+
displayParent?: boolean;
|
|
133
|
+
} | null;
|
|
134
|
+
versionId?: string;
|
|
135
|
+
weight?: number;
|
|
136
|
+
width?: number;
|
|
137
|
+
}[]>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { resolveProductFields } from "../orchestr-helper/requestedFields.js";
|
|
2
|
+
export const fetchAllProducts = async (storefrontClient, { productIds, loadVariants }) => {
|
|
3
|
+
const fields = resolveProductFields({ loadVariants });
|
|
4
|
+
if (productIds.length === 0) {
|
|
5
|
+
return [];
|
|
6
|
+
}
|
|
7
|
+
const response = await storefrontClient.invoke("readProduct post /product", {
|
|
8
|
+
body: {
|
|
9
|
+
ids: productIds,
|
|
10
|
+
...fields
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
const products = response.data.elements ?? [];
|
|
14
|
+
const all = [...products.flatMap((product) => product.children ?? []), ...products];
|
|
15
|
+
return all;
|
|
16
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { StorefrontClient } from '../types/shopware.js';
|
|
2
|
+
import { Schemas } from '../types/storeApiTypes.js';
|
|
3
|
+
/**
|
|
4
|
+
* Fetch the current session cart from the Shopware store-api.
|
|
5
|
+
*
|
|
6
|
+
* The cart is identified per unique context session (the `sw-context-token`
|
|
7
|
+
* header/cookie set by the client factory). Callers cache the result in the
|
|
8
|
+
* `cartFragmentToken` passthrough so a single request resolves the cart once.
|
|
9
|
+
*/
|
|
10
|
+
export declare const getCart: (client: StorefrontClient) => Promise<Schemas["Cart"]>;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** This is not absolutely correct, but it's a good enough approximation. */
|
|
2
|
+
export declare const isShopwareId: (id: string) => boolean;
|
|
3
|
+
/** Extract the shopware id from a string. E.g. fallback slugs of format "slug-<shopware-id>" */
|
|
4
|
+
export declare const extractShopwareId: (str: string) => string | undefined;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ShopwareCategory, ShopwareProduct, ShopwareSeoUrl, WithSeoUrl } from '../../types/shopware.js';
|
|
2
|
+
/** Determine whether a given slug matches a seoPath */
|
|
3
|
+
export declare const isSlugMatchingSeoPath: (slug: string, seoPath: string) => boolean;
|
|
4
|
+
/** Return the canonical seoUrl or the first seoUrl if no canonical is available */
|
|
5
|
+
export declare const findCanonicalSeoUrl: (seoUrls: ShopwareSeoUrl[]) => ShopwareSeoUrl;
|
|
6
|
+
/** Map a generic entity with seoUrls to a slug */
|
|
7
|
+
export declare const getEntitySeoSlug: (rawEntity: WithSeoUrl) => string | undefined;
|
|
8
|
+
export declare const createFallbackSlug: (name: string, id: string) => string;
|
|
9
|
+
/** Get slug of a product. Falls back to sluggified name if no seoUrl is available */
|
|
10
|
+
export declare const entitySlug: (rawEntity: ShopwareProduct | ShopwareCategory) => string;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import slug from "slug";
|
|
2
|
+
import { swTranslated } from "../swTranslated.js";
|
|
3
|
+
const extractSlugFromPath = (path) => path.replace(/\/+$/, "");
|
|
4
|
+
const seoUrlToSlug = (seoUrl) => extractSlugFromPath(seoUrl.seoPathInfo);
|
|
5
|
+
export const isSlugMatchingSeoPath = (slug2, seoPath) => extractSlugFromPath(seoPath).toLowerCase() === extractSlugFromPath(slug2).toLowerCase();
|
|
6
|
+
export const findCanonicalSeoUrl = (seoUrls) => seoUrls.find((seoUrl) => seoUrl.isCanonical) ?? seoUrls[0];
|
|
7
|
+
export const getEntitySeoSlug = (rawEntity) => rawEntity.seoUrls && rawEntity.seoUrls.length > 0 ? seoUrlToSlug(findCanonicalSeoUrl(rawEntity.seoUrls)) : void 0;
|
|
8
|
+
export const createFallbackSlug = (name, id) => `${slug(name)}-${id}`;
|
|
9
|
+
export const entitySlug = (rawEntity) => getEntitySeoSlug(rawEntity) ?? createFallbackSlug(swTranslated(rawEntity, "name"), rawEntity.id);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Media, MediaSourceImage, MediaSourceVideo } from '@laioutr-core/core-types/common';
|
|
2
|
+
import { Schemas } from '../types/storeApiTypes.js';
|
|
3
|
+
type SwMedia = Schemas['Media'];
|
|
4
|
+
export declare const mediaToSrc: (media: SwMedia) => string;
|
|
5
|
+
export declare const mapMediaSourceImage: (media: SwMedia) => MediaSourceImage;
|
|
6
|
+
export declare const mapMediaSourceVideo: (media: SwMedia) => MediaSourceVideo;
|
|
7
|
+
export declare const mapMedia: (media: SwMedia) => Media;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { FALLBACK_IMAGE, FALLBACK_IMAGE_URL } from "../const/fallbacks.js";
|
|
2
|
+
export const mediaToSrc = (media) => {
|
|
3
|
+
if ((!media.thumbnails || media.thumbnails.length === 0) && !media.url) {
|
|
4
|
+
return FALLBACK_IMAGE_URL;
|
|
5
|
+
}
|
|
6
|
+
const thumbnails = media.thumbnails ?? [];
|
|
7
|
+
const thumbnailSrc = thumbnails.map((thumb) => `${thumb.url} ${thumb.width}x${thumb.height}`);
|
|
8
|
+
const orgSrc = `${media.url} ${media.metaData?.width ?? 0}x${media.metaData?.height ?? 0}`;
|
|
9
|
+
return `${media.url}#${encodeURIComponent([...thumbnailSrc, orgSrc].join(", "))}`;
|
|
10
|
+
};
|
|
11
|
+
export const mapMediaSourceImage = (media) => {
|
|
12
|
+
const src = mediaToSrc(media);
|
|
13
|
+
if (src === FALLBACK_IMAGE_URL) {
|
|
14
|
+
return FALLBACK_IMAGE.sources[0];
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
provider: "shopware",
|
|
18
|
+
width: media.metaData?.width ?? 100,
|
|
19
|
+
height: media.metaData?.height ?? 100,
|
|
20
|
+
src
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
export const mapMediaSourceVideo = (media) => ({
|
|
24
|
+
provider: "shopware",
|
|
25
|
+
src: media.url,
|
|
26
|
+
width: media.metaData?.width ?? 0,
|
|
27
|
+
height: media.metaData?.height ?? 0
|
|
28
|
+
});
|
|
29
|
+
export const mapMedia = (media) => {
|
|
30
|
+
const type = media.mimeType?.startsWith("video/") ? "video" : "image";
|
|
31
|
+
const source = type === "image" ? mapMediaSourceImage(media) : mapMediaSourceVideo(media);
|
|
32
|
+
return {
|
|
33
|
+
type,
|
|
34
|
+
// Shopware exposes both `alt` and `title`; prefer the explicit alt text and
|
|
35
|
+
// fall back to the title, treating blank/`null` as unset. This also keeps the
|
|
36
|
+
// value `string | undefined` (never `null`) — the canonical Media schema's `alt`
|
|
37
|
+
// rejects `null`, so an unset-alt asset would otherwise fail validation and be
|
|
38
|
+
// dropped by the media-library trust boundary (wrapProvider.sanitizeProviderItem).
|
|
39
|
+
alt: media.alt || media.title || void 0,
|
|
40
|
+
sources: [source]
|
|
41
|
+
};
|
|
42
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ShopwareCategory, ShopwareProduct } from '../types/shopware.js';
|
|
2
|
+
import type { PageIndexEntry } from '@laioutr-core/core-types/orchestr';
|
|
3
|
+
/**
|
|
4
|
+
* Shared by `query` entries and `locate` results so both describe a product page identically.
|
|
5
|
+
*
|
|
6
|
+
* `previewImage` is the cover media's **plain** URL, deliberately not `mapMedia`: its encoded srcset
|
|
7
|
+
* can't be dereferenced by the consumers that read `PageIndexEntry.meta.previewImage` (Cockpit picker,
|
|
8
|
+
* sitemaps, indexers), which all live outside the Nuxt image pipeline.
|
|
9
|
+
*/
|
|
10
|
+
export declare const toProductPageMeta: (product: ShopwareProduct) => PageIndexEntry["meta"];
|
|
11
|
+
export declare const toProductPageEntry: (product: ShopwareProduct) => PageIndexEntry;
|
|
12
|
+
/**
|
|
13
|
+
* Shared by `list`/`search` entries and `locate` results so both describe a category page identically.
|
|
14
|
+
*
|
|
15
|
+
* `previewImage` is the category media's plain URL for the same reason the product mapper uses one:
|
|
16
|
+
* the consumers that read it run outside the Nuxt image pipeline and cannot dereference an encoded
|
|
17
|
+
* srcset.
|
|
18
|
+
*/
|
|
19
|
+
export declare const toCategoryPageMeta: (category: ShopwareCategory) => PageIndexEntry["meta"];
|
|
20
|
+
export declare const toCategoryPageEntry: (category: ShopwareCategory) => PageIndexEntry;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { entitySlug } from "./mappers/slugMapper.js";
|
|
2
|
+
import { swTranslated } from "./swTranslated.js";
|
|
3
|
+
export const toProductPageMeta = (product) => ({
|
|
4
|
+
title: swTranslated(product, "name") ?? product.id,
|
|
5
|
+
previewImage: product.cover?.media?.url,
|
|
6
|
+
lastModified: product.updatedAt ?? void 0
|
|
7
|
+
});
|
|
8
|
+
export const toProductPageEntry = (product) => ({
|
|
9
|
+
params: { slug: entitySlug(product) },
|
|
10
|
+
subject: { type: "Product", id: product.id },
|
|
11
|
+
meta: toProductPageMeta(product)
|
|
12
|
+
});
|
|
13
|
+
export const toCategoryPageMeta = (category) => ({
|
|
14
|
+
title: swTranslated(category, "name") ?? category.name,
|
|
15
|
+
previewImage: category.media?.url,
|
|
16
|
+
lastModified: category.updatedAt ?? void 0
|
|
17
|
+
});
|
|
18
|
+
export const toCategoryPageEntry = (category) => ({
|
|
19
|
+
params: { slug: entitySlug(category) },
|
|
20
|
+
subject: { type: "Category", id: category.id },
|
|
21
|
+
meta: toCategoryPageMeta(category)
|
|
22
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { H3Event } from 'h3';
|
|
2
|
+
/**
|
|
3
|
+
* Persist / refresh the Shopware context-token cookie (httpOnly), then notify host-registered
|
|
4
|
+
* `shopware:context-token:changed` handlers so a project can mirror the token into its own
|
|
5
|
+
* session store. No-op when the response carries no token.
|
|
6
|
+
*/
|
|
7
|
+
export declare const persistContextToken: (event: H3Event, token: string | null | undefined) => Promise<void>;
|
|
8
|
+
/**
|
|
9
|
+
* Clear the context-token cookie and notify host handlers with a null token, so a storefront
|
|
10
|
+
* logout ends laioutr's mirrored session too. The next store-api call re-bootstraps a guest
|
|
11
|
+
* context.
|
|
12
|
+
*/
|
|
13
|
+
export declare const clearContextToken: (event: H3Event) => Promise<void>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { contextTokenCookieOptions } from "./contextTokenCookie.js";
|
|
2
|
+
import { deleteManagedCookie, setManagedCookie, useNitroApp } from "#imports";
|
|
3
|
+
import { CONTEXT_TOKEN_COOKIE } from "../const/cookieKeys.js";
|
|
4
|
+
export const persistContextToken = async (event, token) => {
|
|
5
|
+
if (!token) return;
|
|
6
|
+
setManagedCookie(event, CONTEXT_TOKEN_COOKIE, token, contextTokenCookieOptions());
|
|
7
|
+
await useNitroApp().hooks.callHook("shopware:context-token:changed", { event, token });
|
|
8
|
+
};
|
|
9
|
+
export const clearContextToken = async (event) => {
|
|
10
|
+
deleteManagedCookie(event, CONTEXT_TOKEN_COOKIE, { path: "/" });
|
|
11
|
+
await useNitroApp().hooks.callHook("shopware:context-token:changed", { event, token: null });
|
|
12
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { PageIndexLocateResult } from '@laioutr-core/core-types/orchestr';
|
|
2
|
+
/**
|
|
3
|
+
* Shape a product `locate` result from a resolved product id.
|
|
4
|
+
*
|
|
5
|
+
* `undefined` when the product could not be resolved, so the endpoint answers `null`. Without `meta`,
|
|
6
|
+
* consumers fall back to the route params.
|
|
7
|
+
*
|
|
8
|
+
* Pass `slugsByLocale` — a BCP-47 locale → slug map — only when every one of the project's locales
|
|
9
|
+
* was looked up. A missing key asserts the product has no page in that locale, which drops its
|
|
10
|
+
* hreflang alternate and sends the locale switcher to the homepage; omit the argument entirely while
|
|
11
|
+
* only the current locale is resolved, so consumers treat the rest as unknown. Locales with an empty
|
|
12
|
+
* slug are dropped from a map that is passed.
|
|
13
|
+
*/
|
|
14
|
+
export declare const buildProductLocate: (productId: string | undefined, meta?: PageIndexLocateResult["meta"], slugsByLocale?: Record<string, string | undefined>) => PageIndexLocateResult | undefined;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const buildProductLocate = (productId, meta, slugsByLocale) => {
|
|
2
|
+
if (!productId) return void 0;
|
|
3
|
+
if (!slugsByLocale) return { subject: { type: "Product", id: productId }, meta };
|
|
4
|
+
const locales = {};
|
|
5
|
+
for (const [locale, slug] of Object.entries(slugsByLocale)) {
|
|
6
|
+
if (slug) locales[locale] = { params: { slug } };
|
|
7
|
+
}
|
|
8
|
+
return { subject: { type: "Product", id: productId }, meta, locales };
|
|
9
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { StorefrontClient } from '../types/shopware.js';
|
|
2
|
+
import type { PageIndexLocateResult } from '@laioutr-core/core-types/orchestr';
|
|
3
|
+
/**
|
|
4
|
+
* Read a resolved category's page metadata for `pageIndex.locate`. Translations follow the client's
|
|
5
|
+
* `sw-language-id`, so the name comes back in the locale the lookup was made in.
|
|
6
|
+
*
|
|
7
|
+
* Swallows a missing category and a failed read — metadata is a labelling nicety and must never cost
|
|
8
|
+
* the caller the subject it already resolved.
|
|
9
|
+
*/
|
|
10
|
+
export declare const readCategoryPageMeta: (storefrontClient: StorefrontClient, categoryId: string) => Promise<PageIndexLocateResult["meta"]>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { toCategoryPageMeta } from "./pageIndexEntries.js";
|
|
2
|
+
const metaCriteria = {
|
|
3
|
+
associations: { media: {} },
|
|
4
|
+
includes: {
|
|
5
|
+
category: ["id", "name", "translated", "updatedAt", "media"],
|
|
6
|
+
media: ["url"]
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
export const readCategoryPageMeta = async (storefrontClient, categoryId) => {
|
|
10
|
+
try {
|
|
11
|
+
const response = await storefrontClient.invoke("readCategoryList post /category", {
|
|
12
|
+
body: { ...metaCriteria, ids: [categoryId], limit: 1 }
|
|
13
|
+
});
|
|
14
|
+
const category = response.data.elements?.[0];
|
|
15
|
+
return category ? toCategoryPageMeta(category) : void 0;
|
|
16
|
+
} catch {
|
|
17
|
+
return void 0;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { StorefrontClient } from '../types/shopware.js';
|
|
2
|
+
import type { PageIndexLocateResult } from '@laioutr-core/core-types/orchestr';
|
|
3
|
+
/**
|
|
4
|
+
* Read a resolved product's page metadata for `pageIndex.locate`. Translations follow the client's
|
|
5
|
+
* `sw-language-id`, so the name comes back in the locale the lookup was made in.
|
|
6
|
+
*
|
|
7
|
+
* Swallows a missing product and a failed read — metadata is a labelling nicety and must never cost
|
|
8
|
+
* the caller the subject it already resolved.
|
|
9
|
+
*/
|
|
10
|
+
export declare const readProductPageMeta: (storefrontClient: StorefrontClient, productId: string) => Promise<PageIndexLocateResult["meta"]>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { toProductPageMeta } from "./pageIndexEntries.js";
|
|
2
|
+
const metaCriteria = {
|
|
3
|
+
associations: { cover: {} },
|
|
4
|
+
includes: {
|
|
5
|
+
product: ["id", "name", "translated", "updatedAt", "cover"],
|
|
6
|
+
product_media: ["media"],
|
|
7
|
+
media: ["url"]
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
export const readProductPageMeta = async (storefrontClient, productId) => {
|
|
11
|
+
try {
|
|
12
|
+
const response = await storefrontClient.invoke("readProduct post /product", {
|
|
13
|
+
body: { ...metaCriteria, ids: [productId], limit: 1 }
|
|
14
|
+
});
|
|
15
|
+
const product = response.data.elements?.[0];
|
|
16
|
+
return product ? toProductPageMeta(product) : void 0;
|
|
17
|
+
} catch {
|
|
18
|
+
return void 0;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { AdoptSessionTokenParams } from './adoptSession.js';
|
|
2
|
+
export interface AdoptSessionDeps {
|
|
3
|
+
config: {
|
|
4
|
+
endpoint: string;
|
|
5
|
+
accessToken: string;
|
|
6
|
+
};
|
|
7
|
+
/** The `code` from a `laioutr:auth-changed` message; absent/empty means logout. */
|
|
8
|
+
code: string | null | undefined;
|
|
9
|
+
/** Injected {@link adoptSessionToken}. */
|
|
10
|
+
adopt: (params: AdoptSessionTokenParams) => Promise<string>;
|
|
11
|
+
/** Injected token persister (real: `persistContextToken` bound to the event). */
|
|
12
|
+
persist: (token: string) => Promise<void>;
|
|
13
|
+
/** Injected token clearer (real: `clearContextToken` bound to the event). */
|
|
14
|
+
clear: () => Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Decide how a `laioutr:auth-changed` message affects laioutr's stored session. Pure and
|
|
18
|
+
* free of `#imports` so the whole matrix is unit-testable: a `code` means login (redeem →
|
|
19
|
+
* persist); no `code` means logout (clear). The redeemed token stays server-side.
|
|
20
|
+
*/
|
|
21
|
+
export declare const adoptSession: (deps: AdoptSessionDeps) => Promise<{
|
|
22
|
+
authenticated: boolean;
|
|
23
|
+
}>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const adoptSession = async (deps) => {
|
|
2
|
+
if (!deps.code) {
|
|
3
|
+
await deps.clear();
|
|
4
|
+
return { authenticated: false };
|
|
5
|
+
}
|
|
6
|
+
const token = await deps.adopt({
|
|
7
|
+
endpoint: deps.config.endpoint,
|
|
8
|
+
accessToken: deps.config.accessToken,
|
|
9
|
+
code: deps.code
|
|
10
|
+
});
|
|
11
|
+
await deps.persist(token);
|
|
12
|
+
return { authenticated: true };
|
|
13
|
+
};
|