@decocms/apps 0.20.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.
Files changed (113) hide show
  1. package/.github/workflows/release.yml +34 -0
  2. package/.releaserc.json +25 -0
  3. package/commerce/components/Image.tsx +209 -0
  4. package/commerce/components/JsonLd.tsx +285 -0
  5. package/commerce/sdk/analytics.ts +24 -0
  6. package/commerce/sdk/formatPrice.ts +23 -0
  7. package/commerce/sdk/url.ts +9 -0
  8. package/commerce/sdk/useOffer.ts +75 -0
  9. package/commerce/sdk/useVariantPossibilities.ts +43 -0
  10. package/commerce/types/commerce.ts +1105 -0
  11. package/commerce/utils/canonical.ts +11 -0
  12. package/commerce/utils/constants.ts +9 -0
  13. package/commerce/utils/filters.ts +10 -0
  14. package/commerce/utils/productToAnalyticsItem.ts +67 -0
  15. package/commerce/utils/stateByZip.ts +50 -0
  16. package/knip.json +19 -0
  17. package/package.json +77 -0
  18. package/shopify/actions/cart/addItems.ts +37 -0
  19. package/shopify/actions/cart/updateCoupons.ts +32 -0
  20. package/shopify/actions/cart/updateItems.ts +32 -0
  21. package/shopify/actions/user/signIn.ts +45 -0
  22. package/shopify/actions/user/signUp.ts +36 -0
  23. package/shopify/client.ts +58 -0
  24. package/shopify/index.ts +32 -0
  25. package/shopify/init.ts +40 -0
  26. package/shopify/loaders/ProductDetailsPage.ts +35 -0
  27. package/shopify/loaders/ProductList.ts +101 -0
  28. package/shopify/loaders/ProductListingPage.ts +180 -0
  29. package/shopify/loaders/RelatedProducts.ts +45 -0
  30. package/shopify/loaders/cart.ts +73 -0
  31. package/shopify/loaders/shop.ts +40 -0
  32. package/shopify/loaders/user.ts +44 -0
  33. package/shopify/utils/admin/admin.ts +57 -0
  34. package/shopify/utils/admin/queries.ts +29 -0
  35. package/shopify/utils/cart.ts +28 -0
  36. package/shopify/utils/cookies.ts +85 -0
  37. package/shopify/utils/enums.ts +438 -0
  38. package/shopify/utils/graphql.ts +69 -0
  39. package/shopify/utils/storefront/queries.ts +530 -0
  40. package/shopify/utils/storefront/storefront.graphql.gen.ts +113 -0
  41. package/shopify/utils/transform.ts +436 -0
  42. package/shopify/utils/types.ts +191 -0
  43. package/shopify/utils/user.ts +23 -0
  44. package/shopify/utils/utils.ts +164 -0
  45. package/tsconfig.json +11 -0
  46. package/vtex/README.md +6 -0
  47. package/vtex/actions/address.ts +211 -0
  48. package/vtex/actions/auth.ts +337 -0
  49. package/vtex/actions/checkout.ts +497 -0
  50. package/vtex/actions/index.ts +11 -0
  51. package/vtex/actions/masterData.ts +170 -0
  52. package/vtex/actions/misc.ts +196 -0
  53. package/vtex/actions/newsletter.ts +108 -0
  54. package/vtex/actions/orders.ts +37 -0
  55. package/vtex/actions/profile.ts +119 -0
  56. package/vtex/actions/session.ts +87 -0
  57. package/vtex/actions/trigger.ts +43 -0
  58. package/vtex/actions/wishlist.ts +116 -0
  59. package/vtex/client.ts +423 -0
  60. package/vtex/hooks/index.ts +4 -0
  61. package/vtex/hooks/useAutocomplete.ts +89 -0
  62. package/vtex/hooks/useCart.ts +219 -0
  63. package/vtex/hooks/useUser.ts +78 -0
  64. package/vtex/hooks/useWishlist.ts +119 -0
  65. package/vtex/index.ts +14 -0
  66. package/vtex/inline-loaders/productDetailsPage.ts +75 -0
  67. package/vtex/inline-loaders/productList.ts +163 -0
  68. package/vtex/inline-loaders/productListingPage.ts +447 -0
  69. package/vtex/inline-loaders/relatedProducts.ts +83 -0
  70. package/vtex/inline-loaders/suggestions.ts +49 -0
  71. package/vtex/inline-loaders/workflowProducts.ts +68 -0
  72. package/vtex/invoke.ts +202 -0
  73. package/vtex/loaders/address.ts +120 -0
  74. package/vtex/loaders/brands.ts +51 -0
  75. package/vtex/loaders/cart.ts +49 -0
  76. package/vtex/loaders/catalog.ts +165 -0
  77. package/vtex/loaders/collections.ts +57 -0
  78. package/vtex/loaders/index.ts +19 -0
  79. package/vtex/loaders/legacy.ts +671 -0
  80. package/vtex/loaders/logistics.ts +115 -0
  81. package/vtex/loaders/navbar.ts +29 -0
  82. package/vtex/loaders/orders.ts +103 -0
  83. package/vtex/loaders/pageType.ts +62 -0
  84. package/vtex/loaders/payment.ts +107 -0
  85. package/vtex/loaders/profile.ts +138 -0
  86. package/vtex/loaders/promotion.ts +33 -0
  87. package/vtex/loaders/search.ts +127 -0
  88. package/vtex/loaders/session.ts +91 -0
  89. package/vtex/loaders/user.ts +89 -0
  90. package/vtex/loaders/wishlist.ts +89 -0
  91. package/vtex/loaders/wishlistProducts.ts +81 -0
  92. package/vtex/loaders/workflow.ts +323 -0
  93. package/vtex/logo.png +0 -0
  94. package/vtex/middleware.ts +229 -0
  95. package/vtex/types.ts +248 -0
  96. package/vtex/utils/batch.ts +21 -0
  97. package/vtex/utils/cookies.ts +76 -0
  98. package/vtex/utils/enrichment.ts +540 -0
  99. package/vtex/utils/fetchCache.ts +150 -0
  100. package/vtex/utils/index.ts +17 -0
  101. package/vtex/utils/intelligentSearch.ts +84 -0
  102. package/vtex/utils/legacy.ts +155 -0
  103. package/vtex/utils/pickAndOmit.ts +30 -0
  104. package/vtex/utils/proxy.ts +196 -0
  105. package/vtex/utils/resourceRange.ts +10 -0
  106. package/vtex/utils/segment.ts +163 -0
  107. package/vtex/utils/similars.ts +38 -0
  108. package/vtex/utils/sitemap.ts +133 -0
  109. package/vtex/utils/slugCache.ts +32 -0
  110. package/vtex/utils/slugify.ts +13 -0
  111. package/vtex/utils/transform.ts +1331 -0
  112. package/vtex/utils/types.ts +1884 -0
  113. package/vtex/utils/vtexId.ts +103 -0
@@ -0,0 +1,497 @@
1
+ /**
2
+ * VTEX Checkout API actions.
3
+ * Pure async functions using vtexFetch. Require configureVtex() to have been called.
4
+ *
5
+ * Ported from deco-cx/apps vtex/actions/cart/*.ts
6
+ * @see https://developers.vtex.com/docs/api-reference/checkout-api
7
+ */
8
+
9
+ import type { VtexFetchResult } from "../client";
10
+ import { getVtexConfig, vtexFetch, vtexFetchWithCookies } from "../client";
11
+ import type { OrderForm } from "../types";
12
+
13
+ export const DEFAULT_EXPECTED_SECTIONS = [
14
+ "items",
15
+ "totalizers",
16
+ "clientProfileData",
17
+ "shippingData",
18
+ "paymentData",
19
+ "sellers",
20
+ "messages",
21
+ "marketingData",
22
+ "clientPreferencesData",
23
+ "storePreferencesData",
24
+ "giftRegistryData",
25
+ "ratesAndBenefitsData",
26
+ "openTextField",
27
+ "commercialConditionData",
28
+ "customData",
29
+ ];
30
+
31
+ function scParam(): string {
32
+ const sc = getVtexConfig().salesChannel;
33
+ return sc ? `sc=${sc}` : "";
34
+ }
35
+
36
+ function appendSc(params: URLSearchParams): URLSearchParams {
37
+ const sc = getVtexConfig().salesChannel;
38
+ if (sc) params.set("sc", sc);
39
+ return params;
40
+ }
41
+
42
+ function forceHttpsOnAssets(orderForm: OrderForm): OrderForm {
43
+ if (!orderForm?.items) return orderForm;
44
+ return {
45
+ ...orderForm,
46
+ items: orderForm.items.map((item: any) => ({
47
+ ...item,
48
+ imageUrl: item.imageUrl?.replace(/^http:/, "https:"),
49
+ })),
50
+ };
51
+ }
52
+
53
+ // ---------------------------------------------------------------------------
54
+ // Cart (OrderForm) — core CRUD
55
+ // ---------------------------------------------------------------------------
56
+
57
+ export async function getOrCreateCart(
58
+ orderFormId?: string,
59
+ cookieHeader?: string,
60
+ ): Promise<VtexFetchResult<OrderForm>> {
61
+ const sc = scParam();
62
+ const headers: Record<string, string> = {};
63
+ if (cookieHeader) headers["cookie"] = cookieHeader;
64
+
65
+ if (orderFormId) {
66
+ const result = await vtexFetchWithCookies<OrderForm>(
67
+ `/api/checkout/pub/orderForm/${orderFormId}${sc ? `?${sc}` : ""}`,
68
+ { headers },
69
+ );
70
+ result.data = forceHttpsOnAssets(result.data);
71
+ return result;
72
+ }
73
+ const result = await vtexFetchWithCookies<OrderForm>(
74
+ `/api/checkout/pub/orderForm${sc ? `?${sc}` : ""}`,
75
+ {
76
+ method: "POST",
77
+ body: JSON.stringify({
78
+ expectedOrderFormSections: DEFAULT_EXPECTED_SECTIONS,
79
+ }),
80
+ headers,
81
+ },
82
+ );
83
+ result.data = forceHttpsOnAssets(result.data);
84
+ return result;
85
+ }
86
+
87
+ export async function addItemsToCart(
88
+ orderFormId: string,
89
+ orderItems: Array<{
90
+ id: string;
91
+ seller: string;
92
+ quantity: number;
93
+ index?: number;
94
+ price?: number;
95
+ }>,
96
+ allowedOutdatedData: string[] = ["paymentData"],
97
+ cookieHeader?: string,
98
+ ): Promise<VtexFetchResult<OrderForm>> {
99
+ const params = appendSc(new URLSearchParams());
100
+ for (const d of allowedOutdatedData) params.append("allowedOutdatedData", d);
101
+ const headers: Record<string, string> = {};
102
+ if (cookieHeader) headers["cookie"] = cookieHeader;
103
+ const result = await vtexFetchWithCookies<OrderForm>(
104
+ `/api/checkout/pub/orderForm/${orderFormId}/items?${params}`,
105
+ { method: "POST", body: JSON.stringify({ orderItems }), headers },
106
+ );
107
+ result.data = forceHttpsOnAssets(result.data);
108
+ return result;
109
+ }
110
+
111
+ export async function updateCartItems(
112
+ orderFormId: string,
113
+ orderItems: Array<{ index: number; quantity: number }>,
114
+ opts?: {
115
+ allowedOutdatedData?: string[];
116
+ noSplitItem?: boolean;
117
+ cookieHeader?: string;
118
+ },
119
+ ): Promise<VtexFetchResult<OrderForm>> {
120
+ const params = appendSc(new URLSearchParams());
121
+ for (const d of opts?.allowedOutdatedData ?? ["paymentData"]) {
122
+ params.append("allowedOutdatedData", d);
123
+ }
124
+ const headers: Record<string, string> = {};
125
+ if (opts?.cookieHeader) headers["cookie"] = opts.cookieHeader;
126
+ const result = await vtexFetchWithCookies<OrderForm>(
127
+ `/api/checkout/pub/orderForm/${orderFormId}/items/update?${params}`,
128
+ {
129
+ method: "POST",
130
+ body: JSON.stringify({
131
+ orderItems,
132
+ noSplitItem: Boolean(opts?.noSplitItem),
133
+ }),
134
+ headers,
135
+ },
136
+ );
137
+ result.data = forceHttpsOnAssets(result.data);
138
+ return result;
139
+ }
140
+
141
+ export async function removeAllItems(
142
+ orderFormId: string,
143
+ cookieHeader?: string,
144
+ ): Promise<VtexFetchResult<OrderForm>> {
145
+ const sc = scParam();
146
+ const headers: Record<string, string> = {};
147
+ if (cookieHeader) headers["cookie"] = cookieHeader;
148
+ const result = await vtexFetchWithCookies<OrderForm>(
149
+ `/api/checkout/pub/orderForm/${orderFormId}/items/removeAll${sc ? `?${sc}` : ""}`,
150
+ { method: "POST", body: JSON.stringify({}), headers },
151
+ );
152
+ result.data = forceHttpsOnAssets(result.data);
153
+ return result;
154
+ }
155
+
156
+ export async function addCouponToCart(
157
+ orderFormId: string,
158
+ text: string,
159
+ cookieHeader?: string,
160
+ ): Promise<VtexFetchResult<OrderForm>> {
161
+ const sc = scParam();
162
+ const headers: Record<string, string> = {};
163
+ if (cookieHeader) headers["cookie"] = cookieHeader;
164
+ const result = await vtexFetchWithCookies<OrderForm>(
165
+ `/api/checkout/pub/orderForm/${orderFormId}/coupons${sc ? `?${sc}` : ""}`,
166
+ { method: "POST", body: JSON.stringify({ text }), headers },
167
+ );
168
+ result.data = forceHttpsOnAssets(result.data);
169
+ return result;
170
+ }
171
+
172
+ // ---------------------------------------------------------------------------
173
+ // Cart — simulation
174
+ // ---------------------------------------------------------------------------
175
+
176
+ export interface SimulationItem {
177
+ id: number | string;
178
+ quantity: number;
179
+ seller: string;
180
+ }
181
+
182
+ export async function simulateCart(
183
+ items: SimulationItem[],
184
+ postalCode: string,
185
+ country?: string,
186
+ RnbBehavior: 0 | 1 = 1,
187
+ cookieHeader?: string,
188
+ ) {
189
+ const config = getVtexConfig();
190
+ const params = appendSc(
191
+ new URLSearchParams({ RnbBehavior: String(RnbBehavior) }),
192
+ );
193
+ const headers: Record<string, string> = {};
194
+ if (cookieHeader) headers["cookie"] = cookieHeader;
195
+ return vtexFetch<any>(`/api/checkout/pub/orderForms/simulation?${params}`, {
196
+ method: "POST",
197
+ body: JSON.stringify({
198
+ items,
199
+ postalCode,
200
+ country: country ?? config.country ?? "BRA",
201
+ }),
202
+ headers,
203
+ });
204
+ }
205
+
206
+ // ---------------------------------------------------------------------------
207
+ // Cart — offerings (services attached to items)
208
+ // ---------------------------------------------------------------------------
209
+
210
+ export async function addOffering(
211
+ orderFormId: string,
212
+ itemIndex: number,
213
+ offeringId: string | number,
214
+ expectedOrderFormSections: string[] = DEFAULT_EXPECTED_SECTIONS,
215
+ cookieHeader?: string,
216
+ ): Promise<VtexFetchResult<OrderForm>> {
217
+ const headers: Record<string, string> = {};
218
+ if (cookieHeader) headers["cookie"] = cookieHeader;
219
+ const result = await vtexFetchWithCookies<OrderForm>(
220
+ `/api/checkout/pub/orderForm/${orderFormId}/items/${itemIndex}/offerings`,
221
+ {
222
+ method: "POST",
223
+ body: JSON.stringify({
224
+ expectedOrderFormSections,
225
+ id: offeringId,
226
+ info: null,
227
+ }),
228
+ headers,
229
+ },
230
+ );
231
+ result.data = forceHttpsOnAssets(result.data);
232
+ return result;
233
+ }
234
+
235
+ export async function removeOffering(
236
+ orderFormId: string,
237
+ itemIndex: number,
238
+ offeringId: string | number,
239
+ expectedOrderFormSections: string[] = DEFAULT_EXPECTED_SECTIONS,
240
+ cookieHeader?: string,
241
+ ): Promise<VtexFetchResult<OrderForm>> {
242
+ const headers: Record<string, string> = {};
243
+ if (cookieHeader) headers["cookie"] = cookieHeader;
244
+ const result = await vtexFetchWithCookies<OrderForm>(
245
+ `/api/checkout/pub/orderForm/${orderFormId}/items/${itemIndex}/offerings/${offeringId}/remove`,
246
+ {
247
+ method: "POST",
248
+ body: JSON.stringify({ expectedOrderFormSections }),
249
+ headers,
250
+ },
251
+ );
252
+ result.data = forceHttpsOnAssets(result.data);
253
+ return result;
254
+ }
255
+
256
+ // ---------------------------------------------------------------------------
257
+ // Cart — attachments
258
+ // ---------------------------------------------------------------------------
259
+
260
+ export async function updateOrderFormAttachment(
261
+ orderFormId: string,
262
+ attachment: string,
263
+ body: Record<string, unknown>,
264
+ expectedOrderFormSections: string[] = DEFAULT_EXPECTED_SECTIONS,
265
+ cookieHeader?: string,
266
+ ): Promise<VtexFetchResult<OrderForm>> {
267
+ if (!orderFormId) throw new Error("Order form ID is required");
268
+ const headers: Record<string, string> = {};
269
+ if (cookieHeader) headers["cookie"] = cookieHeader;
270
+ const result = await vtexFetchWithCookies<OrderForm>(
271
+ `/api/checkout/pub/orderForm/${orderFormId}/attachments/${attachment}`,
272
+ {
273
+ method: "POST",
274
+ body: JSON.stringify({ expectedOrderFormSections, ...body }),
275
+ headers,
276
+ },
277
+ );
278
+ result.data = forceHttpsOnAssets(result.data);
279
+ return result;
280
+ }
281
+
282
+ export async function updateItemAttachment(
283
+ orderFormId: string,
284
+ itemIndex: number,
285
+ attachment: string,
286
+ content: Record<string, unknown>,
287
+ opts?: {
288
+ noSplitItem?: boolean;
289
+ expectedOrderFormSections?: string[];
290
+ cookieHeader?: string;
291
+ },
292
+ ): Promise<VtexFetchResult<OrderForm>> {
293
+ const sections = opts?.expectedOrderFormSections ?? DEFAULT_EXPECTED_SECTIONS;
294
+ const headers: Record<string, string> = {};
295
+ if (opts?.cookieHeader) headers["cookie"] = opts.cookieHeader;
296
+ const result = await vtexFetchWithCookies<OrderForm>(
297
+ `/api/checkout/pub/orderForm/${orderFormId}/items/${itemIndex}/attachments/${attachment}`,
298
+ {
299
+ method: "POST",
300
+ body: JSON.stringify({
301
+ content,
302
+ noSplitItem: opts?.noSplitItem ?? true,
303
+ expectedOrderFormSections: sections,
304
+ }),
305
+ headers,
306
+ },
307
+ );
308
+ result.data = forceHttpsOnAssets(result.data);
309
+ return result;
310
+ }
311
+
312
+ export async function removeItemAttachment(
313
+ orderFormId: string,
314
+ itemIndex: number,
315
+ attachment: string,
316
+ content: Record<string, unknown>,
317
+ opts?: {
318
+ noSplitItem?: boolean;
319
+ expectedOrderFormSections?: string[];
320
+ cookieHeader?: string;
321
+ },
322
+ ): Promise<VtexFetchResult<OrderForm>> {
323
+ const sections = opts?.expectedOrderFormSections ?? DEFAULT_EXPECTED_SECTIONS;
324
+ const headers: Record<string, string> = {};
325
+ if (opts?.cookieHeader) headers["cookie"] = opts.cookieHeader;
326
+ const result = await vtexFetchWithCookies<OrderForm>(
327
+ `/api/checkout/pub/orderForm/${orderFormId}/items/${itemIndex}/attachments/${attachment}`,
328
+ {
329
+ method: "DELETE",
330
+ body: JSON.stringify({
331
+ content,
332
+ noSplitItem: opts?.noSplitItem ?? true,
333
+ expectedOrderFormSections: sections,
334
+ }),
335
+ headers,
336
+ },
337
+ );
338
+ result.data = forceHttpsOnAssets(result.data);
339
+ return result;
340
+ }
341
+
342
+ // ---------------------------------------------------------------------------
343
+ // Cart — price override
344
+ // ---------------------------------------------------------------------------
345
+
346
+ export async function updateItemPrice(
347
+ orderFormId: string,
348
+ itemIndex: number,
349
+ price: number,
350
+ ): Promise<OrderForm> {
351
+ return vtexFetch<OrderForm>(
352
+ `/api/checkout/pub/orderForm/${orderFormId}/items/${itemIndex}/price`,
353
+ { method: "PUT", body: JSON.stringify({ price }) },
354
+ );
355
+ }
356
+
357
+ // ---------------------------------------------------------------------------
358
+ // Cart — selectable gifts
359
+ // ---------------------------------------------------------------------------
360
+
361
+ export async function updateSelectableGifts(
362
+ orderFormId: string,
363
+ giftId: string,
364
+ selectedGifts: Array<{ id: string; seller: string; quantity: number }>,
365
+ expectedOrderFormSections: string[] = DEFAULT_EXPECTED_SECTIONS,
366
+ cookieHeader?: string,
367
+ ): Promise<VtexFetchResult<OrderForm>> {
368
+ const headers: Record<string, string> = {};
369
+ if (cookieHeader) headers["cookie"] = cookieHeader;
370
+ const result = await vtexFetchWithCookies<OrderForm>(
371
+ `/api/checkout/pub/orderForm/${orderFormId}/selectable-gifts/${giftId}`,
372
+ {
373
+ method: "POST",
374
+ body: JSON.stringify({
375
+ expectedOrderFormSections,
376
+ selectedGifts,
377
+ id: giftId,
378
+ }),
379
+ headers,
380
+ },
381
+ );
382
+ result.data = forceHttpsOnAssets(result.data);
383
+ return result;
384
+ }
385
+
386
+ // ---------------------------------------------------------------------------
387
+ // Cart — installments
388
+ // ---------------------------------------------------------------------------
389
+
390
+ export async function getInstallments(
391
+ orderFormId: string,
392
+ paymentSystem: number,
393
+ cookieHeader?: string,
394
+ ) {
395
+ const params = new URLSearchParams({ paymentSystem: String(paymentSystem) });
396
+ appendSc(params);
397
+ const headers: Record<string, string> = {};
398
+ if (cookieHeader) headers["cookie"] = cookieHeader;
399
+ return vtexFetch<any>(
400
+ `/api/checkout/pub/orderForm/${orderFormId}/installments?${params}`,
401
+ { headers },
402
+ );
403
+ }
404
+
405
+ // ---------------------------------------------------------------------------
406
+ // Cart — profile & messages
407
+ // ---------------------------------------------------------------------------
408
+
409
+ export async function updateOrderFormProfile(
410
+ orderFormId: string,
411
+ fields: Record<string, unknown>,
412
+ opts?: { ignoreProfileData?: boolean; cookieHeader?: string },
413
+ ): Promise<VtexFetchResult<OrderForm>> {
414
+ const body = opts?.ignoreProfileData
415
+ ? { ...fields, ignoreProfileData: true }
416
+ : fields;
417
+ const headers: Record<string, string> = {};
418
+ if (opts?.cookieHeader) headers["cookie"] = opts.cookieHeader;
419
+ const result = await vtexFetchWithCookies<OrderForm>(
420
+ `/api/checkout/pub/orderForm/${orderFormId}/profile`,
421
+ { method: "PATCH", body: JSON.stringify(body), headers },
422
+ );
423
+ result.data = forceHttpsOnAssets(result.data);
424
+ return result;
425
+ }
426
+
427
+ export async function changeToAnonymousUser(
428
+ orderFormId: string,
429
+ ): Promise<OrderForm> {
430
+ return vtexFetch<OrderForm>(
431
+ `/api/checkout/changeToAnonymousUser/${orderFormId}`,
432
+ );
433
+ }
434
+
435
+ export async function clearOrderFormMessages(
436
+ orderFormId: string,
437
+ cookieHeader?: string,
438
+ ): Promise<OrderForm> {
439
+ const headers: Record<string, string> = {};
440
+ if (cookieHeader) headers["cookie"] = cookieHeader;
441
+ return vtexFetch<OrderForm>(
442
+ `/api/checkout/pub/orderForm/${orderFormId}/messages/clear`,
443
+ { method: "POST", body: JSON.stringify({}), headers },
444
+ );
445
+ }
446
+
447
+ // ---------------------------------------------------------------------------
448
+ // Shipping / Regions
449
+ // ---------------------------------------------------------------------------
450
+
451
+ export interface Seller {
452
+ id: string;
453
+ name: string;
454
+ }
455
+
456
+ export interface RegionResult {
457
+ id: string;
458
+ sellers: Seller[];
459
+ }
460
+
461
+ export async function getSellersByRegion(
462
+ postalCode: string,
463
+ salesChannel?: string,
464
+ ): Promise<RegionResult | null> {
465
+ const params = new URLSearchParams({ country: "BRA", postalCode });
466
+ const sc = salesChannel ?? getVtexConfig().salesChannel;
467
+ if (sc) params.set("sc", sc);
468
+ const resp = await vtexFetch<RegionResult[]>(
469
+ `/api/checkout/pub/regions/?${params}`,
470
+ );
471
+ return resp[0]?.sellers?.length > 0 ? resp[0] : null;
472
+ }
473
+
474
+ export async function setShippingPostalCode(
475
+ orderFormId: string,
476
+ postalCode: string,
477
+ country = "BRA",
478
+ cookieHeader?: string,
479
+ ): Promise<boolean> {
480
+ try {
481
+ const headers: Record<string, string> = {};
482
+ if (cookieHeader) headers["cookie"] = cookieHeader;
483
+ await vtexFetch<any>(
484
+ `/api/checkout/pub/orderForm/${orderFormId}/attachments/shippingData`,
485
+ {
486
+ method: "POST",
487
+ body: JSON.stringify({
488
+ selectedAddresses: [{ postalCode, country }],
489
+ }),
490
+ headers,
491
+ },
492
+ );
493
+ return true;
494
+ } catch {
495
+ return false;
496
+ }
497
+ }
@@ -0,0 +1,11 @@
1
+ export * from "./address";
2
+ export * from "./auth";
3
+ export * from "./checkout";
4
+ export * from "./masterData";
5
+ export * from "./misc";
6
+ export * from "./newsletter";
7
+ export * from "./orders";
8
+ export * from "./profile";
9
+ export * from "./session";
10
+ export * from "./trigger";
11
+ export * from "./wishlist";
@@ -0,0 +1,170 @@
1
+ /**
2
+ * VTEX MasterData v2 API actions.
3
+ * Generic CRUD operations on data entities.
4
+ */
5
+ import { getVtexConfig, 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 async function createDocument(
20
+ entity: string,
21
+ data: Record<string, any>,
22
+ ): Promise<CreateDocumentResult> {
23
+ return vtexFetch<CreateDocumentResult>(
24
+ `/api/dataentities/${entity}/documents`,
25
+ { method: "POST", body: JSON.stringify(removeEmptyFields(data)) },
26
+ );
27
+ }
28
+
29
+ export async function getDocument<T = unknown>(
30
+ entity: string,
31
+ documentId: string,
32
+ ): Promise<T> {
33
+ return vtexFetch<T>(`/api/dataentities/${entity}/documents/${documentId}`);
34
+ }
35
+
36
+ export async function patchDocument(
37
+ entity: string,
38
+ documentId: string,
39
+ data: Record<string, any>,
40
+ ): Promise<void> {
41
+ await vtexFetch<any>(`/api/dataentities/${entity}/documents/${documentId}`, {
42
+ method: "PATCH",
43
+ body: JSON.stringify(removeEmptyFields(data)),
44
+ });
45
+ }
46
+
47
+ export interface MasterDataSearchResult {
48
+ id: string;
49
+ accountId: string;
50
+ accountName: string;
51
+ dataEntityId: string;
52
+ [key: string]: any;
53
+ }
54
+
55
+ /**
56
+ * Simple search — kept for backward compat.
57
+ */
58
+ export async function searchDocuments<T = MasterDataSearchResult>(
59
+ entity: string,
60
+ filter: string,
61
+ ): Promise<T[]> {
62
+ return vtexFetch<T[]>(
63
+ `/api/dataentities/${entity}/search?_where=${encodeURIComponent(filter)}`,
64
+ );
65
+ }
66
+
67
+ /**
68
+ * Full MasterData search with pagination, field selection, and sorting.
69
+ * Ported from deco-cx/apps vtex/loaders/masterdata/searchDocuments.ts
70
+ *
71
+ * @see https://developers.vtex.com/docs/api-reference/masterdata-api#get-/api/dataentities/-acronym-/search
72
+ */
73
+ export interface SearchDocumentsOpts {
74
+ acronym: string;
75
+ fields?: string;
76
+ where?: string;
77
+ sort?: string;
78
+ /** @default 10 (max 100) */
79
+ take?: number;
80
+ /** @default 0 */
81
+ skip?: number;
82
+ /** Auth cookie header for authenticated queries */
83
+ cookieHeader?: string;
84
+ }
85
+
86
+ export async function searchDocumentsFull<T = Record<string, unknown>>(
87
+ opts: SearchDocumentsOpts,
88
+ ): Promise<T[]> {
89
+ const {
90
+ acronym,
91
+ fields,
92
+ where,
93
+ sort,
94
+ skip = 0,
95
+ take = 10,
96
+ cookieHeader,
97
+ } = opts;
98
+ const from = Math.max(skip, 0);
99
+ const to = from + Math.min(100, take);
100
+
101
+ const params = new URLSearchParams();
102
+ if (fields) params.set("_fields", fields);
103
+ if (where) params.set("_where", where);
104
+ if (sort) params.set("_sort", sort);
105
+
106
+ const headers: Record<string, string> = {
107
+ accept: "application/vnd.vtex.ds.v10+json",
108
+ "content-type": "application/json",
109
+ "REST-Range": `resources=${from}-${to}`,
110
+ };
111
+ if (cookieHeader) headers["cookie"] = cookieHeader;
112
+
113
+ return vtexFetchResponse(`/api/dataentities/${acronym}/search?${params}`, {
114
+ headers,
115
+ }).then((res) => res.json());
116
+ }
117
+
118
+ // ---------------------------------------------------------------------------
119
+ // Attachments (file upload)
120
+ // ---------------------------------------------------------------------------
121
+
122
+ export interface UploadAttachmentOpts {
123
+ entity: string;
124
+ documentId: string;
125
+ field: string;
126
+ fileName: string;
127
+ /** Base64-encoded file content */
128
+ fileBase64: string;
129
+ contentType: string;
130
+ }
131
+
132
+ /**
133
+ * Upload a file attachment to a MasterData document.
134
+ * Uses the VTEX MasterData attachment API with appKey/appToken auth.
135
+ */
136
+ export async function uploadAttachment(
137
+ opts: UploadAttachmentOpts,
138
+ ): Promise<{ ok: true }> {
139
+ const { entity, documentId, field, fileName, fileBase64, contentType } = opts;
140
+ const config = getVtexConfig();
141
+ const url = `https://${config.account}.vtexcommercestable.${config.domain ?? "com.br"}/api/dataentities/${entity}/documents/${documentId}/${field}/attachments`;
142
+
143
+ const binary = atob(fileBase64);
144
+ const bytes = new Uint8Array(binary.length);
145
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
146
+
147
+ const blob = new Blob([bytes], { type: contentType });
148
+ const formData = new FormData();
149
+ formData.append("file", blob, fileName);
150
+
151
+ const headers: Record<string, string> = {};
152
+ if (config.appKey && config.appToken) {
153
+ headers["X-VTEX-API-AppKey"] = config.appKey;
154
+ headers["X-VTEX-API-AppToken"] = config.appToken;
155
+ }
156
+
157
+ const response = await fetch(url, {
158
+ method: "POST",
159
+ headers,
160
+ body: formData,
161
+ });
162
+
163
+ if (!response.ok) {
164
+ throw new Error(
165
+ `VTEX attachment upload failed: ${response.status} ${response.statusText}`,
166
+ );
167
+ }
168
+
169
+ return { ok: true };
170
+ }