@faststore/core 3.42.0 → 3.43.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.
Files changed (89) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +16 -16
  3. package/.next/cache/.tsbuildinfo +1 -1
  4. package/.next/cache/config.json +3 -3
  5. package/.next/cache/webpack/client-production/0.pack +0 -0
  6. package/.next/cache/webpack/client-production/index.pack +0 -0
  7. package/.next/cache/webpack/server-production/0.pack +0 -0
  8. package/.next/cache/webpack/server-production/index.pack +0 -0
  9. package/.next/prerender-manifest.js +1 -1
  10. package/.next/prerender-manifest.json +1 -1
  11. package/.next/react-loadable-manifest.json +4 -4
  12. package/.next/routes-manifest.json +1 -1
  13. package/.next/server/chunks/1506.js +1 -1
  14. package/.next/server/chunks/4289.js +4 -4
  15. package/.next/server/chunks/6968.js +9 -0
  16. package/.next/server/chunks/8646.js +1 -1
  17. package/.next/server/middleware-build-manifest.js +1 -1
  18. package/.next/server/middleware-react-loadable-manifest.js +1 -1
  19. package/.next/server/pages/404.js +1 -1
  20. package/.next/server/pages/404.js.nft.json +1 -1
  21. package/.next/server/pages/500.js +1 -1
  22. package/.next/server/pages/500.js.nft.json +1 -1
  23. package/.next/server/pages/[...slug].js +1 -1
  24. package/.next/server/pages/[...slug].js.nft.json +1 -1
  25. package/.next/server/pages/[slug]/p.js +1 -1
  26. package/.next/server/pages/[slug]/p.js.nft.json +1 -1
  27. package/.next/server/pages/_app.js.nft.json +1 -1
  28. package/.next/server/pages/_document.js.nft.json +1 -1
  29. package/.next/server/pages/_error.js.nft.json +1 -1
  30. package/.next/server/pages/account/profile.js +1 -1
  31. package/.next/server/pages/account/profile.js.nft.json +1 -1
  32. package/.next/server/pages/account.js.nft.json +1 -1
  33. package/.next/server/pages/api/graphql.js.nft.json +1 -1
  34. package/.next/server/pages/api/health/live.js.nft.json +1 -1
  35. package/.next/server/pages/api/health/ready.js.nft.json +1 -1
  36. package/.next/server/pages/api/preview.js +1 -1
  37. package/.next/server/pages/api/preview.js.nft.json +1 -1
  38. package/.next/server/pages/checkout.js +1 -1
  39. package/.next/server/pages/checkout.js.nft.json +1 -1
  40. package/.next/server/pages/en-US/404.html +2 -2
  41. package/.next/server/pages/en-US/500.html +2 -2
  42. package/.next/server/pages/en-US/checkout.html +2 -2
  43. package/.next/server/pages/en-US/login.html +2 -2
  44. package/.next/server/pages/en-US/s.html +2 -2
  45. package/.next/server/pages/en-US.html +2 -2
  46. package/.next/server/pages/index.js +1 -1
  47. package/.next/server/pages/index.js.nft.json +1 -1
  48. package/.next/server/pages/login.js +1 -1
  49. package/.next/server/pages/login.js.nft.json +1 -1
  50. package/.next/server/pages/s.js +1 -1
  51. package/.next/server/pages/s.js.nft.json +1 -1
  52. package/.next/server/pages-manifest.json +1 -1
  53. package/.next/static/chunks/{1552.e7cece605fc2e1cd.js → 1552.b5a073e7ac834965.js} +1 -1
  54. package/.next/static/chunks/630.13d3dd939b789798.js +6 -0
  55. package/.next/static/chunks/8827.179b49f8ab3afe48.js +1 -0
  56. package/.next/static/chunks/pages/[...slug]-2cfb2b1b8ee3b7a9.js +1 -0
  57. package/.next/static/chunks/pages/{_app-0f16b6b5d7dfab2a.js → _app-f02182ccd58f2781.js} +1 -1
  58. package/.next/static/chunks/webpack-0dd5a14ceff64065.js +1 -0
  59. package/.next/static/{Ra_8xXVEVCNHcT2KePSUO → f5jWOXDXh3GdBy9EK8IDc}/_buildManifest.js +1 -1
  60. package/.next/trace +111 -109
  61. package/.turbo/turbo-build.log +17 -17
  62. package/.turbo/turbo-test.log +5 -5
  63. package/CHANGELOG.md +6 -0
  64. package/discovery.config.default.js +6 -0
  65. package/package.json +3 -2
  66. package/src/components/cms/GlobalSections.tsx +13 -9
  67. package/src/components/templates/LandingPage/LandingPage.tsx +22 -18
  68. package/src/components/templates/ProductListingPage/ProductListing.tsx +6 -1
  69. package/src/experimental/searchServerSideFunctions/getServerSideProps.ts +8 -5
  70. package/src/experimental/searchServerSideFunctions/getStaticProps.ts +9 -7
  71. package/src/pages/404.tsx +6 -5
  72. package/src/pages/500.tsx +6 -5
  73. package/src/pages/[...slug].tsx +11 -4
  74. package/src/pages/[slug]/p.tsx +14 -5
  75. package/src/pages/api/preview.ts +43 -18
  76. package/src/pages/index.tsx +9 -7
  77. package/src/pages/login.tsx +6 -5
  78. package/src/sdk/error/MissingContentError/MissingContentError.ts +6 -4
  79. package/src/sdk/error/MultipleContentError/MultipleContentError.ts +6 -4
  80. package/src/server/cms/index.ts +1 -2
  81. package/src/server/content/service.ts +227 -0
  82. package/src/server/content/types.ts +26 -0
  83. package/src/server/content/utils.ts +8 -0
  84. package/.next/server/chunks/6026.js +0 -9
  85. package/.next/static/chunks/1036.27f5244aaf7d0915.js +0 -1
  86. package/.next/static/chunks/485.a35fab0c7c75a11b.js +0 -1
  87. package/.next/static/chunks/pages/[...slug]-f4fd6c8d7dc53f8f.js +0 -1
  88. package/.next/static/chunks/webpack-d72e5eb27bf80195.js +0 -1
  89. /package/.next/static/{Ra_8xXVEVCNHcT2KePSUO → f5jWOXDXh3GdBy9EK8IDc}/_ssgManifest.js +0 -0
@@ -1,8 +1,9 @@
1
1
  import type { NextApiHandler, NextApiRequest } from 'next'
2
- import type { Locator } from '@vtex/client-cms'
3
2
 
4
- import { clientCMS } from 'src/server/cms'
5
3
  import { previewRedirects } from '../../../discovery.config'
4
+ import { contentService } from 'src/server/content/service'
5
+ import { isLocator } from 'src/server/cms'
6
+ import { isContentPlatformSource } from 'src/server/content/utils'
6
7
 
7
8
  type Settings = {
8
9
  seo: {
@@ -22,17 +23,29 @@ class StatusError extends Error {
22
23
 
23
24
  const pickParam = (req: NextApiRequest, parameter: string) => {
24
25
  const maybeParam = req.query[parameter]
26
+ return typeof maybeParam === 'string' ? maybeParam : undefined
27
+ }
25
28
 
26
- if (typeof maybeParam !== 'string') {
27
- return undefined
28
- }
29
-
30
- return maybeParam
29
+ const setPreviewAndRedirect = (
30
+ res: any,
31
+ previewData: Record<string, string>,
32
+ redirectPath: string
33
+ ) => {
34
+ res.setPreviewData(previewData, {
35
+ maxAge: 3600,
36
+ path: redirectPath,
37
+ })
38
+ res.redirect(redirectPath)
31
39
  }
32
40
 
33
41
  // TODO: Improve security by disabling CMS preview in production
34
42
  const handler: NextApiHandler = async (req, res) => {
35
43
  try {
44
+ let slug = pickParam(req, 'slug')
45
+ if (slug && !slug.startsWith('/')) {
46
+ slug = `/${slug}`
47
+ }
48
+
36
49
  const locator = [
37
50
  'contentType',
38
51
  'documentId',
@@ -65,8 +78,19 @@ const handler: NextApiHandler = async (req, res) => {
65
78
  )
66
79
  }
67
80
 
81
+ if (!isLocator(locator)) {
82
+ throw new StatusError('Invalid locator object', 400)
83
+ }
84
+
68
85
  // Fetch CMS to check if the provided `locator` exists
69
- const page = await clientCMS.getCMSPage(locator as Locator)
86
+ const page = await contentService.getSingleContent({
87
+ contentType: locator.contentType,
88
+ previewData: locator,
89
+ slug,
90
+ documentId: locator.documentId,
91
+ versionId: locator.versionId,
92
+ releaseId: locator.releaseId,
93
+ })
70
94
 
71
95
  // If the content doesn't exist prevent preview mode from being enabled
72
96
  if (!page) {
@@ -76,25 +100,26 @@ const handler: NextApiHandler = async (req, res) => {
76
100
  )
77
101
  }
78
102
 
79
- // Enable Preview Mode by setting the cookies
80
- res.setPreviewData(locator, {
81
- maxAge: 3600,
82
- path: (page.settings as Settings)?.seo?.slug,
83
- })
103
+ if (
104
+ isContentPlatformSource() &&
105
+ slug &&
106
+ ['landingPage', 'plp', 'pdp'].includes(locator.contentType)
107
+ ) {
108
+ return setPreviewAndRedirect(res, locator, slug)
109
+ }
84
110
 
85
111
  // Redirect to the path from the fetched locator
86
112
  const redirects = previewRedirects as Record<string, string>
87
113
  if (redirects[locator.contentType]) {
88
- res.redirect(redirects[locator.contentType])
89
- return
114
+ return setPreviewAndRedirect(res, locator, redirects[locator.contentType])
90
115
  }
91
116
 
92
117
  if (locator.contentType === 'landingPage') {
93
- res.redirect(`${(page.settings as Settings)?.seo.slug}`)
94
- return
118
+ slug = (page.settings as Settings)?.seo?.slug
119
+ return setPreviewAndRedirect(res, locator, slug)
95
120
  }
96
121
 
97
- res.redirect('/')
122
+ return setPreviewAndRedirect(res, locator, '/')
98
123
  } catch (error) {
99
124
  if (error instanceof StatusError) {
100
125
  res.status(error.status).end(error.message)
@@ -1,10 +1,8 @@
1
- import type { Locator } from '@vtex/client-cms'
2
1
  import type { GetStaticProps } from 'next'
3
2
  import { NextSeo, OrganizationJsonLd, SiteLinksSearchBoxJsonLd } from 'next-seo'
4
3
 
5
4
  import RenderSections from 'src/components/cms/RenderSections'
6
5
  import type { PageContentType } from 'src/server/cms'
7
- import { getPage } from 'src/server/cms'
8
6
 
9
7
  import {
10
8
  type GlobalSectionsData,
@@ -15,6 +13,8 @@ import PageProvider from 'src/sdk/overrides/PageProvider'
15
13
  import { injectGlobalSections } from 'src/server/cms/global'
16
14
  import { getDynamicContent } from 'src/utils/dynamicContent'
17
15
  import storeConfig from '../../discovery.config'
16
+ import { contentService } from 'src/server/content/service'
17
+ import type { PreviewData } from 'src/server/content/types'
18
18
 
19
19
  type Props = {
20
20
  page: PageContentType
@@ -146,14 +146,13 @@ function Page({
146
146
  export const getStaticProps: GetStaticProps<
147
147
  Props,
148
148
  Record<string, string>,
149
- Locator
149
+ PreviewData
150
150
  > = async ({ previewData }) => {
151
151
  const [
152
152
  globalSectionsPromise,
153
153
  globalSectionsHeaderPromise,
154
154
  globalSectionsFooterPromise,
155
155
  ] = getGlobalSectionsData(previewData)
156
-
157
156
  const serverDataPromise = getDynamicContent({ pageType: 'home' })
158
157
 
159
158
  let cmsPage = null
@@ -161,15 +160,18 @@ export const getStaticProps: GetStaticProps<
161
160
  const cmsData = JSON.parse(storeConfig.cms.data)
162
161
  cmsPage = cmsData['home'][0]
163
162
  }
163
+
164
164
  const pagePromise = cmsPage
165
- ? getPage<PageContentType>({
165
+ ? contentService.getSingleContent<PageContentType>({
166
166
  contentType: 'home',
167
+ previewData,
167
168
  documentId: cmsPage.documentId,
168
169
  versionId: cmsPage.versionId,
170
+ releaseId: cmsPage.releaseId,
169
171
  })
170
- : getPage<PageContentType>({
171
- ...(previewData?.contentType === 'home' && previewData),
172
+ : contentService.getSingleContent<PageContentType>({
172
173
  contentType: 'home',
174
+ previewData,
173
175
  })
174
176
 
175
177
  const [
@@ -2,7 +2,6 @@ import { NextSeo } from 'next-seo'
2
2
  import type { ComponentType } from 'react'
3
3
  import { useEffect } from 'react'
4
4
 
5
- import type { Locator } from '@vtex/client-cms'
6
5
  import type { GetStaticProps } from 'next'
7
6
  import { default as GLOBAL_COMPONENTS } from 'src/components/cms/global/Components'
8
7
  import {
@@ -13,9 +12,11 @@ import RenderSections from 'src/components/cms/RenderSections'
13
12
  import { OverriddenDefaultEmptyState as EmptyState } from 'src/components/sections/EmptyState/OverriddenDefaultEmptyState'
14
13
  import CUSTOM_COMPONENTS from 'src/customizations/src/components'
15
14
  import PLUGINS_COMPONENTS from 'src/plugins'
16
- import { type PageContentType, getPage } from 'src/server/cms'
15
+ import type { PageContentType } from 'src/server/cms'
17
16
  import { injectGlobalSections } from 'src/server/cms/global'
18
17
  import storeConfig from '../../discovery.config'
18
+ import { contentService } from 'src/server/content/service'
19
+ import type { PreviewData } from 'src/server/content/types'
19
20
 
20
21
  /* A list of components that can be used in the CMS. */
21
22
  const COMPONENTS: Record<string, ComponentType<any>> = {
@@ -68,7 +69,7 @@ function Page({ page: { sections }, globalSections }: Props) {
68
69
  export const getStaticProps: GetStaticProps<
69
70
  Props,
70
71
  Record<string, string>,
71
- Locator
72
+ PreviewData
72
73
  > = async ({ previewData }) => {
73
74
  const [
74
75
  globalSectionsPromise,
@@ -78,9 +79,9 @@ export const getStaticProps: GetStaticProps<
78
79
 
79
80
  const [page, globalSections, globalSectionsHeader, globalSectionsFooter] =
80
81
  await Promise.all([
81
- getPage<PageContentType>({
82
- ...(previewData?.contentType === 'login' && previewData),
82
+ contentService.getSingleContent<PageContentType>({
83
83
  contentType: 'login',
84
+ previewData,
84
85
  }),
85
86
  globalSectionsPromise,
86
87
  globalSectionsHeaderPromise,
@@ -1,12 +1,14 @@
1
1
  import type { Options } from 'src/server/cms'
2
+ import type { EntryPathParams } from '@vtex/client-cp'
3
+ import { contentSource } from 'discovery.config.default'
2
4
 
3
5
  export default class MissingContentError extends Error {
4
- constructor(options: Options) {
6
+ constructor(params: Options | EntryPathParams) {
5
7
  super(
6
- `Missing content on the CMS for content type ${
7
- options.contentType
8
+ `Missing content on the ${contentSource.type} for content type ${
9
+ params.contentType
8
10
  }. Add content before proceeding. Context: ${JSON.stringify(
9
- options,
11
+ params,
10
12
  null,
11
13
  2
12
14
  )}`
@@ -1,12 +1,14 @@
1
1
  import type { Options } from 'src/server/cms'
2
+ import type { EntryPathParams } from '@vtex/client-cp'
3
+ import { contentSource } from 'discovery.config.default'
2
4
 
3
5
  export default class MultipleContentError extends Error {
4
- constructor(options: Options) {
6
+ constructor(params: Options | EntryPathParams) {
5
7
  super(
6
- `Multiple content defined on the CMS for content type ${
7
- options.contentType
8
+ `Multiple content defined on the ${contentSource.type} for content type ${
9
+ params.contentType
8
10
  }. Remove duplicated content before proceeding. Context: ${JSON.stringify(
9
- options,
11
+ params,
10
12
  null,
11
13
  2
12
14
  )}`
@@ -1,7 +1,6 @@
1
1
  import type { ContentData, ContentTypeOptions, Locator } from '@vtex/client-cms'
2
2
  import ClientCMS from '@vtex/client-cms'
3
3
 
4
- import MissingContentError from 'src/sdk/error/MissingContentError'
5
4
  import MultipleContentError from 'src/sdk/error/MultipleContentError'
6
5
  import config from '../../../discovery.config'
7
6
 
@@ -57,7 +56,7 @@ export type PageContentType = ContentData & {
57
56
  }
58
57
  }
59
58
 
60
- const isLocator = (x: any): x is Locator =>
59
+ export const isLocator = (x: any): x is Locator =>
61
60
  typeof x.contentType === 'string' &&
62
61
  (typeof x.releaseId === 'string' ||
63
62
  typeof x.documentId === 'string' ||
@@ -0,0 +1,227 @@
1
+ import type { ContentData, Locator } from '@vtex/client-cms'
2
+ import ClientCP from '@vtex/client-cp'
3
+ import type { ContentEntry, EntryPathParams } from '@vtex/client-cp'
4
+ import { getCMSPage, getPage, type PageContentType } from 'src/server/cms'
5
+ import type { ContentOptions, ContentParams } from './types'
6
+ import config from '../../../discovery.config'
7
+ import { getPLP, type PLPContentType } from '../cms/plp'
8
+ import {
9
+ findBestPDPTemplate,
10
+ findBestPLPTemplate,
11
+ type Rewrite,
12
+ type RewritesConfig,
13
+ } from 'src/utils/multipleTemplates'
14
+ import MissingContentError from 'src/sdk/error/MissingContentError'
15
+ import { getPDP, type PDPContentType } from '../cms/pdp'
16
+ import MultipleContentError from 'src/sdk/error/MultipleContentError'
17
+ import type { ServerProductQueryQuery } from '@generated/graphql'
18
+ import { isContentPlatformSource } from './utils'
19
+
20
+ export class ContentService {
21
+ private clientCP: ClientCP
22
+
23
+ constructor() {
24
+ this.clientCP = new ClientCP({
25
+ tenant: config.api.storeId,
26
+ })
27
+ }
28
+
29
+ async getSingleContent<T extends ContentData>(
30
+ params: ContentParams
31
+ ): Promise<T> {
32
+ const options = this.createContentOptions(params)
33
+
34
+ if (isContentPlatformSource()) {
35
+ return this.getFromCP<T>(options)
36
+ }
37
+ return getPage(options.cmsOptions)
38
+ }
39
+
40
+ async getContent(params: ContentParams) {
41
+ const options = this.createContentOptions(params)
42
+
43
+ if (isContentPlatformSource()) {
44
+ const serviceParams = this.convertOptionsToParams(options)
45
+ const { entries } = await this.clientCP.listEntries(serviceParams)
46
+ const data = await Promise.all(
47
+ entries.map(async (entry) => {
48
+ const entryData = await this.getSingleEntry(
49
+ { ...serviceParams, entryId: entry.id },
50
+ !!options.isPreview
51
+ )
52
+ return this.mergeEntryWithData(entry, entryData)
53
+ })
54
+ )
55
+ return { data }
56
+ }
57
+ return getCMSPage(options.cmsOptions)
58
+ }
59
+
60
+ async getPlpContent(
61
+ params: ContentParams,
62
+ rewrites: Rewrite[] | RewritesConfig
63
+ ): Promise<PLPContentType> {
64
+ const plpParams = { ...params, contentType: 'plp' }
65
+ const options = this.createContentOptions(plpParams)
66
+
67
+ if (isContentPlatformSource()) {
68
+ const pages = (await this.getContent(plpParams)).data
69
+ if (!pages?.length) throw new MissingContentError(options.cmsOptions)
70
+ return findBestPLPTemplate(
71
+ pages,
72
+ options.slug,
73
+ rewrites
74
+ ) as PLPContentType
75
+ }
76
+ return getPLP(options.slug, options.cmsOptions as Locator, rewrites)
77
+ }
78
+
79
+ async getPdpContent(
80
+ product: ServerProductQueryQuery['product'],
81
+ params: ContentParams
82
+ ): Promise<PDPContentType> {
83
+ const pdpParams = { ...params, contentType: 'pdp' }
84
+ const options = this.createContentOptions(pdpParams)
85
+
86
+ if (isContentPlatformSource()) {
87
+ const pages = (await this.getContent(pdpParams)).data
88
+ if (!pages.length) throw new MissingContentError(options.cmsOptions)
89
+ return findBestPDPTemplate(pages, product) as PDPContentType
90
+ }
91
+ return getPDP(product, options.cmsOptions as Locator)
92
+ }
93
+
94
+ private async getFromCP<T extends ContentData>(
95
+ options: ContentOptions
96
+ ): Promise<T> {
97
+ const params = this.convertOptionsToParams(options)
98
+ try {
99
+ const entry: PageContentType =
100
+ params.entryId || params.slug
101
+ ? await this.getSingleEntry(params, !!options.isPreview)
102
+ : await this.fetchFirstEntry(params, !!options.isPreview)
103
+
104
+ return entry as T
105
+ } catch (err: unknown) {
106
+ if (isNotFoundError(err)) console.error('Content not found', err)
107
+ else throw err
108
+ }
109
+ }
110
+
111
+ private async getSingleEntry(
112
+ params: EntryPathParams,
113
+ isPreview: boolean
114
+ ): Promise<PageContentType> {
115
+ if (isPreview) {
116
+ if (params.entryId)
117
+ return this.clientCP.previewEntryById(
118
+ params
119
+ ) as Promise<PageContentType>
120
+ if (params.slug)
121
+ return this.clientCP.previewEntryBySlug(
122
+ params
123
+ ) as Promise<PageContentType>
124
+ throw new Error('Preview requires entryId or slug')
125
+ }
126
+ if (params.entryId)
127
+ return this.clientCP.getEntry(params) as Promise<PageContentType>
128
+ if (params.slug)
129
+ return this.clientCP.getEntryBySlug(params) as Promise<PageContentType>
130
+ throw new Error('getEntry requires entryId or slug')
131
+ }
132
+
133
+ private async fetchFirstEntry(
134
+ params: EntryPathParams,
135
+ isPreview: boolean
136
+ ): Promise<PageContentType> {
137
+ const { entries } = await this.clientCP.listEntries(params)
138
+ if (!entries || entries.length === 0) {
139
+ console.error('No entries found for params', params)
140
+ return {} as PageContentType
141
+ }
142
+ if (entries.length > 1) {
143
+ throw new MultipleContentError(params)
144
+ }
145
+ return this.getSingleEntry({ ...params, entryId: entries[0].id }, isPreview)
146
+ }
147
+
148
+ private mergeEntryWithData(
149
+ entry: ContentEntry,
150
+ data: PageContentType
151
+ ): ContentEntry & PageContentType {
152
+ return {
153
+ ...entry,
154
+ ...data,
155
+ id: entry.id,
156
+ name: entry.name || '',
157
+ }
158
+ }
159
+
160
+ private convertOptionsToParams(options: ContentOptions): EntryPathParams {
161
+ const { cmsOptions } = options
162
+ const params: Partial<EntryPathParams> = {
163
+ accountName: config.api.storeId,
164
+ storeId: 'faststore',
165
+ contentType: cmsOptions.contentType,
166
+ slug: options.slug,
167
+ }
168
+
169
+ if ('documentId' in cmsOptions && cmsOptions.documentId) {
170
+ params.entryId = cmsOptions.documentId
171
+ }
172
+ if ('versionId' in cmsOptions && cmsOptions.versionId) {
173
+ params.branchId = cmsOptions.versionId
174
+ }
175
+ if ('releaseId' in cmsOptions && cmsOptions.releaseId) {
176
+ params.branchId = cmsOptions.releaseId
177
+ }
178
+ if ('filters' in cmsOptions && cmsOptions.filters) {
179
+ const nested = cmsOptions.filters.filters as Record<string, any>
180
+ if (nested['settings.seo.slug']) {
181
+ const seo = nested['settings.seo.slug'] as string
182
+ params.slug = seo.replace(/^\//, '')
183
+ }
184
+ Object.assign(params, cmsOptions.filters)
185
+ }
186
+
187
+ return params as EntryPathParams
188
+ }
189
+
190
+ private createContentOptions(params: ContentParams): ContentOptions {
191
+ const {
192
+ contentType,
193
+ previewData,
194
+ slug,
195
+ documentId,
196
+ versionId,
197
+ releaseId,
198
+ filters,
199
+ } = params
200
+
201
+ const isPreview = previewData?.contentType === contentType
202
+ const { slug: _, ...previewLocator } = previewData || {}
203
+
204
+ const cmsOptions = {
205
+ contentType,
206
+ ...(isPreview ? previewLocator : {}),
207
+ ...(documentId !== undefined && { documentId }),
208
+ ...(versionId !== undefined && { versionId }),
209
+ ...(releaseId !== undefined && { releaseId }),
210
+ ...(filters && { filters }),
211
+ }
212
+
213
+ return {
214
+ cmsOptions,
215
+ ...(slug !== undefined && { slug }),
216
+ isPreview,
217
+ }
218
+ }
219
+ }
220
+
221
+ function isNotFoundError(err: unknown): boolean {
222
+ if (err instanceof MissingContentError) return true
223
+ if (err instanceof Error && /\b404\b/.test(err.message)) return true
224
+ return false
225
+ }
226
+
227
+ export const contentService = new ContentService()
@@ -0,0 +1,26 @@
1
+ import type { Locator } from '@vtex/client-cms'
2
+ import type { Options } from '../cms'
3
+
4
+ export const ContentSourceType = {
5
+ ContentPlatform: 'cp',
6
+ }
7
+
8
+ export type PreviewData = Locator & {
9
+ slug?: string
10
+ }
11
+
12
+ export interface ContentParams {
13
+ contentType?: string
14
+ previewData?: PreviewData | null
15
+ slug?: string
16
+ documentId?: string
17
+ versionId?: string
18
+ releaseId?: string
19
+ filters?: Record<string, any>
20
+ }
21
+
22
+ export interface ContentOptions {
23
+ cmsOptions: Options
24
+ slug?: string
25
+ isPreview: boolean
26
+ }
@@ -0,0 +1,8 @@
1
+ import { contentSource } from '../../../discovery.config'
2
+ import { ContentSourceType } from './types'
3
+
4
+ export function isContentPlatformSource(): boolean {
5
+ return (
6
+ contentSource.type.toLocaleLowerCase() === ContentSourceType.ContentPlatform
7
+ )
8
+ }