@enonic/nextjs-adapter 6.0.0-beta.3 → 6.0.0-beta.4

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 (75) hide show
  1. package/README.md +65 -106
  2. package/dist/client/DraftModeButton.d.ts +2 -0
  3. package/dist/client/DraftModeButton.jsx +28 -0
  4. package/dist/client/DraftModeButton.jsx.map +1 -0
  5. package/dist/client/DraftModeButton.module.css +43 -0
  6. package/dist/client/replaceLocation.d.ts +1 -0
  7. package/dist/client/replaceLocation.js +5 -0
  8. package/dist/client/replaceLocation.js.map +1 -0
  9. package/dist/common/constants.d.ts +4 -0
  10. package/dist/common/constants.js +4 -0
  11. package/dist/common/constants.js.map +1 -1
  12. package/dist/common/env.d.ts +5 -1
  13. package/dist/common/env.js +8 -3
  14. package/dist/common/env.js.map +1 -1
  15. package/dist/guillotine/applyProcessors.js +18 -29
  16. package/dist/guillotine/applyProcessors.js.map +1 -1
  17. package/dist/guillotine/fetchContent.js +180 -191
  18. package/dist/guillotine/fetchContent.js.map +1 -1
  19. package/dist/guillotine/fetchContentData.js +3 -12
  20. package/dist/guillotine/fetchContentData.js.map +1 -1
  21. package/dist/guillotine/fetchContentPathsForAllLocales.js +4 -15
  22. package/dist/guillotine/fetchContentPathsForAllLocales.js.map +1 -1
  23. package/dist/guillotine/fetchContentPathsForLocale.js +25 -36
  24. package/dist/guillotine/fetchContentPathsForLocale.js.map +1 -1
  25. package/dist/guillotine/fetchFromApi.js +48 -59
  26. package/dist/guillotine/fetchFromApi.js.map +1 -1
  27. package/dist/guillotine/fetchGuillotine.js +49 -60
  28. package/dist/guillotine/fetchGuillotine.js.map +1 -1
  29. package/dist/guillotine/fetchMetaData.js +3 -12
  30. package/dist/guillotine/fetchMetaData.js.map +1 -1
  31. package/dist/guillotine/metadata/richTextQuery.js +4 -9
  32. package/dist/guillotine/metadata/richTextQuery.js.map +1 -1
  33. package/dist/guillotine/urlQueries.d.ts +5 -0
  34. package/dist/guillotine/urlQueries.js +23 -0
  35. package/dist/guillotine/urlQueries.js.map +1 -0
  36. package/dist/guillotine/urls.d.ts +9 -0
  37. package/dist/guillotine/urls.js +18 -0
  38. package/dist/guillotine/urls.js.map +1 -0
  39. package/dist/guillotine/validateShortcut.js +4 -5
  40. package/dist/guillotine/validateShortcut.js.map +1 -1
  41. package/dist/i18n/LocaleContext.jsx +2 -11
  42. package/dist/i18n/LocaleContext.jsx.map +1 -1
  43. package/dist/i18n/i18n.js +9 -20
  44. package/dist/i18n/i18n.js.map +1 -1
  45. package/dist/index.d.ts +4 -2
  46. package/dist/index.js +4 -2
  47. package/dist/index.js.map +1 -1
  48. package/dist/query/Shortcut.js +2 -3
  49. package/dist/query/Shortcut.js.map +1 -1
  50. package/dist/server/disableDraftMode.d.ts +1 -0
  51. package/dist/server/disableDraftMode.js +7 -0
  52. package/dist/server/disableDraftMode.js.map +1 -0
  53. package/dist/types/guillotine.d.ts +2 -2
  54. package/dist/types/index.d.ts +6 -0
  55. package/dist/types/urls.d.ts +34 -0
  56. package/dist/types/urls.js +2 -0
  57. package/dist/types/urls.js.map +1 -0
  58. package/dist/utils/getMediaUrl.d.ts +3 -0
  59. package/dist/utils/getMediaUrl.js +17 -0
  60. package/dist/utils/getMediaUrl.js.map +1 -0
  61. package/dist/utils/getPageUrl.d.ts +3 -0
  62. package/dist/utils/getPageUrl.js +8 -0
  63. package/dist/utils/getPageUrl.js.map +1 -0
  64. package/dist/utils/localizeMappings.d.ts +3 -0
  65. package/dist/utils/localizeMappings.js +6 -0
  66. package/dist/utils/localizeMappings.js.map +1 -0
  67. package/dist/views/DraftModeIndicator.d.ts +2 -0
  68. package/dist/views/DraftModeIndicator.jsx +12 -0
  69. package/dist/views/DraftModeIndicator.jsx.map +1 -0
  70. package/dist/views/RichTextView.jsx +10 -3
  71. package/dist/views/RichTextView.jsx.map +1 -1
  72. package/package.json +7 -7
  73. package/dist/common/UrlProcessor.d.ts +0 -17
  74. package/dist/common/UrlProcessor.js +0 -33
  75. package/dist/common/UrlProcessor.js.map +0 -1
package/README.md CHANGED
@@ -303,27 +303,51 @@ They are available at `@enonic/nextjs-adapter`
303
303
 
304
304
  <br/>
305
305
 
306
- #### <a id="get-url"></a>`getUrl(url: string, meta: MetaData) => string`
307
-
308
- Converts a site-relative or absolute URL to relative one for current viewer (Next.js/Enonic XP). Also takes care of locale if needed.
309
-
310
- > **INFO:** For your URLs to work both in Enonic XP and Next.js you need to:
311
- > 1. Query site-relative or absolute URLs from guillotine
312
- > 2. Wrap them with `getUrl()` function in the views
313
-
314
- | Argument | Description |
315
- |----------|---------------------------------------------------------|
316
- | `url` | URL you want to transform |
317
- | `meta` | Runtime data returned by [fetchContent](#fetch-content) |
306
+ #### <a id="url-helpers"></a>Guillotine URL helpers
307
+
308
+ Every Guillotine `*Url` field comes with two functions: `*UrlQuery(args)` builds the GraphQL selection for the field, and `*Url(result)`
309
+ turns the returned object into the final URL string.
310
+
311
+ | Field | Query builder | String function |
312
+ |-----------------|-------------------------------------------|-----------------------|
313
+ | `imageUrl` | `imageUrlQuery(args: ImageUrlArgs)` | `imageUrl(data)` |
314
+ | `mediaUrl` | `mediaUrlQuery(args?: MediaUrlArgs)` | `mediaUrl(data)` |
315
+ | `attachmentUrl` | `attachmentUrlQuery(args?: MediaUrlArgs)` | `attachmentUrl(data)` |
316
+ | `pageUrl` | `pageUrlQuery(args?: PageUrlArgs)` | `pageUrl(data, meta)` |
317
+
318
+ The query builders accept the same arguments as the Guillotine field (`scale`, `quality`, `background`, `format`, `filter`, `params` for
319
+ `imageUrl`; `download`, `params` for `mediaUrl`/`attachmentUrl`; `params` for `pageUrl`) and select every field of the returned object:
320
+ `url`, `path`, `queryString`, `context`, `id`, `fingerprint`, `name`, plus `scale` for images and `intent` for media and attachments
321
+ (`pageUrl` has `url`, `path` and `queryString`).
322
+ Media functions return absolute URLs pointing at the XP media API (`ENONIC_MEDIA_CDN`, defaulting to `ENONIC_API`). `pageUrl` returns a
323
+ relative path for the Next.js router, prefixed with the locale unless it is the default locale (`meta.locale` vs `meta.defaultLocale`);
324
+ `meta` is the runtime data returned by [fetchContent](#fetch-content). Pass `{locale}` alone to always prefix, e.g. for `revalidatePath`.
325
+ The string functions take that object (`ImageUrl`, `AttachmentUrl` or `PageUrl` from the adapter types) and return `undefined` when given `undefined`;
326
+ All four append `queryString` to `path` and need only `path` (`queryString` is optional), so links without a Guillotine object can
327
+ pass a bare `{path}`, e.g. `pageUrl({path: '/'}, meta)` for the site root.
318
328
 
319
329
  Usage:
320
330
 
321
331
  ```tsx
322
- import {getUrl} from '@enonic/nextjs-adapter';
332
+ import {imageUrlQuery, pageUrlQuery, imageUrl, pageUrl} from '@enonic/nextjs-adapter';
333
+
334
+ const query = `
335
+ photos {
336
+ ... on media_Image {
337
+ ${imageUrlQuery({scale: 'width(500)'})}
338
+ }
339
+ }
340
+ parent {
341
+ ${pageUrlQuery()}
342
+ }`;
323
343
 
324
- const urlRelativeToViewer = getUrl('/some/content/url', meta);
344
+ <img src={imageUrl(photo.imageUrl)}/>
345
+ <Link href={pageUrl(parent.pageUrl, meta)}>Back</Link>
325
346
  ```
326
347
 
348
+ `localizeMappings(mappings: UrlMappingRule[], mapping: LocaleMapping) => UrlMappingRule[]` prefixes `/api/mappings` targets with the
349
+ mapping's locale unless it is the default one, leaving sources untouched.
350
+
327
351
  <br/>
328
352
 
329
353
  #### <a id="rich-text-query"></a>`richTextQuery(fieldName: string) => string`
@@ -638,98 +662,7 @@ import {ComponentRegistry} from '@enonic/nextjs-adapter';
638
662
  const macros = ComponentRegistry.getMacros();
639
663
  ```
640
664
 
641
- Response type: list of `[name, ComponentDefinition]` tuples — see [component definition](#comp-def).
642
-
643
- <br/>
644
-
645
- ### `UrlProcessor`
646
-
647
- Helper singleton for processing URLs.
648
-
649
- <br/>
650
-
651
- #### <a id="process"></a>`static process(url: string, meta: MetaData, serverSide = false, isResource = false): string`
652
-
653
- Processes the absolute URL to become relative for the current viewer, while keeping in mind Next.js assets and Enonic XP binary content
654
- links
655
-
656
- > **NOTE:** There is a convenience alias to this function called [getUrl()](#get-url)
657
-
658
- | Argument | Description |
659
- |--------------|------------------------------------------------------------------------------|
660
- | `url` | Absolute URL |
661
- | `meta` | Runtime data returned by [fetchContent](#fetch-content) |
662
- | `serverSide` | Whether URL is going to be used on the server side _(Skips adding basePath)_ |
663
- | `isResource` | Whether URL is a resource _(Skips adding locale)_ |
664
-
665
- Usage:
666
-
667
- ```tsx
668
- import {UrlProcessor} from '@enonic/nextjs-adapter';
669
-
670
- const url = UrlProcessor.process('http://www.some.site.com/url/to/content', meta, true, false);
671
- ```
672
-
673
- <br/>
674
-
675
- #### `static processSrcSet(srcset: string, meta: MetaData): string`
676
-
677
- Processes the image srcset attribute to transform each URL with [`process`](#process) method
678
-
679
- | Argument | Description |
680
- |----------|---------------------------------------------------------|
681
- | `srcset` | Value of the srcset attribute |
682
- | `meta` | Runtime data returned by [fetchContent](#fetch-content) |
683
-
684
- Usage:
685
-
686
- ```tsx
687
- import {UrlProcessor} from '@enonic/nextjs-adapter';
688
-
689
- const url = UrlProcessor.processSrcSet('<srcset value>', meta);
690
- ```
691
-
692
- <br/>
693
-
694
- #### `static isMediaLink(ref: string, linkData: LinkData[]): boolean`
695
-
696
- Checks if link data array contains link with provided ref. Positive result means that this is a link to Enonic XP content.
697
-
698
- > **NOTE:** link data array is contained in response of the query generated by [richTextQuery('fieldName')](#rich-text-query)
699
-
700
- | Argument | Description |
701
- |------------|-----------------|
702
- | `ref` | Link ref |
703
- | `linkData` | Link data array |
704
-
705
- Usage:
706
-
707
- ```tsx
708
- import {UrlProcessor} from '@enonic/nextjs-adapter';
709
-
710
- UrlProcessor.isMediaLink('<link ref>', linkData);
711
- ```
712
-
713
- <br/>
714
-
715
- #### `static isContentImage(ref: string, imageData: ImageData[]): boolean`
716
-
717
- Checks if image data array contains image with provided ref. Positive response means that this is an Enonic XP image.
718
-
719
- > **NOTE:** image data array is contained in response of the query generated by [richTextQuery('fieldName')](#rich-text-query)
720
-
721
- | Argument | Description |
722
- |-------------|------------------|
723
- | `ref` | Image ref |
724
- | `imageData` | Image data array |
725
-
726
- Usage:
727
-
728
- ```tsx
729
- import {UrlProcessor} from '@enonic/nextjs-adapter';
730
-
731
- UrlProcessor.isContentImage('<image ref>', imageData);
732
- ```
665
+ Response type: list of `[name, ComponentDefinition]` tuples — see [component definition](#comp-def).
733
666
 
734
667
  <br/>
735
668
 
@@ -1038,6 +971,32 @@ import StaticContent from '@enonic/nextjs-adapter/views/StaticContent';
1038
971
 
1039
972
  <br/>
1040
973
 
974
+ #### `<DraftModeIndicator label="Draft">`
975
+
976
+ Floating badge in the bottom-right corner, styled like the Next.js dev indicator, for leaving Next.js draft mode. It renders only when `draftMode().isEnabled` is true, and hides itself inside iframes (i.e. in Content Studio). The label defaults to `Draft`, is followed by a `✕` glyph, and can be localized by the caller, e.g. with `I18n.localize()`. Clicking it runs a Server Action that calls `draftMode().disable()` and then reloads the page without the `xp` query parameter (the Content Studio preview marker, which would otherwise make the proxy re-enable draft mode), so content is served from the `master` branch again. The badge ships its own CSS module; its colors can be overridden through the `--draft-mode-*` custom properties set on the button.
977
+
978
+ | Argument | Type | Description |
979
+ |-----------------------------|-----------------------|--------------------------------------------------------------------------|
980
+ | `label = 'Draft'` | `String` | Badge text, e.g. a localized phrase |
981
+
982
+ Usage:
983
+
984
+ ```tsx
985
+ import {I18n, IS_DEV_MODE} from '@enonic/nextjs-adapter';
986
+ import DraftModeIndicator from '@enonic/nextjs-adapter/views/DraftModeIndicator';
987
+
988
+ // Root layout: render it conditionally (here: only under `next dev`) directly inside <body>,
989
+ // not inside <StaticContent> (which disables hydration)
990
+ <body>
991
+ {children}
992
+ {IS_DEV_MODE && <DraftModeIndicator label={I18n.localize('draft-mode')}/>}
993
+ </body>
994
+ ```
995
+
996
+ Requires `transpilePackages: ['@enonic/nextjs-adapter']` in `next.config.js` so that Next.js compiles the bundled Server Action.
997
+
998
+ <br/>
999
+
1041
1000
  #### <a id="rich-text-view"></a>
1042
1001
  `<RichTextView className="css-class" tag="section" data={data} meta={meta} customReplacer={customReplacerFn}>`
1043
1002
 
@@ -0,0 +1,2 @@
1
+ import type { DraftModeButtonProps } from '../types';
2
+ export default function DraftModeButton({ action, label }: DraftModeButtonProps): import("react").JSX.Element;
@@ -0,0 +1,28 @@
1
+ 'use client';
2
+ import { useSyncExternalStore, useTransition } from 'react';
3
+ import { FROM_XP_PARAM } from '../common/constants';
4
+ import { replaceLocation } from './replaceLocation';
5
+ import styles from './DraftModeButton.module.css';
6
+ const subscribe = () => () => undefined;
7
+ const isTopWindow = () => window.self === window.top;
8
+ const isTopWindowOnServer = () => false;
9
+ function urlWithoutXpParam() {
10
+ const url = new URL(window.location.href);
11
+ url.searchParams.delete(FROM_XP_PARAM);
12
+ return url.toString();
13
+ }
14
+ export default function DraftModeButton({ action, label }) {
15
+ const visible = useSyncExternalStore(subscribe, isTopWindow, isTopWindowOnServer);
16
+ const [isPending, startTransition] = useTransition();
17
+ if (!visible) {
18
+ return null;
19
+ }
20
+ return (<button type="button" className={styles.button} disabled={isPending} onClick={() => startTransition(async () => {
21
+ await action();
22
+ // Full reload without the CS marker: with it and no draft cookie the proxy would re-enable draft mode
23
+ replaceLocation(urlWithoutXpParam());
24
+ })}>
25
+ {label} <span className={styles.glyph}>✕</span>
26
+ </button>);
27
+ }
28
+ //# sourceMappingURL=DraftModeButton.jsx.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DraftModeButton.jsx","sourceRoot":"","sources":["../../src/client/DraftModeButton.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAGb,OAAO,EAAC,oBAAoB,EAAE,aAAa,EAAC,MAAM,OAAO,CAAC;AAC1D,OAAO,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAC;AAClD,OAAO,MAAM,MAAM,8BAA8B,CAAC;AAElD,MAAM,SAAS,GAAG,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC;AACxC,MAAM,WAAW,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,GAAG,CAAC;AACrD,MAAM,mBAAmB,GAAG,GAAG,EAAE,CAAC,KAAK,CAAC;AAExC,SAAS,iBAAiB;IACtB,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC1C,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;IACvC,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC;AAC1B,CAAC;AAED,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EAAC,MAAM,EAAE,KAAK,EAAuB;IACzE,MAAM,OAAO,GAAG,oBAAoB,CAAC,SAAS,EAAE,WAAW,EAAE,mBAAmB,CAAC,CAAC;IAClF,MAAM,CAAC,SAAS,EAAE,eAAe,CAAC,GAAG,aAAa,EAAE,CAAC;IAErD,IAAI,CAAC,OAAO,EAAE,CAAC;QACX,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,OAAO,CACH,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CACb,SAAS,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CACzB,QAAQ,CAAC,CAAC,SAAS,CAAC,CACpB,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,eAAe,CAAC,KAAK,IAAI,EAAE;YACtC,MAAM,MAAM,EAAE,CAAC;YACf,sGAAsG;YACtG,eAAe,CAAC,iBAAiB,EAAE,CAAC,CAAC;QACzC,CAAC,CAAC,CAAC,CACP;YAAA,CAAC,KAAK,CAAE,CAAA,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,CAClD;QAAA,EAAE,MAAM,CAAC,CACZ,CAAC;AACN,CAAC"}
@@ -0,0 +1,43 @@
1
+ .button {
2
+ --draft-mode-bg: #000;
3
+ --draft-mode-bg-hover: #1a1a1a;
4
+ --draft-mode-border: #333;
5
+ --draft-mode-border-hover: #555;
6
+ --draft-mode-fg: #eee;
7
+ --draft-mode-fg-hover: #fff;
8
+
9
+ position: fixed;
10
+ right: 16px;
11
+ bottom: 16px;
12
+ z-index: 9999;
13
+ display: inline-flex;
14
+ align-items: center;
15
+ gap: 8px;
16
+ height: 36px;
17
+ padding: 0 14px;
18
+ border: 1px solid var(--draft-mode-border);
19
+ border-radius: 9999px;
20
+ background: var(--draft-mode-bg);
21
+ color: var(--draft-mode-fg);
22
+ font: 13px/1 system-ui, sans-serif;
23
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
24
+ cursor: pointer;
25
+ }
26
+ .button > * {
27
+ color: var(--draft-mode-fg);
28
+ }
29
+
30
+ .button:hover {
31
+ background: var(--draft-mode-bg-hover);
32
+ border-color: var(--draft-mode-border-hover);
33
+ color: var(--draft-mode-fg-hover);
34
+ }
35
+
36
+ .button:disabled {
37
+ cursor: wait;
38
+ opacity: 0.6;
39
+ }
40
+
41
+ .button:hover > * {
42
+ color: var(--draft-mode-fg-hover);
43
+ }
@@ -0,0 +1 @@
1
+ export declare function replaceLocation(url: string): void;
@@ -0,0 +1,5 @@
1
+ 'use client';
2
+ export function replaceLocation(url) {
3
+ window.location.replace(url);
4
+ }
5
+ //# sourceMappingURL=replaceLocation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replaceLocation.js","sourceRoot":"","sources":["../../src/client/replaceLocation.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,MAAM,UAAU,eAAe,CAAC,GAAW;IACvC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;AACjC,CAAC"}
@@ -3,9 +3,12 @@ export declare enum ENV_VARS {
3
3
  MAPPINGS = "ENONIC_MAPPINGS",
4
4
  API_TOKEN = "ENONIC_API_TOKEN",
5
5
  API_URL = "ENONIC_API",
6
+ GUILLOTINE_API = "GUILLOTINE_API",
7
+ MEDIA_URL = "ENONIC_MEDIA_CDN",
6
8
  APP_NAME = "ENONIC_APP_NAME",
7
9
  LOG = "ENONIC_LOGGING"
8
10
  }
11
+ export declare const DEFAULT_GUILLOTINE_API = "com.enonic.app.guillotine:graphql";
9
12
  export declare const FRAGMENT_CONTENTTYPE_NAME = "portal:fragment";
10
13
  export declare const FRAGMENT_DEFAULT_REGION_NAME = "fragment";
11
14
  /** excluded contents:
@@ -37,6 +40,7 @@ export declare enum RENDER_MODE {
37
40
  }
38
41
  export declare const RENDER_MODE_HEADER = "content-studio-mode";
39
42
  export declare const SITE_CONTENTTYPE_NAME = "portal:site";
43
+ export declare const FROM_XP_PARAM = "xp";
40
44
  export declare const XP_BASE_URL_HEADER = "xpbaseurl";
41
45
  export declare enum XP_COMPONENT_TYPE {
42
46
  PART = "part",
@@ -4,9 +4,12 @@ export var ENV_VARS;
4
4
  ENV_VARS["MAPPINGS"] = "ENONIC_MAPPINGS";
5
5
  ENV_VARS["API_TOKEN"] = "ENONIC_API_TOKEN";
6
6
  ENV_VARS["API_URL"] = "ENONIC_API";
7
+ ENV_VARS["GUILLOTINE_API"] = "GUILLOTINE_API";
8
+ ENV_VARS["MEDIA_URL"] = "ENONIC_MEDIA_CDN";
7
9
  ENV_VARS["APP_NAME"] = "ENONIC_APP_NAME";
8
10
  ENV_VARS["LOG"] = "ENONIC_LOGGING";
9
11
  })(ENV_VARS || (ENV_VARS = {}));
12
+ export const DEFAULT_GUILLOTINE_API = 'com.enonic.app.guillotine:graphql';
10
13
  export const FRAGMENT_CONTENTTYPE_NAME = 'portal:fragment';
11
14
  export const FRAGMENT_DEFAULT_REGION_NAME = 'fragment';
12
15
  import { queryGuillotine } from '../guillotine/getMetaData';
@@ -68,6 +71,7 @@ export var RENDER_MODE;
68
71
  export const RENDER_MODE_HEADER = 'content-studio-mode';
69
72
  export const SITE_CONTENTTYPE_NAME = 'portal:site';
70
73
  // URI parameter marking that a request is for a preview for CS. MUST MATCH THE VALUE OF 'FROM_XP_PARAM' on XP side.
74
+ export const FROM_XP_PARAM = 'xp';
71
75
  export const XP_BASE_URL_HEADER = 'xpbaseurl';
72
76
  // TODO: Use these instead of hardcoded strings everywhere
73
77
  export var XP_COMPONENT_TYPE;
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/common/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,SAAS,GAAG,GAAG,CAAC;AAE7B,MAAM,CAAN,IAAY,QAMX;AAND,WAAY,QAAQ;IAChB,wCAA4B,CAAA;IAC5B,0CAA8B,CAAA;IAC9B,kCAAsB,CAAA;IACtB,wCAA4B,CAAA;IAC5B,kCAAsB,CAAA;AAC1B,CAAC,EANW,QAAQ,KAAR,QAAQ,QAMnB;AAED,MAAM,CAAC,MAAM,yBAAyB,GAAG,iBAAiB,CAAC;AAC3D,MAAM,CAAC,MAAM,4BAA4B,GAAG,UAAU,CAAC;AAEvD,OAAO,EAAC,eAAe,EAAC,MAAM,2BAA2B,CAAC;AAE1D;;;;;GAKG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,eAAe,CAAC;;;;;;;;;;;;;;;;;;;;;MAqBhD,EACF;IACI,YAAY,EAAE,SAAS;IACvB,QAAQ,EAAE,KAAK;CAClB,CAAC,CAAC;AAEP,8EAA8E;AAC9E,qBAAqB;AACrB,MAAM,CAAN,IAAY,0BAKX;AALD,WAAY,0BAA0B;IAClC,+CAAiB,CAAA;IACjB,+CAAiB,CAAA;IACjB,iDAAmB,CAAA;IACnB,+DAAiC,CAAA;AACrC,CAAC,EALW,0BAA0B,KAA1B,0BAA0B,QAKrC;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAG,YAAY,CAAC;AAE9C,MAAM,CAAC,MAAM,8BAA8B,GAAG,sBAAsB,CAAC;AACrE,MAAM,CAAC,MAAM,oBAAoB,GAAG,wBAAwB,CAAC;AAC7D,MAAM,CAAC,MAAM,0BAA0B,GAAG,4BAA4B,CAAC;AACvE,MAAM,CAAC,MAAM,uBAAuB,GAAG,oBAAoB,CAAC;AAC5D,MAAM,CAAC,MAAM,iBAAiB,GAAG,wBAAwB,CAAC;AAE1D,MAAM,CAAN,IAAY,WAOX;AAPD,WAAY,WAAW;IACnB,gCAAiB,CAAA;IACjB,4BAAa,CAAA;IACb,kCAAmB,CAAA;IACnB,4BAAa,CAAA;IACb,8BAAe,CAAA;IACf,4BAAa,CAAA,CAAO,mEAAmE;AAC3F,CAAC,EAPW,WAAW,KAAX,WAAW,QAOtB;AAED,MAAM,CAAC,MAAM,kBAAkB,GAAG,qBAAqB,CAAC;AAExD,MAAM,CAAC,MAAM,qBAAqB,GAAG,aAAa,CAAC;AAEnD,oHAAoH;AACpH,MAAM,CAAC,MAAM,kBAAkB,GAAG,WAAW,CAAC;AAE9C,0DAA0D;AAC1D,MAAM,CAAN,IAAY,iBAMX;AAND,WAAY,iBAAiB;IACzB,kCAAa,CAAA;IACb,sCAAiB,CAAA;IACjB,kCAAa,CAAA;IACb,0CAAqB,CAAA;IACrB,kCAAa,CAAA;AACjB,CAAC,EANW,iBAAiB,KAAjB,iBAAiB,QAM5B;AAED,MAAM,CAAN,IAAY,eAIX;AAJD,WAAY,eAAe;IACvB,0CAAuB,CAAA;IACvB,gCAAa,CAAA;IACb,gCAAa,CAAA;AACjB,CAAC,EAJW,eAAe,KAAf,eAAe,QAI1B"}
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/common/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,SAAS,GAAG,GAAG,CAAC;AAE7B,MAAM,CAAN,IAAY,QAQX;AARD,WAAY,QAAQ;IAChB,wCAA4B,CAAA;IAC5B,0CAA8B,CAAA;IAC9B,kCAAsB,CAAA;IACtB,6CAAiC,CAAA;IACjC,0CAA8B,CAAA;IAC9B,wCAA4B,CAAA;IAC5B,kCAAsB,CAAA;AAC1B,CAAC,EARW,QAAQ,KAAR,QAAQ,QAQnB;AAED,MAAM,CAAC,MAAM,sBAAsB,GAAG,mCAAmC,CAAC;AAE1E,MAAM,CAAC,MAAM,yBAAyB,GAAG,iBAAiB,CAAC;AAC3D,MAAM,CAAC,MAAM,4BAA4B,GAAG,UAAU,CAAC;AAEvD,OAAO,EAAC,eAAe,EAAC,MAAM,2BAA2B,CAAC;AAE1D;;;;;GAKG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,eAAe,CAAC;;;;;;;;;;;;;;;;;;;;;MAqBhD,EACF;IACI,YAAY,EAAE,SAAS;IACvB,QAAQ,EAAE,KAAK;CAClB,CAAC,CAAC;AAEP,8EAA8E;AAC9E,qBAAqB;AACrB,MAAM,CAAN,IAAY,0BAKX;AALD,WAAY,0BAA0B;IAClC,+CAAiB,CAAA;IACjB,+CAAiB,CAAA;IACjB,iDAAmB,CAAA;IACnB,+DAAiC,CAAA;AACrC,CAAC,EALW,0BAA0B,KAA1B,0BAA0B,QAKrC;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAG,YAAY,CAAC;AAE9C,MAAM,CAAC,MAAM,8BAA8B,GAAG,sBAAsB,CAAC;AACrE,MAAM,CAAC,MAAM,oBAAoB,GAAG,wBAAwB,CAAC;AAC7D,MAAM,CAAC,MAAM,0BAA0B,GAAG,4BAA4B,CAAC;AACvE,MAAM,CAAC,MAAM,uBAAuB,GAAG,oBAAoB,CAAC;AAC5D,MAAM,CAAC,MAAM,iBAAiB,GAAG,wBAAwB,CAAC;AAE1D,MAAM,CAAN,IAAY,WAOX;AAPD,WAAY,WAAW;IACnB,gCAAiB,CAAA;IACjB,4BAAa,CAAA;IACb,kCAAmB,CAAA;IACnB,4BAAa,CAAA;IACb,8BAAe,CAAA;IACf,4BAAa,CAAA,CAAO,mEAAmE;AAC3F,CAAC,EAPW,WAAW,KAAX,WAAW,QAOtB;AAED,MAAM,CAAC,MAAM,kBAAkB,GAAG,qBAAqB,CAAC;AAExD,MAAM,CAAC,MAAM,qBAAqB,GAAG,aAAa,CAAC;AAEnD,oHAAoH;AACpH,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,CAAC;AAElC,MAAM,CAAC,MAAM,kBAAkB,GAAG,WAAW,CAAC;AAE9C,0DAA0D;AAC1D,MAAM,CAAN,IAAY,iBAMX;AAND,WAAY,iBAAiB;IACzB,kCAAa,CAAA;IACb,sCAAiB,CAAA;IACjB,kCAAa,CAAA;IACb,0CAAqB,CAAA;IACrB,kCAAa,CAAA;AACjB,CAAC,EANW,iBAAiB,KAAjB,iBAAiB,QAM5B;AAED,MAAM,CAAN,IAAY,eAIX;AAJD,WAAY,eAAe;IACvB,0CAAuB,CAAA;IACvB,gCAAa,CAAA;IACb,gCAAa,CAAA;AACjB,CAAC,EAJW,eAAe,KAAf,eAAe,QAI1B"}
@@ -1,5 +1,9 @@
1
- /** URL to the guillotine API (server-side only) */
1
+ /** Absolute URL to the Enonic XP API root (server-side only) */
2
2
  export declare const API_URL: string;
3
+ /** URL to the Guillotine GraphQL endpoint: API_URL + GUILLOTINE_API (server-side only) */
4
+ export declare const GUILLOTINE_URL: string;
5
+ /** Absolute URL to the media API root, defaults to API_URL (server-side only) */
6
+ export declare const MEDIA_URL: string;
3
7
  /** Optional utility value - defining in one place the name of the target app (the app that defines the content types, the app name is therefore part of the content type strings used both in typeselector and in query introspections) (server-side only) */
4
8
  export declare const APP_NAME: string;
5
9
  /** Optional utility value - derived from APP_NAME, only with underscores instead of dots */
@@ -1,9 +1,14 @@
1
- import { ENV_VARS } from './constants';
1
+ import { DEFAULT_GUILLOTINE_API, ENV_VARS } from './constants';
2
+ import { stripOutsideSlashes, stripTrailingSlashes } from '../utils/fixDoubleSlashes';
2
3
  const isServer = typeof window === 'undefined';
3
4
  // IMPORTANT:
4
5
  // NEXT_PUBLIC_ vars should be explicitly referenced to be made available on the client side (substituted with constants) !!!
5
- /** URL to the guillotine API (server-side only) */
6
- export const API_URL = process.env[ENV_VARS.API_URL];
6
+ /** Absolute URL to the Enonic XP API root (server-side only) */
7
+ export const API_URL = stripTrailingSlashes(process.env[ENV_VARS.API_URL] || '');
8
+ /** URL to the Guillotine GraphQL endpoint: API_URL + GUILLOTINE_API (server-side only) */
9
+ export const GUILLOTINE_URL = API_URL && `${API_URL}/${stripOutsideSlashes(process.env[ENV_VARS.GUILLOTINE_API] || DEFAULT_GUILLOTINE_API)}`;
10
+ /** Absolute URL to the media API root, defaults to API_URL (server-side only) */
11
+ export const MEDIA_URL = stripTrailingSlashes(process.env[ENV_VARS.MEDIA_URL] || '') || API_URL;
7
12
  /** Optional utility value - defining in one place the name of the target app (the app that defines the content types, the app name is therefore part of the content type strings used both in typeselector and in query introspections) (server-side only) */
8
13
  export const APP_NAME = process.env[ENV_VARS.APP_NAME];
9
14
  /** Optional utility value - derived from APP_NAME, only with underscores instead of dots */
@@ -1 +1 @@
1
- {"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/common/env.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAC,MAAM,aAAa,CAAC;AAErC,MAAM,QAAQ,GAAG,OAAO,MAAM,KAAK,WAAW,CAAC;AAE/C,aAAa;AACb,6HAA6H;AAC7H,mDAAmD;AACnD,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;AAErD,8PAA8P;AAC9P,MAAM,CAAC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAEvD,4FAA4F;AAC5F,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AAEzE,uFAAuF;AACvF,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AAEpE,sFAAsF;AACtF,MAAM,CAAC,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,aAAa,CAAC;AAElE,MAAM,CAAC,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;AAExG,4EAA4E;AAC5E,MAAM,CAAC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAEvD,6CAA6C;AAC7C,IAAI,QAAQ,EAAE,CAAC;IACX,MAAM,iBAAiB,GAAG;QACtB,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ;QAC7B,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO;QAC3B,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ;KAChC,CAAC;IAEF,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC,CAAC,GAAW,EAAE,EAAE;QACnD,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,yBAAyB,GAAG,2BAA2B,CAAC,CAAC;QAC7E,CAAC;IACL,CAAC,CAAC,CAAC;AACP,CAAC"}
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/common/env.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,sBAAsB,EAAE,QAAQ,EAAC,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAC,mBAAmB,EAAE,oBAAoB,EAAC,MAAM,2BAA2B,CAAC;AAEpF,MAAM,QAAQ,GAAG,OAAO,MAAM,KAAK,WAAW,CAAC;AAE/C,aAAa;AACb,6HAA6H;AAC7H,gEAAgE;AAChE,MAAM,CAAC,MAAM,OAAO,GAAG,oBAAoB,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;AAEjF,0FAA0F;AAC1F,MAAM,CAAC,MAAM,cAAc,GAAG,OAAO,IAAI,GAAG,OAAO,IAAI,mBAAmB,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,sBAAsB,CAAC,EAAE,CAAC;AAE7I,iFAAiF;AACjF,MAAM,CAAC,MAAM,SAAS,GAAG,oBAAoB,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,IAAI,OAAO,CAAC;AAEhG,8PAA8P;AAC9P,MAAM,CAAC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAEvD,4FAA4F;AAC5F,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AAEzE,uFAAuF;AACvF,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AAEpE,sFAAsF;AACtF,MAAM,CAAC,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,aAAa,CAAC;AAElE,MAAM,CAAC,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;AAExG,4EAA4E;AAC5E,MAAM,CAAC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAEvD,6CAA6C;AAC7C,IAAI,QAAQ,EAAE,CAAC;IACX,MAAM,iBAAiB,GAAG;QACtB,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ;QAC7B,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO;QAC3B,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ;KAChC,CAAC;IAEF,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC,CAAC,GAAW,EAAE,EAAE;QACnD,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,yBAAyB,GAAG,2BAA2B,CAAC,CAAC;QAC7E,CAAC;IACL,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -1,33 +1,22 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
1
  import { getComponentConfig } from './getComponentConfig';
11
- const NO_PROPS_PROCESSOR = (props) => __awaiter(void 0, void 0, void 0, function* () { var _a; return (_a = yield props) !== null && _a !== void 0 ? _a : {}; });
12
- export function applyProcessors(componentDescriptors, contentResults, context) {
13
- return __awaiter(this, void 0, void 0, function* () {
14
- let dataCounter = 0;
15
- const processorPromises = componentDescriptors.map((desc) => __awaiter(this, void 0, void 0, function* () {
16
- var _a;
17
- // we're iterating component descriptors here
18
- // some of them might not have provided graphql requests
19
- // but we still need to run props processor for them
20
- // in case they want to fetch their data from elsewhere
21
- const propsProcessor = ((_a = desc.type) === null || _a === void 0 ? void 0 : _a.processor) || NO_PROPS_PROCESSOR;
22
- let data;
23
- if (desc.queryAndVariables) {
24
- // if there is a query then there must be a result for it
25
- data = contentResults.contents[dataCounter++];
26
- }
27
- const config = getComponentConfig(desc.component);
28
- return yield propsProcessor(data, context, config);
29
- }));
30
- return Promise.allSettled(processorPromises);
2
+ const NO_PROPS_PROCESSOR = async (props) => { var _a; return (_a = await props) !== null && _a !== void 0 ? _a : {}; };
3
+ export async function applyProcessors(componentDescriptors, contentResults, context) {
4
+ let dataCounter = 0;
5
+ const processorPromises = componentDescriptors.map(async (desc) => {
6
+ var _a;
7
+ // we're iterating component descriptors here
8
+ // some of them might not have provided graphql requests
9
+ // but we still need to run props processor for them
10
+ // in case they want to fetch their data from elsewhere
11
+ const propsProcessor = ((_a = desc.type) === null || _a === void 0 ? void 0 : _a.processor) || NO_PROPS_PROCESSOR;
12
+ let data;
13
+ if (desc.queryAndVariables) {
14
+ // if there is a query then there must be a result for it
15
+ data = contentResults.contents[dataCounter++];
16
+ }
17
+ const config = getComponentConfig(desc.component);
18
+ return await propsProcessor(data, context, config);
31
19
  });
20
+ return Promise.allSettled(processorPromises);
32
21
  }
33
22
  //# sourceMappingURL=applyProcessors.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"applyProcessors.js","sourceRoot":"","sources":["../../src/guillotine/applyProcessors.ts"],"names":[],"mappings":";;;;;;;;;AAGA,OAAO,EAAC,kBAAkB,EAAC,MAAM,sBAAsB,CAAC;AAGxD,MAAM,kBAAkB,GAAG,CAAO,KAAU,EAAE,EAAE,0DAAC,OAAA,MAAA,MAAM,KAAK,mCAAI,EAAE,CAAA,GAAA,CAAC;AAGnE,MAAM,UAAgB,eAAe,CACjC,oBAA2C,EAC3C,cAA6B,EAC7B,OAAgB;;QAGhB,IAAI,WAAW,GAAG,CAAC,CAAC;QACpB,MAAM,iBAAiB,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAO,IAAyB,EAAE,EAAE;;YACnF,6CAA6C;YAC7C,wDAAwD;YACxD,oDAAoD;YACpD,uDAAuD;YACvD,MAAM,cAAc,GAAG,CAAA,MAAA,IAAI,CAAC,IAAI,0CAAE,SAAS,KAAI,kBAAkB,CAAC;YAClE,IAAI,IAAI,CAAC;YACT,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;gBACzB,yDAAyD;gBACzD,IAAI,GAAG,cAAc,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;YAClD,CAAC;YAED,MAAM,MAAM,GAAG,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAClD,OAAO,MAAM,cAAc,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;QACvD,CAAC,CAAA,CAAC,CAAC;QAEH,OAAO,OAAO,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC;IACjD,CAAC;CAAA"}
1
+ {"version":3,"file":"applyProcessors.js","sourceRoot":"","sources":["../../src/guillotine/applyProcessors.ts"],"names":[],"mappings":"AAGA,OAAO,EAAC,kBAAkB,EAAC,MAAM,sBAAsB,CAAC;AAGxD,MAAM,kBAAkB,GAAG,KAAK,EAAE,KAAU,EAAE,EAAE,WAAC,OAAA,MAAA,MAAM,KAAK,mCAAI,EAAE,CAAA,EAAA,CAAC;AAGnE,MAAM,CAAC,KAAK,UAAU,eAAe,CACjC,oBAA2C,EAC3C,cAA6B,EAC7B,OAAgB;IAGhB,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,MAAM,iBAAiB,GAAG,oBAAoB,CAAC,GAAG,CAAC,KAAK,EAAE,IAAyB,EAAE,EAAE;;QACnF,6CAA6C;QAC7C,wDAAwD;QACxD,oDAAoD;QACpD,uDAAuD;QACvD,MAAM,cAAc,GAAG,CAAA,MAAA,IAAI,CAAC,IAAI,0CAAE,SAAS,KAAI,kBAAkB,CAAC;QAClE,IAAI,IAAI,CAAC;QACT,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACzB,yDAAyD;YACzD,IAAI,GAAG,cAAc,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;QAClD,CAAC;QAED,MAAM,MAAM,GAAG,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAClD,OAAO,MAAM,cAAc,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,OAAO,OAAO,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC;AACjD,CAAC"}