@decocms/apps-vtex 8.0.0 → 8.1.0-next.1
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/package.json +7 -52
- package/src/index.ts +12 -15
- package/src/utils/constants.ts +2 -56
- package/src/utils/cookieSanitizer.ts +59 -59
- package/src/utils/operationRouter.ts +76 -72
- package/src/utils/types.ts +709 -1557
- package/src/vtexClient.ts +444 -0
- package/src/README.md +0 -6
- package/src/__tests__/client-segment-cookie.test.ts +0 -255
- package/src/__tests__/client-set-cookie-forward.test.ts +0 -257
- package/src/__tests__/schemas.test.ts +0 -71
- package/src/actions/address.ts +0 -250
- package/src/actions/analytics/sendEvent.ts +0 -86
- package/src/actions/auth.ts +0 -333
- package/src/actions/checkout.ts +0 -659
- package/src/actions/index.ts +0 -11
- package/src/actions/masterData.ts +0 -168
- package/src/actions/misc.ts +0 -188
- package/src/actions/newsletter.ts +0 -105
- package/src/actions/orders.ts +0 -34
- package/src/actions/profile.ts +0 -201
- package/src/actions/session.ts +0 -85
- package/src/actions/trigger.ts +0 -43
- package/src/actions/wishlist.ts +0 -114
- package/src/client.ts +0 -667
- package/src/commerceLoaders.ts +0 -267
- package/src/hooks/__tests__/createCart.render.test.ts +0 -114
- package/src/hooks/__tests__/createCart.test.ts +0 -92
- package/src/hooks/__tests__/createUseCart.test.ts +0 -184
- package/src/hooks/__tests__/createUseUser.test.ts +0 -48
- package/src/hooks/__tests__/createUseWishlist.test.ts +0 -87
- package/src/hooks/cartQuery.ts +0 -204
- package/src/hooks/createCart.ts +0 -427
- package/src/hooks/createUseCart.ts +0 -360
- package/src/hooks/createUseUser.ts +0 -153
- package/src/hooks/createUseWishlist.ts +0 -242
- package/src/hooks/index.ts +0 -25
- package/src/hooks/useAutocomplete.ts +0 -83
- package/src/hooks/useCart.ts +0 -243
- package/src/hooks/useUser.ts +0 -78
- package/src/hooks/useWishlist.ts +0 -119
- package/src/invoke.ts +0 -235
- package/src/loaders/ProductDetailsPage.ts +0 -1
- package/src/loaders/ProductList.ts +0 -1
- package/src/loaders/ProductListingPage.ts +0 -1
- package/src/loaders/address.ts +0 -115
- package/src/loaders/autocomplete.ts +0 -58
- package/src/loaders/brands.ts +0 -44
- package/src/loaders/cart/attachments.ts +0 -53
- package/src/loaders/cart/full.ts +0 -67
- package/src/loaders/cart/gifts.ts +0 -43
- package/src/loaders/cart/orderFormId.ts +0 -31
- package/src/loaders/cart/shipping.test.ts +0 -125
- package/src/loaders/cart/shipping.ts +0 -136
- package/src/loaders/cart/summary.ts +0 -36
- package/src/loaders/cart.ts +0 -52
- package/src/loaders/catalog.ts +0 -163
- package/src/loaders/collections.ts +0 -55
- package/src/loaders/index.ts +0 -20
- package/src/loaders/intelligentSearch/__tests__/productListingPage.test.ts +0 -51
- package/src/loaders/intelligentSearch/productDetailsPage.ts +0 -113
- package/src/loaders/intelligentSearch/productList.ts +0 -154
- package/src/loaders/intelligentSearch/productListingPage.ts +0 -526
- package/src/loaders/intelligentSearch/suggestions.ts +0 -46
- package/src/loaders/legacy/productDetailsPage.ts +0 -1
- package/src/loaders/legacy/productList.ts +0 -1
- package/src/loaders/legacy/relatedProductsLoader.ts +0 -81
- package/src/loaders/legacy.ts +0 -635
- package/src/loaders/logistics.ts +0 -111
- package/src/loaders/minicart.ts +0 -78
- package/src/loaders/navbar.ts +0 -26
- package/src/loaders/orders.ts +0 -92
- package/src/loaders/pageType.ts +0 -68
- package/src/loaders/paths/PDPDefaultPath.ts +0 -51
- package/src/loaders/payment.ts +0 -97
- package/src/loaders/productListFull.ts +0 -159
- package/src/loaders/profile.ts +0 -138
- package/src/loaders/promotion.ts +0 -30
- package/src/loaders/search.ts +0 -124
- package/src/loaders/session.ts +0 -83
- package/src/loaders/user.ts +0 -87
- package/src/loaders/wishlist.ts +0 -89
- package/src/loaders/wishlistProducts.ts +0 -69
- package/src/loaders/workflow/products.ts +0 -64
- package/src/loaders/workflow.ts +0 -316
- package/src/logo.png +0 -0
- package/src/manifest.gen.ts +0 -89
- package/src/middleware.ts +0 -240
- package/src/mod.ts +0 -157
- package/src/registry.ts +0 -9
- package/src/schemas.gen.ts +0 -3191
- package/src/schemas.ts +0 -30
- package/src/types.ts +0 -248
- package/src/utils/__tests__/cartProjection.test.ts +0 -118
- package/src/utils/__tests__/cookieSanitizer.test.ts +0 -162
- package/src/utils/__tests__/fetch.test.ts +0 -80
- package/src/utils/__tests__/fetchCache.test.ts +0 -155
- package/src/utils/__tests__/instrumentedFetch.test.ts +0 -158
- package/src/utils/__tests__/intelligentSearch.test.ts +0 -88
- package/src/utils/__tests__/minicart.test.ts +0 -184
- package/src/utils/__tests__/operationRouter.test.ts +0 -227
- package/src/utils/__tests__/resilience.test.ts +0 -205
- package/src/utils/__tests__/resourceRange.test.ts +0 -32
- package/src/utils/__tests__/simulationCache.test.ts +0 -40
- package/src/utils/__tests__/sitemap.test.ts +0 -185
- package/src/utils/__tests__/slugify.test.ts +0 -31
- package/src/utils/__tests__/transform.test.ts +0 -769
- package/src/utils/accountLoaders.ts +0 -203
- package/src/utils/authHelpers.ts +0 -85
- package/src/utils/batch.ts +0 -18
- package/src/utils/cartProjection.ts +0 -96
- package/src/utils/cookies.ts +0 -167
- package/src/utils/enrichment.ts +0 -560
- package/src/utils/fetch.ts +0 -107
- package/src/utils/fetchCache.ts +0 -199
- package/src/utils/index.ts +0 -19
- package/src/utils/instrumentedFetch.ts +0 -97
- package/src/utils/intelligentSearch.ts +0 -96
- package/src/utils/legacy.ts +0 -139
- package/src/utils/minicart.ts +0 -139
- package/src/utils/pickAndOmit.ts +0 -25
- package/src/utils/proxy.ts +0 -435
- package/src/utils/resilience.ts +0 -352
- package/src/utils/resourceRange.ts +0 -10
- package/src/utils/segment.ts +0 -152
- package/src/utils/similars.ts +0 -37
- package/src/utils/simulationCache.ts +0 -73
- package/src/utils/sitemap.ts +0 -282
- package/src/utils/slugCache.ts +0 -28
- package/src/utils/slugify.ts +0 -13
- package/src/utils/transform.ts +0 -1522
- package/src/utils/vtexId.ts +0 -138
- package/tsconfig.json +0 -7
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* VTEX MasterData v2 API actions.
|
|
3
|
-
* Generic CRUD operations on data entities.
|
|
4
|
-
*/
|
|
5
|
-
import { getVtexConfig, getVtexFetch, vtexFetch, vtexFetchResponse } from "../client";
|
|
6
|
-
|
|
7
|
-
function removeEmptyFields(obj: Record<string, any>): Record<string, any> {
|
|
8
|
-
return Object.fromEntries(
|
|
9
|
-
Object.entries(obj).filter(
|
|
10
|
-
([_, value]) => value !== "" && value !== undefined && value !== null,
|
|
11
|
-
),
|
|
12
|
-
);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface CreateDocumentResult {
|
|
16
|
-
DocumentId: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface CreateDocumentProps {
|
|
20
|
-
entity: string;
|
|
21
|
-
data: Record<string, any>;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export async function createDocument(props: CreateDocumentProps): Promise<CreateDocumentResult> {
|
|
25
|
-
const { entity, data } = props;
|
|
26
|
-
return vtexFetch<CreateDocumentResult>(`/api/dataentities/${entity}/documents`, {
|
|
27
|
-
method: "POST",
|
|
28
|
-
body: JSON.stringify(removeEmptyFields(data)),
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface GetDocumentProps {
|
|
33
|
-
entity: string;
|
|
34
|
-
documentId: string;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export async function getDocument<T = unknown>(props: GetDocumentProps): Promise<T> {
|
|
38
|
-
const { entity, documentId } = props;
|
|
39
|
-
return vtexFetch<T>(`/api/dataentities/${entity}/documents/${documentId}`);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export interface PatchDocumentProps {
|
|
43
|
-
entity: string;
|
|
44
|
-
documentId: string;
|
|
45
|
-
data: Record<string, any>;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export async function patchDocument(props: PatchDocumentProps): Promise<void> {
|
|
49
|
-
const { entity, documentId, data } = props;
|
|
50
|
-
await vtexFetch<any>(`/api/dataentities/${entity}/documents/${documentId}`, {
|
|
51
|
-
method: "PATCH",
|
|
52
|
-
body: JSON.stringify(removeEmptyFields(data)),
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export interface MasterDataSearchResult {
|
|
57
|
-
id: string;
|
|
58
|
-
accountId: string;
|
|
59
|
-
accountName: string;
|
|
60
|
-
dataEntityId: string;
|
|
61
|
-
[key: string]: any;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export interface SearchDocumentsProps {
|
|
65
|
-
entity: string;
|
|
66
|
-
filter: string;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Simple search — kept for backward compat.
|
|
71
|
-
*/
|
|
72
|
-
export async function searchDocuments<T = MasterDataSearchResult>(
|
|
73
|
-
props: SearchDocumentsProps,
|
|
74
|
-
): Promise<T[]> {
|
|
75
|
-
const { entity, filter } = props;
|
|
76
|
-
return vtexFetch<T[]>(`/api/dataentities/${entity}/search?_where=${encodeURIComponent(filter)}`);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Full MasterData search with pagination, field selection, and sorting.
|
|
81
|
-
* Ported from deco-cx/apps vtex/loaders/masterdata/searchDocuments.ts
|
|
82
|
-
*
|
|
83
|
-
* @see https://developers.vtex.com/docs/api-reference/masterdata-api#get-/api/dataentities/-acronym-/search
|
|
84
|
-
*/
|
|
85
|
-
export interface SearchDocumentsFullProps {
|
|
86
|
-
acronym: string;
|
|
87
|
-
fields?: string;
|
|
88
|
-
where?: string;
|
|
89
|
-
sort?: string;
|
|
90
|
-
/** @default 10 (max 100) */
|
|
91
|
-
take?: number;
|
|
92
|
-
/** @default 0 */
|
|
93
|
-
skip?: number;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export async function searchDocumentsFull<T = Record<string, unknown>>(
|
|
97
|
-
props: SearchDocumentsFullProps,
|
|
98
|
-
): Promise<T[]> {
|
|
99
|
-
const { acronym, fields, where, sort, skip = 0, take = 10 } = props;
|
|
100
|
-
const from = Math.max(skip, 0);
|
|
101
|
-
const to = from + Math.min(100, take);
|
|
102
|
-
|
|
103
|
-
const params = new URLSearchParams();
|
|
104
|
-
if (fields) params.set("_fields", fields);
|
|
105
|
-
if (where) params.set("_where", where);
|
|
106
|
-
if (sort) params.set("_sort", sort);
|
|
107
|
-
|
|
108
|
-
const headers: Record<string, string> = {
|
|
109
|
-
accept: "application/vnd.vtex.ds.v10+json",
|
|
110
|
-
"content-type": "application/json",
|
|
111
|
-
"REST-Range": `resources=${from}-${to}`,
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
return vtexFetchResponse(`/api/dataentities/${acronym}/search?${params}`, {
|
|
115
|
-
headers,
|
|
116
|
-
}).then((res) => res.json());
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// ---------------------------------------------------------------------------
|
|
120
|
-
// Attachments (file upload)
|
|
121
|
-
// ---------------------------------------------------------------------------
|
|
122
|
-
|
|
123
|
-
export interface UploadAttachmentOpts {
|
|
124
|
-
entity: string;
|
|
125
|
-
documentId: string;
|
|
126
|
-
field: string;
|
|
127
|
-
fileName: string;
|
|
128
|
-
/** Base64-encoded file content */
|
|
129
|
-
fileBase64: string;
|
|
130
|
-
contentType: string;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Upload a file attachment to a MasterData document.
|
|
135
|
-
* Uses the VTEX MasterData attachment API with appKey/appToken auth.
|
|
136
|
-
*/
|
|
137
|
-
export async function uploadAttachment(opts: UploadAttachmentOpts): Promise<{ ok: true }> {
|
|
138
|
-
const { entity, documentId, field, fileName, fileBase64, contentType } = opts;
|
|
139
|
-
const config = getVtexConfig();
|
|
140
|
-
const url = `https://${config.account}.vtexcommercestable.${config.domain ?? "com.br"}/api/dataentities/${entity}/documents/${documentId}/${field}/attachments`;
|
|
141
|
-
|
|
142
|
-
const binary = atob(fileBase64);
|
|
143
|
-
const bytes = new Uint8Array(binary.length);
|
|
144
|
-
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
145
|
-
|
|
146
|
-
const blob = new Blob([bytes], { type: contentType });
|
|
147
|
-
const formData = new FormData();
|
|
148
|
-
formData.append("file", blob, fileName);
|
|
149
|
-
|
|
150
|
-
const headers: Record<string, string> = {};
|
|
151
|
-
if (config.appKey && config.appToken) {
|
|
152
|
-
headers["X-VTEX-API-AppKey"] = config.appKey;
|
|
153
|
-
headers["X-VTEX-API-AppToken"] = config.appToken;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
const response = await getVtexFetch()(url, {
|
|
157
|
-
method: "POST",
|
|
158
|
-
headers,
|
|
159
|
-
body: formData,
|
|
160
|
-
operation: "masterdata.attachment.upload",
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
if (!response.ok) {
|
|
164
|
-
throw new Error(`VTEX attachment upload failed: ${response.status} ${response.statusText}`);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return { ok: true };
|
|
168
|
-
}
|
package/src/actions/misc.ts
DELETED
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Miscellaneous VTEX actions that don't warrant their own module.
|
|
3
|
-
* Ported from deco-cx/apps:
|
|
4
|
-
* - vtex/actions/notifyme.ts
|
|
5
|
-
* - vtex/actions/analytics/sendEvent.ts
|
|
6
|
-
* - vtex/actions/review/submit.ts
|
|
7
|
-
* - vtex/actions/payment/deletePaymentToken.ts
|
|
8
|
-
* @see https://developers.vtex.com/docs/api-reference
|
|
9
|
-
*/
|
|
10
|
-
import { getVtexConfig, getVtexFetch, vtexFetch } from "../client";
|
|
11
|
-
import { buildAuthCookieHeader, VTEX_AUTH_COOKIE } from "../utils/vtexId";
|
|
12
|
-
|
|
13
|
-
// ---------------------------------------------------------------------------
|
|
14
|
-
// Types
|
|
15
|
-
// ---------------------------------------------------------------------------
|
|
16
|
-
|
|
17
|
-
export interface NotifyMeProps {
|
|
18
|
-
email: string;
|
|
19
|
-
skuId: string;
|
|
20
|
-
name?: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface ReviewData {
|
|
24
|
-
productId: string;
|
|
25
|
-
rating: number;
|
|
26
|
-
title: string;
|
|
27
|
-
text: string;
|
|
28
|
-
reviewerName: string;
|
|
29
|
-
approved: boolean;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export type AnalyticsEvent =
|
|
33
|
-
| {
|
|
34
|
-
type: "session.ping";
|
|
35
|
-
url: string;
|
|
36
|
-
}
|
|
37
|
-
| {
|
|
38
|
-
type: "page.cart";
|
|
39
|
-
products: { productId: string; quantity: number }[];
|
|
40
|
-
}
|
|
41
|
-
| {
|
|
42
|
-
type: "page.empty_cart";
|
|
43
|
-
products: Record<string, never>;
|
|
44
|
-
}
|
|
45
|
-
| {
|
|
46
|
-
type: "page.confirmation";
|
|
47
|
-
order: string;
|
|
48
|
-
products: { productId: string; quantity: number; price: number }[];
|
|
49
|
-
}
|
|
50
|
-
| {
|
|
51
|
-
type: "search.click";
|
|
52
|
-
position: number;
|
|
53
|
-
text: string;
|
|
54
|
-
productId: string;
|
|
55
|
-
url: string;
|
|
56
|
-
}
|
|
57
|
-
| {
|
|
58
|
-
type: "search.query";
|
|
59
|
-
url: string;
|
|
60
|
-
text: string;
|
|
61
|
-
misspelled: boolean;
|
|
62
|
-
match: number;
|
|
63
|
-
operator: string;
|
|
64
|
-
locale: string;
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
export interface ISCookies {
|
|
68
|
-
anonymous: string;
|
|
69
|
-
session: string;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export interface DeletePaymentTokenResult {
|
|
73
|
-
deletePaymentToken: boolean;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// ---------------------------------------------------------------------------
|
|
77
|
-
// GraphQL helper (myvtex.com private graphql)
|
|
78
|
-
// ---------------------------------------------------------------------------
|
|
79
|
-
|
|
80
|
-
interface GqlResponse<T> {
|
|
81
|
-
data: T;
|
|
82
|
-
errors?: Array<{ message: string }>;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
async function gql<T>(
|
|
86
|
-
query: string,
|
|
87
|
-
variables: Record<string, unknown>,
|
|
88
|
-
authCookie: string,
|
|
89
|
-
): Promise<T> {
|
|
90
|
-
const { account } = getVtexConfig();
|
|
91
|
-
const result = await vtexFetch<GqlResponse<T>>(
|
|
92
|
-
`https://${account}.myvtex.com/_v/private/graphql/v1`,
|
|
93
|
-
{
|
|
94
|
-
method: "POST",
|
|
95
|
-
body: JSON.stringify({ query, variables }),
|
|
96
|
-
headers: { Cookie: buildAuthCookieHeader(authCookie, account) },
|
|
97
|
-
},
|
|
98
|
-
);
|
|
99
|
-
if (result.errors?.length) {
|
|
100
|
-
throw new Error(`GraphQL error: ${result.errors[0].message}`);
|
|
101
|
-
}
|
|
102
|
-
return result.data;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// ---------------------------------------------------------------------------
|
|
106
|
-
// Mutation
|
|
107
|
-
// ---------------------------------------------------------------------------
|
|
108
|
-
|
|
109
|
-
const DELETE_PAYMENT_TOKEN = `mutation DeleteCreditCardToken($tokenId: ID!) {
|
|
110
|
-
deletePaymentToken(tokenId: $tokenId) @context(provider: "vtex.my-cards-graphql@2.x")
|
|
111
|
-
}`;
|
|
112
|
-
|
|
113
|
-
// ---------------------------------------------------------------------------
|
|
114
|
-
// Actions
|
|
115
|
-
// ---------------------------------------------------------------------------
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Register a "notify me when back in stock" request.
|
|
119
|
-
* Uses the legacy VTEX .aspx form endpoint (FormData, not JSON).
|
|
120
|
-
*/
|
|
121
|
-
export async function notifyMe(props: NotifyMeProps): Promise<void> {
|
|
122
|
-
const { account } = getVtexConfig();
|
|
123
|
-
const { email, skuId, name = "" } = props;
|
|
124
|
-
|
|
125
|
-
const form = new FormData();
|
|
126
|
-
form.append("notifymeClientName", name);
|
|
127
|
-
form.append("notifymeClientEmail", email);
|
|
128
|
-
form.append("notifymeIdSku", skuId);
|
|
129
|
-
|
|
130
|
-
await getVtexFetch()(`https://${account}.vtexcommercestable.com.br/no-cache/AviseMe.aspx`, {
|
|
131
|
-
method: "POST",
|
|
132
|
-
body: form,
|
|
133
|
-
operation: "notifyme",
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Send an Intelligent Search analytics event to sp.vtex.com.
|
|
139
|
-
*
|
|
140
|
-
* @param event - The typed event payload.
|
|
141
|
-
* @param isCookies - IS session tracking IDs (anonymous + session).
|
|
142
|
-
* In the original, these came from getISCookiesFromBag(ctx).
|
|
143
|
-
* @param userAgent - Forwarded user-agent string.
|
|
144
|
-
*/
|
|
145
|
-
export async function sendEvent(
|
|
146
|
-
event: AnalyticsEvent,
|
|
147
|
-
isCookies: ISCookies,
|
|
148
|
-
userAgent?: string,
|
|
149
|
-
): Promise<void> {
|
|
150
|
-
const { account } = getVtexConfig();
|
|
151
|
-
|
|
152
|
-
await getVtexFetch()(`https://sp.vtex.com/event-api/v1/${account}/event`, {
|
|
153
|
-
method: "POST",
|
|
154
|
-
headers: { "Content-Type": "application/json" },
|
|
155
|
-
body: JSON.stringify({
|
|
156
|
-
...event,
|
|
157
|
-
...isCookies,
|
|
158
|
-
agent: userAgent || "deco-sites/apps",
|
|
159
|
-
}),
|
|
160
|
-
operation: "analytics.event",
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Submit a product review via the Reviews & Ratings API.
|
|
166
|
-
* Hits POST https://{account}.myvtex.com/reviews-and-ratings/api/review.
|
|
167
|
-
* Auth is passed via the `VtexIdclientAutCookie` header (not a Cookie header).
|
|
168
|
-
*/
|
|
169
|
-
export async function submitReview(data: ReviewData, authCookie: string): Promise<unknown> {
|
|
170
|
-
const { account } = getVtexConfig();
|
|
171
|
-
|
|
172
|
-
return vtexFetch<unknown>(`https://${account}.myvtex.com/reviews-and-ratings/api/review`, {
|
|
173
|
-
method: "POST",
|
|
174
|
-
body: JSON.stringify(data),
|
|
175
|
-
headers: { [VTEX_AUTH_COOKIE]: authCookie },
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Delete a saved payment token (credit card) for the authenticated user.
|
|
181
|
-
* Uses the my-cards-graphql VTEX IO app.
|
|
182
|
-
*/
|
|
183
|
-
export async function deletePaymentToken(
|
|
184
|
-
tokenId: string,
|
|
185
|
-
authCookie: string,
|
|
186
|
-
): Promise<DeletePaymentTokenResult> {
|
|
187
|
-
return gql<DeletePaymentTokenResult>(DELETE_PAYMENT_TOKEN, { tokenId }, authCookie);
|
|
188
|
-
}
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* VTEX Newsletter actions.
|
|
3
|
-
* Ported from deco-cx/apps:
|
|
4
|
-
* - vtex/actions/newsletter/subscribe.ts
|
|
5
|
-
* - vtex/actions/newsletter/updateNewsletterOptIn.ts
|
|
6
|
-
* @see https://developers.vtex.com/docs/guides/newsletter
|
|
7
|
-
*/
|
|
8
|
-
import { getVtexConfig, getVtexFetch, vtexFetch } from "../client";
|
|
9
|
-
import { buildAuthCookieHeader } from "../utils/vtexId";
|
|
10
|
-
|
|
11
|
-
// ---------------------------------------------------------------------------
|
|
12
|
-
// Types
|
|
13
|
-
// ---------------------------------------------------------------------------
|
|
14
|
-
|
|
15
|
-
export interface SubscribeProps {
|
|
16
|
-
email: string;
|
|
17
|
-
name?: string;
|
|
18
|
-
page?: string;
|
|
19
|
-
part?: string;
|
|
20
|
-
/** Intentionally preserving the original typo from the VTEX legacy form field. */
|
|
21
|
-
campaing?: string;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// ---------------------------------------------------------------------------
|
|
25
|
-
// GraphQL helper (myvtex.com private graphql)
|
|
26
|
-
// ---------------------------------------------------------------------------
|
|
27
|
-
|
|
28
|
-
interface GqlResponse<T> {
|
|
29
|
-
data: T;
|
|
30
|
-
errors?: Array<{ message: string }>;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async function gql<T>(
|
|
34
|
-
query: string,
|
|
35
|
-
variables: Record<string, unknown>,
|
|
36
|
-
authCookie: string,
|
|
37
|
-
): Promise<T> {
|
|
38
|
-
const { account } = getVtexConfig();
|
|
39
|
-
const result = await vtexFetch<GqlResponse<T>>(
|
|
40
|
-
`https://${account}.myvtex.com/_v/private/graphql/v1`,
|
|
41
|
-
{
|
|
42
|
-
method: "POST",
|
|
43
|
-
body: JSON.stringify({ query, variables }),
|
|
44
|
-
headers: { Cookie: buildAuthCookieHeader(authCookie, account) },
|
|
45
|
-
},
|
|
46
|
-
);
|
|
47
|
-
if (result.errors?.length) {
|
|
48
|
-
throw new Error(`GraphQL error: ${result.errors[0].message}`);
|
|
49
|
-
}
|
|
50
|
-
return result.data;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// ---------------------------------------------------------------------------
|
|
54
|
-
// Mutation
|
|
55
|
-
// ---------------------------------------------------------------------------
|
|
56
|
-
|
|
57
|
-
const SUBSCRIBE_NEWSLETTER = `mutation SubscribeNewsletter($email: String!, $isNewsletterOptIn: Boolean!) {
|
|
58
|
-
subscribeNewsletter(email: $email, isNewsletterOptIn: $isNewsletterOptIn) @context(provider: "vtex.store-graphql@2.x")
|
|
59
|
-
}`;
|
|
60
|
-
|
|
61
|
-
// ---------------------------------------------------------------------------
|
|
62
|
-
// Actions
|
|
63
|
-
// ---------------------------------------------------------------------------
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Subscribe to the newsletter via the legacy VTEX .aspx form endpoint.
|
|
67
|
-
* Uses raw fetch because the body is FormData (not JSON).
|
|
68
|
-
*/
|
|
69
|
-
export async function subscribe(props: SubscribeProps): Promise<void> {
|
|
70
|
-
const { account } = getVtexConfig();
|
|
71
|
-
const {
|
|
72
|
-
email,
|
|
73
|
-
name = "",
|
|
74
|
-
part = "newsletter",
|
|
75
|
-
page = "_",
|
|
76
|
-
campaing = "newsletter:opt-in",
|
|
77
|
-
} = props;
|
|
78
|
-
|
|
79
|
-
const form = new FormData();
|
|
80
|
-
form.append("newsletterClientName", name);
|
|
81
|
-
form.append("newsletterClientEmail", email);
|
|
82
|
-
form.append("newsInternalPage", page);
|
|
83
|
-
form.append("newsInternalPart", part);
|
|
84
|
-
form.append("newsInternalCampaign", campaing);
|
|
85
|
-
|
|
86
|
-
await getVtexFetch()(`https://${account}.vtexcommercestable.com.br/no-cache/Newsletter.aspx`, {
|
|
87
|
-
method: "POST",
|
|
88
|
-
body: form,
|
|
89
|
-
operation: "newsletter.subscribe",
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Toggle the newsletter opt-in flag for an authenticated user.
|
|
95
|
-
* The original Deco action extracted `email` from the auth cookie payload.
|
|
96
|
-
* Here the caller must provide it explicitly.
|
|
97
|
-
*/
|
|
98
|
-
export async function updateNewsletterOptIn(
|
|
99
|
-
subscribed: boolean,
|
|
100
|
-
email: string,
|
|
101
|
-
authCookie: string,
|
|
102
|
-
): Promise<{ subscribed: boolean }> {
|
|
103
|
-
await gql<unknown>(SUBSCRIBE_NEWSLETTER, { email, isNewsletterOptIn: subscribed }, authCookie);
|
|
104
|
-
return { subscribed };
|
|
105
|
-
}
|
package/src/actions/orders.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* VTEX Order management actions.
|
|
3
|
-
* Ported from deco-cx/apps:
|
|
4
|
-
* - vtex/actions/orders/cancel.ts
|
|
5
|
-
* @see https://developers.vtex.com/docs/api-reference/orders-api
|
|
6
|
-
*/
|
|
7
|
-
import { getVtexConfig, vtexFetch } from "../client";
|
|
8
|
-
import { buildAuthCookieHeader } from "../utils/vtexId";
|
|
9
|
-
|
|
10
|
-
// ---------------------------------------------------------------------------
|
|
11
|
-
// Action
|
|
12
|
-
// ---------------------------------------------------------------------------
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Cancel an order on behalf of the authenticated user.
|
|
16
|
-
* Hits POST /api/checkout/pub/orders/{orderId}/user-cancel-request
|
|
17
|
-
* on vtexcommercestable.com.br.
|
|
18
|
-
*/
|
|
19
|
-
export async function cancelOrder(
|
|
20
|
-
orderId: string,
|
|
21
|
-
reason: string,
|
|
22
|
-
authCookie?: string,
|
|
23
|
-
): Promise<void> {
|
|
24
|
-
const headers: Record<string, string> = {};
|
|
25
|
-
if (authCookie) {
|
|
26
|
-
headers.Cookie = buildAuthCookieHeader(authCookie, getVtexConfig().account);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
await vtexFetch<unknown>(`/api/checkout/pub/orders/${orderId}/user-cancel-request`, {
|
|
30
|
-
method: "POST",
|
|
31
|
-
body: JSON.stringify({ reason }),
|
|
32
|
-
headers,
|
|
33
|
-
});
|
|
34
|
-
}
|
package/src/actions/profile.ts
DELETED
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* VTEX Profile update action (store-graphql).
|
|
3
|
-
* Ported from deco-cx/apps:
|
|
4
|
-
* - vtex/actions/profile/updateProfile.ts
|
|
5
|
-
* @see https://developers.vtex.com/docs/guides/profile-system
|
|
6
|
-
*/
|
|
7
|
-
import { getVtexConfig, getVtexFetch, vtexFetch } from "../client";
|
|
8
|
-
import { buildAuthCookieHeader } from "../utils/vtexId";
|
|
9
|
-
|
|
10
|
-
// ---------------------------------------------------------------------------
|
|
11
|
-
// Types
|
|
12
|
-
// ---------------------------------------------------------------------------
|
|
13
|
-
|
|
14
|
-
export interface ProfileInput {
|
|
15
|
-
firstName?: string;
|
|
16
|
-
lastName?: string;
|
|
17
|
-
birthDate?: string;
|
|
18
|
-
gender?: string;
|
|
19
|
-
homePhone?: string;
|
|
20
|
-
businessPhone?: string;
|
|
21
|
-
document?: string;
|
|
22
|
-
email: string;
|
|
23
|
-
tradeName?: string;
|
|
24
|
-
corporateName?: string;
|
|
25
|
-
corporateDocument?: string;
|
|
26
|
-
stateRegistration?: string;
|
|
27
|
-
isCorporate?: boolean;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface Profile {
|
|
31
|
-
cacheId: string;
|
|
32
|
-
firstName: string;
|
|
33
|
-
lastName: string;
|
|
34
|
-
birthDate: string;
|
|
35
|
-
gender: string;
|
|
36
|
-
homePhone: string;
|
|
37
|
-
businessPhone: string;
|
|
38
|
-
document: string;
|
|
39
|
-
email: string;
|
|
40
|
-
tradeName: string;
|
|
41
|
-
corporateName: string;
|
|
42
|
-
corporateDocument: string;
|
|
43
|
-
stateRegistration: string;
|
|
44
|
-
isCorporate: boolean;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// ---------------------------------------------------------------------------
|
|
48
|
-
// GraphQL helper (myvtex.com private graphql)
|
|
49
|
-
// ---------------------------------------------------------------------------
|
|
50
|
-
|
|
51
|
-
interface GqlResponse<T> {
|
|
52
|
-
data: T;
|
|
53
|
-
errors?: Array<{ message: string }>;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async function gql<T>(
|
|
57
|
-
query: string,
|
|
58
|
-
variables: Record<string, unknown>,
|
|
59
|
-
authCookie: string,
|
|
60
|
-
): Promise<T> {
|
|
61
|
-
const { account } = getVtexConfig();
|
|
62
|
-
const result = await vtexFetch<GqlResponse<T>>(
|
|
63
|
-
`https://${account}.myvtex.com/_v/private/graphql/v1`,
|
|
64
|
-
{
|
|
65
|
-
method: "POST",
|
|
66
|
-
body: JSON.stringify({ query, variables }),
|
|
67
|
-
headers: { Cookie: buildAuthCookieHeader(authCookie, account) },
|
|
68
|
-
},
|
|
69
|
-
);
|
|
70
|
-
if (result.errors?.length) {
|
|
71
|
-
throw new Error(`GraphQL error: ${result.errors[0].message}`);
|
|
72
|
-
}
|
|
73
|
-
return result.data;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// ---------------------------------------------------------------------------
|
|
77
|
-
// Mutation
|
|
78
|
-
// ---------------------------------------------------------------------------
|
|
79
|
-
|
|
80
|
-
const UPDATE_PROFILE = `mutation UpdateProfile($input: ProfileInput!) {
|
|
81
|
-
updateProfile(fields: $input) @context(provider: "vtex.store-graphql") {
|
|
82
|
-
cacheId
|
|
83
|
-
firstName
|
|
84
|
-
lastName
|
|
85
|
-
birthDate
|
|
86
|
-
gender
|
|
87
|
-
homePhone
|
|
88
|
-
businessPhone
|
|
89
|
-
document
|
|
90
|
-
email
|
|
91
|
-
tradeName
|
|
92
|
-
corporateName
|
|
93
|
-
corporateDocument
|
|
94
|
-
stateRegistration
|
|
95
|
-
isCorporate
|
|
96
|
-
}
|
|
97
|
-
}`;
|
|
98
|
-
|
|
99
|
-
// ---------------------------------------------------------------------------
|
|
100
|
-
// Action
|
|
101
|
-
// ---------------------------------------------------------------------------
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Update user profile.
|
|
105
|
-
* The original Deco action extracted `email` from the auth cookie payload.
|
|
106
|
-
* Here the caller must provide it explicitly.
|
|
107
|
-
*/
|
|
108
|
-
export async function updateProfile(
|
|
109
|
-
fields: Omit<ProfileInput, "email">,
|
|
110
|
-
email: string,
|
|
111
|
-
authCookie: string,
|
|
112
|
-
): Promise<Profile> {
|
|
113
|
-
const { updateProfile: profile } = await gql<{ updateProfile: Profile }>(
|
|
114
|
-
UPDATE_PROFILE,
|
|
115
|
-
{ input: { ...fields, email } },
|
|
116
|
-
authCookie,
|
|
117
|
-
);
|
|
118
|
-
return profile;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// ---------------------------------------------------------------------------
|
|
122
|
-
// Request-aware wrappers (for COMMERCE_LOADERS / invoke proxy)
|
|
123
|
-
// ---------------------------------------------------------------------------
|
|
124
|
-
|
|
125
|
-
import { getCurrentProfile } from "../loaders/profile";
|
|
126
|
-
import { getVtexCookies } from "../utils/cookies";
|
|
127
|
-
import { deletePaymentToken } from "./misc";
|
|
128
|
-
import { updateNewsletterOptIn } from "./newsletter";
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Normalize birthDate strings to ISO 8601.
|
|
132
|
-
* Handles dd/mm/yyyy (Brazilian format), yyyy-mm-dd, and full ISO.
|
|
133
|
-
*/
|
|
134
|
-
function normalizeBirthDate(profile: Record<string, any>): void {
|
|
135
|
-
if (!profile.birthDate || typeof profile.birthDate !== "string") return;
|
|
136
|
-
const ddmmyyyy = profile.birthDate.match(/^(\d{2})\/(\d{2})\/(\d{4})$/);
|
|
137
|
-
if (ddmmyyyy) {
|
|
138
|
-
profile.birthDate = `${ddmmyyyy[3]}-${ddmmyyyy[2]}-${ddmmyyyy[1]}T00:00:00.000Z`;
|
|
139
|
-
} else if (!profile.birthDate.includes("T")) {
|
|
140
|
-
const isoMatch = profile.birthDate.match(/(\d{4})-(\d{2})-(\d{2})/);
|
|
141
|
-
if (isoMatch) {
|
|
142
|
-
profile.birthDate = `${isoMatch[0]}T00:00:00.000Z`;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Update user profile via VTEX IO GraphQL. Handles cookie extraction,
|
|
149
|
-
* birthDate normalization, and undefined-key cleanup.
|
|
150
|
-
*/
|
|
151
|
-
export async function updateProfileFromRequest(
|
|
152
|
-
props: Record<string, any>,
|
|
153
|
-
request: Request,
|
|
154
|
-
): Promise<any> {
|
|
155
|
-
const { account } = getVtexConfig();
|
|
156
|
-
const cookie = getVtexCookies(request);
|
|
157
|
-
const profile = { ...props };
|
|
158
|
-
normalizeBirthDate(profile);
|
|
159
|
-
for (const key of Object.keys(profile)) {
|
|
160
|
-
if (profile[key] === undefined) delete profile[key];
|
|
161
|
-
}
|
|
162
|
-
const QUERY = `mutation UpdateProfile($profile: ProfileInput!) {
|
|
163
|
-
updateProfile(fields: $profile) @context(provider: "vtex.store-graphql@2.x") {
|
|
164
|
-
cacheId firstName lastName email document gender homePhone
|
|
165
|
-
businessPhone birthDate isCorporate corporateName
|
|
166
|
-
corporateDocument tradeName stateRegistration
|
|
167
|
-
}
|
|
168
|
-
}`;
|
|
169
|
-
const res = await getVtexFetch()(`https://${account}.myvtex.com/_v/private/graphql/v1`, {
|
|
170
|
-
method: "POST",
|
|
171
|
-
body: JSON.stringify({ query: QUERY, variables: { profile } }),
|
|
172
|
-
headers: { "Content-Type": "application/json", cookie },
|
|
173
|
-
operation: "io.graphql.UpdateProfile",
|
|
174
|
-
});
|
|
175
|
-
return res.json();
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
export async function newsletterProfileFromRequest(
|
|
179
|
-
props: Record<string, any>,
|
|
180
|
-
request: Request,
|
|
181
|
-
): Promise<any> {
|
|
182
|
-
const cookie = request.headers.get("cookie") ?? "";
|
|
183
|
-
return updateNewsletterOptIn(props.isNewsletterOptIn, props.email, cookie);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
export async function deletePaymentFromRequest(
|
|
187
|
-
props: Record<string, any>,
|
|
188
|
-
request: Request,
|
|
189
|
-
): Promise<any> {
|
|
190
|
-
const cookie = getVtexCookies(request);
|
|
191
|
-
return deletePaymentToken(props.id, cookie);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
export async function getPasswordLastUpdate(
|
|
195
|
-
_props: Record<string, any>,
|
|
196
|
-
request: Request,
|
|
197
|
-
): Promise<string | null> {
|
|
198
|
-
const cookie = getVtexCookies(request);
|
|
199
|
-
const profile = await getCurrentProfile(cookie);
|
|
200
|
-
return profile?.passwordLastUpdate ?? null;
|
|
201
|
-
}
|