@faststore/api 1.12.37 → 1.12.39

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.
@@ -26,7 +26,7 @@ export declare const VtexCommerce: ({ account, environment }: Options, ctx: Cont
26
26
  pagetype: (slug: string) => Promise<PortalPagetype>;
27
27
  };
28
28
  products: {
29
- crossselling: ({ type, productId, groupByProduct }: {
29
+ crossselling: ({ type, productId, groupByProduct, }: {
30
30
  type: ValueOf<typeof FACET_CROSS_SELLING_MAP>;
31
31
  productId: string;
32
32
  groupByProduct?: boolean | undefined;
@@ -35,7 +35,7 @@ export declare const VtexCommerce: ({ account, environment }: Options, ctx: Cont
35
35
  };
36
36
  checkout: {
37
37
  simulation: (args: SimulationArgs, { salesChannel }?: SimulationOptions) => Promise<Simulation>;
38
- shippingData: ({ id, body }: {
38
+ shippingData: ({ id, body, }: {
39
39
  id: string;
40
40
  body: unknown;
41
41
  }) => Promise<OrderForm>;
@@ -60,6 +60,7 @@ export declare const VtexCommerce: ({ account, environment }: Options, ctx: Cont
60
60
  address: ({ postalCode, country, }: AddressInput) => Promise<Address>;
61
61
  };
62
62
  session: (search: string) => Promise<Session>;
63
+ getSessionOrder: () => Promise<Session>;
63
64
  subscribeToNewsletter: (data: {
64
65
  name: string;
65
66
  email: string;
@@ -5,6 +5,7 @@ export interface Session {
5
5
  export interface Namespaces {
6
6
  profile?: Profile;
7
7
  store?: Store;
8
+ checkout?: Checkout;
8
9
  }
9
10
  export interface Value {
10
11
  value: string;
@@ -22,3 +23,6 @@ export interface Profile {
22
23
  firstName?: Value;
23
24
  lastName?: Value;
24
25
  }
26
+ export interface Checkout {
27
+ orderFormId?: Value;
28
+ }
@@ -20,7 +20,7 @@ export declare const getClients: (options: Options, ctx: Context) => {
20
20
  pagetype: (slug: string) => Promise<import("./commerce/types/Portal").PortalPagetype>;
21
21
  };
22
22
  products: {
23
- crossselling: ({ type, productId, groupByProduct }: {
23
+ crossselling: ({ type, productId, groupByProduct, }: {
24
24
  type: "whoboughtalsobought" | "whosawalsosaw" | "similars" | "whosawalsobought" | "accessories" | "suggestions";
25
25
  productId: string;
26
26
  groupByProduct?: boolean | undefined;
@@ -29,7 +29,7 @@ export declare const getClients: (options: Options, ctx: Context) => {
29
29
  };
30
30
  checkout: {
31
31
  simulation: (args: import("./commerce/types/Simulation").SimulationArgs, { salesChannel }?: import("./commerce/types/Simulation").SimulationOptions) => Promise<import("./commerce/types/Simulation").Simulation>;
32
- shippingData: ({ id, body }: {
32
+ shippingData: ({ id, body, }: {
33
33
  id: string;
34
34
  body: unknown;
35
35
  }) => Promise<import("./commerce/types/OrderForm").OrderForm>;
@@ -54,6 +54,7 @@ export declare const getClients: (options: Options, ctx: Context) => {
54
54
  address: ({ postalCode, country, }: import("./commerce/types/Address").AddressInput) => Promise<import("./commerce/types/Address").Address>;
55
55
  };
56
56
  session: (search: string) => Promise<import("./commerce/types/Session").Session>;
57
+ getSessionOrder: () => Promise<import("./commerce/types/Session").Session>;
57
58
  subscribeToNewsletter: (data: {
58
59
  name: string;
59
60
  email: string;
@@ -1,5 +1,5 @@
1
- import type { MutationValidateCartArgs } from '../../../__generated__/schema';
2
1
  import type { Context } from '..';
2
+ import type { MutationValidateCartArgs } from '../../../__generated__/schema';
3
3
  /**
4
4
  * This resolver implements the optimistic cart behavior. The main idea in here
5
5
  * is that we receive a cart from the UI (as query params) and we validate it with
@@ -0,0 +1 @@
1
+ export declare const getCookie: (name: string, cookie: string) => string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faststore/api",
3
- "version": "1.12.37",
3
+ "version": "1.12.39",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -46,5 +46,5 @@
46
46
  "peerDependencies": {
47
47
  "graphql": "^15.6.0"
48
48
  },
49
- "gitHead": "d4dce561e18aaae368982240b4000d9242b984bc"
49
+ "gitHead": "872ef5ad4ff1db650a89222f96ed7073434da66c"
50
50
  }
@@ -15,6 +15,7 @@ import type {
15
15
  } from './types/Simulation'
16
16
  import type { Session } from './types/Session'
17
17
  import type { Channel } from '../../utils/channel'
18
+ import { getCookie } from '../../utils/getCookies'
18
19
  import type { SalesChannel } from './types/SalesChannel'
19
20
  import { MasterDataResponse } from './types/Newsletter'
20
21
  import type { Address, AddressInput } from './types/Address'
@@ -51,20 +52,22 @@ export const VtexCommerce = (
51
52
  fetchAPI(`${base}/api/catalog_system/pub/portal/pagetype/${slug}`),
52
53
  },
53
54
  products: {
54
- crossselling: (
55
- { type, productId, groupByProduct = true }: {
56
- type: ValueOf<typeof FACET_CROSS_SELLING_MAP>;
57
- productId: string;
58
- groupByProduct?: boolean;
59
- },
60
- ): Promise<PortalProduct[]> => {
55
+ crossselling: ({
56
+ type,
57
+ productId,
58
+ groupByProduct = true,
59
+ }: {
60
+ type: ValueOf<typeof FACET_CROSS_SELLING_MAP>
61
+ productId: string
62
+ groupByProduct?: boolean
63
+ }): Promise<PortalProduct[]> => {
61
64
  const params = new URLSearchParams({
62
65
  sc: ctx.storage.channel.salesChannel,
63
66
  groupByProduct: groupByProduct.toString(),
64
67
  })
65
68
 
66
69
  return fetchAPI(
67
- `${base}/api/catalog_system/pub/products/crossselling/${type}/${productId}?${params}`,
70
+ `${base}/api/catalog_system/pub/products/crossselling/${type}/${productId}?${params}`
68
71
  )
69
72
  },
70
73
  },
@@ -86,16 +89,20 @@ export const VtexCommerce = (
86
89
  }
87
90
  )
88
91
  },
89
- shippingData: (
90
- { id, body }: { id: string; body: unknown },
91
- ): Promise<OrderForm> => {
92
+ shippingData: ({
93
+ id,
94
+ body,
95
+ }: {
96
+ id: string
97
+ body: unknown
98
+ }): Promise<OrderForm> => {
92
99
  return fetchAPI(
93
100
  `${base}/api/checkout/pub/orderForm/${id}/attachments/shippingData`,
94
101
  {
95
102
  ...BASE_INIT,
96
103
  body: JSON.stringify(body),
97
- },
98
- );
104
+ }
105
+ )
99
106
  },
100
107
  orderForm: ({
101
108
  id,
@@ -159,7 +166,7 @@ export const VtexCommerce = (
159
166
  ...BASE_INIT,
160
167
  body: JSON.stringify({ value }),
161
168
  method: 'PUT',
162
- },
169
+ }
163
170
  )
164
171
  },
165
172
  region: async ({
@@ -189,14 +196,34 @@ export const VtexCommerce = (
189
196
  'items',
190
197
  'profile.id,profile.email,profile.firstName,profile.lastName,store.channel,store.countryCode,store.cultureInfo,store.currencyCode,store.currencySymbol'
191
198
  )
192
-
193
- return fetchAPI(`${base}/api/sessions?${params.toString()}`, {
194
- method: 'POST',
199
+ if (getCookie('vtex_session', ctx.headers.cookie)) {
200
+ // cookie set
201
+ return fetchAPI(`${base}/api/sessions?${params.toString()}`, {
202
+ method: 'GET',
203
+ headers: {
204
+ 'content-type': 'application/json',
205
+ cookie: ctx.headers.cookie,
206
+ },
207
+ })
208
+ } else {
209
+ // cookie unset -> create session
210
+ return fetchAPI(`${base}/api/sessions?${params.toString()}`, {
211
+ method: 'POST',
212
+ headers: {
213
+ 'content-type': 'application/json',
214
+ cookie: ctx.headers.cookie,
215
+ },
216
+ body: '{}',
217
+ })
218
+ }
219
+ },
220
+ getSessionOrder: (): Promise<Session> => {
221
+ return fetchAPI(`${base}/api/sessions?items=checkout.orderFormId`, {
222
+ method: 'GET',
195
223
  headers: {
196
224
  'content-type': 'application/json',
197
225
  cookie: ctx.headers.cookie,
198
226
  },
199
- body: '{}',
200
227
  })
201
228
  },
202
229
  subscribeToNewsletter: (data: {
@@ -6,6 +6,7 @@ export interface Session {
6
6
  export interface Namespaces {
7
7
  profile?: Profile
8
8
  store?: Store
9
+ checkout?: Checkout
9
10
  }
10
11
 
11
12
  export interface Value {
@@ -26,3 +27,7 @@ export interface Profile {
26
27
  firstName?: Value
27
28
  lastName?: Value
28
29
  }
30
+
31
+ export interface Checkout {
32
+ orderFormId?: Value
33
+ }
@@ -1,7 +1,16 @@
1
1
  import fetch from 'isomorphic-unfetch'
2
+ import packageJson from '../../../../package.json'
3
+
4
+ const USER_AGENT = `${packageJson.name}@${packageJson.version}`
2
5
 
3
6
  export const fetchAPI = async (info: RequestInfo, init?: RequestInit) => {
4
- const response = await fetch(info, init)
7
+ const response = await fetch(info, {
8
+ ...init,
9
+ headers: {
10
+ ...init?.headers,
11
+ 'User-Agent': USER_AGENT,
12
+ },
13
+ })
5
14
 
6
15
  if (response.ok) {
7
16
  return response.status !== 204 ? response.json() : undefined
@@ -1,27 +1,26 @@
1
1
  import deepEquals from 'fast-deep-equal'
2
2
 
3
+ import { mutateChannelContext, mutateLocaleContext } from '../utils/contex'
4
+ import { getCookie } from '../utils/getCookies'
3
5
  import { md5 } from '../utils/md5'
4
6
  import {
5
7
  attachmentToPropertyValue,
6
8
  getPropertyId,
7
- VALUE_REFERENCES,
9
+ VALUE_REFERENCES
8
10
  } from '../utils/propertyValue'
9
- import { mutateChannelContext, mutateLocaleContext } from '../utils/contex'
10
11
 
12
+ import type { Context } from '..'
11
13
  import type {
12
- IStoreSession,
13
14
  IStoreOffer,
14
15
  IStoreOrder,
15
- IStorePropertyValue,
16
- Maybe,
17
- MutationValidateCartArgs,
16
+ IStorePropertyValue, IStoreSession, Maybe,
17
+ MutationValidateCartArgs
18
18
  } from '../../../__generated__/schema'
19
19
  import type {
20
20
  OrderForm,
21
21
  OrderFormInputItem,
22
- OrderFormItem,
22
+ OrderFormItem
23
23
  } from '../clients/commerce/types/OrderForm'
24
- import type { Context } from '..'
25
24
 
26
25
  type Indexed<T> = T & { index?: number }
27
26
 
@@ -200,6 +199,20 @@ const isOrderFormStale = (form: OrderForm) => {
200
199
  return newEtag !== oldEtag
201
200
  }
202
201
 
202
+ async function getOrderNumberFromSession(
203
+ headers: Record<string, string> = {},
204
+ commerce: Context['clients']['commerce']
205
+ ) {
206
+
207
+ const cookieSession = getCookie('vtex_session', headers.cookie)
208
+
209
+ if (cookieSession) {
210
+ const { namespaces } = await commerce.getSessionOrder()
211
+ return namespaces.checkout?.orderFormId?.value
212
+ }
213
+ return ;
214
+ }
215
+
203
216
  // Returns the regionalized orderForm
204
217
  const getOrderForm = async (
205
218
  id: string,
@@ -253,11 +266,11 @@ export const validateCart = async (
253
266
  { cart: { order }, session }: MutationValidateCartArgs,
254
267
  ctx: Context
255
268
  ) => {
256
- const { enableOrderFormSync } = ctx.storage.flags
257
- const { orderNumber, acceptedOffer } = order
269
+ const { orderNumber: orderNumberFromCart, acceptedOffer } = order
258
270
  const {
259
271
  clients: { commerce },
260
272
  loaders: { skuLoader },
273
+ headers,
261
274
  } = ctx
262
275
 
263
276
  const channel = session?.channel
@@ -271,22 +284,26 @@ export const validateCart = async (
271
284
  mutateLocaleContext(ctx, locale)
272
285
  }
273
286
 
287
+ const orderNumberFromSession = await getOrderNumberFromSession(
288
+ headers,
289
+ commerce
290
+ )
291
+
292
+ const orderNumber = orderNumberFromSession ?? orderNumberFromCart ?? ''
293
+
274
294
  // Step1: Get OrderForm from VTEX Commerce
275
295
  const orderForm = await getOrderForm(orderNumber, session, ctx)
276
296
 
277
297
  // Step1.5: Check if another system changed the orderForm with this orderNumber
278
298
  // If so, this means the user interacted with this cart elsewhere and expects
279
299
  // to see this new cart state instead of what's stored on the user's browser.
280
- if (enableOrderFormSync === true) {
281
- const isStale = isOrderFormStale(orderForm)
282
-
283
- if (isStale === true && orderNumber) {
284
- const newOrderForm = await setOrderFormEtag(orderForm, commerce).then(
285
- joinItems
300
+ const isStale = isOrderFormStale(orderForm)
301
+
302
+ if (isStale && orderNumber) {
303
+ const newOrderForm = await setOrderFormEtag(orderForm, commerce).then(
304
+ joinItems
286
305
  )
287
-
288
- return orderFormToCart(newOrderForm, skuLoader)
289
- }
306
+ return orderFormToCart(newOrderForm, skuLoader)
290
307
  }
291
308
 
292
309
  // Step2: Process items from both browser and checkout so they have the same shape
@@ -349,7 +366,7 @@ export const validateCart = async (
349
366
  })
350
367
  // update orderForm etag so we know last time we touched this orderForm
351
368
  .then((form) =>
352
- enableOrderFormSync ? setOrderFormEtag(form, commerce) : form
369
+ setOrderFormEtag(form, commerce)
353
370
  )
354
371
  .then(joinItems)
355
372
 
@@ -0,0 +1,8 @@
1
+ export const getCookie = (name: string, cookie: string): string => {
2
+ const value = `; ${cookie}`
3
+ const parts = value.split(`; ${name}=`)
4
+ if (parts.length === 2) {
5
+ return parts?.pop()?.split(';').shift() ?? ''
6
+ }
7
+ return ''
8
+ }
@@ -0,0 +1,4 @@
1
+ declare module '*.json' {
2
+ const value: any;
3
+ export default value;
4
+ }