@scayle/storefront-core 8.7.0 → 8.8.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 +12 -0
- package/dist/api/customer.d.ts +73 -0
- package/dist/api/customer.mjs +59 -0
- package/dist/api/oauth.d.ts +62 -12
- package/dist/api/oauth.mjs +51 -15
- package/dist/cache/cache.d.ts +47 -0
- package/dist/cache/cached.d.ts +93 -0
- package/dist/cache/cached.mjs +62 -0
- package/dist/cache/providers/unstorage.d.ts +64 -0
- package/dist/cache/providers/unstorage.mjs +64 -0
- package/dist/constants/cache.d.ts +4 -0
- package/dist/constants/hash.d.ts +4 -0
- package/dist/constants/hash.mjs +1 -0
- package/dist/constants/httpStatus.d.ts +12 -2
- package/dist/constants/httpStatus.mjs +2 -2
- package/dist/constants/product.d.ts +3 -0
- package/dist/constants/promotion.d.ts +6 -0
- package/dist/constants/sorting.d.ts +6 -0
- package/dist/constants/withParameters.d.ts +28 -0
- package/dist/errors/errorResponse.d.ts +37 -0
- package/dist/errors/errorResponse.mjs +14 -0
- package/dist/helpers/filterHelper.mjs +1 -1
- package/dist/index.d.ts +48 -0
- package/dist/rpc/methods/basket/basket.mjs +9 -4
- package/dist/rpc/methods/campaign.d.ts +13 -0
- package/dist/rpc/methods/campaign.mjs +23 -0
- package/dist/rpc/methods/checkout/checkout.mjs +4 -2
- package/dist/rpc/methods/index.d.ts +1 -0
- package/dist/rpc/methods/index.mjs +1 -0
- package/dist/rpc/methods/products.d.ts +4 -4
- package/dist/rpc/methods/products.mjs +19 -7
- package/dist/rpc/methods/variants.d.ts +1 -1
- package/dist/rpc/methods/variants.mjs +4 -1
- package/dist/rpc/methods/wishlist.mjs +7 -3
- package/dist/types/api/context.d.ts +3 -0
- package/dist/utils/basket.d.ts +23 -0
- package/dist/utils/campaign.d.ts +23 -0
- package/dist/utils/campaign.mjs +17 -0
- package/dist/utils/fetch.d.ts +21 -2
- package/dist/utils/hash.d.ts +67 -0
- package/dist/utils/hash.mjs +3 -3
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.mjs +1 -0
- package/dist/utils/keys.d.ts +38 -0
- package/dist/utils/log.d.ts +77 -8
- package/dist/utils/log.mjs +61 -6
- package/dist/utils/response.d.ts +8 -3
- package/dist/utils/sapi.d.ts +10 -2
- package/dist/utils/timeout.d.ts +18 -0
- package/dist/utils/user.d.ts +22 -0
- package/dist/utils/user.mjs +5 -2
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -6,15 +6,63 @@ export * from './constants';
|
|
|
6
6
|
export * from './cache';
|
|
7
7
|
export * from './helpers';
|
|
8
8
|
export * from './types';
|
|
9
|
+
/**
|
|
10
|
+
* Core RPC methods available within `@scayle/storefront-core`.
|
|
11
|
+
*/
|
|
9
12
|
type RpcMethodsCore = typeof rpcMethods;
|
|
13
|
+
/**
|
|
14
|
+
* Interface for extending RPC methods within `@scayle/storefront-core`. Allows custom RPC methods
|
|
15
|
+
* to be added to the SCAYLE Storefront application by extending this interface.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* // nuxt.config.ts
|
|
20
|
+
* import * as customRpcMethods from './rpc'
|
|
21
|
+
* declare module '@scayle/storefront-core' {
|
|
22
|
+
* export interface RpcMethodsStorefrontInternal extends typeof customRpcMethods {}
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
10
26
|
export interface RpcMethodsStorefrontInternal {
|
|
11
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Combined RPC methods, merging core methods with SCAYLE Storefront application extensions.
|
|
30
|
+
* Extensions from `@scayle/storefront-core` take precedence in case of naming conflicts.
|
|
31
|
+
*/
|
|
12
32
|
export type RpcMethods = {
|
|
13
33
|
[Key in keyof RpcMethodsCore | keyof RpcMethodsStorefrontInternal]: Key extends keyof RpcMethodsStorefrontInternal ? RpcMethodsStorefrontInternal[Key] : Key extends keyof RpcMethodsCore ? RpcMethodsCore[Key] : never;
|
|
14
34
|
};
|
|
35
|
+
/**
|
|
36
|
+
* Type alias representing the name of an RPC method. This uses a
|
|
37
|
+
* resolution technique to avoid inlining method names in compiled
|
|
38
|
+
* type declarations, which is essential for supporting dynamically
|
|
39
|
+
* added RPC methods from the SCAYLE Storefront application.
|
|
40
|
+
*
|
|
41
|
+
* @see https://github.com/microsoft/TypeScript/pull/42149#issuecomment-865385560
|
|
42
|
+
* @
|
|
43
|
+
* Without this workaround, the signature to `rpcCall` looks something like
|
|
44
|
+
* `rpcCall(m: 'addItemToBasket' | 'addItemToWishlist'...)`.
|
|
45
|
+
* But we need it to look like `rpcCall(m: RpcMethodName)` in order to include
|
|
46
|
+
* RPC methods added by the SCAYLE Storefront application.
|
|
47
|
+
*/
|
|
15
48
|
type Resolve<T> = T extends T ? T : never;
|
|
16
49
|
export type RpcMethodName = Resolve<keyof RpcMethods>;
|
|
50
|
+
/**
|
|
51
|
+
* Describes the type of a specific RPC method by its name.
|
|
52
|
+
*
|
|
53
|
+
* @template N The name of the RPC method.
|
|
54
|
+
*/
|
|
17
55
|
export type RpcMethod<N extends RpcMethodName> = RpcMethods[N];
|
|
56
|
+
/**
|
|
57
|
+
* Describes the type of the parameters for a specific RPC method.
|
|
58
|
+
*
|
|
59
|
+
* @template N The name of the RPC method.
|
|
60
|
+
*/
|
|
18
61
|
export type RpcMethodParameters<N extends RpcMethodName> = Parameters<RpcMethod<N>>[0];
|
|
62
|
+
/**
|
|
63
|
+
* Describes the return type of a specific RPC method.
|
|
64
|
+
*
|
|
65
|
+
* @template N The name of the RPC method.
|
|
66
|
+
*/
|
|
19
67
|
export type RpcMethodReturnType<N extends RpcMethodName> = ReturnType<RpcMethod<N>>;
|
|
20
68
|
export { ExistingItemHandling, AddToBasketFailureKind, UpdateBasketItemFailureKind, AddToWishlistFailureKind, PromotionEffectType, FilterTypes, APISortOption, APISortOrder, type ProductSortConfig, } from '@scayle/storefront-api';
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
import { mergeBaskets as mergeBasketFunction } from "../../../utils/user.mjs";
|
|
10
10
|
import { unwrap } from "../../../utils/response.mjs";
|
|
11
11
|
import { wasAddedWithReducedQuantity } from "../../../utils/index.mjs";
|
|
12
|
+
import { getCampaignKey } from "../campaign.mjs";
|
|
12
13
|
const SAPI_ERROR_NAME = "SAPI ERROR";
|
|
13
14
|
function getWithParams(params, context) {
|
|
14
15
|
return params.with ?? context.withParams?.basket ?? MIN_WITH_PARAMS_BASKET;
|
|
@@ -31,7 +32,8 @@ export const addItemToBasket = async function addItemToBasket2({
|
|
|
31
32
|
"No Session found"
|
|
32
33
|
);
|
|
33
34
|
}
|
|
34
|
-
const {
|
|
35
|
+
const { sapiClient, basketKey } = context;
|
|
36
|
+
const campaignKey = await getCampaignKey(context);
|
|
35
37
|
const resolvedWith = getWithParams(
|
|
36
38
|
{ with: _with },
|
|
37
39
|
context
|
|
@@ -91,7 +93,8 @@ export const addItemsToBasket = async function addItemsToBasket2(params, context
|
|
|
91
93
|
"No Session found"
|
|
92
94
|
);
|
|
93
95
|
}
|
|
94
|
-
const {
|
|
96
|
+
const { sapiClient, basketKey } = context;
|
|
97
|
+
const campaignKey = await getCampaignKey(context);
|
|
95
98
|
const resolvedWith = getWithParams(params, context);
|
|
96
99
|
const itemsToBeAddedOrUpdated = params.items.map((item) => ({
|
|
97
100
|
variantId: item.variantId,
|
|
@@ -151,7 +154,8 @@ export const getBasket = async function getBasket2(options, context) {
|
|
|
151
154
|
"No Session found"
|
|
152
155
|
);
|
|
153
156
|
}
|
|
154
|
-
const { sapiClient,
|
|
157
|
+
const { sapiClient, basketKey } = context;
|
|
158
|
+
const campaignKey = await getCampaignKey(context);
|
|
155
159
|
const resolvedWith = getWithParams(
|
|
156
160
|
{ with: options },
|
|
157
161
|
context
|
|
@@ -183,7 +187,8 @@ export const removeItemFromBasket = async function removeItemFromBasket2(options
|
|
|
183
187
|
"No Session found"
|
|
184
188
|
);
|
|
185
189
|
}
|
|
186
|
-
const { sapiClient,
|
|
190
|
+
const { sapiClient, basketKey } = context;
|
|
191
|
+
const campaignKey = await getCampaignKey(context);
|
|
187
192
|
const resolvedWith = getWithParams(options, context);
|
|
188
193
|
const basket = await sapiClient.basket.deleteItem(
|
|
189
194
|
basketKey,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { RpcContext } from '../../types';
|
|
2
|
+
/**
|
|
3
|
+
* RPC method to retrieve the current campaign key.
|
|
4
|
+
* Used by other RPC methods which depend on a campaign key.
|
|
5
|
+
* Will return the key for the earliest active campaign.
|
|
6
|
+
*
|
|
7
|
+
* Uses SAPI's list-campaigns endpoint
|
|
8
|
+
* @see https://scayle.dev/en/api-guides/storefront-api/resources/campaigns/list-campaigns
|
|
9
|
+
*
|
|
10
|
+
* @param context The RPC Context
|
|
11
|
+
* @returns The current active campaign key.
|
|
12
|
+
*/
|
|
13
|
+
export declare const getCampaignKey: (context: RpcContext) => Promise<string | undefined>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import {
|
|
2
|
+
campaignHasNotEnded,
|
|
3
|
+
sortCampaignsByDateAscending,
|
|
4
|
+
isCampaignActive
|
|
5
|
+
} from "../../utils/campaign.mjs";
|
|
6
|
+
export const getCampaignKey = async (context) => {
|
|
7
|
+
if (context.campaignKey) {
|
|
8
|
+
return context.campaignKey;
|
|
9
|
+
}
|
|
10
|
+
const { cached, sapiClient } = context;
|
|
11
|
+
const { campaigns } = await cached(async () => {
|
|
12
|
+
const { entities } = await sapiClient.campaigns.get();
|
|
13
|
+
return {
|
|
14
|
+
campaigns: entities.filter((campaign) => {
|
|
15
|
+
return campaignHasNotEnded(campaign);
|
|
16
|
+
}).sort(sortCampaignsByDateAscending)
|
|
17
|
+
};
|
|
18
|
+
}, {
|
|
19
|
+
cacheKeyPrefix: "get-campaignKey",
|
|
20
|
+
ttl: 5 * 60
|
|
21
|
+
})();
|
|
22
|
+
return campaigns.find(isCampaignActive)?.key;
|
|
23
|
+
};
|
|
@@ -3,6 +3,7 @@ import { hasSession } from "../../../types/index.mjs";
|
|
|
3
3
|
import { ErrorResponse } from "../../../errors/index.mjs";
|
|
4
4
|
import { HttpStatusCode, HttpStatusMessage } from "../../../constants/index.mjs";
|
|
5
5
|
import { getAccessToken } from "../user.mjs";
|
|
6
|
+
import { getCampaignKey } from "../campaign.mjs";
|
|
6
7
|
export const getCheckoutToken = async function getCheckoutToken2(jwtPayload = {}, context) {
|
|
7
8
|
if (!hasSession(context)) {
|
|
8
9
|
return new ErrorResponse(
|
|
@@ -28,14 +29,15 @@ export const getCheckoutToken = async function getCheckoutToken2(jwtPayload = {}
|
|
|
28
29
|
const secret = new TextEncoder().encode(context.checkout.secret);
|
|
29
30
|
const now = /* @__PURE__ */ new Date();
|
|
30
31
|
const { voucher, customData, preferredCollectionPoint, carrier } = jwtPayload;
|
|
32
|
+
const campaignKey = await getCampaignKey(context);
|
|
31
33
|
const checkoutJwt = await new SignJWT({
|
|
32
34
|
voucher,
|
|
33
35
|
customData,
|
|
34
36
|
preferredCollectionPoint,
|
|
35
37
|
carrier,
|
|
36
38
|
basketId: context.basketKey,
|
|
37
|
-
campaignKey
|
|
38
|
-
}).setIssuedAt(now).setNotBefore(now).setExpirationTime("1h").setIssuer(`${"@scayle/storefront-core"}@${"8.
|
|
39
|
+
campaignKey
|
|
40
|
+
}).setIssuedAt(now).setNotBefore(now).setExpirationTime("1h").setIssuer(`${"@scayle/storefront-core"}@${"8.8.0"}`).setProtectedHeader({ alg: "HS256", typ: "JWT" }).sign(secret);
|
|
39
41
|
return {
|
|
40
42
|
accessToken: refreshedAccessToken,
|
|
41
43
|
checkoutJwt
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * from './basket/basket';
|
|
2
2
|
export * from './brands';
|
|
3
3
|
export * from './categories';
|
|
4
|
+
export { getCampaignKey } from './campaign';
|
|
4
5
|
export * from './cbd';
|
|
5
6
|
export { getProductById, getProductsByIds, getProductsByReferenceKeys, getProductsCount, fetchAllFiltersForCategory, getFilters, getProductsByCategory, } from './products';
|
|
6
7
|
export * from './search';
|
|
@@ -20,9 +20,9 @@ export declare function resolveCategoryIdFromParams(context: RpcContext, params:
|
|
|
20
20
|
category: string | undefined;
|
|
21
21
|
categoryId: number | undefined;
|
|
22
22
|
}>;
|
|
23
|
-
export declare const getProductById: (options: FetchProductParams,
|
|
24
|
-
export declare const getProductsByIds: (options: FetchProductsByIdsParams,
|
|
25
|
-
export declare const getProductsByReferenceKeys: (options: FetchProductsByReferenceKeysParams,
|
|
23
|
+
export declare const getProductById: (options: FetchProductParams, context: RpcContext) => Promise<Response | Product>;
|
|
24
|
+
export declare const getProductsByIds: (options: FetchProductsByIdsParams, context: RpcContext) => Promise<Response | Product[]>;
|
|
25
|
+
export declare const getProductsByReferenceKeys: (options: FetchProductsByReferenceKeysParams, context: RpcContext) => Promise<Response | Product[]>;
|
|
26
26
|
export declare const getProductsCount: (params: FetchProductsCountParams, context: RpcContext) => Promise<(string[] & ErrorResponse) | {
|
|
27
27
|
count: number;
|
|
28
28
|
}>;
|
|
@@ -31,7 +31,7 @@ export declare const fetchAllFiltersForCategory: ({ includedFilters, category }:
|
|
|
31
31
|
category: {
|
|
32
32
|
id?: number;
|
|
33
33
|
};
|
|
34
|
-
},
|
|
34
|
+
}, context: RpcContext) => Promise<string[]>;
|
|
35
35
|
export declare const getFilters: (params: FetchFiltersParams, context: RpcContext) => Promise<(string[] & ErrorResponse) | {
|
|
36
36
|
filters: FiltersEndpointResponseData;
|
|
37
37
|
unfilteredCount: number;
|
|
@@ -4,6 +4,7 @@ import { MINUTE } from "../../cache/index.mjs";
|
|
|
4
4
|
import { unwrap } from "../../utils/response.mjs";
|
|
5
5
|
import { mapSAPIFetchErrorToResponse } from "../../utils/sapi.mjs";
|
|
6
6
|
import { ErrorResponse } from "../../errors/index.mjs";
|
|
7
|
+
import { getCampaignKey } from "./campaign.mjs";
|
|
7
8
|
const MAX_PER_PAGE = 100;
|
|
8
9
|
const sanitizeAttributesForSAPI = ({
|
|
9
10
|
attributes,
|
|
@@ -34,7 +35,9 @@ export async function resolveCategoryIdFromParams(context, params) {
|
|
|
34
35
|
}
|
|
35
36
|
return { category, categoryId };
|
|
36
37
|
}
|
|
37
|
-
export const getProductById = async function getProductById2(options,
|
|
38
|
+
export const getProductById = async function getProductById2(options, context) {
|
|
39
|
+
const { sapiClient, cached, withParams } = context;
|
|
40
|
+
const campaignKey = await getCampaignKey(context);
|
|
38
41
|
return await cached(
|
|
39
42
|
mapSAPIFetchErrorToResponse(sapiClient.products.getById),
|
|
40
43
|
{
|
|
@@ -48,7 +51,9 @@ export const getProductById = async function getProductById2(options, { sapiClie
|
|
|
48
51
|
includeSellableForFree: options.includeSellableForFree
|
|
49
52
|
});
|
|
50
53
|
};
|
|
51
|
-
export const getProductsByIds = async function getProductsByIds2(options,
|
|
54
|
+
export const getProductsByIds = async function getProductsByIds2(options, context) {
|
|
55
|
+
const { sapiClient, cached, withParams } = context;
|
|
56
|
+
const campaignKey = await getCampaignKey(context);
|
|
52
57
|
return await cached(
|
|
53
58
|
mapSAPIFetchErrorToResponse(sapiClient.products.getByIds),
|
|
54
59
|
{
|
|
@@ -61,7 +66,9 @@ export const getProductsByIds = async function getProductsByIds2(options, { sapi
|
|
|
61
66
|
pricePromotionKey: options.pricePromotionKey
|
|
62
67
|
});
|
|
63
68
|
};
|
|
64
|
-
export const getProductsByReferenceKeys = async function getProductsByReferenceKeys2(options,
|
|
69
|
+
export const getProductsByReferenceKeys = async function getProductsByReferenceKeys2(options, context) {
|
|
70
|
+
const { sapiClient, cached, withParams } = context;
|
|
71
|
+
const campaignKey = await getCampaignKey(context);
|
|
65
72
|
return await cached(
|
|
66
73
|
mapSAPIFetchErrorToResponse(sapiClient.products.getByReferenceKeys),
|
|
67
74
|
{
|
|
@@ -82,7 +89,8 @@ export const getProductsCount = async function getProductsCount2(params, context
|
|
|
82
89
|
includeSellableForFree = false,
|
|
83
90
|
orFiltersOperator = void 0
|
|
84
91
|
} = params;
|
|
85
|
-
const { cached, sapiClient
|
|
92
|
+
const { cached, sapiClient } = context;
|
|
93
|
+
const campaignKey = await getCampaignKey(context);
|
|
86
94
|
const { categoryId } = await resolveCategoryIdFromParams(context, params);
|
|
87
95
|
const response = await getSanitizedAttributes(
|
|
88
96
|
context,
|
|
@@ -121,7 +129,9 @@ export const getProductsCount = async function getProductsCount2(params, context
|
|
|
121
129
|
count: productCount?.pagination.total
|
|
122
130
|
};
|
|
123
131
|
};
|
|
124
|
-
export const fetchAllFiltersForCategory = async function fetchAllFiltersForCategory2({ includedFilters = [], category },
|
|
132
|
+
export const fetchAllFiltersForCategory = async function fetchAllFiltersForCategory2({ includedFilters = [], category }, context) {
|
|
133
|
+
const { cached, sapiClient } = context;
|
|
134
|
+
const campaignKey = await getCampaignKey(context);
|
|
125
135
|
const fetchAndMapFiltersToSlugs = async () => {
|
|
126
136
|
const filtersFromSAPI = await cached(sapiClient.filters.get)({
|
|
127
137
|
where: { categoryId: category.id },
|
|
@@ -162,7 +172,8 @@ export const getFilters = async function getFilters2(params, context) {
|
|
|
162
172
|
includeSellableForFree = false,
|
|
163
173
|
orFiltersOperator
|
|
164
174
|
} = params;
|
|
165
|
-
const { cached, sapiClient
|
|
175
|
+
const { cached, sapiClient } = context;
|
|
176
|
+
const campaignKey = await getCampaignKey(context);
|
|
166
177
|
const { category, categoryId } = await resolveCategoryIdFromParams(
|
|
167
178
|
context,
|
|
168
179
|
params
|
|
@@ -241,7 +252,8 @@ export const getProductsByCategory = async function getProductsByCategory2(param
|
|
|
241
252
|
orFiltersOperator = void 0,
|
|
242
253
|
trackSearchAnalyticsEvent = void 0
|
|
243
254
|
} = params;
|
|
244
|
-
const { cached, sapiClient
|
|
255
|
+
const { cached, sapiClient } = context;
|
|
256
|
+
const campaignKey = await getCampaignKey(context);
|
|
245
257
|
const { category, categoryId } = await resolveCategoryIdFromParams(
|
|
246
258
|
context,
|
|
247
259
|
params
|
|
@@ -5,4 +5,4 @@ export interface FetchVariantsParams {
|
|
|
5
5
|
include?: ProductWith;
|
|
6
6
|
pricePromotionKey?: string;
|
|
7
7
|
}
|
|
8
|
-
export declare const getVariantById: ({ ids, include, pricePromotionKey }: FetchVariantsParams,
|
|
8
|
+
export declare const getVariantById: ({ ids, include, pricePromotionKey }: FetchVariantsParams, context: RpcContext) => Promise<Response | VariantDetail[]>;
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { mapSAPIFetchErrorToResponse } from "../../utils/sapi.mjs";
|
|
2
|
+
import { getCampaignKey } from "./campaign.mjs";
|
|
2
3
|
const defaultWith = {
|
|
3
4
|
attributes: "all"
|
|
4
5
|
};
|
|
5
|
-
export const getVariantById = async function getVariantById2({ ids, include = defaultWith, pricePromotionKey = "default" },
|
|
6
|
+
export const getVariantById = async function getVariantById2({ ids, include = defaultWith, pricePromotionKey = "default" }, context) {
|
|
7
|
+
const { sapiClient, cached, withParams } = context;
|
|
8
|
+
const campaignKey = await getCampaignKey(context);
|
|
6
9
|
const resolvedWith = include ?? withParams?.variant ?? defaultWith;
|
|
7
10
|
return await cached(
|
|
8
11
|
mapSAPIFetchErrorToResponse(sapiClient.variants.getByIds),
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
import { unwrap } from "../../utils/response.mjs";
|
|
8
8
|
import { ErrorResponse } from "../../errors/index.mjs";
|
|
9
9
|
import { mapSAPIFetchErrorToResponse } from "../../utils/sapi.mjs";
|
|
10
|
+
import { getCampaignKey } from "./campaign.mjs";
|
|
10
11
|
function getWithParams(params, context) {
|
|
11
12
|
return params.with ?? context.withParams?.wishlist ?? MIN_WITH_PARAMS_WISHLIST;
|
|
12
13
|
}
|
|
@@ -18,7 +19,8 @@ export const getWishlist = async function getWishlist2(options, context) {
|
|
|
18
19
|
"No Session found"
|
|
19
20
|
);
|
|
20
21
|
}
|
|
21
|
-
const { sapiClient,
|
|
22
|
+
const { sapiClient, wishlistKey } = context;
|
|
23
|
+
const campaignKey = await getCampaignKey(context);
|
|
22
24
|
const resolvedWith = getWithParams({ with: options }, context);
|
|
23
25
|
return await mapSAPIFetchErrorToResponse(sapiClient.wishlist.get)(
|
|
24
26
|
wishlistKey,
|
|
@@ -37,7 +39,8 @@ export const addItemToWishlist = async function addItemToWishlist2(options, cont
|
|
|
37
39
|
"No Session found"
|
|
38
40
|
);
|
|
39
41
|
}
|
|
40
|
-
const { sapiClient,
|
|
42
|
+
const { sapiClient, wishlistKey } = context;
|
|
43
|
+
const campaignKey = await getCampaignKey(context);
|
|
41
44
|
const { productId, variantId } = options;
|
|
42
45
|
const resolvedWith = getWithParams(options, context);
|
|
43
46
|
const pricePromotionKey = resolvedWith?.pricePromotionKey ?? "";
|
|
@@ -73,7 +76,8 @@ export const removeItemFromWishlist = async function removeItemFromWishlist2(opt
|
|
|
73
76
|
"No Session found"
|
|
74
77
|
);
|
|
75
78
|
}
|
|
76
|
-
const { sapiClient,
|
|
79
|
+
const { sapiClient, wishlistKey } = context;
|
|
80
|
+
const campaignKey = await getCampaignKey(context);
|
|
77
81
|
const resolvedWith = getWithParams(options, context);
|
|
78
82
|
return await mapSAPIFetchErrorToResponse(sapiClient.wishlist.deleteItem)(
|
|
79
83
|
wishlistKey,
|
|
@@ -77,6 +77,9 @@ export type RpcContext = {
|
|
|
77
77
|
shopId: number;
|
|
78
78
|
domain?: string;
|
|
79
79
|
withParams?: WithParams;
|
|
80
|
+
/**
|
|
81
|
+
* @deprecated Use the `getCampaignKey` RPC method instead.
|
|
82
|
+
*/
|
|
80
83
|
campaignKey?: string;
|
|
81
84
|
destroySessionsForUserId: (userId: number, sessionsToKeep?: string[]) => Promise<void>;
|
|
82
85
|
generateBasketKeyForUserId: (userId: string) => Promise<string>;
|
package/dist/utils/basket.d.ts
CHANGED
|
@@ -1,2 +1,25 @@
|
|
|
1
1
|
import type { AddOrUpdateItemError } from '@scayle/storefront-api';
|
|
2
|
+
/**
|
|
3
|
+
* Checks if all basket add/update errors indicate that items were added with reduced quantity.
|
|
4
|
+
* This function helps determine if a basket operation partially succeeded, meaning some items were added but with a lower quantity than requested.
|
|
5
|
+
*
|
|
6
|
+
* @param errors An array of `AddOrUpdateItemError` objects, typically returned from a basket add/update operation.
|
|
7
|
+
* @returns `true` if all errors indicate reduced quantity, `false` otherwise (including when the `errors` array is empty, null, or undefined).
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const errors: AddOrUpdateItemError[] = [
|
|
12
|
+
* { operation: 'add', kind: AddToBasketFailureKind.ITEM_ADDED_WITH_REDUCED_QUANTITY, variantId: 123 },
|
|
13
|
+
* { operation: 'update', kind: UpdateBasketItemFailureKind.ITEM_ADDED_WITH_REDUCED_QUANTITY, variantId: 456 },
|
|
14
|
+
* ]
|
|
15
|
+
*
|
|
16
|
+
* const allReducedQuantity = wasAddedWithReducedQuantity(errors) // true
|
|
17
|
+
*
|
|
18
|
+
* const noErrors = wasAddedWithReducedQuantity() // false
|
|
19
|
+
*
|
|
20
|
+
* const otherErrors = wasAddedWithReducedQuantity([
|
|
21
|
+
* { operation: 'add', kind: AddToBasketFailureKind.ITEM_NOT_FOUND, variantId: 789 },
|
|
22
|
+
* ]) // false
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
2
25
|
export declare const wasAddedWithReducedQuantity: (errors?: AddOrUpdateItemError[]) => boolean;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Campaign } from '@scayle/storefront-api';
|
|
2
|
+
/**
|
|
3
|
+
* Determine if a campaign is active for the current date
|
|
4
|
+
*
|
|
5
|
+
* @param campaign the campaign to test
|
|
6
|
+
* @returns True when the current datetime is within the campaign's timeframe, false otherwise
|
|
7
|
+
*/
|
|
8
|
+
export declare const isCampaignActive: (campaign?: Campaign) => boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Determine if a campaign has ended based on the current datetime
|
|
11
|
+
*
|
|
12
|
+
* @param campaign
|
|
13
|
+
* @returns True if the campaign's end date is later than the current datetime, false otherwise
|
|
14
|
+
*/
|
|
15
|
+
export declare const campaignHasNotEnded: (campaign: Campaign) => boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Comparison function for campaigns based on their start date. To be used with Array.prototype.sort()
|
|
18
|
+
*
|
|
19
|
+
* @param a The first campaign for comparison
|
|
20
|
+
* @param b The second campaign for comparison
|
|
21
|
+
* @returns A positive value when a should come before b, negative value when b should come before a, or 0 when they are considered equal
|
|
22
|
+
*/
|
|
23
|
+
export declare const sortCampaignsByDateAscending: (a: Campaign, b: Campaign) => number;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const isCampaignActive = (campaign) => {
|
|
2
|
+
if (!campaign) {
|
|
3
|
+
return false;
|
|
4
|
+
}
|
|
5
|
+
const timeNow = Date.now();
|
|
6
|
+
const campaignStart = new Date(campaign.start_at).getTime();
|
|
7
|
+
const hasStarted = timeNow >= campaignStart;
|
|
8
|
+
const campaignEnd = new Date(campaign.end_at).getTime();
|
|
9
|
+
const hasEnded = timeNow >= campaignEnd;
|
|
10
|
+
return hasStarted && !hasEnded;
|
|
11
|
+
};
|
|
12
|
+
export const campaignHasNotEnded = (campaign) => {
|
|
13
|
+
return new Date(campaign.end_at).getTime() > Date.now();
|
|
14
|
+
};
|
|
15
|
+
export const sortCampaignsByDateAscending = (a, b) => {
|
|
16
|
+
return new Date(a.start_at).getTime() - new Date(b.start_at).getTime();
|
|
17
|
+
};
|
package/dist/utils/fetch.d.ts
CHANGED
|
@@ -1,6 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* A custom Error type for errors that occur
|
|
3
|
-
*
|
|
2
|
+
* A custom Error type for errors that occur during a `fetch()` request.
|
|
3
|
+
* Includes the `Response` object and optional response data for more detailed error analysis.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```typescript
|
|
7
|
+
* try {
|
|
8
|
+
* const response = await fetch('/some/url')
|
|
9
|
+
*
|
|
10
|
+
* if (!response.ok) {
|
|
11
|
+
* const data = await response.json(); // Attempt to parse JSON error data
|
|
12
|
+
* throw new FetchError(response, data)
|
|
13
|
+
* }
|
|
14
|
+
* // ... process successful response
|
|
15
|
+
* } catch (error) {
|
|
16
|
+
* if (error instanceof FetchError) {
|
|
17
|
+
* console.error("Fetch error:", error.message, error.response.status, error.data)
|
|
18
|
+
* } else {
|
|
19
|
+
* console.error("Other error:", error)
|
|
20
|
+
* }
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
4
23
|
*/
|
|
5
24
|
export declare class FetchError extends Error {
|
|
6
25
|
response: Response;
|
package/dist/utils/hash.d.ts
CHANGED
|
@@ -1,8 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Calculates the MD5 hash of a string.
|
|
3
|
+
* Uses `CryptoJS` for MD5 calculation. Throws an error if MD5 support is omitted in the build.
|
|
4
|
+
*
|
|
5
|
+
* @see https://cryptojs.gitbook.io/docs/
|
|
6
|
+
*
|
|
7
|
+
* @param value The string to hash.
|
|
8
|
+
*
|
|
9
|
+
* @returns A Promise that resolves with the MD5 hash as a hex string.
|
|
10
|
+
*
|
|
11
|
+
* @throws {Error} If SFC is built without MD5 support (SFC_OMIT_MD5 environment variable is set).
|
|
12
|
+
*/
|
|
1
13
|
export declare const md5: (value: string) => Promise<any>;
|
|
14
|
+
/**
|
|
15
|
+
* Calculates the SHA256 hash of a string.
|
|
16
|
+
*
|
|
17
|
+
* @param value The input string.
|
|
18
|
+
*
|
|
19
|
+
* @returns A Promise that resolves with the SHA256 hash as a hexadecimal string.
|
|
20
|
+
*/
|
|
2
21
|
export declare const sha256: (value: string) => Promise<string>;
|
|
22
|
+
/**
|
|
23
|
+
* Calculates the HMAC (Hash-based Message Authentication Code) of a string.
|
|
24
|
+
*
|
|
25
|
+
* @param value The input string (message).
|
|
26
|
+
* @param secret The secret key.
|
|
27
|
+
* @param algorithm The hashing algorithm to use (e.g., 'SHA-256'). Defaults to 'SHA-256'.
|
|
28
|
+
*
|
|
29
|
+
* @returns A Promise that resolves with the HMAC as a hexadecimal string.
|
|
30
|
+
*/
|
|
3
31
|
export declare const hmac: (value: string, secret: string, algorithm?: string) => Promise<string>;
|
|
32
|
+
/**
|
|
33
|
+
* Encodes a string to Base64. Works in both Node.js and browser environments.
|
|
34
|
+
*
|
|
35
|
+
* @param string The string to encode.
|
|
36
|
+
*
|
|
37
|
+
* @returns The Base64 encoded string.
|
|
38
|
+
*/
|
|
4
39
|
export declare const encodeBase64: (string: string) => string;
|
|
40
|
+
/**
|
|
41
|
+
* Decodes a Base64 encoded string. Works in both Node.js and browser environments.
|
|
42
|
+
*
|
|
43
|
+
* @param string The Base64 encoded string.
|
|
44
|
+
*
|
|
45
|
+
* @returns The decoded string.
|
|
46
|
+
*/
|
|
5
47
|
export declare const decodeBase64: (string: string) => string;
|
|
48
|
+
/**
|
|
49
|
+
* Verifies an order success token by comparing its signature with an expected signature.
|
|
50
|
+
*
|
|
51
|
+
* @template T The type of the parsed payload.
|
|
52
|
+
*
|
|
53
|
+
* @param token The token to verify.
|
|
54
|
+
* @param secret The secret key used to generate the signature.
|
|
55
|
+
*
|
|
56
|
+
* @returns A Promise resolving to the parsed token payload if the signature is valid, or undefined otherwise.
|
|
57
|
+
*/
|
|
6
58
|
export declare const verifyOrderSuccessToken: <T = unknown>(token: string, secret: string) => Promise<T | undefined>;
|
|
59
|
+
/**
|
|
60
|
+
* Builds a hash payload by stringifying and Base64 encoding a given payload.
|
|
61
|
+
*
|
|
62
|
+
* @param payload The payload to hash.
|
|
63
|
+
*
|
|
64
|
+
* @returns The Base64 encoded hash payload.
|
|
65
|
+
*/
|
|
7
66
|
export declare const buildHashPayload: (payload: unknown) => string;
|
|
67
|
+
/**
|
|
68
|
+
* Builds a signature for a given payload hash and secret.
|
|
69
|
+
*
|
|
70
|
+
* @param payloadHash The hash of the payload.
|
|
71
|
+
* @param secret The secret key.
|
|
72
|
+
*
|
|
73
|
+
* @returns A Promise resolving to the Base64 encoded signature.
|
|
74
|
+
*/
|
|
8
75
|
export declare const buildSignature: (payloadHash: string, secret: string) => Promise<string>;
|
package/dist/utils/hash.mjs
CHANGED
|
@@ -56,10 +56,10 @@ export const decodeBase64 = (string) => {
|
|
|
56
56
|
export const verifyOrderSuccessToken = async (token, secret) => {
|
|
57
57
|
const [encodedPayload, signature] = token.split(".", 2);
|
|
58
58
|
const expectedSignature = encodeBase64(await hmac(encodedPayload, secret));
|
|
59
|
-
if (expectedSignature
|
|
60
|
-
return
|
|
59
|
+
if (expectedSignature !== signature) {
|
|
60
|
+
return;
|
|
61
61
|
}
|
|
62
|
-
return
|
|
62
|
+
return JSON.parse(decodeBase64(encodedPayload));
|
|
63
63
|
};
|
|
64
64
|
export const buildHashPayload = (payload) => encodeBase64(JSON.stringify(payload));
|
|
65
65
|
export const buildSignature = async (payloadHash, secret) => encodeBase64(await hmac(payloadHash, secret));
|
package/dist/utils/index.d.ts
CHANGED
package/dist/utils/index.mjs
CHANGED
package/dist/utils/keys.d.ts
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
import type { Log } from '..';
|
|
2
2
|
import { HashAlgorithm } from '../constants/hash';
|
|
3
|
+
/**
|
|
4
|
+
* Generates a key based on a template, shop ID, user ID, and optional hash algorithm.
|
|
5
|
+
*
|
|
6
|
+
* @param params The required asynchronous function parameters.
|
|
7
|
+
* @param params.keyName Deprecated and unused value to specify the name of a key.
|
|
8
|
+
* @param params.keyTemplate The template string for the key, which can include placeholders for `shopId` and `userId`.
|
|
9
|
+
* @param params.hashAlgorithm The hash algorithm to use (MD5, SHA256, or none). Defaults to SHA256.
|
|
10
|
+
* @param params.shopId The shop ID.
|
|
11
|
+
* @param params.userId The user ID.
|
|
12
|
+
* @param params.log Optional logger instance.
|
|
13
|
+
*
|
|
14
|
+
* @returns A Promise resolving to the generated key.
|
|
15
|
+
*/
|
|
3
16
|
export declare const generateKey: ({ keyTemplate, hashAlgorithm, shopId, userId, log, }: {
|
|
17
|
+
/** @deprecated */
|
|
4
18
|
keyName?: string;
|
|
5
19
|
keyTemplate: string;
|
|
6
20
|
hashAlgorithm: HashAlgorithm;
|
|
@@ -8,6 +22,18 @@ export declare const generateKey: ({ keyTemplate, hashAlgorithm, shopId, userId,
|
|
|
8
22
|
userId: string;
|
|
9
23
|
log?: Log;
|
|
10
24
|
}) => Promise<string>;
|
|
25
|
+
/**
|
|
26
|
+
* Generates a wishlist key.
|
|
27
|
+
*
|
|
28
|
+
* @param params The required asynchronous function parameters.
|
|
29
|
+
* @param params.keyTemplate The template string for the key.
|
|
30
|
+
* @param params.hashAlgorithm The hash algorithm to use. Defaults to SHA256.
|
|
31
|
+
* @param params.shopId The shop ID.
|
|
32
|
+
* @param params.userId The user ID.
|
|
33
|
+
* @param params.log Optional logger instance.
|
|
34
|
+
*
|
|
35
|
+
* @returns A Promise resolving to the generated wishlist key.
|
|
36
|
+
*/
|
|
11
37
|
export declare const generateWishlistKey: ({ keyTemplate, hashAlgorithm, shopId, userId, log, }: {
|
|
12
38
|
keyTemplate: string;
|
|
13
39
|
hashAlgorithm: HashAlgorithm;
|
|
@@ -15,6 +41,18 @@ export declare const generateWishlistKey: ({ keyTemplate, hashAlgorithm, shopId,
|
|
|
15
41
|
userId: string;
|
|
16
42
|
log?: Log;
|
|
17
43
|
}) => Promise<string>;
|
|
44
|
+
/**
|
|
45
|
+
* Generates a basket key.
|
|
46
|
+
*
|
|
47
|
+
* @param params The required asynchronous function parameters.
|
|
48
|
+
* @param params.keyTemplate The template string for the key.
|
|
49
|
+
* @param params.hashAlgorithm The hash algorithm to use. Defaults to SHA256.
|
|
50
|
+
* @param params.shopId The shop ID.
|
|
51
|
+
* @param params.userId The user ID.
|
|
52
|
+
* @param params.log Optional logger instance.
|
|
53
|
+
*
|
|
54
|
+
* @returns A Promise resolving to the generated basket key.
|
|
55
|
+
*/
|
|
18
56
|
export declare const generateBasketKey: ({ keyTemplate, hashAlgorithm, shopId, userId, log, }: {
|
|
19
57
|
keyTemplate: string;
|
|
20
58
|
hashAlgorithm: HashAlgorithm;
|