@contentful/experiences-core 3.7.0-prerelease-20250917T1034-42f0486.0 → 3.7.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
  })
@@ -1035,19 +1037,6 @@ const ComponentVariableSchema = z.object({
1035
1037
  });
1036
1038
  const ComponentTreeNodeSchema = BaseComponentTreeNodeSchema.extend({
1037
1039
  children: z.lazy(() => ComponentTreeNodeSchema.array()),
1038
- }).superRefine(({ id, prebindingId, parameters }, ctx) => {
1039
- if (prebindingId && !parameters) {
1040
- ctx.addIssue({
1041
- code: z.ZodIssueCode.custom,
1042
- message: `Found "prebindingId" but no "parameters" for node with id: "${id}"`,
1043
- });
1044
- }
1045
- if (parameters && !prebindingId) {
1046
- ctx.addIssue({
1047
- code: z.ZodIssueCode.custom,
1048
- message: `Found "parameters" but no "prebindingId" for node with id: "${id}"`,
1049
- });
1050
- }
1051
1040
  });
1052
1041
  const ComponentTreeSchema = z
1053
1042
  .object({
@@ -1065,6 +1054,25 @@ z.object({
1065
1054
  usedComponents: localeWrapper(UsedComponentsSchema).optional(),
1066
1055
  });
1067
1056
 
1057
+ function treeVisit$1(initialNode, onNode) {
1058
+ const _treeVisit = (currentNode) => {
1059
+ const children = [...currentNode.children];
1060
+ onNode(currentNode);
1061
+ for (const child of children) {
1062
+ _treeVisit(child);
1063
+ }
1064
+ };
1065
+ if (Array.isArray(initialNode)) {
1066
+ for (const node of initialNode) {
1067
+ _treeVisit(node);
1068
+ }
1069
+ }
1070
+ else {
1071
+ _treeVisit(initialNode);
1072
+ }
1073
+ }
1074
+
1075
+ const MAX_ALLOWED_PATHS = 200;
1068
1076
  const THUMBNAIL_IDS = [
1069
1077
  'columns',
1070
1078
  'columnsPlusRight',
@@ -1095,7 +1103,17 @@ const THUMBNAIL_IDS = [
1095
1103
  const VariableMappingSchema = z.object({
1096
1104
  parameterId: propertyKeySchema,
1097
1105
  type: z.literal('ContentTypeMapping'),
1098
- pathsByContentType: z.record(z.string(), z.object({ path: z.string() })),
1106
+ pathsByContentType: z
1107
+ .record(z.string(), z.object({ path: z.string() }))
1108
+ .superRefine((paths, ctx) => {
1109
+ const variableId = ctx.path[ctx.path.length - 2];
1110
+ if (Object.keys(paths).length > MAX_ALLOWED_PATHS) {
1111
+ ctx.addIssue({
1112
+ code: z.ZodIssueCode.custom,
1113
+ message: `Too many paths defined for variable mapping with id "${variableId}", maximum allowed is ${MAX_ALLOWED_PATHS}`,
1114
+ });
1115
+ }
1116
+ }),
1099
1117
  });
1100
1118
  const PassToNodeSchema = z
1101
1119
  .object({
@@ -1119,7 +1137,10 @@ const ParameterDefinitionSchema = z.object({
1119
1137
  })
1120
1138
  .optional(),
1121
1139
  contentTypes: z.array(z.string()).min(1),
1122
- passToNodes: z.array(PassToNodeSchema).optional(),
1140
+ passToNodes: z
1141
+ .array(PassToNodeSchema)
1142
+ .max(1, 'At most one "passToNodes" element is allowed per parameter definition.')
1143
+ .optional(), // we might change this to be empty array for native parameter definitions, that's why we don't use .length(1)
1123
1144
  });
1124
1145
  const ParameterDefinitionsSchema = z.record(propertyKeySchema, ParameterDefinitionSchema);
1125
1146
  const VariableMappingsSchema = z.record(propertyKeySchema, VariableMappingSchema);
@@ -1140,14 +1161,108 @@ const ComponentSettingsSchema = z
1140
1161
  category: z.string().max(50, 'Category must contain at most 50 characters').optional(),
1141
1162
  prebindingDefinitions: z.array(PrebindingDefinitionSchema).length(1).optional(),
1142
1163
  })
1143
- .strict();
1144
- z.object({
1164
+ .strict()
1165
+ .superRefine((componentSettings, ctx) => {
1166
+ const { variableDefinitions, prebindingDefinitions } = componentSettings;
1167
+ if (!prebindingDefinitions || prebindingDefinitions.length === 0) {
1168
+ return;
1169
+ }
1170
+ const { parameterDefinitions, variableMappings, allowedVariableOverrides } = prebindingDefinitions[0];
1171
+ validateAtMostOneNativeParameterDefinition(parameterDefinitions, ctx);
1172
+ validateNoOverlapBetweenMappingAndOverrides(variableMappings, allowedVariableOverrides, ctx);
1173
+ validateMappingsAgainstVariableDefinitions(variableMappings, allowedVariableOverrides, variableDefinitions, ctx);
1174
+ validateMappingsAgainstParameterDefinitions(variableMappings, parameterDefinitions, ctx);
1175
+ });
1176
+ z
1177
+ .object({
1145
1178
  componentTree: localeWrapper(ComponentTreeSchema),
1146
1179
  dataSource: localeWrapper(DataSourceSchema),
1147
1180
  unboundValues: localeWrapper(UnboundValuesSchema),
1148
1181
  usedComponents: localeWrapper(UsedComponentsSchema).optional(),
1149
1182
  componentSettings: localeWrapper(ComponentSettingsSchema),
1183
+ })
1184
+ .superRefine((patternFields, ctx) => {
1185
+ const { componentTree, componentSettings } = patternFields;
1186
+ // values at this point are wrapped under locale code
1187
+ const nonLocalisedComponentTree = Object.values(componentTree)[0];
1188
+ const nonLocalisedComponentSettings = Object.values(componentSettings)[0];
1189
+ if (!nonLocalisedComponentSettings || !nonLocalisedComponentTree) {
1190
+ return;
1191
+ }
1192
+ validatePassToNodes(nonLocalisedComponentTree.children || [], nonLocalisedComponentSettings || {}, ctx);
1150
1193
  });
1194
+ const validateAtMostOneNativeParameterDefinition = (parameterDefinitions, ctx) => {
1195
+ const nativeParamDefinitions = Object.values(parameterDefinitions).filter((paramDefinition) => !(paramDefinition.passToNodes && paramDefinition.passToNodes.length > 0));
1196
+ if (nativeParamDefinitions.length > 1) {
1197
+ ctx.addIssue({
1198
+ code: z.ZodIssueCode.custom,
1199
+ message: `Only one native parameter definition (parameter definition without passToNodes) is allowed per prebinding definition.`,
1200
+ });
1201
+ }
1202
+ };
1203
+ const validateNoOverlapBetweenMappingAndOverrides = (variableMappings, allowedVariableOverrides, ctx) => {
1204
+ const variableMappingKeys = Object.keys(variableMappings || {});
1205
+ const overridesSet = new Set(allowedVariableOverrides || []);
1206
+ const overlap = variableMappingKeys.filter((key) => overridesSet.has(key));
1207
+ if (overlap.length > 0) {
1208
+ ctx.addIssue({
1209
+ code: z.ZodIssueCode.custom,
1210
+ message: `Found both variable mapping and allowed override for the following keys: ${overlap.map((key) => `"${key}"`).join(', ')}.`,
1211
+ });
1212
+ }
1213
+ };
1214
+ const validateMappingsAgainstVariableDefinitions = (variableMappings, allowedVariableOverrides, variableDefinitions, ctx) => {
1215
+ const nonDesignVariableDefinitionKeys = Object.entries(variableDefinitions)
1216
+ .filter(([_, def]) => def.group !== 'style')
1217
+ .map(([key]) => key);
1218
+ const variableMappingKeys = Object.keys(variableMappings || {});
1219
+ const allKeys = [...variableMappingKeys, ...(allowedVariableOverrides || [])];
1220
+ const invalidMappings = allKeys.filter((key) => !nonDesignVariableDefinitionKeys.includes(key));
1221
+ if (invalidMappings.length > 0) {
1222
+ ctx.addIssue({
1223
+ code: z.ZodIssueCode.custom,
1224
+ message: `The following variable mappings or overrides are missing from the variable definitions: ${invalidMappings.map((key) => `"${key}"`).join(', ')}.`,
1225
+ });
1226
+ }
1227
+ };
1228
+ const validateMappingsAgainstParameterDefinitions = (variableMappings, parameterDefinitions, ctx) => {
1229
+ const parameterDefinitionKeys = Object.keys(parameterDefinitions || {});
1230
+ for (const [mappingKey, mappingValue] of Object.entries(variableMappings || {})) {
1231
+ if (!parameterDefinitionKeys.includes(mappingValue.parameterId)) {
1232
+ ctx.addIssue({
1233
+ code: z.ZodIssueCode.custom,
1234
+ message: `The variable mapping with id "${mappingKey}" references a non-existing parameterId "${mappingValue.parameterId}".`,
1235
+ });
1236
+ }
1237
+ }
1238
+ };
1239
+ const validatePassToNodes = (rootChildren, componentSettings, ctx) => {
1240
+ if (!componentSettings.prebindingDefinitions ||
1241
+ componentSettings.prebindingDefinitions.length === 0) {
1242
+ return;
1243
+ }
1244
+ const { parameterDefinitions } = componentSettings.prebindingDefinitions[0];
1245
+ let nodeIds = new Set();
1246
+ for (const paramDef of Object.values(parameterDefinitions || {})) {
1247
+ paramDef.passToNodes?.forEach((n) => nodeIds.add(n.nodeId));
1248
+ }
1249
+ treeVisit$1(rootChildren, (node) => {
1250
+ if (!node.id)
1251
+ return;
1252
+ if (nodeIds.has(node.id)) {
1253
+ nodeIds.delete(node.id);
1254
+ }
1255
+ });
1256
+ if (nodeIds.size > 0) {
1257
+ const stringifiedNodeIds = Array.from(nodeIds)
1258
+ .map((id) => `"${id}"`)
1259
+ .join(', ');
1260
+ ctx.addIssue({
1261
+ code: z.ZodIssueCode.custom,
1262
+ message: `The following node IDs referenced in passToNodes are not present in the component tree: ${stringifiedNodeIds}.`,
1263
+ });
1264
+ }
1265
+ };
1151
1266
 
1152
1267
  z
1153
1268
  .object({
@@ -1406,11 +1521,42 @@ const validateBreakpointsDefinition = (breakpoints) => {
1406
1521
  return { success: true };
1407
1522
  };
1408
1523
 
1409
- let breakpointsRegistry = [];
1524
+ const breakpointsRegistry = [];
1410
1525
  /**
1411
- * Register custom breakpoints
1412
- * @param breakpoints - [{[key:string]: string}]
1413
- * @returns void
1526
+ * Define custom breakpoints that should be used for all your experiences.
1527
+ * A breakpoint consists of:
1528
+ * - id: a unique identifier for this breakpoint
1529
+ * - query: a media query string that defines when this breakpoint is active
1530
+ * - previewSize: an optional fixed preview size to be used in the Studio editor when selecting this breakpoint
1531
+ * - displayName: the name to be displayed in the Studio editor for this breakpoint
1532
+ * - displayIcon: an optional icon to be displayed in the Studio editor for this breakpoint
1533
+ *
1534
+ * The first breakpoint must use a wildcard query (`*`) to match all sizes.
1535
+ *
1536
+ * Every subsequent breakpoint inherits the designs of the previous ones by default.
1537
+ *
1538
+ * The order of breakpoints must be either:
1539
+ * - desktop first: from largest to smallest, using `<` operators
1540
+ * - mobile first: from smallest to largest, using `>` operators
1541
+ *
1542
+ * @note changing breakpoints after you have created experiences may break those experiences
1543
+ * @example
1544
+ * defineBreakpoints([{
1545
+ * id: 'desktop',
1546
+ * query: '*',
1547
+ * displayName: 'Desktop',
1548
+ * displayIcon: 'desktop',
1549
+ * }, {
1550
+ * id: 'tablet',
1551
+ * query: '<992px',
1552
+ * displayName: 'Tablet',
1553
+ * displayIcon: 'tablet',
1554
+ * }, {
1555
+ * id: 'mobile',
1556
+ * query: '<576px',
1557
+ * displayName: 'Mobile',
1558
+ * displayIcon: 'mobile',
1559
+ * }]);
1414
1560
  */
1415
1561
  const defineBreakpoints = (breakpoints) => {
1416
1562
  Object.assign(breakpointsRegistry, breakpoints);
@@ -1423,12 +1569,6 @@ const runBreakpointsValidation = () => {
1423
1569
  throw new Error(`Invalid breakpoints definition. Failed with errors: \n${JSON.stringify(validation.errors, null, 2)}`);
1424
1570
  }
1425
1571
  };
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
1572
 
1433
1573
  const sdkOptionsRegistry = {};
1434
1574
  /**
@@ -2035,7 +2175,9 @@ const stylesToRemove = CF_STYLE_ATTRIBUTES.filter((style) => !stylesToKeep.inclu
2035
2175
  // cfWrapColumns & cfWrapColumnsCount are no real style attributes as they are handled on the editor side
2036
2176
  const propsToRemove = ['cfSsrClassName', 'cfWrapColumns', 'cfWrapColumnsCount'];
2037
2177
  const sanitizeNodeProps = (nodeProps) => {
2038
- return omit(nodeProps, stylesToRemove, propsToRemove);
2178
+ const keysToRemove = [...stylesToRemove, ...propsToRemove];
2179
+ const sanitizedProps = Object.fromEntries(Object.entries(nodeProps).filter(([key]) => !keysToRemove.includes(key)));
2180
+ return sanitizedProps;
2039
2181
  };
2040
2182
 
2041
2183
  /** Turn the visibility value into a style object that can be used for inline styles in React */
@@ -3166,7 +3308,7 @@ function getArrayValue(entryOrAsset, path, entityStore) {
3166
3308
  }
3167
3309
  const fieldName = path.split('/').slice(2, -1);
3168
3310
  const arrayValue = get(entryOrAsset, fieldName);
3169
- if (!isArray(arrayValue)) {
3311
+ if (!Array.isArray(arrayValue)) {
3170
3312
  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
3313
  return;
3172
3314
  }
@@ -3323,6 +3465,19 @@ function getTargetValueInPixels(targetWidthObject) {
3323
3465
  return targetWidthObject.value;
3324
3466
  }
3325
3467
  }
3468
+ /**
3469
+ * Creates a component definition for an assembly. As all assemblies use the same definition in the SDK,
3470
+ * all should be registered via this function.
3471
+ */
3472
+ const createAssemblyDefinition = (definitionId) => {
3473
+ return {
3474
+ id: definitionId,
3475
+ name: 'Component',
3476
+ variables: {},
3477
+ children: true,
3478
+ category: ASSEMBLY_DEFAULT_CATEGORY,
3479
+ };
3480
+ };
3326
3481
 
3327
3482
  class ParseError extends Error {
3328
3483
  constructor(message) {
@@ -3528,6 +3683,118 @@ const splitDirectAndSlotChildren = (allChildNodes, componentDefinition) => {
3528
3683
  return { slotNodesMap, directChildNodes };
3529
3684
  };
3530
3685
 
3686
+ const flattenNestedPatterns = (fetchedPatterns) => {
3687
+ const patternsById = {};
3688
+ const queue = [...fetchedPatterns];
3689
+ while (queue.length) {
3690
+ const pattern = queue.shift();
3691
+ if (!pattern) {
3692
+ continue;
3693
+ }
3694
+ if (patternsById[pattern.sys.id]) {
3695
+ continue;
3696
+ }
3697
+ patternsById[pattern.sys.id] = pattern;
3698
+ if (!Array.isArray(pattern.fields.usedComponents) || !pattern.fields.usedComponents.length) {
3699
+ continue;
3700
+ }
3701
+ for (const nestedPattern of pattern.fields.usedComponents) {
3702
+ if (!isLink(nestedPattern)) {
3703
+ queue.push(nestedPattern);
3704
+ }
3705
+ }
3706
+ }
3707
+ return Object.values(patternsById);
3708
+ };
3709
+ /**
3710
+ * Given a list of patterns, extract the prebinding data into a more digestable format indexed by the pattern entry id
3711
+ * @param patterns a list of pattern entries
3712
+ * @returns a map of pattern entry ids to their prebinding data
3713
+ */
3714
+ const extractPrebindingDataByPatternId = (patterns) => {
3715
+ const prebindingDataByPatternId = {};
3716
+ for (const pattern of patterns) {
3717
+ const patternId = pattern.sys.id;
3718
+ const [prebindingDefinition] = pattern.fields.componentSettings?.prebindingDefinitions ?? [];
3719
+ if (!prebindingDefinition)
3720
+ continue;
3721
+ const [nativeParameterId] = Object.entries(prebindingDefinition.parameterDefinitions ?? {}).find(([, value]) => value.passToNodes === undefined) ?? [];
3722
+ const prebindingData = {
3723
+ prebindingDefinitionId: prebindingDefinition.id,
3724
+ parameterIds: Object.keys(prebindingDefinition.parameterDefinitions),
3725
+ nativeParameterId,
3726
+ parameterDefinitions: prebindingDefinition.parameterDefinitions,
3727
+ variableMappings: prebindingDefinition.variableMappings,
3728
+ };
3729
+ prebindingDataByPatternId[patternId] = prebindingData;
3730
+ }
3731
+ return prebindingDataByPatternId;
3732
+ };
3733
+ const generateDefaultDataSourceForPrebindingDefinition = (prebindingDefinitions = []) => {
3734
+ if (!prebindingDefinitions ||
3735
+ !Array.isArray(prebindingDefinitions) ||
3736
+ !prebindingDefinitions.length) {
3737
+ return { dataSource: {}, parameters: {} };
3738
+ }
3739
+ const prebindingDefinition = prebindingDefinitions[0];
3740
+ const dataSource = {};
3741
+ const parameters = {};
3742
+ for (const [parameterId, parameterDefinition] of Object.entries(prebindingDefinition.parameterDefinitions ?? {})) {
3743
+ if (parameterDefinition.defaultSource && isLink(parameterDefinition.defaultSource.link)) {
3744
+ const dataSourceKey = generateRandomId(7);
3745
+ dataSource[dataSourceKey] = parameterDefinition.defaultSource.link;
3746
+ parameters[parameterId] = {
3747
+ type: 'BoundValue',
3748
+ path: `/${dataSourceKey}`,
3749
+ };
3750
+ }
3751
+ }
3752
+ return {
3753
+ dataSource,
3754
+ parameters,
3755
+ };
3756
+ };
3757
+ function getTargetPatternMappingsForParameter({ fetchedPatterns, prebindingDataByPatternId, patternNodeDefinitionId, parameterId, }) {
3758
+ const patternPrebindingData = prebindingDataByPatternId[patternNodeDefinitionId];
3759
+ if (!patternPrebindingData)
3760
+ return undefined;
3761
+ if (patternPrebindingData.parameterIds.includes(parameterId)) {
3762
+ if (patternPrebindingData.nativeParameterId === parameterId) {
3763
+ if (!patternPrebindingData.variableMappings)
3764
+ return undefined;
3765
+ return Object.fromEntries(Object.entries(patternPrebindingData.variableMappings).filter(([, mapping]) => mapping.parameterId === parameterId));
3766
+ }
3767
+ else {
3768
+ const parameterDefinition = patternPrebindingData.parameterDefinitions[parameterId];
3769
+ if (!parameterDefinition || !parameterDefinition.passToNodes)
3770
+ return undefined;
3771
+ const patternEntry = fetchedPatterns.find((entry) => entry.sys.id === patternNodeDefinitionId);
3772
+ if (!patternEntry)
3773
+ return undefined;
3774
+ let nestedPatternNode;
3775
+ treeVisit({
3776
+ definitionId: 'root',
3777
+ parameters: {},
3778
+ children: patternEntry.fields.componentTree.children,
3779
+ }, (node) => {
3780
+ if (node.id === parameterDefinition.passToNodes?.[0].nodeId) {
3781
+ nestedPatternNode = node;
3782
+ }
3783
+ return undefined;
3784
+ });
3785
+ if (!nestedPatternNode) {
3786
+ return undefined;
3787
+ }
3788
+ return getTargetPatternMappingsForParameter({
3789
+ fetchedPatterns,
3790
+ prebindingDataByPatternId,
3791
+ patternNodeDefinitionId: nestedPatternNode.definitionId,
3792
+ parameterId: parameterDefinition.passToNodes?.[0].parameterId,
3793
+ });
3794
+ }
3795
+ }
3796
+ }
3797
+
3531
3798
  const sendMessage = (eventType, data) => {
3532
3799
  if (typeof window === 'undefined') {
3533
3800
  return;
@@ -4354,7 +4621,7 @@ function gatherDeepReferencesFromExperienceEntry(experienceEntry) {
4354
4621
  }, (node) => {
4355
4622
  if (!node.variables)
4356
4623
  return;
4357
- for (const [, variableMapping] of Object.entries(node.variables)) {
4624
+ for (const variableMapping of Object.values(node.variables)) {
4358
4625
  if (variableMapping.type !== 'BoundValue')
4359
4626
  continue;
4360
4627
  if (!isDeepPath(variableMapping.path))
@@ -4367,6 +4634,99 @@ function gatherDeepReferencesFromExperienceEntry(experienceEntry) {
4367
4634
  });
4368
4635
  return deepReferences;
4369
4636
  }
4637
+ function gatherDeepPrebindingReferencesFromExperienceEntry({ experienceEntry, fetchedPatterns, prebindingDataByPatternId, fetchedLevel1Entries, }) {
4638
+ const deepPrebindingReferences = [];
4639
+ const dataSource = experienceEntry.fields.dataSource;
4640
+ const { children } = experienceEntry.fields.componentTree;
4641
+ treeVisit({
4642
+ definitionId: 'root',
4643
+ parameters: {},
4644
+ children,
4645
+ }, (node) => {
4646
+ if (!node.parameters)
4647
+ return;
4648
+ for (const [parameterId, parameterValue] of Object.entries(node.parameters)) {
4649
+ const dataSourceKey = parameterValue.path.split('/')[1];
4650
+ const headEntryLink = dataSource[dataSourceKey];
4651
+ if (!headEntryLink)
4652
+ continue;
4653
+ if (headEntryLink.sys.linkType !== 'Entry')
4654
+ continue;
4655
+ const headEntry = fetchedLevel1Entries.find((entry) => entry.sys.id === headEntryLink.sys.id);
4656
+ if (!headEntry)
4657
+ continue;
4658
+ const headEntryContentTypeId = headEntry.sys.contentType.sys.id;
4659
+ // if experience, we don't have any hoisted data on the given experienceEntry
4660
+ // and we have to lookup the pattern instead
4661
+ const variableMappings = getTargetPatternMappingsForParameter({
4662
+ fetchedPatterns,
4663
+ prebindingDataByPatternId,
4664
+ patternNodeDefinitionId: node.definitionId,
4665
+ parameterId,
4666
+ });
4667
+ if (!variableMappings)
4668
+ continue;
4669
+ for (const mappingData of Object.values(variableMappings)) {
4670
+ const targetMapping = mappingData.pathsByContentType[headEntryContentTypeId];
4671
+ if (!targetMapping)
4672
+ continue;
4673
+ // mapping doesn't start with /uuid, but instead starts with /fields
4674
+ // so we add /uuid to make it match the binding path format
4675
+ const path = `/${dataSourceKey}${targetMapping.path}`;
4676
+ if (!isDeepPath(path))
4677
+ continue;
4678
+ deepPrebindingReferences.push(DeepReference.from({
4679
+ path,
4680
+ dataSource,
4681
+ }));
4682
+ }
4683
+ }
4684
+ });
4685
+ return deepPrebindingReferences;
4686
+ }
4687
+ function gatherDeepPrebindingReferencesFromPatternEntry({ patternEntry, fetchedPatterns, prebindingDataByPatternId, fetchedLevel1Entries, }) {
4688
+ const deepPrebindingReferences = [];
4689
+ // patterns can't have parameters in their CDA/CMA JSON, so we can generate random ids here
4690
+ const { dataSource, parameters } = generateDefaultDataSourceForPrebindingDefinition(patternEntry.fields.componentSettings?.prebindingDefinitions);
4691
+ for (const [parameterId, parameterValue] of Object.entries(parameters)) {
4692
+ const dataSourceKey = parameterValue.path.split('/')[1];
4693
+ const headEntryLink = dataSource[dataSourceKey];
4694
+ if (!headEntryLink)
4695
+ continue;
4696
+ if (headEntryLink.sys.linkType !== 'Entry')
4697
+ continue;
4698
+ const headEntry = fetchedLevel1Entries.find((entry) => entry.sys.id === headEntryLink.sys.id);
4699
+ if (!headEntry)
4700
+ continue;
4701
+ const headEntryContentTypeId = headEntry.sys.contentType.sys.id;
4702
+ const variableMappings = getTargetPatternMappingsForParameter({
4703
+ fetchedPatterns,
4704
+ prebindingDataByPatternId,
4705
+ patternNodeDefinitionId: patternEntry.sys.id,
4706
+ parameterId,
4707
+ });
4708
+ if (!variableMappings)
4709
+ continue;
4710
+ for (const mappingData of Object.values(variableMappings)) {
4711
+ const targetMapping = mappingData.pathsByContentType[headEntryContentTypeId];
4712
+ if (!targetMapping)
4713
+ continue;
4714
+ // mapping doesn't start with /uuid, but instead starts with /fields
4715
+ // so we add /uuid to make it match the binding path format
4716
+ const path = `/${dataSourceKey}${targetMapping.path}`;
4717
+ if (!isDeepPath(path))
4718
+ continue;
4719
+ deepPrebindingReferences.push(DeepReference.from({
4720
+ path,
4721
+ dataSource,
4722
+ }));
4723
+ }
4724
+ }
4725
+ return deepPrebindingReferences;
4726
+ }
4727
+ /**
4728
+ * used in editor mode. for delivery mode see `gatherDeepReferencesFromExperienceEntry`
4729
+ */
4370
4730
  function gatherDeepReferencesFromTree(startingNode, dataSource, getEntityFromLink) {
4371
4731
  const deepReferences = [];
4372
4732
  treeVisit(startingNode, (node) => {
@@ -4488,8 +4848,8 @@ const fetchAllEntries = async ({ client, ids, locale, skip = 0, limit = 100, res
4488
4848
  responseIncludes,
4489
4849
  });
4490
4850
  }
4491
- const dedupedEntries = uniqBy(responseIncludes?.Entry, (entry) => entry.sys.id);
4492
- const dedupedAssets = uniqBy(responseIncludes?.Asset, (asset) => asset.sys.id);
4851
+ const dedupedEntries = uniqueById(responseIncludes?.Entry);
4852
+ const dedupedAssets = uniqueById(responseIncludes?.Asset);
4493
4853
  return {
4494
4854
  items: responseItems,
4495
4855
  includes: {
@@ -4573,7 +4933,6 @@ const fetchReferencedEntities = async ({ client, experienceEntry, locale, }) =>
4573
4933
  if (!isExperienceEntry(experienceEntry)) {
4574
4934
  throw new Error('Failed to fetch experience entities. Provided "experienceEntry" does not match experience entry schema');
4575
4935
  }
4576
- const deepReferences = gatherDeepReferencesFromExperienceEntry(experienceEntry);
4577
4936
  const entryIds = new Set();
4578
4937
  const assetIds = new Set();
4579
4938
  for (const dataBinding of Object.values(experienceEntry.fields.dataSource)) {
@@ -4591,12 +4950,52 @@ const fetchReferencedEntities = async ({ client, experienceEntry, locale, }) =>
4591
4950
  fetchAllEntries({ client, ids: [...entryIds], locale }),
4592
4951
  fetchAllAssets({ client, ids: [...assetIds], locale }),
4593
4952
  ]);
4594
- const { autoFetchedReferentAssets, autoFetchedReferentEntries } = gatherAutoFetchedReferentsFromIncludes(deepReferences, entriesResponse);
4953
+ const usedPatterns = experienceEntry.fields.usedComponents ?? [];
4954
+ const isRenderingExperience = Boolean(!experienceEntry.fields.componentSettings);
4955
+ const deepReferences = gatherDeepReferencesFromExperienceEntry(experienceEntry);
4956
+ // If we are previewing a pattern, we want to include the entry itself as well
4957
+ const fetchedPatterns = (isRenderingExperience ? usedPatterns : [...usedPatterns, experienceEntry]);
4958
+ const allFetchedPatterns = flattenNestedPatterns(fetchedPatterns);
4959
+ const prebindingDataByPatternId = extractPrebindingDataByPatternId(allFetchedPatterns);
4960
+ // Patterns do not have dataSource stored in their dataSource field, so head entities won't be there and we need to fetch them
4961
+ if (!isRenderingExperience) {
4962
+ const { dataSource } = generateDefaultDataSourceForPrebindingDefinition(experienceEntry.fields.componentSettings?.prebindingDefinitions);
4963
+ if (Object.keys(dataSource).length) {
4964
+ const prebindingEntriesResponse = await fetchAllEntries({
4965
+ client,
4966
+ ids: Object.values(dataSource).map((link) => link.sys.id),
4967
+ locale,
4968
+ });
4969
+ entriesResponse.items.push(...prebindingEntriesResponse.items);
4970
+ entriesResponse.includes.Asset.push(...(prebindingEntriesResponse.includes?.Asset ?? []));
4971
+ entriesResponse.includes.Entry.push(...(prebindingEntriesResponse.includes?.Entry ?? []));
4972
+ }
4973
+ }
4974
+ // normally, for experience entry, there should be no need to call this method, as `includes=2` will have them resolved
4975
+ // because the entries used for pre-binding are stored in both - the layout of the experience, as well as the dataSource field
4976
+ const deepPrebindingReferences = isRenderingExperience
4977
+ ? gatherDeepPrebindingReferencesFromExperienceEntry({
4978
+ experienceEntry: experienceEntry,
4979
+ fetchedPatterns: allFetchedPatterns,
4980
+ prebindingDataByPatternId,
4981
+ fetchedLevel1Entries: entriesResponse.items,
4982
+ })
4983
+ : // however, for patterns, we have to do it by hand, because a pattern entry doesn't save the pre-binding data neither in the
4984
+ // layout nor in the dataSource field.
4985
+ // for consistency, as well as to be future safe from the change to "includes=2", I added methods for both
4986
+ gatherDeepPrebindingReferencesFromPatternEntry({
4987
+ patternEntry: experienceEntry,
4988
+ fetchedPatterns: allFetchedPatterns,
4989
+ prebindingDataByPatternId,
4990
+ fetchedLevel1Entries: entriesResponse.items,
4991
+ });
4992
+ const allDeepReferences = [...deepReferences, ...deepPrebindingReferences];
4993
+ const { autoFetchedReferentAssets, autoFetchedReferentEntries } = gatherAutoFetchedReferentsFromIncludes(allDeepReferences, entriesResponse);
4595
4994
  // Using client getEntries resolves all linked entry references, so we do not need to resolve entries in usedComponents
4596
4995
  const allResolvedEntries = [
4597
4996
  ...(entriesResponse?.items ?? []),
4598
4997
  ...(entriesResponse.includes?.Entry ?? []),
4599
- ...(experienceEntry.fields.usedComponents || []),
4998
+ ...(usedPatterns || []),
4600
4999
  ...autoFetchedReferentEntries,
4601
5000
  ];
4602
5001
  const allResolvedAssets = [
@@ -4605,8 +5004,14 @@ const fetchReferencedEntities = async ({ client, experienceEntry, locale, }) =>
4605
5004
  ...autoFetchedReferentAssets,
4606
5005
  ];
4607
5006
  return {
4608
- entries: allResolvedEntries,
4609
- assets: allResolvedAssets,
5007
+ // we have to drop duplicates, becasue of the merge of deepReferences and deepPrebindingReferences above
5008
+ // If not, the same entity might appear in this array more than once
5009
+ entries: [
5010
+ ...new Map(allResolvedEntries.map((entry) => [entry.sys.id, entry])).values(),
5011
+ ],
5012
+ assets: [
5013
+ ...new Map(allResolvedAssets.map((asset) => [asset.sys.id, asset])).values(),
5014
+ ],
4610
5015
  };
4611
5016
  };
4612
5017
 
@@ -4885,5 +5290,5 @@ async function fetchById({ client, experienceTypeId, id, localeCode, isEditorMod
4885
5290
  }
4886
5291
  }
4887
5292
 
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 };
5293
+ 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
5294
  //# sourceMappingURL=index.js.map