@salesforce/storefront-next-runtime 1.0.0-alpha.0 → 1.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/ComponentContext.js +199 -4
  2. package/dist/ComponentContext.js.map +1 -1
  3. package/dist/DesignComponent.js +2 -2
  4. package/dist/DesignRegion.js +2 -2
  5. package/dist/RegionContext.js +9 -0
  6. package/dist/RegionContext.js.map +1 -0
  7. package/dist/component.types.d.ts +1 -1
  8. package/dist/config.d.ts +6 -5
  9. package/dist/config.d.ts.map +1 -1
  10. package/dist/config.js +2 -1
  11. package/dist/config.js.map +1 -1
  12. package/dist/data-store.d.ts +3 -3
  13. package/dist/data-store.d.ts.map +1 -1
  14. package/dist/defaults.d.ts +106 -0
  15. package/dist/defaults.d.ts.map +1 -0
  16. package/dist/defaults.js +67 -0
  17. package/dist/defaults.js.map +1 -0
  18. package/dist/design-data.d.ts +10 -332
  19. package/dist/design-data.d.ts.map +1 -1
  20. package/dist/design-data.js +67 -23
  21. package/dist/design-data.js.map +1 -1
  22. package/dist/design-react-core.d.ts +5 -15
  23. package/dist/design-react-core.d.ts.map +1 -1
  24. package/dist/design-react-core.js +2 -2
  25. package/dist/design-react.d.ts +2 -2
  26. package/dist/design.d.ts +2 -2
  27. package/dist/scapi.d.ts.map +1 -1
  28. package/dist/security-react.d.ts +34 -0
  29. package/dist/security-react.d.ts.map +1 -0
  30. package/dist/security-react.js +21 -0
  31. package/dist/security-react.js.map +1 -0
  32. package/dist/security.d.ts +61 -0
  33. package/dist/security.d.ts.map +1 -0
  34. package/dist/security.js +304 -0
  35. package/dist/security.js.map +1 -0
  36. package/dist/site-context.d.ts +2 -2
  37. package/dist/types3.d.ts +1 -35
  38. package/dist/types3.d.ts.map +1 -1
  39. package/package.json +15 -2
  40. package/dist/DesignFrame.js +0 -204
  41. package/dist/DesignFrame.js.map +0 -1
@@ -875,26 +875,34 @@ function validateRule(rule, locale, context) {
875
875
  * Builds a component's `data` map by walking each attribute definition and
876
876
  * picking the first non-undefined value in priority order:
877
877
  *
878
- * active-locale content → fallback-locale content → attrDef.defaultValue
878
+ * active-locale content → fallback content → attrDef.defaultValue
879
+ *
880
+ * The fallback bucket is selected whole-blob style (matching SCAPI/SFRA's
881
+ * `__data` resolution): the site-default-locale bucket if it carries any
882
+ * content, otherwise the literal-default ("default") bucket. Buckets are not
883
+ * per-key merged with each other — only the active-locale bucket layers
884
+ * per-key on top of the chosen fallback (preserving today's locale override
885
+ * semantics).
879
886
  *
880
887
  * If none of those have a value the attribute is omitted from the result.
881
888
  *
882
889
  * When no `typeDefs` are supplied, we fall back to the legacy behavior:
883
- * `{ ...nodeData, ...defaultContent, ...localeContent }`. This keeps
890
+ * `{ ...nodeData, ...fallbackContent, ...localeContent }`. This keeps
884
891
  * already-deployed manifests rendering until the manifest builder starts
885
892
  * emitting `componentTypes`.
886
893
  */
887
- function composeComponentData({ nodeData, defaultContent, localeContent, typeDefs }) {
894
+ function composeComponentData({ nodeData, literalDefaultContent, defaultContent, localeContent, typeDefs }) {
895
+ const fallbackContent = Object.keys(defaultContent).length > 0 ? defaultContent : literalDefaultContent;
888
896
  if (!typeDefs || Object.keys(typeDefs).length === 0) return {
889
897
  ...nodeData ?? {},
890
- ...defaultContent,
898
+ ...fallbackContent,
891
899
  ...localeContent
892
900
  };
893
901
  const result = {};
894
902
  for (const attrId of Object.keys(typeDefs)) {
895
903
  const def = typeDefs[attrId];
896
904
  if (Object.prototype.hasOwnProperty.call(localeContent, attrId)) result[attrId] = localeContent[attrId];
897
- else if (Object.prototype.hasOwnProperty.call(defaultContent, attrId)) result[attrId] = defaultContent[attrId];
905
+ else if (Object.prototype.hasOwnProperty.call(fallbackContent, attrId)) result[attrId] = fallbackContent[attrId];
898
906
  else if (def.defaultValue !== void 0) result[attrId] = def.defaultValue;
899
907
  }
900
908
  return result;
@@ -948,12 +956,14 @@ function processPage(page, processorContext) {
948
956
  isVisible = false;
949
957
  }
950
958
  }
959
+ const literalDefaultContent = componentInfo?.content?.default ?? {};
951
960
  const defaultContent = componentInfo?.content?.[processorContext.defaultLocale] ?? {};
952
961
  const localeContent = componentInfo?.content?.[processorContext.locale] ?? {};
953
962
  const isLocalized = Boolean(componentInfo?.content?.[processorContext.locale]);
954
963
  const typeDefs = processorContext.componentTypes?.[ctx.node.typeId]?.attributeDefinitions;
955
964
  const composedData = composeComponentData({
956
965
  nodeData: ctx.node.data,
966
+ literalDefaultContent,
957
967
  defaultContent,
958
968
  localeContent,
959
969
  typeDefs
@@ -1068,18 +1078,43 @@ const ContentAssignmentResolvers = new Map([["product", (key) => ({
1068
1078
  //#endregion
1069
1079
  //#region src/design/data/manifest/resolve-dynamic-page-id.ts
1070
1080
  /**
1081
+ * Looks up a single content assignment in the site manifest using the
1082
+ * resolver registered for the given identifier type, returning the first
1083
+ * matching `contentId` across the resolver's ordered key list. Returns
1084
+ * `null` when the identifier type has no resolver, or no key in the list
1085
+ * has an assignment for the requested aspect type.
1086
+ */
1087
+ function lookupContentAssignment(id, identifierType, aspectType, siteManifest) {
1088
+ const lookup = ContentAssignmentResolvers.get(identifierType)?.(id, siteManifest);
1089
+ if (!lookup) return null;
1090
+ for (const key of lookup.keys) {
1091
+ const assignment = siteManifest?.contentObjectAssignments?.[aspectType]?.[lookup.objectType]?.[key];
1092
+ if (assignment) return assignment.contentId;
1093
+ }
1094
+ return null;
1095
+ }
1096
+ /**
1071
1097
  * Converts a product or category identifier into a page ID by looking up
1072
1098
  * content assignments in the site manifest. For categories, the lookup
1073
1099
  * traverses the category hierarchy from the given category up to the root,
1074
1100
  * returning the first matching assignment.
1075
1101
  *
1076
- * Returns `null` if no content assignment is found for the identifier or if
1077
- * the identifier type has no registered resolver.
1102
+ * When the identifier type is `'product'` and no assignment is found, an
1103
+ * optional `categoryId` may be supplied as a fallback. The fallback is only
1104
+ * awaited and consulted after the product lookup misses, so callers that
1105
+ * resolve the product's category lazily (e.g. via a SCAPI request) don't
1106
+ * pay for the round trip on the happy path.
1107
+ *
1108
+ * Returns `null` if no content assignment is found for the identifier
1109
+ * (and the optional category fallback, when provided), or if the identifier
1110
+ * type has no registered resolver.
1078
1111
  *
1079
1112
  * @param options - The resolution options.
1080
1113
  * @param options.id - The identifier to resolve (product ID, category ID, or page ID).
1081
1114
  * @param options.identifierType - The type of identifier: `'product'`, `'category'`, or `'page'`.
1115
+ * @param options.aspectType - The aspect type to look up (e.g. `'pdp'`, `'plp'`).
1082
1116
  * @param options.siteManifest - The site manifest containing content assignments and category hierarchy.
1117
+ * @param options.categoryId - Optional fallback category ID (or a Promise resolving to one) used only when `identifierType` is `'product'` and the product lookup misses.
1083
1118
  * @returns The resolved page ID, or `null` if no assignment was found.
1084
1119
  *
1085
1120
  * @example
@@ -1104,25 +1139,31 @@ const ContentAssignmentResolvers = new Map([["product", (key) => ({
1104
1139
  * };
1105
1140
  *
1106
1141
  * // Direct match
1107
- * resolveDynamicPageId({ id: 'mens-shoes', identifierType: 'category', siteManifest });
1142
+ * await resolveDynamicPageId({ id: 'mens-shoes', identifierType: 'category', aspectType: 'plp', siteManifest });
1108
1143
  * // => 'page-mens-shoes-plp'
1109
1144
  *
1110
1145
  * // Inherited from parent category
1111
- * resolveDynamicPageId({ id: 'mens-running-shoes', identifierType: 'category', siteManifest });
1146
+ * await resolveDynamicPageId({ id: 'mens-running-shoes', identifierType: 'category', aspectType: 'plp', siteManifest });
1112
1147
  * // => 'page-mens-shoes-plp' (found via parent traversal)
1113
1148
  *
1114
- * // No assignment found
1115
- * resolveDynamicPageId({ id: 'womens-shoes', identifierType: 'category', siteManifest });
1116
- * // => null
1149
+ * // Product missing but a category fallback is provided
1150
+ * await resolveDynamicPageId({
1151
+ * id: 'unknown-product',
1152
+ * identifierType: 'product',
1153
+ * aspectType: 'plp',
1154
+ * siteManifest,
1155
+ * categoryId: 'mens-running-shoes',
1156
+ * });
1157
+ * // => 'page-mens-shoes-plp'
1117
1158
  * ```
1118
1159
  */
1119
- function resolveDynamicPageId({ id, identifierType, siteManifest, aspectType }) {
1120
- const resolvedContentAssignmentLookup = ContentAssignmentResolvers.get(identifierType)?.(id, siteManifest);
1121
- if (resolvedContentAssignmentLookup) for (const key of resolvedContentAssignmentLookup.keys) {
1122
- const contentAssignment = siteManifest?.contentObjectAssignments?.[aspectType]?.[resolvedContentAssignmentLookup.objectType]?.[key];
1123
- if (contentAssignment) return contentAssignment.contentId;
1124
- }
1125
- return null;
1160
+ async function resolveDynamicPageId({ id, identifierType, siteManifest, aspectType, categoryId }) {
1161
+ const direct = lookupContentAssignment(id, identifierType, aspectType, siteManifest);
1162
+ if (direct) return direct;
1163
+ if (identifierType !== "product" || categoryId == null) return null;
1164
+ const resolvedCategoryId = await categoryId;
1165
+ if (!resolvedCategoryId) return null;
1166
+ return lookupContentAssignment(resolvedCategoryId, "category", aspectType, siteManifest);
1126
1167
  }
1127
1168
 
1128
1169
  //#endregion
@@ -1274,6 +1315,7 @@ function applyPageMetadataOverlay(variation, locale) {
1274
1315
  * @param options.manifestStorage - Storage implementation for fetching manifests.
1275
1316
  * @param options.contextResolver - Optional async function that returns the shopper's qualifier context. Only called if a visibility rule needs it.
1276
1317
  * @param options.aspectType - The aspect type to resolve the page for when the identifier type is `'product'` or `'category'`.
1318
+ * @param options.categoryId - Optional fallback category ID (or a Promise resolving to one) used only when `identifierType` is `'product'` and the product has no content assignment for the requested aspect type. The promise is awaited lazily — the happy path never pays for it.
1277
1319
  * @param options.pruneInvisible - When `true` (default), invisible and overflow components are removed. When `false`, they are kept but marked `visible: false` for design/preview mode.
1278
1320
  * @returns The fully resolved and filtered page, or `null`.
1279
1321
  *
@@ -1311,21 +1353,23 @@ function applyPageMetadataOverlay(variation, locale) {
1311
1353
  * }
1312
1354
  * ```
1313
1355
  */
1314
- async function resolvePage({ id, identifierType, aspectType, locale, defaultLocale, manifestStorage, contextResolver, attrCtx, pruneInvisible = true }) {
1356
+ async function resolvePage({ id, identifierType, aspectType, categoryId, locale, defaultLocale, manifestStorage, contextResolver, attrCtx, pruneInvisible = true }) {
1315
1357
  let resolvedId = null;
1316
1358
  if (ContentAssignmentResolvers.has(identifierType)) {
1317
1359
  const siteManifest = await manifestStorage.getSiteManifest();
1318
1360
  RequiredError.assert(aspectType, `Aspect type is required for identifier type ${identifierType}`, (v) => !v);
1319
- resolvedId = resolveDynamicPageId({
1361
+ resolvedId = await resolveDynamicPageId({
1320
1362
  id,
1321
1363
  identifierType,
1322
1364
  aspectType,
1323
- siteManifest
1365
+ siteManifest,
1366
+ categoryId
1324
1367
  });
1325
1368
  } else resolvedId = id;
1326
1369
  if (!resolvedId) return null;
1327
1370
  const pageManifest = await manifestStorage.getPageManifest(resolvedId);
1328
1371
  if (!pageManifest) return null;
1372
+ if (pageManifest.context?.dataBindings?.length > 0) return null;
1329
1373
  const pageResults = await getPageFromManifest(pageManifest, {
1330
1374
  contextResolver,
1331
1375
  locale
@@ -1351,5 +1395,5 @@ async function resolvePage({ id, identifierType, aspectType, locale, defaultLoca
1351
1395
  }
1352
1396
 
1353
1397
  //#endregion
1354
- export { ContentAssignmentResolvers, RequiredError, getPageFromManifest, parseExpression, processPage, resolveAttributeValues, resolveComponentDataBindings, resolveDynamicPageId, resolveExpression, resolvePage, rewriteMarkup, transformComponent, transformPage, transformRegion, validateRule };
1398
+ export { RequiredError, processPage, resolvePage, transformComponent, transformPage, transformRegion, validateRule };
1355
1399
  //# sourceMappingURL=design-data.js.map