@faststore/api 4.7.0-dev.8 → 4.7.0-dev.9
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/dist/cjs/index.js +17 -9
- package/dist/cjs/index.js.map +1 -1
- package/dist/es/index.mjs +271 -251
- package/dist/es/index.mjs.map +1 -1
- package/dist/src/__generated__/schema.d.ts +8 -0
- package/dist/src/__generated__/schema.d.ts.map +1 -1
- package/dist/src/platforms/vtex/index.d.ts.map +1 -1
- package/dist/src/platforms/vtex/resolvers/index.d.ts.map +1 -1
- package/dist/src/platforms/vtex/resolvers/product.d.ts.map +1 -1
- package/dist/src/platforms/vtex/resolvers/query.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/__generated__/schema.ts +8 -0
- package/src/platforms/vtex/resolvers/product.ts +51 -17
- package/src/platforms/vtex/resolvers/query.ts +5 -4
- package/src/platforms/vtex/typeDefs/product.graphql +8 -0
|
@@ -77,7 +77,15 @@ export async function getLocalizedProductEntry(
|
|
|
77
77
|
categories: result.categories ?? [],
|
|
78
78
|
availableLinkIds: result.availableLinkIds ?? {},
|
|
79
79
|
}
|
|
80
|
-
} catch {
|
|
80
|
+
} catch (err) {
|
|
81
|
+
// Without this the fallback is silent: the breadcrumb renders the IS
|
|
82
|
+
// category names, always in the default language, and the page can be
|
|
83
|
+
// frozen in that state by `revalidate: false`.
|
|
84
|
+
console.warn(
|
|
85
|
+
`[getLocalizedProductEntry] failed to load product ${productId} for locale ${locale}:`,
|
|
86
|
+
err
|
|
87
|
+
)
|
|
88
|
+
|
|
81
89
|
return null
|
|
82
90
|
}
|
|
83
91
|
})()
|
|
@@ -314,7 +322,6 @@ export const StoreProduct: Record<string, GraphqlResolver<Root>> & {
|
|
|
314
322
|
const productId = root.isVariantOf.productId
|
|
315
323
|
const itemId = root.itemId
|
|
316
324
|
const locale = ctx.storage.locale
|
|
317
|
-
const defaultLocale = getDefaultLocale(ctx)
|
|
318
325
|
|
|
319
326
|
// availableLinkIds returns localized slug for every locale,
|
|
320
327
|
// we fetch for the current locale (reusing the request-scoped cache shared with the slug and
|
|
@@ -324,29 +331,56 @@ export const StoreProduct: Record<string, GraphqlResolver<Root>> & {
|
|
|
324
331
|
if (!entry?.availableLinkIds) return null
|
|
325
332
|
|
|
326
333
|
const { availableLinkIds } = entry
|
|
327
|
-
const { linkText } = root.isVariantOf
|
|
328
334
|
|
|
329
335
|
return configuredLocales
|
|
330
336
|
.map((configuredLocale) => {
|
|
331
|
-
//
|
|
332
|
-
//
|
|
333
|
-
//
|
|
334
|
-
//
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// Non-default locales only appear when they have a registered localized slug
|
|
340
|
-
// in availableLinkIds. Untranslated locales are omitted so they are never
|
|
341
|
-
// advertised as hreflang alternates — this keeps the hreflang cluster
|
|
342
|
-
// symmetric across all locale variants of the product (every variant emits
|
|
343
|
-
// the same set: default + translated locales). The LocalizationSelector
|
|
344
|
-
// falls back to the default slug under the target prefix for omitted locales.
|
|
337
|
+
// availableLinkIds is identical whichever locale the Dataplane is queried
|
|
338
|
+
// with, so deriving every slug from it keeps the hreflang cluster
|
|
339
|
+
// reciprocal. The Intelligent Search linkText cannot stand in for a
|
|
340
|
+
// missing entry: it is localized to the locale being browsed, which would
|
|
341
|
+
// both point other locales at a 404 and make the advertised set depend on
|
|
342
|
+
// which locale served the request.
|
|
345
343
|
const linkId = availableLinkIds[configuredLocale]
|
|
344
|
+
|
|
345
|
+
// Locales with no registered localized slug are omitted rather than
|
|
346
|
+
// guessed, so an alternate is only ever advertised for a slug the catalog
|
|
347
|
+
// actually resolves. A product with no translations advertises no
|
|
348
|
+
// alternates at all, from any locale. Navigating to an omitted locale is
|
|
349
|
+
// served by `defaultLocaleSlug`, which is free to guess precisely because
|
|
350
|
+
// it never reaches an hreflang annotation.
|
|
346
351
|
return linkId
|
|
347
352
|
? { locale: configuredLocale, slug: getSlug(linkId, itemId) }
|
|
348
353
|
: null
|
|
349
354
|
})
|
|
350
355
|
.filter((e): e is { locale: string; slug: string } => e !== null)
|
|
351
356
|
},
|
|
357
|
+
defaultLocaleSlug: async (root, _args, ctx) => {
|
|
358
|
+
if (!isLocalizationEnabled(ctx)) return null
|
|
359
|
+
|
|
360
|
+
const defaultLocale = getDefaultLocale(ctx)
|
|
361
|
+
|
|
362
|
+
if (!defaultLocale) return null
|
|
363
|
+
|
|
364
|
+
const { productId, linkText } = root.isVariantOf
|
|
365
|
+
const itemId = root.itemId
|
|
366
|
+
|
|
367
|
+
// Reuses the request-scoped entry `otherLocales` already loads, so serving
|
|
368
|
+
// both fields costs a single Dataplane call.
|
|
369
|
+
const entry = await getLocalizedProductEntry(
|
|
370
|
+
ctx,
|
|
371
|
+
productId,
|
|
372
|
+
ctx.storage.locale
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
// linkText is localized to the locale being browsed, which is what makes it
|
|
376
|
+
// unusable for hreflang. It is acceptable here because an untranslated
|
|
377
|
+
// product carries the same slug in every locale, so linkText *is* the
|
|
378
|
+
// default-locale slug in the exact case where availableLinkIds is empty.
|
|
379
|
+
// A product translated for the browsed locale but not for the default one
|
|
380
|
+
// still yields a wrong guess; that URL 404s, which is the pre-existing
|
|
381
|
+
// behavior and still better than dropping the shopper on the locale root.
|
|
382
|
+
const linkId = entry?.availableLinkIds?.[defaultLocale] ?? linkText
|
|
383
|
+
|
|
384
|
+
return getSlug(linkId, itemId)
|
|
385
|
+
},
|
|
352
386
|
}
|
|
@@ -109,10 +109,11 @@ const shouldFallbackToProductRoute = (error: unknown) =>
|
|
|
109
109
|
* guards that the fetched sku is the one we actually asked for, throwing
|
|
110
110
|
* SLUG_MISMATCH_ERROR (caught by the caller) when it isn't.
|
|
111
111
|
*
|
|
112
|
-
* When localization is enabled, the slug prefix may be a localized LinkId
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
112
|
+
* When localization is enabled, the slug prefix may be a localized LinkId that
|
|
113
|
+
* differs from the IS linkText. linkText follows the locale being browsed
|
|
114
|
+
* rather than the default one, so a mismatch is not by itself evidence of a
|
|
115
|
+
* wrong sku. We validate against the Catalog Dataplane API before rejecting
|
|
116
|
+
* the slug.
|
|
116
117
|
*/
|
|
117
118
|
async function assertSkuMatchesSlug(
|
|
118
119
|
ctx: GraphqlContext,
|
|
@@ -99,6 +99,14 @@ type StoreProduct {
|
|
|
99
99
|
Only populated when localization is enabled.
|
|
100
100
|
"""
|
|
101
101
|
otherLocales: [StoreProductLocale!]
|
|
102
|
+
"""
|
|
103
|
+
Slug for the store's default locale, including the SKU ID suffix.
|
|
104
|
+
Navigation fallback for the locale selector when the target locale is absent
|
|
105
|
+
from `otherLocales`. Best-effort: it may point at a slug the catalog has not
|
|
106
|
+
registered for the default locale, so it must never be used to build hreflang
|
|
107
|
+
annotations. Only populated when localization is enabled.
|
|
108
|
+
"""
|
|
109
|
+
defaultLocaleSlug: String
|
|
102
110
|
}
|
|
103
111
|
|
|
104
112
|
"""
|