@contentful/experiences-core 3.7.0-prerelease-20250917T1034-42f0486.0 → 3.7.1-beta.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.
package/dist/index.d.ts CHANGED
@@ -16,11 +16,12 @@ export { transformVisibility } from './utils/styleUtils/styleTransformers.js';
16
16
  export { transformBoundContentValue } from './utils/transformers/transformBoundContentValue.js';
17
17
  export { treeMap, treeVisit } from './utils/treeTraversal.js';
18
18
  export { isArrayOfLinks, isAsset, isEntry, isExperienceEntry, isPatternEntry } from './utils/typeguards.js';
19
- export { checkIsAssemblyDefinition, checkIsAssemblyEntry, checkIsAssemblyNode, generateRandomId, getDataFromTree, getTargetValueInPixels, parseCSSValue } from './utils/utils.js';
19
+ export { checkIsAssemblyDefinition, checkIsAssemblyEntry, checkIsAssemblyNode, createAssemblyDefinition, generateRandomId, getDataFromTree, getTargetValueInPixels, parseCSSValue } from './utils/utils.js';
20
20
  export { doesMismatchMessageSchema, tryParseMessage, validateExperienceBuilderConfig } from './utils/validations.js';
21
21
  export { extractLeafLinksReferencedFromExperience } from './utils/schema/experienceSchema.js';
22
22
  export { FnShouldFollowReferencesOfEntryField, extractReferencesFromEntries, extractReferencesFromEntriesAsIds, referencesOf, uniqueById } from './utils/schema/references.js';
23
23
  export { splitDirectAndSlotChildren } from './utils/splitDirectAndSlotChildren.js';
24
+ export { PrebindingData, extractPrebindingDataByPatternId, flattenNestedPatterns, generateDefaultDataSourceForPrebindingDefinition, getTargetPatternMappingsForParameter } from './utils/extractPrebindingData.js';
24
25
  export { builtInStyles, columnsBuiltInStyles, containerBuiltInStyles, dividerBuiltInStyles, optionalBuiltInStyles, sectionBuiltInStyles, singleColumnBuiltInStyles } from './definitions/styles.js';
25
26
  export { EditorModeEntityStore } from './entity/EditorModeEntityStore.js';
26
27
  export { EntityStore } from './entity/EntityStore.js';
@@ -36,6 +37,6 @@ export { createExperience } from './fetchers/createExperience.js';
36
37
  export { fetchReferencedEntities } from './fetchers/fetchReferencedEntities.js';
37
38
  export { fetchExperienceEntry } from './fetchers/fetchExperienceEntry.js';
38
39
  export { defineDesignTokens, designTokensRegistry, getDesignTokenRegistration, resetDesignTokenRegistry } from './registries/designTokenRegistry.js';
39
- export { breakpointsRegistry, defineBreakpoints, getBreakpointRegistration, resetBreakpointsRegistry, runBreakpointsValidation } from './registries/breakpointsRegistry.js';
40
+ export { breakpointsRegistry, defineBreakpoints, runBreakpointsValidation } from './registries/breakpointsRegistry.js';
40
41
  export { defineSdkOptions, getSdkOptions, sdkOptionsRegistry } from './registries/sdkOptionsRegistry.js';
41
- export { DeepReference, gatherDeepReferencesFromExperienceEntry, gatherDeepReferencesFromTree } from './deep-binding/DeepReference.js';
42
+ export { DeepReference, gatherDeepPrebindingReferencesFromExperienceEntry, gatherDeepPrebindingReferencesFromPatternEntry, gatherDeepReferencesFromExperienceEntry, gatherDeepReferencesFromTree } from './deep-binding/DeepReference.js';
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { z, ZodIssueCode } from 'zod';
2
- import { cloneDeep, omit, isArray, uniqBy } from 'lodash-es';
2
+ import cloneDeep from 'lodash.clonedeep';
3
3
  import md5 from 'md5';
4
4
  import { BLOCKS } from '@contentful/rich-text-types';
5
5
  import { create } from 'zustand';
@@ -26,7 +26,9 @@ const INCOMING_EVENTS = {
26
26
  /** @deprecated will be removed when dropping backward compatibility for old DND */
27
27
  HoverComponent: 'hoverComponent',
28
28
  UpdatedEntity: 'updatedEntity',
29
+ /** @deprecated not needed after `patternResolution` was introduced. Will be removed in the next major version. */
29
30
  AssembliesAdded: 'assembliesAdded',
31
+ /** @deprecated not needed after `patternResolution` was introduced. Will be removed in the next major version. */
30
32
  AssembliesRegistered: 'assembliesRegistered',
31
33
  /** @deprecated will be removed when dropping backward compatibility for old DND */
32
34
  MouseMove: 'mouseMove',
@@ -998,7 +1000,7 @@ const BreakpointSchema = z
998
1000
  id: propertyKeySchema,
999
1001
  // Can be replace with z.templateLiteral when upgrading to zod v4
1000
1002
  query: z.string().refine((s) => BREAKPOINT_QUERY_REGEX.test(s)),
1001
- previewSize: z.string(),
1003
+ previewSize: z.string().optional(),
1002
1004
  displayName: z.string(),
1003
1005
  displayIcon: z.enum(['desktop', 'tablet', 'mobile']).optional(),
1004
1006
  })
@@ -1065,6 +1067,25 @@ z.object({
1065
1067
  usedComponents: localeWrapper(UsedComponentsSchema).optional(),
1066
1068
  });
1067
1069
 
1070
+ function treeVisit$1(initialNode, onNode) {
1071
+ const _treeVisit = (currentNode) => {
1072
+ const children = [...currentNode.children];
1073
+ onNode(currentNode);
1074
+ for (const child of children) {
1075
+ _treeVisit(child);
1076
+ }
1077
+ };
1078
+ if (Array.isArray(initialNode)) {
1079
+ for (const node of initialNode) {
1080
+ _treeVisit(node);
1081
+ }
1082
+ }
1083
+ else {
1084
+ _treeVisit(initialNode);
1085
+ }
1086
+ }
1087
+
1088
+ const MAX_ALLOWED_PATHS = 200;
1068
1089
  const THUMBNAIL_IDS = [
1069
1090
  'columns',
1070
1091
  'columnsPlusRight',
@@ -1095,7 +1116,17 @@ const THUMBNAIL_IDS = [
1095
1116
  const VariableMappingSchema = z.object({
1096
1117
  parameterId: propertyKeySchema,
1097
1118
  type: z.literal('ContentTypeMapping'),
1098
- pathsByContentType: z.record(z.string(), z.object({ path: z.string() })),
1119
+ pathsByContentType: z
1120
+ .record(z.string(), z.object({ path: z.string() }))
1121
+ .superRefine((paths, ctx) => {
1122
+ const variableId = ctx.path[ctx.path.length - 2];
1123
+ if (Object.keys(paths).length > MAX_ALLOWED_PATHS) {
1124
+ ctx.addIssue({
1125
+ code: z.ZodIssueCode.custom,
1126
+ message: `Too many paths defined for variable mapping with id "${variableId}", maximum allowed is ${MAX_ALLOWED_PATHS}`,
1127
+ });
1128
+ }
1129
+ }),
1099
1130
  });
1100
1131
  const PassToNodeSchema = z
1101
1132
  .object({
@@ -1119,7 +1150,10 @@ const ParameterDefinitionSchema = z.object({
1119
1150
  })
1120
1151
  .optional(),
1121
1152
  contentTypes: z.array(z.string()).min(1),
1122
- passToNodes: z.array(PassToNodeSchema).optional(),
1153
+ passToNodes: z
1154
+ .array(PassToNodeSchema)
1155
+ .max(1, 'At most one "passToNodes" element is allowed per parameter definition.')
1156
+ .optional(), // we might change this to be empty array for native parameter definitions, that's why we don't use .length(1)
1123
1157
  });
1124
1158
  const ParameterDefinitionsSchema = z.record(propertyKeySchema, ParameterDefinitionSchema);
1125
1159
  const VariableMappingsSchema = z.record(propertyKeySchema, VariableMappingSchema);
@@ -1140,14 +1174,108 @@ const ComponentSettingsSchema = z
1140
1174
  category: z.string().max(50, 'Category must contain at most 50 characters').optional(),
1141
1175
  prebindingDefinitions: z.array(PrebindingDefinitionSchema).length(1).optional(),
1142
1176
  })
1143
- .strict();
1144
- z.object({
1177
+ .strict()
1178
+ .superRefine((componentSettings, ctx) => {
1179
+ const { variableDefinitions, prebindingDefinitions } = componentSettings;
1180
+ if (!prebindingDefinitions || prebindingDefinitions.length === 0) {
1181
+ return;
1182
+ }
1183
+ const { parameterDefinitions, variableMappings, allowedVariableOverrides } = prebindingDefinitions[0];
1184
+ validateAtMostOneNativeParameterDefinition(parameterDefinitions, ctx);
1185
+ validateNoOverlapBetweenMappingAndOverrides(variableMappings, allowedVariableOverrides, ctx);
1186
+ validateMappingsAgainstVariableDefinitions(variableMappings, allowedVariableOverrides, variableDefinitions, ctx);
1187
+ validateMappingsAgainstParameterDefinitions(variableMappings, parameterDefinitions, ctx);
1188
+ });
1189
+ z
1190
+ .object({
1145
1191
  componentTree: localeWrapper(ComponentTreeSchema),
1146
1192
  dataSource: localeWrapper(DataSourceSchema),
1147
1193
  unboundValues: localeWrapper(UnboundValuesSchema),
1148
1194
  usedComponents: localeWrapper(UsedComponentsSchema).optional(),
1149
1195
  componentSettings: localeWrapper(ComponentSettingsSchema),
1196
+ })
1197
+ .superRefine((patternFields, ctx) => {
1198
+ const { componentTree, componentSettings } = patternFields;
1199
+ // values at this point are wrapped under locale code
1200
+ const nonLocalisedComponentTree = Object.values(componentTree)[0];
1201
+ const nonLocalisedComponentSettings = Object.values(componentSettings)[0];
1202
+ if (!nonLocalisedComponentSettings || !nonLocalisedComponentTree) {
1203
+ return;
1204
+ }
1205
+ validatePassToNodes(nonLocalisedComponentTree.children || [], nonLocalisedComponentSettings || {}, ctx);
1150
1206
  });
1207
+ const validateAtMostOneNativeParameterDefinition = (parameterDefinitions, ctx) => {
1208
+ const nativeParamDefinitions = Object.values(parameterDefinitions).filter((paramDefinition) => !(paramDefinition.passToNodes && paramDefinition.passToNodes.length > 0));
1209
+ if (nativeParamDefinitions.length > 1) {
1210
+ ctx.addIssue({
1211
+ code: z.ZodIssueCode.custom,
1212
+ message: `Only one native parameter definition (parameter definition without passToNodes) is allowed per prebinding definition.`,
1213
+ });
1214
+ }
1215
+ };
1216
+ const validateNoOverlapBetweenMappingAndOverrides = (variableMappings, allowedVariableOverrides, ctx) => {
1217
+ const variableMappingKeys = Object.keys(variableMappings || {});
1218
+ const overridesSet = new Set(allowedVariableOverrides || []);
1219
+ const overlap = variableMappingKeys.filter((key) => overridesSet.has(key));
1220
+ if (overlap.length > 0) {
1221
+ ctx.addIssue({
1222
+ code: z.ZodIssueCode.custom,
1223
+ message: `Found both variable mapping and allowed override for the following keys: ${overlap.map((key) => `"${key}"`).join(', ')}.`,
1224
+ });
1225
+ }
1226
+ };
1227
+ const validateMappingsAgainstVariableDefinitions = (variableMappings, allowedVariableOverrides, variableDefinitions, ctx) => {
1228
+ const nonDesignVariableDefinitionKeys = Object.entries(variableDefinitions)
1229
+ .filter(([_, def]) => def.group !== 'style')
1230
+ .map(([key]) => key);
1231
+ const variableMappingKeys = Object.keys(variableMappings || {});
1232
+ const allKeys = [...variableMappingKeys, ...(allowedVariableOverrides || [])];
1233
+ const invalidMappings = allKeys.filter((key) => !nonDesignVariableDefinitionKeys.includes(key));
1234
+ if (invalidMappings.length > 0) {
1235
+ ctx.addIssue({
1236
+ code: z.ZodIssueCode.custom,
1237
+ message: `The following variable mappings or overrides are missing from the variable definitions: ${invalidMappings.map((key) => `"${key}"`).join(', ')}.`,
1238
+ });
1239
+ }
1240
+ };
1241
+ const validateMappingsAgainstParameterDefinitions = (variableMappings, parameterDefinitions, ctx) => {
1242
+ const parameterDefinitionKeys = Object.keys(parameterDefinitions || {});
1243
+ for (const [mappingKey, mappingValue] of Object.entries(variableMappings || {})) {
1244
+ if (!parameterDefinitionKeys.includes(mappingValue.parameterId)) {
1245
+ ctx.addIssue({
1246
+ code: z.ZodIssueCode.custom,
1247
+ message: `The variable mapping with id "${mappingKey}" references a non-existing parameterId "${mappingValue.parameterId}".`,
1248
+ });
1249
+ }
1250
+ }
1251
+ };
1252
+ const validatePassToNodes = (rootChildren, componentSettings, ctx) => {
1253
+ if (!componentSettings.prebindingDefinitions ||
1254
+ componentSettings.prebindingDefinitions.length === 0) {
1255
+ return;
1256
+ }
1257
+ const { parameterDefinitions } = componentSettings.prebindingDefinitions[0];
1258
+ let nodeIds = new Set();
1259
+ for (const paramDef of Object.values(parameterDefinitions || {})) {
1260
+ paramDef.passToNodes?.forEach((n) => nodeIds.add(n.nodeId));
1261
+ }
1262
+ treeVisit$1(rootChildren, (node) => {
1263
+ if (!node.id)
1264
+ return;
1265
+ if (nodeIds.has(node.id)) {
1266
+ nodeIds.delete(node.id);
1267
+ }
1268
+ });
1269
+ if (nodeIds.size > 0) {
1270
+ const stringifiedNodeIds = Array.from(nodeIds)
1271
+ .map((id) => `"${id}"`)
1272
+ .join(', ');
1273
+ ctx.addIssue({
1274
+ code: z.ZodIssueCode.custom,
1275
+ message: `The following node IDs referenced in passToNodes are not present in the component tree: ${stringifiedNodeIds}.`,
1276
+ });
1277
+ }
1278
+ };
1151
1279
 
1152
1280
  z
1153
1281
  .object({
@@ -1406,11 +1534,42 @@ const validateBreakpointsDefinition = (breakpoints) => {
1406
1534
  return { success: true };
1407
1535
  };
1408
1536
 
1409
- let breakpointsRegistry = [];
1537
+ const breakpointsRegistry = [];
1410
1538
  /**
1411
- * Register custom breakpoints
1412
- * @param breakpoints - [{[key:string]: string}]
1413
- * @returns void
1539
+ * Define custom breakpoints that should be used for all your experiences.
1540
+ * A breakpoint consists of:
1541
+ * - id: a unique identifier for this breakpoint
1542
+ * - query: a media query string that defines when this breakpoint is active
1543
+ * - previewSize: an optional fixed preview size to be used in the Studio editor when selecting this breakpoint
1544
+ * - displayName: the name to be displayed in the Studio editor for this breakpoint
1545
+ * - displayIcon: an optional icon to be displayed in the Studio editor for this breakpoint
1546
+ *
1547
+ * The first breakpoint must use a wildcard query (`*`) to match all sizes.
1548
+ *
1549
+ * Every subsequent breakpoint inherits the designs of the previous ones by default.
1550
+ *
1551
+ * The order of breakpoints must be either:
1552
+ * - desktop first: from largest to smallest, using `<` operators
1553
+ * - mobile first: from smallest to largest, using `>` operators
1554
+ *
1555
+ * @note changing breakpoints after you have created experiences may break those experiences
1556
+ * @example
1557
+ * defineBreakpoints([{
1558
+ * id: 'desktop',
1559
+ * query: '*',
1560
+ * displayName: 'Desktop',
1561
+ * displayIcon: 'desktop',
1562
+ * }, {
1563
+ * id: 'tablet',
1564
+ * query: '<992px',
1565
+ * displayName: 'Tablet',
1566
+ * displayIcon: 'tablet',
1567
+ * }, {
1568
+ * id: 'mobile',
1569
+ * query: '<576px',
1570
+ * displayName: 'Mobile',
1571
+ * displayIcon: 'mobile',
1572
+ * }]);
1414
1573
  */
1415
1574
  const defineBreakpoints = (breakpoints) => {
1416
1575
  Object.assign(breakpointsRegistry, breakpoints);
@@ -1423,12 +1582,6 @@ const runBreakpointsValidation = () => {
1423
1582
  throw new Error(`Invalid breakpoints definition. Failed with errors: \n${JSON.stringify(validation.errors, null, 2)}`);
1424
1583
  }
1425
1584
  };
1426
- // Used in the tests to get a breakpoint registration
1427
- const getBreakpointRegistration = (id) => breakpointsRegistry.find((breakpoint) => breakpoint.id === id);
1428
- // Used in the tests to reset the registry
1429
- const resetBreakpointsRegistry = () => {
1430
- breakpointsRegistry = [];
1431
- };
1432
1585
 
1433
1586
  const sdkOptionsRegistry = {};
1434
1587
  /**
@@ -2035,7 +2188,9 @@ const stylesToRemove = CF_STYLE_ATTRIBUTES.filter((style) => !stylesToKeep.inclu
2035
2188
  // cfWrapColumns & cfWrapColumnsCount are no real style attributes as they are handled on the editor side
2036
2189
  const propsToRemove = ['cfSsrClassName', 'cfWrapColumns', 'cfWrapColumnsCount'];
2037
2190
  const sanitizeNodeProps = (nodeProps) => {
2038
- return omit(nodeProps, stylesToRemove, propsToRemove);
2191
+ const keysToRemove = [...stylesToRemove, ...propsToRemove];
2192
+ const sanitizedProps = Object.fromEntries(Object.entries(nodeProps).filter(([key]) => !keysToRemove.includes(key)));
2193
+ return sanitizedProps;
2039
2194
  };
2040
2195
 
2041
2196
  /** Turn the visibility value into a style object that can be used for inline styles in React */
@@ -3166,7 +3321,7 @@ function getArrayValue(entryOrAsset, path, entityStore) {
3166
3321
  }
3167
3322
  const fieldName = path.split('/').slice(2, -1);
3168
3323
  const arrayValue = get(entryOrAsset, fieldName);
3169
- if (!isArray(arrayValue)) {
3324
+ if (!Array.isArray(arrayValue)) {
3170
3325
  debug.warn(`[experiences-core::getArrayValue] A field '${fieldName}' of an entity was bound to an Array variable. Expected value of that field to be an array, but got: ${JSON.stringify(arrayValue)}`, { entity: entryOrAsset });
3171
3326
  return;
3172
3327
  }
@@ -3323,6 +3478,19 @@ function getTargetValueInPixels(targetWidthObject) {
3323
3478
  return targetWidthObject.value;
3324
3479
  }
3325
3480
  }
3481
+ /**
3482
+ * Creates a component definition for an assembly. As all assemblies use the same definition in the SDK,
3483
+ * all should be registered via this function.
3484
+ */
3485
+ const createAssemblyDefinition = (definitionId) => {
3486
+ return {
3487
+ id: definitionId,
3488
+ name: 'Component',
3489
+ variables: {},
3490
+ children: true,
3491
+ category: ASSEMBLY_DEFAULT_CATEGORY,
3492
+ };
3493
+ };
3326
3494
 
3327
3495
  class ParseError extends Error {
3328
3496
  constructor(message) {
@@ -3528,6 +3696,118 @@ const splitDirectAndSlotChildren = (allChildNodes, componentDefinition) => {
3528
3696
  return { slotNodesMap, directChildNodes };
3529
3697
  };
3530
3698
 
3699
+ const flattenNestedPatterns = (fetchedPatterns) => {
3700
+ const patternsById = {};
3701
+ const queue = [...fetchedPatterns];
3702
+ while (queue.length) {
3703
+ const pattern = queue.shift();
3704
+ if (!pattern) {
3705
+ continue;
3706
+ }
3707
+ if (patternsById[pattern.sys.id]) {
3708
+ continue;
3709
+ }
3710
+ patternsById[pattern.sys.id] = pattern;
3711
+ if (!Array.isArray(pattern.fields.usedComponents) || !pattern.fields.usedComponents.length) {
3712
+ continue;
3713
+ }
3714
+ for (const nestedPattern of pattern.fields.usedComponents) {
3715
+ if (!isLink(nestedPattern)) {
3716
+ queue.push(nestedPattern);
3717
+ }
3718
+ }
3719
+ }
3720
+ return Object.values(patternsById);
3721
+ };
3722
+ /**
3723
+ * Given a list of patterns, extract the prebinding data into a more digestable format indexed by the pattern entry id
3724
+ * @param patterns a list of pattern entries
3725
+ * @returns a map of pattern entry ids to their prebinding data
3726
+ */
3727
+ const extractPrebindingDataByPatternId = (patterns) => {
3728
+ const prebindingDataByPatternId = {};
3729
+ for (const pattern of patterns) {
3730
+ const patternId = pattern.sys.id;
3731
+ const [prebindingDefinition] = pattern.fields.componentSettings?.prebindingDefinitions ?? [];
3732
+ if (!prebindingDefinition)
3733
+ continue;
3734
+ const [nativeParameterId] = Object.entries(prebindingDefinition.parameterDefinitions ?? {}).find(([, value]) => value.passToNodes === undefined) ?? [];
3735
+ const prebindingData = {
3736
+ prebindingDefinitionId: prebindingDefinition.id,
3737
+ parameterIds: Object.keys(prebindingDefinition.parameterDefinitions),
3738
+ nativeParameterId,
3739
+ parameterDefinitions: prebindingDefinition.parameterDefinitions,
3740
+ variableMappings: prebindingDefinition.variableMappings,
3741
+ };
3742
+ prebindingDataByPatternId[patternId] = prebindingData;
3743
+ }
3744
+ return prebindingDataByPatternId;
3745
+ };
3746
+ const generateDefaultDataSourceForPrebindingDefinition = (prebindingDefinitions = []) => {
3747
+ if (!prebindingDefinitions ||
3748
+ !Array.isArray(prebindingDefinitions) ||
3749
+ !prebindingDefinitions.length) {
3750
+ return { dataSource: {}, parameters: {} };
3751
+ }
3752
+ const prebindingDefinition = prebindingDefinitions[0];
3753
+ const dataSource = {};
3754
+ const parameters = {};
3755
+ for (const [parameterId, parameterDefinition] of Object.entries(prebindingDefinition.parameterDefinitions ?? {})) {
3756
+ if (parameterDefinition.defaultSource && isLink(parameterDefinition.defaultSource.link)) {
3757
+ const dataSourceKey = generateRandomId(7);
3758
+ dataSource[dataSourceKey] = parameterDefinition.defaultSource.link;
3759
+ parameters[parameterId] = {
3760
+ type: 'BoundValue',
3761
+ path: `/${dataSourceKey}`,
3762
+ };
3763
+ }
3764
+ }
3765
+ return {
3766
+ dataSource,
3767
+ parameters,
3768
+ };
3769
+ };
3770
+ function getTargetPatternMappingsForParameter({ fetchedPatterns, prebindingDataByPatternId, patternNodeDefinitionId, parameterId, }) {
3771
+ const patternPrebindingData = prebindingDataByPatternId[patternNodeDefinitionId];
3772
+ if (!patternPrebindingData)
3773
+ return undefined;
3774
+ if (patternPrebindingData.parameterIds.includes(parameterId)) {
3775
+ if (patternPrebindingData.nativeParameterId === parameterId) {
3776
+ if (!patternPrebindingData.variableMappings)
3777
+ return undefined;
3778
+ return Object.fromEntries(Object.entries(patternPrebindingData.variableMappings).filter(([, mapping]) => mapping.parameterId === parameterId));
3779
+ }
3780
+ else {
3781
+ const parameterDefinition = patternPrebindingData.parameterDefinitions[parameterId];
3782
+ if (!parameterDefinition || !parameterDefinition.passToNodes)
3783
+ return undefined;
3784
+ const patternEntry = fetchedPatterns.find((entry) => entry.sys.id === patternNodeDefinitionId);
3785
+ if (!patternEntry)
3786
+ return undefined;
3787
+ let nestedPatternNode;
3788
+ treeVisit({
3789
+ definitionId: 'root',
3790
+ parameters: {},
3791
+ children: patternEntry.fields.componentTree.children,
3792
+ }, (node) => {
3793
+ if (node.id === parameterDefinition.passToNodes?.[0].nodeId) {
3794
+ nestedPatternNode = node;
3795
+ }
3796
+ return undefined;
3797
+ });
3798
+ if (!nestedPatternNode) {
3799
+ return undefined;
3800
+ }
3801
+ return getTargetPatternMappingsForParameter({
3802
+ fetchedPatterns,
3803
+ prebindingDataByPatternId,
3804
+ patternNodeDefinitionId: nestedPatternNode.definitionId,
3805
+ parameterId: parameterDefinition.passToNodes?.[0].parameterId,
3806
+ });
3807
+ }
3808
+ }
3809
+ }
3810
+
3531
3811
  const sendMessage = (eventType, data) => {
3532
3812
  if (typeof window === 'undefined') {
3533
3813
  return;
@@ -4354,7 +4634,7 @@ function gatherDeepReferencesFromExperienceEntry(experienceEntry) {
4354
4634
  }, (node) => {
4355
4635
  if (!node.variables)
4356
4636
  return;
4357
- for (const [, variableMapping] of Object.entries(node.variables)) {
4637
+ for (const variableMapping of Object.values(node.variables)) {
4358
4638
  if (variableMapping.type !== 'BoundValue')
4359
4639
  continue;
4360
4640
  if (!isDeepPath(variableMapping.path))
@@ -4367,6 +4647,99 @@ function gatherDeepReferencesFromExperienceEntry(experienceEntry) {
4367
4647
  });
4368
4648
  return deepReferences;
4369
4649
  }
4650
+ function gatherDeepPrebindingReferencesFromExperienceEntry({ experienceEntry, fetchedPatterns, prebindingDataByPatternId, fetchedLevel1Entries, }) {
4651
+ const deepPrebindingReferences = [];
4652
+ const dataSource = experienceEntry.fields.dataSource;
4653
+ const { children } = experienceEntry.fields.componentTree;
4654
+ treeVisit({
4655
+ definitionId: 'root',
4656
+ parameters: {},
4657
+ children,
4658
+ }, (node) => {
4659
+ if (!node.parameters)
4660
+ return;
4661
+ for (const [parameterId, parameterValue] of Object.entries(node.parameters)) {
4662
+ const dataSourceKey = parameterValue.path.split('/')[1];
4663
+ const headEntryLink = dataSource[dataSourceKey];
4664
+ if (!headEntryLink)
4665
+ continue;
4666
+ if (headEntryLink.sys.linkType !== 'Entry')
4667
+ continue;
4668
+ const headEntry = fetchedLevel1Entries.find((entry) => entry.sys.id === headEntryLink.sys.id);
4669
+ if (!headEntry)
4670
+ continue;
4671
+ const headEntryContentTypeId = headEntry.sys.contentType.sys.id;
4672
+ // if experience, we don't have any hoisted data on the given experienceEntry
4673
+ // and we have to lookup the pattern instead
4674
+ const variableMappings = getTargetPatternMappingsForParameter({
4675
+ fetchedPatterns,
4676
+ prebindingDataByPatternId,
4677
+ patternNodeDefinitionId: node.definitionId,
4678
+ parameterId,
4679
+ });
4680
+ if (!variableMappings)
4681
+ continue;
4682
+ for (const mappingData of Object.values(variableMappings)) {
4683
+ const targetMapping = mappingData.pathsByContentType[headEntryContentTypeId];
4684
+ if (!targetMapping)
4685
+ continue;
4686
+ // mapping doesn't start with /uuid, but instead starts with /fields
4687
+ // so we add /uuid to make it match the binding path format
4688
+ const path = `/${dataSourceKey}${targetMapping.path}`;
4689
+ if (!isDeepPath(path))
4690
+ continue;
4691
+ deepPrebindingReferences.push(DeepReference.from({
4692
+ path,
4693
+ dataSource,
4694
+ }));
4695
+ }
4696
+ }
4697
+ });
4698
+ return deepPrebindingReferences;
4699
+ }
4700
+ function gatherDeepPrebindingReferencesFromPatternEntry({ patternEntry, fetchedPatterns, prebindingDataByPatternId, fetchedLevel1Entries, }) {
4701
+ const deepPrebindingReferences = [];
4702
+ // patterns can't have parameters in their CDA/CMA JSON, so we can generate random ids here
4703
+ const { dataSource, parameters } = generateDefaultDataSourceForPrebindingDefinition(patternEntry.fields.componentSettings?.prebindingDefinitions);
4704
+ for (const [parameterId, parameterValue] of Object.entries(parameters)) {
4705
+ const dataSourceKey = parameterValue.path.split('/')[1];
4706
+ const headEntryLink = dataSource[dataSourceKey];
4707
+ if (!headEntryLink)
4708
+ continue;
4709
+ if (headEntryLink.sys.linkType !== 'Entry')
4710
+ continue;
4711
+ const headEntry = fetchedLevel1Entries.find((entry) => entry.sys.id === headEntryLink.sys.id);
4712
+ if (!headEntry)
4713
+ continue;
4714
+ const headEntryContentTypeId = headEntry.sys.contentType.sys.id;
4715
+ const variableMappings = getTargetPatternMappingsForParameter({
4716
+ fetchedPatterns,
4717
+ prebindingDataByPatternId,
4718
+ patternNodeDefinitionId: patternEntry.sys.id,
4719
+ parameterId,
4720
+ });
4721
+ if (!variableMappings)
4722
+ continue;
4723
+ for (const mappingData of Object.values(variableMappings)) {
4724
+ const targetMapping = mappingData.pathsByContentType[headEntryContentTypeId];
4725
+ if (!targetMapping)
4726
+ continue;
4727
+ // mapping doesn't start with /uuid, but instead starts with /fields
4728
+ // so we add /uuid to make it match the binding path format
4729
+ const path = `/${dataSourceKey}${targetMapping.path}`;
4730
+ if (!isDeepPath(path))
4731
+ continue;
4732
+ deepPrebindingReferences.push(DeepReference.from({
4733
+ path,
4734
+ dataSource,
4735
+ }));
4736
+ }
4737
+ }
4738
+ return deepPrebindingReferences;
4739
+ }
4740
+ /**
4741
+ * used in editor mode. for delivery mode see `gatherDeepReferencesFromExperienceEntry`
4742
+ */
4370
4743
  function gatherDeepReferencesFromTree(startingNode, dataSource, getEntityFromLink) {
4371
4744
  const deepReferences = [];
4372
4745
  treeVisit(startingNode, (node) => {
@@ -4488,8 +4861,8 @@ const fetchAllEntries = async ({ client, ids, locale, skip = 0, limit = 100, res
4488
4861
  responseIncludes,
4489
4862
  });
4490
4863
  }
4491
- const dedupedEntries = uniqBy(responseIncludes?.Entry, (entry) => entry.sys.id);
4492
- const dedupedAssets = uniqBy(responseIncludes?.Asset, (asset) => asset.sys.id);
4864
+ const dedupedEntries = uniqueById(responseIncludes?.Entry);
4865
+ const dedupedAssets = uniqueById(responseIncludes?.Asset);
4493
4866
  return {
4494
4867
  items: responseItems,
4495
4868
  includes: {
@@ -4573,7 +4946,6 @@ const fetchReferencedEntities = async ({ client, experienceEntry, locale, }) =>
4573
4946
  if (!isExperienceEntry(experienceEntry)) {
4574
4947
  throw new Error('Failed to fetch experience entities. Provided "experienceEntry" does not match experience entry schema');
4575
4948
  }
4576
- const deepReferences = gatherDeepReferencesFromExperienceEntry(experienceEntry);
4577
4949
  const entryIds = new Set();
4578
4950
  const assetIds = new Set();
4579
4951
  for (const dataBinding of Object.values(experienceEntry.fields.dataSource)) {
@@ -4591,12 +4963,52 @@ const fetchReferencedEntities = async ({ client, experienceEntry, locale, }) =>
4591
4963
  fetchAllEntries({ client, ids: [...entryIds], locale }),
4592
4964
  fetchAllAssets({ client, ids: [...assetIds], locale }),
4593
4965
  ]);
4594
- const { autoFetchedReferentAssets, autoFetchedReferentEntries } = gatherAutoFetchedReferentsFromIncludes(deepReferences, entriesResponse);
4966
+ const usedPatterns = experienceEntry.fields.usedComponents ?? [];
4967
+ const isRenderingExperience = Boolean(!experienceEntry.fields.componentSettings);
4968
+ const deepReferences = gatherDeepReferencesFromExperienceEntry(experienceEntry);
4969
+ // If we are previewing a pattern, we want to include the entry itself as well
4970
+ const fetchedPatterns = (isRenderingExperience ? usedPatterns : [...usedPatterns, experienceEntry]);
4971
+ const allFetchedPatterns = flattenNestedPatterns(fetchedPatterns);
4972
+ const prebindingDataByPatternId = extractPrebindingDataByPatternId(allFetchedPatterns);
4973
+ // Patterns do not have dataSource stored in their dataSource field, so head entities won't be there and we need to fetch them
4974
+ if (!isRenderingExperience) {
4975
+ const { dataSource } = generateDefaultDataSourceForPrebindingDefinition(experienceEntry.fields.componentSettings?.prebindingDefinitions);
4976
+ if (Object.keys(dataSource).length) {
4977
+ const prebindingEntriesResponse = await fetchAllEntries({
4978
+ client,
4979
+ ids: Object.values(dataSource).map((link) => link.sys.id),
4980
+ locale,
4981
+ });
4982
+ entriesResponse.items.push(...prebindingEntriesResponse.items);
4983
+ entriesResponse.includes.Asset.push(...(prebindingEntriesResponse.includes?.Asset ?? []));
4984
+ entriesResponse.includes.Entry.push(...(prebindingEntriesResponse.includes?.Entry ?? []));
4985
+ }
4986
+ }
4987
+ // normally, for experience entry, there should be no need to call this method, as `includes=2` will have them resolved
4988
+ // because the entries used for pre-binding are stored in both - the layout of the experience, as well as the dataSource field
4989
+ const deepPrebindingReferences = isRenderingExperience
4990
+ ? gatherDeepPrebindingReferencesFromExperienceEntry({
4991
+ experienceEntry: experienceEntry,
4992
+ fetchedPatterns: allFetchedPatterns,
4993
+ prebindingDataByPatternId,
4994
+ fetchedLevel1Entries: entriesResponse.items,
4995
+ })
4996
+ : // however, for patterns, we have to do it by hand, because a pattern entry doesn't save the pre-binding data neither in the
4997
+ // layout nor in the dataSource field.
4998
+ // for consistency, as well as to be future safe from the change to "includes=2", I added methods for both
4999
+ gatherDeepPrebindingReferencesFromPatternEntry({
5000
+ patternEntry: experienceEntry,
5001
+ fetchedPatterns: allFetchedPatterns,
5002
+ prebindingDataByPatternId,
5003
+ fetchedLevel1Entries: entriesResponse.items,
5004
+ });
5005
+ const allDeepReferences = [...deepReferences, ...deepPrebindingReferences];
5006
+ const { autoFetchedReferentAssets, autoFetchedReferentEntries } = gatherAutoFetchedReferentsFromIncludes(allDeepReferences, entriesResponse);
4595
5007
  // Using client getEntries resolves all linked entry references, so we do not need to resolve entries in usedComponents
4596
5008
  const allResolvedEntries = [
4597
5009
  ...(entriesResponse?.items ?? []),
4598
5010
  ...(entriesResponse.includes?.Entry ?? []),
4599
- ...(experienceEntry.fields.usedComponents || []),
5011
+ ...(usedPatterns || []),
4600
5012
  ...autoFetchedReferentEntries,
4601
5013
  ];
4602
5014
  const allResolvedAssets = [
@@ -4605,8 +5017,14 @@ const fetchReferencedEntities = async ({ client, experienceEntry, locale, }) =>
4605
5017
  ...autoFetchedReferentAssets,
4606
5018
  ];
4607
5019
  return {
4608
- entries: allResolvedEntries,
4609
- assets: allResolvedAssets,
5020
+ // we have to drop duplicates, becasue of the merge of deepReferences and deepPrebindingReferences above
5021
+ // If not, the same entity might appear in this array more than once
5022
+ entries: [
5023
+ ...new Map(allResolvedEntries.map((entry) => [entry.sys.id, entry])).values(),
5024
+ ],
5025
+ assets: [
5026
+ ...new Map(allResolvedAssets.map((asset) => [asset.sys.id, asset])).values(),
5027
+ ],
4610
5028
  };
4611
5029
  };
4612
5030
 
@@ -4885,5 +5303,5 @@ async function fetchById({ client, experienceTypeId, id, localeCode, isEditorMod
4885
5303
  }
4886
5304
  }
4887
5305
 
4888
- export { BREAKPOINTS_STRATEGY_DESKTOP_FIRST, BREAKPOINTS_STRATEGY_MOBILE_FIRST, DebugLogger, DeepReference, EditorModeEntityStore, EntityStore, EntityStoreBase, MEDIA_QUERY_REGEXP, VisualEditorMode, addLocale, addMinHeightForEmptyStructures, breakpointsRegistry, buildCfStyles, buildStyleTag, buildTemplate, builtInStyles, calculateNodeDefaultHeight, checkIsAssemblyDefinition, checkIsAssemblyEntry, checkIsAssemblyNode, columnsBuiltInStyles, containerBuiltInStyles, createExperience, debug, defineBreakpoints, defineDesignTokens, defineSdkOptions, designTokensRegistry, detachExperienceStyles, detectBreakpointsStrategy, disableDebug, dividerBuiltInStyles, doesMismatchMessageSchema, enableDebug, extractLeafLinksReferencedFromExperience, extractReferencesFromEntries, extractReferencesFromEntriesAsIds, fetchAllAssets, fetchAllEntries, fetchById, fetchBySlug, fetchExperienceEntry, fetchReferencedEntities, findOutermostCoordinates, flattenDesignTokenRegistry, gatherDeepReferencesFromExperienceEntry, gatherDeepReferencesFromTree, generateRandomId, getActiveBreakpointIndex, getBreakpointRegistration, getDataFromTree, getDesignTokenRegistration, getElementCoordinates, getFallbackBreakpointIndex, getPrebindingPathBySourceEntry, getSdkOptions, getTargetValueInPixels, getTemplateValue, getValueForBreakpoint, inMemoryEntities, inMemoryEntitiesStore, indexByBreakpoint, isArrayOfLinks, isAsset, isCfStyleAttribute, isComponentAllowedOnRoot, isContentfulComponent, isContentfulStructureComponent, isDeepPath, isDeepPrebinding, isElementHidden, isEntry, isExperienceEntry, isLink, isLinkToAsset, isLinkToEntry, isPatternComponent, isPatternEntry, isPreboundProp, isStructureWithRelativeHeight, isValidBreakpointValue, lastPathNamedSegmentEq, localizeEntity, maybePopulateDesignTokenValue, mediaQueryMatcher, mergeDesignValuesByBreakpoint, optionalBuiltInStyles, parseCSSValue, parseDataSourcePathIntoFieldset, parseDataSourcePathWithL1DeepBindings, referencesOf, resetBreakpointsRegistry, resetDesignTokenRegistry, resolveBackgroundImageBinding, resolveHyperlinkPattern, runBreakpointsValidation, sanitizeNodeProps, sdkOptionsRegistry, sectionBuiltInStyles, sendMessage, setDebugLevel, singleColumnBuiltInStyles, splitDirectAndSlotChildren, stringifyCssProperties, toCSSAttribute, toMediaQuery, transformBoundContentValue, transformVisibility, treeMap, treeVisit, tryParseMessage, uniqueById, useInMemoryEntities, validateExperienceBuilderConfig };
5306
+ export { BREAKPOINTS_STRATEGY_DESKTOP_FIRST, BREAKPOINTS_STRATEGY_MOBILE_FIRST, DebugLogger, DeepReference, EditorModeEntityStore, EntityStore, EntityStoreBase, MEDIA_QUERY_REGEXP, VisualEditorMode, addLocale, addMinHeightForEmptyStructures, breakpointsRegistry, buildCfStyles, buildStyleTag, buildTemplate, builtInStyles, calculateNodeDefaultHeight, checkIsAssemblyDefinition, checkIsAssemblyEntry, checkIsAssemblyNode, columnsBuiltInStyles, containerBuiltInStyles, createAssemblyDefinition, createExperience, debug, defineBreakpoints, defineDesignTokens, defineSdkOptions, designTokensRegistry, detachExperienceStyles, detectBreakpointsStrategy, disableDebug, dividerBuiltInStyles, doesMismatchMessageSchema, enableDebug, extractLeafLinksReferencedFromExperience, extractPrebindingDataByPatternId, extractReferencesFromEntries, extractReferencesFromEntriesAsIds, fetchAllAssets, fetchAllEntries, fetchById, fetchBySlug, fetchExperienceEntry, fetchReferencedEntities, findOutermostCoordinates, flattenDesignTokenRegistry, flattenNestedPatterns, gatherDeepPrebindingReferencesFromExperienceEntry, gatherDeepPrebindingReferencesFromPatternEntry, gatherDeepReferencesFromExperienceEntry, gatherDeepReferencesFromTree, generateDefaultDataSourceForPrebindingDefinition, generateRandomId, getActiveBreakpointIndex, getDataFromTree, getDesignTokenRegistration, getElementCoordinates, getFallbackBreakpointIndex, getPrebindingPathBySourceEntry, getSdkOptions, getTargetPatternMappingsForParameter, getTargetValueInPixels, getTemplateValue, getValueForBreakpoint, inMemoryEntities, inMemoryEntitiesStore, indexByBreakpoint, isArrayOfLinks, isAsset, isCfStyleAttribute, isComponentAllowedOnRoot, isContentfulComponent, isContentfulStructureComponent, isDeepPath, isDeepPrebinding, isElementHidden, isEntry, isExperienceEntry, isLink, isLinkToAsset, isLinkToEntry, isPatternComponent, isPatternEntry, isPreboundProp, isStructureWithRelativeHeight, isValidBreakpointValue, lastPathNamedSegmentEq, localizeEntity, maybePopulateDesignTokenValue, mediaQueryMatcher, mergeDesignValuesByBreakpoint, optionalBuiltInStyles, parseCSSValue, parseDataSourcePathIntoFieldset, parseDataSourcePathWithL1DeepBindings, referencesOf, resetDesignTokenRegistry, resolveBackgroundImageBinding, resolveHyperlinkPattern, runBreakpointsValidation, sanitizeNodeProps, sdkOptionsRegistry, sectionBuiltInStyles, sendMessage, setDebugLevel, singleColumnBuiltInStyles, splitDirectAndSlotChildren, stringifyCssProperties, toCSSAttribute, toMediaQuery, transformBoundContentValue, transformVisibility, treeMap, treeVisit, tryParseMessage, uniqueById, useInMemoryEntities, validateExperienceBuilderConfig };
4889
5307
  //# sourceMappingURL=index.js.map