@remkoj/optimizely-cms-nextjs 6.0.0-pre8 → 6.0.0-rc.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 (41) hide show
  1. package/LICENSE +12 -12
  2. package/README.md +5 -1
  3. package/dist/client.d.ts +6 -1
  4. package/dist/client.js +29 -3
  5. package/dist/client.js.map +1 -1
  6. package/dist/cms-page/_base.js +1 -10
  7. package/dist/cms-page/_base.js.map +1 -1
  8. package/dist/cms-page/_getInfoByPath.d.ts +1 -1
  9. package/dist/cms-page/_getInfoByPath.js +4 -4
  10. package/dist/cms-page/_getInfoByPath.js.map +1 -1
  11. package/dist/cms-page/_globals.d.ts +1 -0
  12. package/dist/cms-page/_globals.js +2 -0
  13. package/dist/cms-page/_globals.js.map +1 -0
  14. package/dist/cms-page/_loadContentByPath.d.ts +1 -1
  15. package/dist/cms-page/_loadContentByPath.js +2 -1
  16. package/dist/cms-page/_loadContentByPath.js.map +1 -1
  17. package/dist/cms-page/data.d.ts +3 -2
  18. package/dist/cms-page/middleware.d.ts +4 -2
  19. package/dist/cms-page/middleware.js +25 -7
  20. package/dist/cms-page/middleware.js.map +1 -1
  21. package/dist/cms-page/page.d.ts +13 -3
  22. package/dist/cms-page/page.js +57 -84
  23. package/dist/cms-page/page.js.map +1 -1
  24. package/dist/ope/load-content.d.ts +21 -0
  25. package/dist/ope/load-content.js +69 -0
  26. package/dist/ope/load-content.js.map +1 -0
  27. package/dist/ope/middleware.js +1 -1
  28. package/dist/ope/middleware.js.map +1 -1
  29. package/dist/ope/page.js +25 -46
  30. package/dist/ope/page.js.map +1 -1
  31. package/dist/ope/tools.d.ts +1 -1
  32. package/dist/ope/tools.js +7 -7
  33. package/dist/ope/tools.js.map +1 -1
  34. package/dist/ope/types.d.ts +7 -5
  35. package/dist/publish/index.js +1 -1
  36. package/dist/publish/index.js.map +1 -1
  37. package/dist/types.d.ts +3 -2
  38. package/package.json +19 -19
  39. package/dist/ope/data.d.ts +0 -2
  40. package/dist/ope/data.js +0 -28
  41. package/dist/ope/data.js.map +0 -1
package/LICENSE CHANGED
@@ -1,13 +1,13 @@
1
- Copyright 2024 Remko Jantzen
2
-
3
- Licensed under the Apache License, Version 2.0 (the "License");
4
- you may not use this file except in compliance with the License.
5
- You may obtain a copy of the License at
6
-
7
- http://www.apache.org/licenses/LICENSE-2.0
8
-
9
- Unless required by applicable law or agreed to in writing, software
10
- distributed under the License is distributed on an "AS IS" BASIS,
11
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- See the License for the specific language governing permissions and
1
+ Copyright 2024 Remko Jantzen
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
13
  limitations under the License.
package/README.md CHANGED
@@ -1,4 +1,8 @@
1
1
  # Optimizely SaaS CMS - Next.js Integration
2
+
3
+ > [!WARNING]
4
+ > There'll be an update of Optimizely SaaS CMS that is incompatible with all SDK versions prior to 5.1.6. If you don't upgrade, you will see empty pages (main website) and "Component not found" messages (preview).
5
+
2
6
  [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](./LICENSE)
3
7
 
4
8
  > [!TIP]
@@ -76,4 +80,4 @@ The package provides the following enhancements for Next.js middleware:
76
80
  | withEditFallback | `@remkoj/optimizely-cms-nextjs/preview` | Rewrite incoming Optimizely CMS 12 preview / on-page-edit URLs to Optimizely SaaS CMS preview URLs |
77
81
  | withLanguagePrefix | `@remkoj/optimizely-cms-nextjs/page` | Handle the redirect of the homepage `/` to the locale that best matches the incoming request, such as `/en`. The locales and their URLs are taken from the second parameter - the ChannelDefinition.
78
82
 
79
- If you use both wrappers, the `withEditFallback` wrapper must wrap the `withLanguagePrefix` wrapper, to ensure that the edit mode URL is rewritten before the language prefix is applied.
83
+ If you use both wrappers, the `withEditFallback` wrapper must wrap the `withLanguagePrefix` wrapper, to ensure that the edit mode URL is rewritten before the language prefix is applied.
package/dist/client.d.ts CHANGED
@@ -2,7 +2,8 @@ import 'server-only';
2
2
  import { type IOptiGraphClient, type OptimizelyGraphConfig } from '@remkoj/optimizely-graph-client';
3
3
  /**
4
4
  * Create a new client instance. This is a direct wrapper for the `createClient` function
5
- * exported from `@remkoj/optimizely-graph-client`.
5
+ * exported from `@remkoj/optimizely-graph-client`. It creates the instance with the
6
+ * recommended configuration for published content in a Next.JS frontend.
6
7
  *
7
8
  * @returns The newly created GraphQL Client
8
9
  */
@@ -12,6 +13,10 @@ export declare function createClient(): IOptiGraphClient;
12
13
  * wraps the `createClient` function from `@remkoj/optimizely-graph-client` and applies some
13
14
  * configuration defaults.
14
15
  *
16
+ * It creates the instance with the recommended configuration for published content in a Next.JS
17
+ * frontend. When the token is a non-empty string, the configuration applied is intended to access
18
+ * non-published content in a Next.JS frontend, for example in the preview endpoint of Optimizely CMS.
19
+ *
15
20
  * @returns The newly created GraphQL Client
16
21
  */
17
22
  export declare function createAuthorizedClient(token?: string, config?: OptimizelyGraphConfig): IOptiGraphClient;
package/dist/client.js CHANGED
@@ -1,8 +1,15 @@
1
1
  import 'server-only';
2
2
  import { createClient as createBaseClient, AuthMode } from '@remkoj/optimizely-graph-client';
3
+ import { isNonEmptyString } from '@remkoj/optimizely-cms-react/utils';
4
+ const AuthModeValues = Object.getOwnPropertyNames(AuthMode).reduce((a, b) => {
5
+ //@ts-ignore
6
+ a.push(AuthMode[b]);
7
+ return a;
8
+ }, []);
3
9
  /**
4
10
  * Create a new client instance. This is a direct wrapper for the `createClient` function
5
- * exported from `@remkoj/optimizely-graph-client`.
11
+ * exported from `@remkoj/optimizely-graph-client`. It creates the instance with the
12
+ * recommended configuration for published content in a Next.JS frontend.
6
13
  *
7
14
  * @returns The newly created GraphQL Client
8
15
  */
@@ -14,18 +21,37 @@ export function createClient() {
14
21
  * wraps the `createClient` function from `@remkoj/optimizely-graph-client` and applies some
15
22
  * configuration defaults.
16
23
  *
24
+ * It creates the instance with the recommended configuration for published content in a Next.JS
25
+ * frontend. When the token is a non-empty string, the configuration applied is intended to access
26
+ * non-published content in a Next.JS frontend, for example in the preview endpoint of Optimizely CMS.
27
+ *
17
28
  * @returns The newly created GraphQL Client
18
29
  */
19
30
  export function createAuthorizedClient(token, config) {
20
- const client = createBaseClient(config);
31
+ // Check the token
32
+ const isPotentialValidToken = isNonEmptyString(token);
33
+ // Create the client, but keep the cache disabled if we've got a non-empty string as token
34
+ const client = createBaseClient(config, undefined, {
35
+ nextJsFetchDirectives: true,
36
+ cache: !isPotentialValidToken,
37
+ queryCache: !isPotentialValidToken,
38
+ });
21
39
  if (client.debug)
22
40
  console.log('⚪ [ContentGraph Client] Created new Optimizely Graph client');
23
41
  // Apply token if needed
24
- if (typeof (token) == 'string' && token.length > 0) {
42
+ if (isPotentialValidToken) {
25
43
  if (token == client.siteInfo.publishToken) {
26
44
  console.warn(`🔐 [ContentGraph Client] Allowed authenticated access by publish token, switching to HMAC`);
27
45
  client.updateAuthentication(AuthMode.HMAC);
28
46
  }
47
+ else if (AuthModeValues.includes(token)) {
48
+ if (client.isDebugOrDevelopment() && (token === AuthMode.HMAC || token === AuthMode.Basic)) {
49
+ console.warn(`🔐 [ContentGraph Client] Allowing authenticated access by using a mode as token in development or debug`);
50
+ client.updateAuthentication(token);
51
+ }
52
+ else
53
+ console.warn(`🔐 [ContentGraph Client] Blocked authenticated access by using a mode as token`);
54
+ }
29
55
  else {
30
56
  client.updateAuthentication(token);
31
57
  }
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAA;AACpB,OAAO,EAAE,YAAY,IAAI,gBAAgB,EAAE,QAAQ,EAAqD,MAAM,iCAAiC,CAAA;AAE/I;;;;;GAKG;AACH,MAAM,UAAU,YAAY;IAC1B,OAAO,sBAAsB,EAAE,CAAA;AACjC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAAc,EAAE,MAA8B;IACnF,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAA;IACvC,IAAI,MAAM,CAAC,KAAK;QACd,OAAO,CAAC,GAAG,CAAC,6DAA6D,CAAC,CAAA;IAE5E,wBAAwB;IACxB,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACnD,IAAI,KAAK,IAAI,MAAM,CAAC,QAAQ,CAAC,YAAY,EAAE,CAAC;YAC1C,OAAO,CAAC,IAAI,CAAC,2FAA2F,CAAC,CAAA;YACzG,MAAM,CAAC,oBAAoB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;QAC5C,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAA;QACpC,CAAC;QAED,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,OAAO,CAAC,IAAI,CAAC,kEAAkE,MAAM,CAAC,eAAe,EAAE,CAAC,CAAA;YACxG,OAAO,CAAC,GAAG,CAAC,6DAA6D,CAAC,CAAA;QAC5E,CAAC;QACD,MAAM,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,EAAE,KAAK,CAAC,CAAA;IACnF,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,sBAAsB,CAAA;AAE/D;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,YAAY,CAAA;AAE3C,eAAe,YAAY,CAAA"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAA;AACpB,OAAO,EAAE,YAAY,IAAI,gBAAgB,EAAE,QAAQ,EAAqD,MAAM,iCAAiC,CAAA;AAC/I,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAA;AAErE,MAAM,cAAc,GAAG,MAAM,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAW,CAAC,CAAW,EAAE,CAAS,EAAE,EAAE;IACtG,YAAY;IACZ,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IACpB,OAAO,CAAC,CAAA;AACV,CAAC,EAAE,EAAc,CAAC,CAAA;AAElB;;;;;;GAMG;AACH,MAAM,UAAU,YAAY;IAC1B,OAAO,sBAAsB,EAAE,CAAA;AACjC,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAAc,EAAE,MAA8B;IACnF,kBAAkB;IAClB,MAAM,qBAAqB,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAA;IAErD,0FAA0F;IAC1F,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE;QACjD,qBAAqB,EAAE,IAAI;QAC3B,KAAK,EAAE,CAAC,qBAAqB;QAC7B,UAAU,EAAE,CAAC,qBAAqB;KACnC,CAAC,CAAA;IACF,IAAI,MAAM,CAAC,KAAK;QACd,OAAO,CAAC,GAAG,CAAC,6DAA6D,CAAC,CAAA;IAE5E,wBAAwB;IACxB,IAAI,qBAAqB,EAAE,CAAC;QAC1B,IAAI,KAAK,IAAI,MAAM,CAAC,QAAQ,CAAC,YAAY,EAAE,CAAC;YAC1C,OAAO,CAAC,IAAI,CAAC,2FAA2F,CAAC,CAAA;YACzG,MAAM,CAAC,oBAAoB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;QAC5C,CAAC;aAAM,IAAI,cAAc,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1C,IAAI,MAAM,CAAC,oBAAoB,EAAE,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,IAAI,IAAI,KAAK,KAAK,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC3F,OAAO,CAAC,IAAI,CAAC,yGAAyG,CAAC,CAAA;gBACvH,MAAM,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAA;YACpC,CAAC;;gBACC,OAAO,CAAC,IAAI,CAAC,gFAAgF,CAAC,CAAA;QAClG,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAA;QACpC,CAAC;QAED,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,OAAO,CAAC,IAAI,CAAC,kEAAkE,MAAM,CAAC,eAAe,EAAE,CAAC,CAAA;YACxG,OAAO,CAAC,GAAG,CAAC,6DAA6D,CAAC,CAAA;QAC5E,CAAC;QACD,MAAM,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,EAAE,KAAK,CAAC,CAAA;IACnF,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,sBAAsB,CAAA;AAE/D;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,YAAY,CAAA;AAE3C,eAAe,YAAY,CAAA"}
@@ -11,16 +11,7 @@ export function getChannelId(client, channel) {
11
11
  return channel;
12
12
  return client.currentOptiCmsSchema == OptiCmsSchema.CMS12
13
13
  ? channel.id
14
- : getPrimaryURL(channel).origin;
15
- }
16
- function getPrimaryURL(chnl) {
17
- const dd = chnl.domains.filter((x) => x.isPrimary).at(0) ?? chnl.domains.at(0);
18
- if (!dd)
19
- return chnl.getPrimaryDomain();
20
- const s = dd.name.startsWith('localhost') || dd.name.indexOf('.local') > 0
21
- ? 'http:'
22
- : 'https:';
23
- return new URL(`${s}//${dd.name}`);
14
+ : channel.getPrimaryDomain().origin;
24
15
  }
25
16
  function isString(toTest) {
26
17
  return typeof toTest === 'string';
@@ -1 +1 @@
1
- {"version":3,"file":"_base.js","sourceRoot":"","sources":["../../src/cms-page/_base.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAyB,MAAM,wCAAwC,CAAA;AAK7F,MAAM,CAAN,IAAY,aAGX;AAHD,WAAY,aAAa;IACvB,4BAAW,CAAA;IACX,oCAAmB,CAAA;AACrB,CAAC,EAHW,aAAa,KAAb,aAAa,QAGxB;AAUD,MAAM,UAAU,YAAY,CAAC,MAAwB,EAAE,OAAoC;IACzF,IAAI,CAAC,OAAO;QACV,OAAO,SAAS,CAAA;IAClB,IAAI,QAAQ,CAAC,OAAO,CAAC;QACnB,OAAO,OAAO,CAAA;IAChB,OAAO,MAAM,CAAC,oBAAoB,IAAI,aAAa,CAAC,KAAK;QACvD,CAAC,CAAC,OAAO,CAAC,EAAE;QACZ,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,MAAM,CAAA;AACnC,CAAC;AAED,SAAS,aAAa,CAAC,IAAuB;IAC5C,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;IAC9E,IAAI,CAAC,EAAE;QAAE,OAAO,IAAI,CAAC,gBAAgB,EAAE,CAAA;IACvC,MAAM,CAAC,GACL,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;QAC9D,CAAC,CAAC,OAAO;QACT,CAAC,CAAC,QAAQ,CAAA;IACd,OAAO,IAAI,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,CAAA;AACpC,CAAC;AAED,SAAS,QAAQ,CAAC,MAAW;IAC3B,OAAO,OAAO,MAAM,KAAK,QAAQ,CAAC;AACpC,CAAC"}
1
+ {"version":3,"file":"_base.js","sourceRoot":"","sources":["../../src/cms-page/_base.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAyB,MAAM,wCAAwC,CAAA;AAK7F,MAAM,CAAN,IAAY,aAGX;AAHD,WAAY,aAAa;IACvB,4BAAW,CAAA;IACX,oCAAmB,CAAA;AACrB,CAAC,EAHW,aAAa,KAAb,aAAa,QAGxB;AAUD,MAAM,UAAU,YAAY,CAAC,MAAwB,EAAE,OAAoC;IACzF,IAAI,CAAC,OAAO;QACV,OAAO,SAAS,CAAA;IAClB,IAAI,QAAQ,CAAC,OAAO,CAAC;QACnB,OAAO,OAAO,CAAA;IAChB,OAAO,MAAM,CAAC,oBAAoB,IAAI,aAAa,CAAC,KAAK;QACvD,CAAC,CAAC,OAAO,CAAC,EAAE;QACZ,CAAC,CAAC,OAAO,CAAC,gBAAgB,EAAE,CAAC,MAAM,CAAA;AACvC,CAAC;AAED,SAAS,QAAQ,CAAC,MAAW;IAC3B,OAAO,OAAO,MAAM,KAAK,QAAQ,CAAC;AACpC,CAAC"}
@@ -2,4 +2,4 @@ import { type IOptiGraphClient } from "@remkoj/optimizely-graph-client/client";
2
2
  import { type IRouteResolver } from "@remkoj/optimizely-graph-client/router";
3
3
  import { type ChannelDefinition } from "@remkoj/optimizely-graph-client/channels";
4
4
  import { type LookupResponse } from "./_base.js";
5
- export declare function getInfoByPath(client: IOptiGraphClient, routerFactory: (client?: IOptiGraphClient) => IRouteResolver, requestPath: string, channel?: ChannelDefinition | string): Promise<LookupResponse | undefined>;
5
+ export declare function getInfoByPath(client: IOptiGraphClient, routerFactory: (client?: IOptiGraphClient) => IRouteResolver, requestPath: string, channel?: ChannelDefinition | string, variation?: string): Promise<LookupResponse | undefined>;
@@ -3,12 +3,10 @@ import { ifChannelDefinition } from "@remkoj/optimizely-graph-client/channels";
3
3
  import { getChannelId } from "./_base.js";
4
4
  import { localeToGraphLocale } from "./utils.js";
5
5
  // Helper function to obtain the info by path
6
- export async function getInfoByPath(client, routerFactory, requestPath, channel) {
7
- if (client.debug)
8
- console.log(`⚪ [CmsPage.getInfoByPath] Loading content for path "${requestPath}" using RouteResolver`);
6
+ export async function getInfoByPath(client, routerFactory, requestPath, channel, variation) {
9
7
  const channelId = getChannelId(client, channel);
10
8
  const router = routerFactory(client);
11
- const route = await router.getContentInfoByPath(requestPath, channelId);
9
+ const route = await router.getContentInfoByPath(requestPath, channelId, variation);
12
10
  if (!route) {
13
11
  if (client.debug)
14
12
  console.warn(`🟠 [CmsPage.getInfoByPath] The RouteResolver was unable to resolve the route information for "${requestPath}"`);
@@ -17,6 +15,8 @@ export async function getInfoByPath(client, routerFactory, requestPath, channel)
17
15
  const contentLink = router.routeToContentLink(route);
18
16
  const contentType = route.contentType;
19
17
  const graphLocale = localeToGraphLocale(route.locale, ifChannelDefinition(channel));
18
+ if (client.debug)
19
+ console.log(`⚪ [CmsPage.getInfoByPath] Resolved path "${requestPath}"${variation ? ` in variation ${variation}` : ""} to content ${JSON.stringify(contentLink)} of type ${contentType.join('/')} using RouteResolver`);
20
20
  return [route, contentLink, contentType, graphLocale, null];
21
21
  }
22
22
  //# sourceMappingURL=_getInfoByPath.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"_getInfoByPath.js","sourceRoot":"","sources":["../../src/cms-page/_getInfoByPath.ts"],"names":[],"mappings":"AAAA,YAAY,CAAA;AAIZ,OAAO,EAA0B,mBAAmB,EAAE,MAAM,0CAA0C,CAAC;AAEvG,OAAO,EAAE,YAAY,EAAuB,MAAM,YAAY,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEjD,6CAA6C;AAC7C,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAAwB,EACxB,aAA4D,EAC5D,WAAmB,EACnB,OAAoC;IAEpC,IAAI,MAAM,CAAC,KAAK;QACd,OAAO,CAAC,GAAG,CACT,uDAAuD,WAAW,uBAAuB,CAC1F,CAAA;IACH,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/C,MAAM,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,CAAA;IACpC,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,WAAW,EAAE,SAAS,CAAC,CAAA;IACvE,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,IAAI,MAAM,CAAC,KAAK;YACd,OAAO,CAAC,IAAI,CACV,iGAAiG,WAAW,GAAG,CAChH,CAAA;QACH,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,MAAM,WAAW,GAAG,MAAM,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAA;IACpD,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,CAAA;IACrC,MAAM,WAAW,GAAG,mBAAmB,CAAC,KAAK,CAAC,MAAM,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAA;IACnF,OAAO,CAAC,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,IAAI,CAAmB,CAAA;AAC/E,CAAC"}
1
+ {"version":3,"file":"_getInfoByPath.js","sourceRoot":"","sources":["../../src/cms-page/_getInfoByPath.ts"],"names":[],"mappings":"AAAA,YAAY,CAAA;AAIZ,OAAO,EAA0B,mBAAmB,EAAE,MAAM,0CAA0C,CAAC;AAEvG,OAAO,EAAE,YAAY,EAAuB,MAAM,YAAY,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEjD,6CAA6C;AAC7C,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAAwB,EACxB,aAA4D,EAC5D,WAAmB,EACnB,OAAoC,EACpC,SAAkB;IAElB,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/C,MAAM,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,CAAA;IACpC,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,WAAW,EAAE,SAAS,EAAE,SAAS,CAAC,CAAA;IAClF,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,IAAI,MAAM,CAAC,KAAK;YACd,OAAO,CAAC,IAAI,CACV,iGAAiG,WAAW,GAAG,CAChH,CAAA;QACH,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,MAAM,WAAW,GAAG,MAAM,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAA;IACpD,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,CAAA;IACrC,MAAM,WAAW,GAAG,mBAAmB,CAAC,KAAK,CAAC,MAAM,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAA;IACnF,IAAI,MAAM,CAAC,KAAK;QACd,OAAO,CAAC,GAAG,CACT,4CAA4C,WAAW,IAAI,SAAS,CAAC,CAAC,CAAC,iBAAiB,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,eAAe,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,YAAY,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,sBAAsB,CAC1M,CAAA;IACH,OAAO,CAAC,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,IAAI,CAAmB,CAAA;AAC/E,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=_globals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_globals.js","sourceRoot":"","sources":["../../src/cms-page/_globals.ts"],"names":[],"mappings":""}
@@ -2,4 +2,4 @@ import { type IOptiGraphClient } from "@remkoj/optimizely-graph-client";
2
2
  import { type ChannelDefinition } from "@remkoj/optimizely-graph-client/channels";
3
3
  import { type GetContentByPathMethod } from './data.js';
4
4
  import { SystemLocales, type LookupResponse } from "./_base.js";
5
- export declare function loadContentByPath<LocaleEnum = SystemLocales>(client: IOptiGraphClient, getContentByPath: GetContentByPathMethod<LocaleEnum>, requestPath: string, channel?: ChannelDefinition | string, locale?: LocaleEnum | LocaleEnum[]): Promise<LookupResponse | undefined | null>;
5
+ export declare function loadContentByPath<LocaleEnum = SystemLocales>(client: IOptiGraphClient, getContentByPath: GetContentByPathMethod<LocaleEnum>, requestPath: string, channel?: ChannelDefinition | string, locale?: LocaleEnum | LocaleEnum[], variation?: string): Promise<LookupResponse | undefined | null>;
@@ -4,7 +4,7 @@ import { normalizeContentLinkWithLocale, isContentLinkWithSetLocale } from '@rem
4
4
  import { Utils } from '@remkoj/optimizely-cms-react/rsc';
5
5
  import { getChannelId } from "./_base.js";
6
6
  import { localeToGraphLocale } from './utils.js';
7
- export async function loadContentByPath(client, getContentByPath, requestPath, channel, locale) {
7
+ export async function loadContentByPath(client, getContentByPath, requestPath, channel, locale, variation) {
8
8
  if (client.debug)
9
9
  console.log(`⚪ [CmsPage.loadContentByPath] Loading content for path "${requestPath}" using getContentByPath method`);
10
10
  const channelId = getChannelId(client, channel);
@@ -18,6 +18,7 @@ export async function loadContentByPath(client, getContentByPath, requestPath, c
18
18
  path: pathForRequest,
19
19
  siteId: channelId,
20
20
  locale,
21
+ variation: variation ? { include: "SOME", value: [variation] } : undefined
21
22
  };
22
23
  if (client.isPreviewEnabled())
23
24
  requestVars.changeset = client?.getChangeset();
@@ -1 +1 @@
1
- {"version":3,"file":"_loadContentByPath.js","sourceRoot":"","sources":["../../src/cms-page/_loadContentByPath.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"_loadContentByPath.js","sourceRoot":"","sources":["../../src/cms-page/_loadContentByPath.ts"],"names":[],"mappings":"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"}
@@ -1,9 +1,11 @@
1
- import { type IOptiGraphClient } from "@remkoj/optimizely-graph-client";
1
+ import type { IOptiGraphClient, VariationInput } from "@remkoj/optimizely-graph-client";
2
+ export type { VariationInput } from "@remkoj/optimizely-graph-client";
2
3
  export type GetContentByPathVariables<LocaleType = string> = {
3
4
  path: string | string[];
4
5
  locale?: Array<LocaleType> | LocaleType;
5
6
  siteId?: string;
6
7
  changeset?: string | null;
8
+ variation?: VariationInput | null;
7
9
  };
8
10
  type MayBe<T> = T extends Array<infer R> ? Array<R | null> | null : T | null;
9
11
  export type GetContentByPathResponse = {
@@ -32,4 +34,3 @@ export type GetMetaDataByPathResponse = {
32
34
  };
33
35
  export type GetContentByPathMethod<LocaleType = string> = (client: IOptiGraphClient, variables: GetContentByPathVariables<LocaleType>) => Promise<GetContentByPathResponse>;
34
36
  export type GetMetaDataByPathMethod<LocaleType = string> = (client: IOptiGraphClient, variables: GetContentByPathVariables<LocaleType>) => Promise<GetMetaDataByPathResponse>;
35
- export {};
@@ -1,5 +1,5 @@
1
1
  import 'server-only';
2
- import { type NextMiddleware } from "next/server.js";
2
+ import { type NextProxy } from "next/server.js";
3
3
  import { type ChannelDefinition } from "@remkoj/optimizely-graph-client";
4
4
  /**
5
5
  * Wrap the provided middleware with enforcement of the language code as the first
@@ -7,6 +7,8 @@ import { type ChannelDefinition } from "@remkoj/optimizely-graph-client";
7
7
  *
8
8
  * @param next The Next.JS Middleware to wrap, will be invoked if there's no redirect needed
9
9
  * @param channel The Optimizely CMS Application definition
10
+ * @param debug When set to debug, redirects will be temporary (302) and information will be
11
+ * logged. If omitted, it will determine debug mode based upon the NODE_ENV value.
10
12
  * @returns The newly constructed Next.JS Middleware
11
13
  */
12
- export declare function withLanguagePrefix<T extends NextMiddleware>(next: T, channel: ChannelDefinition): T;
14
+ export declare function withLanguagePrefix<T extends NextProxy>(next: T, channel: ChannelDefinition, enableDebug?: boolean): T;
@@ -8,9 +8,12 @@ import Negotiator from 'negotiator';
8
8
  *
9
9
  * @param next The Next.JS Middleware to wrap, will be invoked if there's no redirect needed
10
10
  * @param channel The Optimizely CMS Application definition
11
+ * @param debug When set to debug, redirects will be temporary (302) and information will be
12
+ * logged. If omitted, it will determine debug mode based upon the NODE_ENV value.
11
13
  * @returns The newly constructed Next.JS Middleware
12
14
  */
13
- export function withLanguagePrefix(next, channel) {
15
+ export function withLanguagePrefix(next, channel, enableDebug) {
16
+ const debug = enableDebug === undefined ? process.env.NODE_ENV === 'development' : enableDebug;
14
17
  if (!channel)
15
18
  throw new Error("The language prefix handling requires a CMS Channel Definition");
16
19
  const defaultLocale = channel.defaultLocale;
@@ -24,19 +27,34 @@ export function withLanguagePrefix(next, channel) {
24
27
  }
25
28
  const newMiddleware = (request, ...params) => {
26
29
  const pathname = request.nextUrl.pathname;
27
- const DEBUG = process.env.NODE_ENV == 'development';
28
- const pathnameIsMissingLocale = !slugs.some(slug => pathname.toLowerCase().startsWith(`/${slug.toLowerCase()}/`) || pathname.toLowerCase() === `/${slug.toLowerCase()}`);
29
- if (pathnameIsMissingLocale) {
30
+ // Only process non internal routes
31
+ if (pathname.startsWith("/_") || pathname.startsWith("/."))
32
+ return next(request, ...params);
33
+ // Get the locale slug from the URL
34
+ const pathSlugs = pathname.split('/').filter(x => x);
35
+ const localeSlug = pathSlugs.length >= 1 && slugs.includes(pathSlugs.at(0) ?? '-') ? pathSlugs.at(0) : undefined;
36
+ if (!localeSlug) {
37
+ // We don't have a locale, so we're going to redirect to the best matching
38
+ // locale.
30
39
  const locale = getLocale(request);
31
40
  const slug = channel.resolveSlug(locale);
32
- if (DEBUG)
33
- console.log(`💬 [Middleware] Detected locale missing in ${pathname}, redirecting to /${slug}${pathname}`);
41
+ if (debug)
42
+ console.log(`💬 [Language Prefix Middleware] Detected locale missing in ${pathname}, redirecting to /${slug}${pathname}`);
34
43
  const newUrl = request.nextUrl.clone();
35
44
  newUrl.pathname = `/${slug}${pathname}`;
36
45
  return NextResponse.redirect(newUrl, {
37
- status: DEBUG ? 302 : 301
46
+ status: debug ? 302 : 301
38
47
  });
39
48
  }
49
+ else {
50
+ // We have a locale, so we're going to make it available to the application
51
+ // using request headers.
52
+ const locale = channel.slugToLocale(localeSlug);
53
+ request.headers.append("x-app-locale", locale ?? channel.defaultLocale);
54
+ request.headers.append("x-app-locale-slug", localeSlug);
55
+ if (debug)
56
+ console.log(`💬 [Language Prefix Middleware] Detected locale ${locale}`);
57
+ }
40
58
  return next(request, ...params);
41
59
  };
42
60
  return newMiddleware;
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/cms-page/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAA;AACpB,OAAO,EAAE,YAAY,EAAyC,MAAM,gBAAgB,CAAA;AAEpF,OAAO,EAAE,KAAK,EAAE,MAAM,8BAA8B,CAAA;AACpD,OAAO,UAAU,MAAM,YAAY,CAAA;AAEnC;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAA2B,IAAO,EAAE,OAA0B;IAE5F,IAAI,CAAC,OAAO;QACR,MAAM,IAAI,KAAK,CAAC,gEAAgE,CAAC,CAAA;IAErF,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,CAAA;IAC3C,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;IAChD,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAA;IAEhC,SAAS,SAAS,CAAC,OAAoB;QAEnC,MAAM,OAAO,GAA6B,EAAE,CAAA;QAC5C,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA,CAAA,CAAC,CAAC,CAAA;QACpD,MAAM,SAAS,GAAG,IAAI,UAAU,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,SAAS,EAAE,CAAA;QACzD,OAAO,KAAK,CAAC,SAAS,EAAE,OAAO,EAAE,aAAa,CAAC,CAAA;IACnD,CAAC;IAED,MAAM,aAAa,GAAoB,CAAC,OAAO,EAAE,GAAG,MAAM,EAAE,EAAE;QAE1D,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAA;QACzC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,aAAa,CAAA;QACnD,MAAM,uBAAuB,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,IAAI,QAAQ,CAAC,WAAW,EAAE,KAAK,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC,CAAA;QACxK,IAAI,uBAAuB,EAAE,CAAC;YAC1B,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC,CAAA;YACjC,MAAM,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;YACxC,IAAI,KAAK;gBACL,OAAO,CAAC,GAAG,CAAC,8CAA+C,QAAS,qBAAsB,IAAK,GAAI,QAAS,EAAE,CAAC,CAAA;YACnH,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,CAAA;YACtC,MAAM,CAAC,QAAQ,GAAG,IAAK,IAAK,GAAI,QAAS,EAAE,CAAA;YAC3C,OAAO,YAAY,CAAC,QAAQ,CAAC,MAAM,EAAE;gBACjC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG;aAC5B,CAAC,CAAA;QACN,CAAC;QACD,OAAO,IAAI,CAAC,OAAO,EAAE,GAAG,MAAM,CAAC,CAAA;IACnC,CAAC,CAAA;IACD,OAAO,aAAkB,CAAA;AAC7B,CAAC"}
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/cms-page/middleware.ts"],"names":[],"mappings":"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"}
@@ -32,7 +32,7 @@ export type OptiCmsNextJsPage<TParams extends Record<string, string | Array<stri
32
32
  * @param resolving The metadata that is currently resolving
33
33
  * @returns Updated metadat
34
34
  */
35
- generateMetadata: (props: DefaultCmsPageProps<TParams, TSearchParams>, resolving: ResolvingMetadata) => Promise<Metadata>;
35
+ generateMetadata: (props: DefaultCmsPageProps<TParams, TSearchParams>, resolving: ResolvingMetadata) => Promise<Metadata> | Metadata;
36
36
  /**
37
37
  * The actual component that performs the page rendering
38
38
  *
@@ -55,7 +55,7 @@ export type CreatePageOptions<LocaleEnum = SystemLocales, TParams extends Record
55
55
  * draftMode in configuring the client
56
56
  * @returns The client instance
57
57
  */
58
- client: (token?: string, scope?: 'request' | 'metadata') => IOptiGraphClient | Promise<IOptiGraphClient>;
58
+ client: ((token?: string, scope?: 'request' | 'metadata') => IOptiGraphClient) | ((token?: string, scope?: 'request' | 'metadata') => Promise<IOptiGraphClient>);
59
59
  /**
60
60
  * The channel information used to resolve locales, domains and more.
61
61
  *
@@ -84,7 +84,17 @@ export type CreatePageOptions<LocaleEnum = SystemLocales, TParams extends Record
84
84
  * @return The path to be retrieved from Router or getContentByPath
85
85
  * function
86
86
  */
87
- propsToCmsPath: (props: DefaultCmsPageProps<TParams, TSearchParams>) => Promise<string | null>;
87
+ propsToCmsPath: (props: DefaultCmsPageProps<TParams, TSearchParams>, channel?: ChannelDefinition) => Promise<string | null>;
88
+ /**
89
+ * Take the props received by the CmsPage from Next.JS and tranform those
90
+ * into a the page variant used by the CMS.
91
+ *
92
+ * @param props The Properties (slugs & search params) received
93
+ * by Next.JS
94
+ * @return The path to be retrieved from Router or getContentByPath
95
+ * function
96
+ */
97
+ propsToVariant: (props: DefaultCmsPageProps<TParams, TSearchParams>, channel?: ChannelDefinition) => Promise<string | null | undefined>;
88
98
  /**
89
99
  * Take the route from the Routing Service and transform that to the route
90
100
  * params used by Next.JS. The default implementation assumes that the CMS
@@ -1,28 +1,50 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import 'server-only';
3
- import deepmerge from 'deepmerge';
4
3
  import { notFound } from 'next/navigation.js';
4
+ import { cache } from 'react';
5
5
  // GraphQL Client & Services
6
6
  import { RouteResolver, } from '@remkoj/optimizely-graph-client/router';
7
7
  import { ifChannelDefinition, } from '@remkoj/optimizely-graph-client/channels';
8
8
  // React Support
9
- import { CmsContent, ServerContext, updateSharedServerContext, } from '@remkoj/optimizely-cms-react/rsc';
9
+ import { CmsContent, ServerContext, isNonEmptyString, } from '@remkoj/optimizely-cms-react/rsc';
10
10
  // Within package
11
11
  import { MetaDataResolver } from '../metadata.js';
12
12
  import { urlToPath } from './utils.js';
13
13
  import { createClient } from '../client.js';
14
14
  import { getChannelId } from './_base.js';
15
- import { loadContentByPath } from './_loadContentByPath.js';
16
- import { getInfoByPath } from './_getInfoByPath.js';
15
+ import { loadContentByPath as loadContentByPathBase } from './_loadContentByPath.js';
16
+ import { getInfoByPath as getInfoByPathBase } from './_getInfoByPath.js';
17
+ const getInfoByPath = cache(getInfoByPathBase);
18
+ const loadContentByPath = cache(loadContentByPathBase);
17
19
  export { SystemLocales } from './_base.js';
18
20
  const CreatePageOptionDefaults = {
19
21
  client: createClient,
20
22
  routerFactory: (client) => new RouteResolver(client),
21
- propsToCmsPath: async ({ params }) => buildRequestPath(await params),
22
- routeToParams: (route) => {
23
- return { path: urlToPath(route.url), lang: route.locale };
23
+ async propsToCmsPath(props) {
24
+ const params = await props.params; // Read the parameters
25
+ const slugs = [params.lang, ...(params.path ?? [])] // Build the full set of slugs for the path
26
+ .filter(isNonEmptyString) // Remove empty values
27
+ .filter((x) => !x.startsWith(encodeURIComponent('var:'))) // Remove any variant specification
28
+ .map((x) => decodeURIComponent(x)); // Decode the URI components
29
+ if (slugs.length === 0)
30
+ return '/'; // We're requesting the homepage, so just return that path
31
+ // Build the URL
32
+ const fullPath = !slugs[slugs.length - 1].includes('.') ? '/' + slugs.join('/') + '/' : '/' + slugs.join('/');
33
+ return fullPath;
24
34
  },
25
- paramsToLocale: async (params, channel) => {
35
+ async propsToVariant({ params }) {
36
+ const pathSegments = (await params).path;
37
+ const variantPrefix = encodeURIComponent('var:');
38
+ const variantSegment = Array.isArray(pathSegments) ? pathSegments.filter(x => typeof x === 'string' && x.startsWith(variantPrefix)).at(0) : undefined;
39
+ return variantSegment ? variantSegment.substring(variantPrefix.length) : undefined;
40
+ },
41
+ routeToParams(route) {
42
+ const pathSegments = urlToPath(route.url);
43
+ if (route.variation)
44
+ pathSegments.push(`var:${route.variation}`);
45
+ return { path: pathSegments, lang: route.locale };
46
+ },
47
+ async paramsToLocale(params, channel) {
26
48
  // If there's no channel, just return undefined
27
49
  if (!channel)
28
50
  return undefined;
@@ -51,7 +73,7 @@ const CreatePageOptionDefaults = {
51
73
  */
52
74
  export function createPage(factory, options) {
53
75
  // Build the global/shared configuration for the Optimizely CMS Page
54
- const { getContentByPath, client: clientFactory, channel, propsToCmsPath, routeToParams, routerFactory, paramsToLocale, } = {
76
+ const { getContentByPath, client: clientFactory, channel, propsToCmsPath, propsToVariant, routeToParams, routerFactory, paramsToLocale, } = {
55
77
  ...CreatePageOptionDefaults,
56
78
  ...options,
57
79
  };
@@ -68,74 +90,53 @@ export function createPage(factory, options) {
68
90
  const client = await clientFactory(undefined, 'metadata');
69
91
  const router = routerFactory(client);
70
92
  const channelId = getChannelId(client, channel);
71
- const allRoutes = await router.getRoutes(channelId, channel ? undefined : true);
93
+ const allRoutes = await router.getRoutes(channelId, channel ? undefined : true, true);
72
94
  return allRoutes.map((r) => routeToParams(r));
73
95
  },
74
- generateMetadata: async ({ params, searchParams }, parent) => {
75
- // Get context
96
+ generateMetadata: async ({ params, searchParams }, resolvingMetadata) => {
97
+ // Prepare the context
76
98
  const context = await buildContext();
77
- const channelId = getChannelId(context.client, channel);
78
- // Read variables from request
79
- const [requestPath, initialLocale] = await Promise.all([
80
- propsToCmsPath({ params, searchParams }),
99
+ // Analyze the Next.JS Request props
100
+ const [requestPath, initialLocale, variation] = await Promise.all([
101
+ propsToCmsPath({ params, searchParams }, ifChannelDefinition(channel)),
81
102
  paramsToLocale(params, ifChannelDefinition(channel)),
103
+ propsToVariant({ params, searchParams }, ifChannelDefinition(channel)).then(variant => variant || undefined)
82
104
  ]);
105
+ // Valdiate path
83
106
  if (!requestPath)
84
- return Promise.resolve({});
107
+ return {};
108
+ // Track initial locale
85
109
  if (initialLocale)
86
110
  context.setLocale(initialLocale);
87
- const awaitedParams = await params;
88
- // Debug output
89
- if (context.isDebug)
90
- console.log(`⚪ [CmsPage.generateMetadata] Processed Next.JS route: ${JSON.stringify(awaitedParams)} => Optimizely CMS route: ${JSON.stringify({ path: requestPath, siteId: channelId })}`);
91
- // Resolve the route to a content link
92
- const routeInfo = await getInfoByPath(context.client, routerFactory, requestPath, channel);
93
- if (!routeInfo || !routeInfo[0]) {
94
- if (context.isDebug)
95
- console.log('⚪ [CmsPage.generateMetadata] No data received');
96
- return Promise.resolve({});
97
- }
98
- const [route, contentLink, contentType, graphLocale] = routeInfo;
99
- if (context.isDebug)
100
- console.log(`⚪ [CmsPage.generateMetadata] Retrieved content info:`, route);
111
+ // Resolve route
112
+ const pathInfo = await getInfoByPath(context.client, routerFactory, requestPath, channel, variation);
113
+ if (!pathInfo)
114
+ return {};
115
+ const [route, contentLink, contentType, graphLocale] = pathInfo;
116
+ if (!contentLink || !contentType)
117
+ return {};
101
118
  // Update context from route
102
- context.setLocale(route.locale);
103
- // Make the shared server context available
104
- updateSharedServerContext(context);
119
+ if (route)
120
+ context.setLocale(route.locale);
105
121
  // Fetch the metadata based upon the actual content type and resolve parent
106
122
  const metaResolver = new MetaDataResolver(context.client);
107
123
  const [pageMetadata, baseMetadata] = await Promise.all([
108
124
  metaResolver.resolve(factory, contentLink, contentType, graphLocale),
109
- parent,
125
+ resolvingMetadata,
110
126
  ]);
111
- if (context.isDebug)
112
- console.log(`⚪ [CmsPage.generateMetadata] Component yielded metadata:`, pageMetadata);
113
- // Make sure merging of objects goes correctly
114
- for (const metaKey of Object.getOwnPropertyNames(pageMetadata)) {
115
- if (isObject(pageMetadata[metaKey]) &&
116
- isObject(baseMetadata[metaKey])) {
117
- //@ts-expect-error Silence error due to failed introspection...
118
- pageMetadata[metaKey] = deepmerge(baseMetadata[metaKey], pageMetadata[metaKey], { arrayMerge: (target, source) => [...source] });
119
- }
120
- }
121
- // Not sure, but needed somehow...
122
- if (typeof baseMetadata.metadataBase == 'string' &&
123
- baseMetadata.metadataBase.length > 1) {
124
- pageMetadata.metadataBase = new URL(baseMetadata.metadataBase);
125
- }
126
127
  return pageMetadata;
127
128
  },
128
129
  CmsPage: async ({ params, searchParams }) => {
129
130
  // Prepare the context
130
131
  const context = await buildContext();
131
- //const params = await asyncParams;
132
132
  // Analyze the Next.JS Request props
133
- const [requestPath, initialLocale] = await Promise.all([
134
- propsToCmsPath({ params, searchParams }),
133
+ const [requestPath, initialLocale, requestVariant] = await Promise.all([
134
+ propsToCmsPath({ params, searchParams }, ifChannelDefinition(channel)),
135
135
  paramsToLocale(params, ifChannelDefinition(channel)),
136
+ (await propsToVariant({ params, searchParams })) ?? undefined
136
137
  ]);
137
138
  if (context.isDebug)
138
- console.log(`⚪ [CmsPage] Processed Next.JS route: ${JSON.stringify(await params)} => Optimizely CMS route: ${JSON.stringify({ path: requestPath })}`);
139
+ console.log(`⚪ [CmsPage] Processed Next.JS route: ${JSON.stringify(await params)} => Optimizely CMS route: ${JSON.stringify({ path: requestPath, variant: requestVariant })}`);
139
140
  // If we don't have the path, or the path is an internal Next.js route reject it.
140
141
  if (!requestPath || requestPath.startsWith('/_next/'))
141
142
  return notFound();
@@ -144,8 +145,8 @@ export function createPage(factory, options) {
144
145
  context.setLocale(initialLocale);
145
146
  // Resolve the content based upon the path
146
147
  const lookupData = await (getContentByPath
147
- ? loadContentByPath(context.client, getContentByPath, requestPath, channel, initialLocale)
148
- : getInfoByPath(context.client, routerFactory, requestPath, channel));
148
+ ? loadContentByPath(context.client, getContentByPath, requestPath, channel, initialLocale, requestVariant)
149
+ : getInfoByPath(context.client, routerFactory, requestPath, channel, requestVariant));
149
150
  if (!lookupData) {
150
151
  console.error(`🔴 [CmsPage] Unable to resolve the content for ${JSON.stringify(await params)}!`);
151
152
  return notFound();
@@ -153,38 +154,10 @@ export function createPage(factory, options) {
153
154
  const [route, contentLink, contentType, graphLocale, contentData] = lookupData;
154
155
  if (contentLink?.locale)
155
156
  context.setLocale(contentLink.locale);
156
- // Make the shared server context available
157
- updateSharedServerContext(context);
158
157
  // Render the content link
159
158
  return (_jsx(CmsContent, { contentType: contentType, contentLink: contentLink, fragmentData: contentData ?? undefined, ctx: context }));
160
159
  },
161
160
  };
162
161
  return pageDefintion;
163
162
  }
164
- /**
165
- *
166
- *
167
- * @param param0 The URL parameters
168
- * @returns The request path as understood by Graph
169
- */
170
- function buildRequestPath({ lang, path, }) {
171
- const slugs = [];
172
- if (path)
173
- slugs.push(...path.filter((x) => x));
174
- if (lang)
175
- slugs.unshift(lang);
176
- if (slugs.length == 0)
177
- return '/';
178
- const fullPath = '/' +
179
- slugs
180
- .filter((x) => x && x.length > 0)
181
- .map((x) => decodeURIComponent(x))
182
- .join('/');
183
- if (!slugs[slugs.length - 1].includes('.'))
184
- return fullPath + '/';
185
- return fullPath;
186
- }
187
- function isObject(toTest) {
188
- return typeof toTest == 'object' && toTest != null && toTest != undefined;
189
- }
190
163
  //# sourceMappingURL=page.js.map